[automerger skipped] Merge "Import translations. DO NOT MERGE" am: 8069da859c -s ours
am: 58685f02a2 -s ours
am skip reason: subject contains skip directive

Change-Id: I5686dfd9f4897ce3e134932ea7016be970058658
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index b45eaff..e6fe0c4 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,7 +1,4 @@
 [Hook Scripts]
-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/LICENSE b/library/LICENSE
new file mode 100644
index 0000000..7a4a3ea
--- /dev/null
+++ b/library/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this 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.
\ No newline at end of file
diff --git a/library/gingerbread/AndroidManifest.xml b/library/gingerbread/AndroidManifest.xml
new file mode 100644
index 0000000..bf7d42f
--- /dev/null
+++ b/library/gingerbread/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2015 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.setupwizardlib">
+
+  <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="28" />
+
+</manifest>
diff --git a/library/gingerbread/res/drawable-xhdpi/suw_navbar_ic_down_arrow.png b/library/gingerbread/res/drawable-xhdpi/suw_navbar_ic_down_arrow.png
index 94016f4..a7084c5 100644
--- a/library/gingerbread/res/drawable-xhdpi/suw_navbar_ic_down_arrow.png
+++ b/library/gingerbread/res/drawable-xhdpi/suw_navbar_ic_down_arrow.png
Binary files differ
diff --git a/library/gingerbread/res/drawable-xxhdpi/suw_navbar_ic_down_arrow.png b/library/gingerbread/res/drawable-xxhdpi/suw_navbar_ic_down_arrow.png
index 17811ae..ed3c3b0 100644
--- a/library/gingerbread/res/drawable-xxhdpi/suw_navbar_ic_down_arrow.png
+++ b/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
index 97fed92..be42712 100644
--- a/library/gingerbread/res/drawable-xxhdpi/suw_navbar_ic_left_arrow.png
+++ 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
index f874955..d7bc4e3 100644
--- a/library/gingerbread/res/drawable-xxhdpi/suw_navbar_ic_right_arrow.png
+++ b/library/gingerbread/res/drawable-xxhdpi/suw_navbar_ic_right_arrow.png
Binary files differ
diff --git a/library/gingerbread/res/drawable-xxxhdpi/suw_navbar_ic_down_arrow.png b/library/gingerbread/res/drawable-xxxhdpi/suw_navbar_ic_down_arrow.png
index cb6a422..dcc1f3c 100644
--- a/library/gingerbread/res/drawable-xxxhdpi/suw_navbar_ic_down_arrow.png
+++ b/library/gingerbread/res/drawable-xxxhdpi/suw_navbar_ic_down_arrow.png
Binary files differ
diff --git a/library/gingerbread/res/layout/suw_items_switch.xml b/library/gingerbread/res/layout/suw_items_switch.xml
index 5296a62..5614044 100644
--- a/library/gingerbread/res/layout/suw_items_switch.xml
+++ b/library/gingerbread/res/layout/suw_items_switch.xml
@@ -17,17 +17,17 @@
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
-    style="@style/SuwItemContainer.Verbose"
+    style="@style/SuwItemContainer"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:clipToPadding="false"
+    android:baselineAligned="false"
     android:orientation="horizontal">
 
     <FrameLayout
         android:id="@+id/suw_items_icon_container"
         android:layout_width="@dimen/suw_items_icon_container_width"
         android:layout_height="wrap_content"
-        android:layout_gravity="top"
+        android:layout_gravity="center_vertical"
         android:gravity="start">
 
         <ImageView
@@ -41,13 +41,13 @@
     <LinearLayout
         android:layout_width="0dp"
         android:layout_height="wrap_content"
-        android:layout_marginBottom="@dimen/suw_items_verbose_padding_bottom_extra"
+        android:layout_marginBottom="@dimen/suw_items_padding_bottom_extra"
         android:layout_weight="1"
         android:orientation="vertical">
 
         <com.android.setupwizardlib.view.RichTextView
             android:id="@+id/suw_items_title"
-            style="@style/SuwItemTitle.Verbose"
+            style="@style/SuwItemTitle"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:gravity="start"
diff --git a/library/gingerbread/res/layout/suw_items_switch_verbose.xml b/library/gingerbread/res/layout/suw_items_switch_verbose.xml
new file mode 100644
index 0000000..8911acc
--- /dev/null
+++ b/library/gingerbread/res/layout/suw_items_switch_verbose.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Copyright (C) 2016 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    style="@style/SuwItemContainer.Verbose"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:baselineAligned="false"
+    android:clipToPadding="false"
+    android:orientation="horizontal"
+    tools:ignore="UnusedResources">
+    <!-- Ignore UnusedResources: can be used by clients -->
+
+    <FrameLayout
+        android:id="@+id/suw_items_icon_container"
+        android:layout_width="@dimen/suw_items_icon_container_width"
+        android:layout_height="wrap_content"
+        android:layout_gravity="top"
+        android:gravity="start">
+
+        <ImageView
+            android:id="@+id/suw_items_icon"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            tools:ignore="ContentDescription" />
+
+    </FrameLayout>
+
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="@dimen/suw_items_verbose_padding_bottom_extra"
+        android:layout_weight="1"
+        android:orientation="vertical">
+
+        <com.android.setupwizardlib.view.RichTextView
+            android:id="@+id/suw_items_title"
+            style="@style/SuwItemTitle.Verbose"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="start"
+            android:labelFor="@+id/suw_items_switch"
+            android:textAlignment="viewStart"
+            tools:ignore="UnusedAttribute" />
+
+        <com.android.setupwizardlib.view.RichTextView
+            android:id="@+id/suw_items_summary"
+            style="@style/SuwItemSummary"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="start"
+            android:textAlignment="viewStart"
+            android:visibility="gone"
+            tools:ignore="UnusedAttribute" />
+
+    </LinearLayout>
+
+    <androidx.appcompat.widget.SwitchCompat
+        android:id="@+id/suw_items_switch"
+        style="@style/SuwSwitchStyle"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_gravity="center_vertical" />
+
+</LinearLayout>
diff --git a/library/gingerbread/res/values-v21/styles.xml b/library/gingerbread/res/values-v21/styles.xml
new file mode 100644
index 0000000..3c0c254
--- /dev/null
+++ b/library/gingerbread/res/values-v21/styles.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2018 Google Inc.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<resources xmlns:tools="http://schemas.android.com/tools">
+
+    <!-- Button styles -->
+
+    <style name="SuwGlifButton.Tertiary" parent="SuwGlifButton.BaseTertiary">
+        <item name="android:fontFamily">sans-serif-medium</item>
+        <item name="textAllCaps" tools:targetApi="ice_cream_sandwich">false</item>
+    </style>
+
+</resources>
diff --git a/library/gingerbread/res/values/styles.xml b/library/gingerbread/res/values/styles.xml
index 241f037..b008e1e 100644
--- a/library/gingerbread/res/values/styles.xml
+++ b/library/gingerbread/res/values/styles.xml
@@ -41,6 +41,7 @@
         <item name="listPreferredItemPaddingRight">?attr/suwMarginSides</item>
         <item name="suwButtonAllCaps">true</item>
         <item name="suwButtonFontFamily">sans-serif</item>
+        <item name="suwButtonHighlightAlpha">0.24</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>
@@ -75,6 +76,7 @@
         <item name="listPreferredItemPaddingRight">?attr/suwMarginSides</item>
         <item name="suwButtonAllCaps">true</item>
         <item name="suwButtonFontFamily">sans-serif</item>
+        <item name="suwButtonHighlightAlpha">0.24</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>
@@ -109,7 +111,8 @@
         <item name="listPreferredItemPaddingRight">?attr/suwMarginSides</item>
         <item name="suwButtonAllCaps">true</item>
         <item name="suwButtonCornerRadius">@dimen/suw_glif_button_corner_radius</item>
-        <item name="suwButtonFontFamily">sans-serif</item>
+        <item name="suwButtonFontFamily">sans-serif-medium</item>
+        <item name="suwButtonHighlightAlpha">0.24</item>
         <item name="suwColorPrimary">?attr/colorPrimary</item>
         <item name="suwFillContentLayoutStyle">@style/SuwFillContentLayout</item>
         <item name="suwDividerInsetEnd">0dp</item>
@@ -148,7 +151,8 @@
         <item name="listPreferredItemPaddingRight">?attr/suwMarginSides</item>
         <item name="suwButtonAllCaps">true</item>
         <item name="suwButtonCornerRadius">@dimen/suw_glif_button_corner_radius</item>
-        <item name="suwButtonFontFamily">sans-serif</item>
+        <item name="suwButtonFontFamily">sans-serif-medium</item>
+        <item name="suwButtonHighlightAlpha">0.12</item>
         <item name="suwColorPrimary">?attr/colorPrimary</item>
         <item name="suwFillContentLayoutStyle">@style/SuwFillContentLayout</item>
         <item name="suwDividerInsetEnd">0dp</item>
@@ -262,6 +266,13 @@
         <item name="colorControlHighlight">@color/suw_flat_button_highlight</item>
     </style>
 
+    <!-- Ignore UnusedResources: used by clients -->
+    <style name="SuwGlifButton.Tertiary"
+        parent="SuwGlifButton.BaseTertiary"
+        tools:ignore="UnusedResources">
+        <item name="textAllCaps" tools:targetApi="ice_cream_sandwich">false</item>
+    </style>
+
     <!-- Card layout (for tablets) -->
 
     <style name="TextAppearance.SuwCardTitle" parent="@style/TextAppearance.AppCompat.Display1">
@@ -279,6 +290,10 @@
     <style name="SuwFourColorIndeterminateProgressBar" parent="SuwBase.ProgressBarLarge">
         <item name="android:layout_gravity">center</item>
         <item name="android:indeterminate">true</item>
+        <item name="android:paddingEnd" tools:targetApi="17" >@dimen/suw_glif_progress_bar_padding</item>
+        <item name="android:paddingLeft">@dimen/suw_glif_progress_bar_padding</item>
+        <item name="android:paddingRight">@dimen/suw_glif_progress_bar_padding</item>
+        <item name="android:paddingStart" tools:targetApi="17" >@dimen/suw_glif_progress_bar_padding</item>
     </style>
 
     <!-- Navigation bar styles -->
diff --git a/library/gingerbread/src/com/android/setupwizardlib/items/ExpandableSwitchItem.java b/library/gingerbread/src/com/android/setupwizardlib/items/ExpandableSwitchItem.java
index 71d1bb6..4b9347f 100644
--- a/library/gingerbread/src/com/android/setupwizardlib/items/ExpandableSwitchItem.java
+++ b/library/gingerbread/src/com/android/setupwizardlib/items/ExpandableSwitchItem.java
@@ -28,7 +28,6 @@
 import android.view.View.OnClickListener;
 import android.widget.CompoundButton.OnCheckedChangeListener;
 import android.widget.TextView;
-
 import com.android.setupwizardlib.R;
 import com.android.setupwizardlib.view.CheckableLinearLayout;
 
@@ -37,140 +36,130 @@
  * summary, and when that is clicked, will expand to show a longer summary. The end (right for LTR)
  * side is a switch which can be toggled by the user.
  *
- * Note: It is highly recommended to use this item with recycler view rather than list view, because
- * list view draws the touch ripple effect on top of the item, rather than letting the item handle
- * it. Therefore you might see a double-ripple, one for the expandable area and one for the entire
- * list item, when using this in list view.
+ * <p>Note: It is highly recommended to use this item with recycler view rather than list view,
+ * because list view draws the touch ripple effect on top of the item, rather than letting the item
+ * handle it. Therefore you might see a double-ripple, one for the expandable area and one for the
+ * entire list item, when using this in list view.
  */
 public class ExpandableSwitchItem extends SwitchItem
-        implements OnCheckedChangeListener, OnClickListener {
+    implements OnCheckedChangeListener, OnClickListener {
 
-    private CharSequence mCollapsedSummary;
-    private CharSequence mExpandedSummary;
-    private boolean mIsExpanded = false;
+  private CharSequence collapsedSummary;
+  private CharSequence expandedSummary;
+  private boolean isExpanded = false;
 
-    public ExpandableSwitchItem() {
-        super();
+  public ExpandableSwitchItem() {
+    super();
+  }
+
+  public ExpandableSwitchItem(Context context, AttributeSet attrs) {
+    super(context, attrs);
+    final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SuwExpandableSwitchItem);
+    collapsedSummary = a.getText(R.styleable.SuwExpandableSwitchItem_suwCollapsedSummary);
+    expandedSummary = a.getText(R.styleable.SuwExpandableSwitchItem_suwExpandedSummary);
+    a.recycle();
+  }
+
+  @Override
+  protected int getDefaultLayoutResource() {
+    return R.layout.suw_items_expandable_switch;
+  }
+
+  @Override
+  public CharSequence getSummary() {
+    return isExpanded ? getExpandedSummary() : getCollapsedSummary();
+  }
+
+  /** @return True if the item is currently expanded. */
+  public boolean isExpanded() {
+    return isExpanded;
+  }
+
+  /** Sets whether the item should be expanded. */
+  public void setExpanded(boolean expanded) {
+    if (isExpanded == expanded) {
+      return;
+    }
+    isExpanded = expanded;
+    notifyItemChanged();
+  }
+
+  /** @return The summary shown when in collapsed state. */
+  public CharSequence getCollapsedSummary() {
+    return collapsedSummary;
+  }
+
+  /**
+   * Sets the summary text shown when the item is collapsed. Corresponds to the {@code
+   * app:suwCollapsedSummary} XML attribute.
+   */
+  public void setCollapsedSummary(CharSequence collapsedSummary) {
+    this.collapsedSummary = collapsedSummary;
+    if (!isExpanded()) {
+      notifyChanged();
+    }
+  }
+
+  /** @return The summary shown when in expanded state. */
+  public CharSequence getExpandedSummary() {
+    return expandedSummary;
+  }
+
+  /**
+   * Sets the summary text shown when the item is expanded. Corresponds to the {@code
+   * app:suwExpandedSummary} XML attribute.
+   */
+  public void setExpandedSummary(CharSequence expandedSummary) {
+    this.expandedSummary = expandedSummary;
+    if (isExpanded()) {
+      notifyChanged();
+    }
+  }
+
+  @Override
+  public void onBindView(View view) {
+    // TODO: If it is possible to detect, log a warning if this is being used with ListView.
+    super.onBindView(view);
+    View content = view.findViewById(R.id.suw_items_expandable_switch_content);
+    content.setOnClickListener(this);
+
+    if (content instanceof CheckableLinearLayout) {
+      ((CheckableLinearLayout) content).setChecked(isExpanded());
     }
 
-    public ExpandableSwitchItem(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        final TypedArray a =
-                context.obtainStyledAttributes(attrs, R.styleable.SuwExpandableSwitchItem);
-        mCollapsedSummary = a.getText(R.styleable.SuwExpandableSwitchItem_suwCollapsedSummary);
-        mExpandedSummary = a.getText(R.styleable.SuwExpandableSwitchItem_suwExpandedSummary);
-        a.recycle();
-    }
+    tintCompoundDrawables(view);
 
-    @Override
-    protected int getDefaultLayoutResource() {
-        return R.layout.suw_items_expandable_switch;
-    }
+    // Expandable switch item has focusability on the expandable layout on the left, and the
+    // switch on the right, but not the item itself.
+    view.setFocusable(false);
+  }
 
-    @Override
-    public CharSequence getSummary() {
-        return mIsExpanded ? getExpandedSummary() : getCollapsedSummary();
-    }
+  @Override
+  public void onClick(View v) {
+    setExpanded(!isExpanded());
+  }
 
-    /**
-     * @return True if the item is currently expanded.
-     */
-    public boolean isExpanded() {
-        return mIsExpanded;
-    }
+  // Tint the expand arrow with the text color
+  private void tintCompoundDrawables(View view) {
+    final TypedArray a =
+        view.getContext().obtainStyledAttributes(new int[] {android.R.attr.textColorPrimary});
+    final ColorStateList tintColor = a.getColorStateList(0);
+    a.recycle();
 
-    /**
-     * Sets whether the item should be expanded.
-     */
-    public void setExpanded(boolean expanded) {
-        if (mIsExpanded == expanded) {
-            return;
+    if (tintColor != null) {
+      TextView titleView = (TextView) view.findViewById(R.id.suw_items_title);
+      for (Drawable drawable : titleView.getCompoundDrawables()) {
+        if (drawable != null) {
+          drawable.setColorFilter(tintColor.getDefaultColor(), Mode.SRC_IN);
         }
-        mIsExpanded = expanded;
-        notifyItemChanged();
-    }
-
-    /**
-     * @return The summary shown when in collapsed state.
-     */
-    public CharSequence getCollapsedSummary() {
-        return mCollapsedSummary;
-    }
-
-    /**
-     * Sets the summary text shown when the item is collapsed. Corresponds to the
-     * {@code app:suwCollapsedSummary} XML attribute.
-     */
-    public void setCollapsedSummary(CharSequence collapsedSummary) {
-        mCollapsedSummary = collapsedSummary;
-        if (!isExpanded()) {
-            notifyChanged();
+      }
+      if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
+        for (Drawable drawable : titleView.getCompoundDrawablesRelative()) {
+          if (drawable != null) {
+            drawable.setColorFilter(tintColor.getDefaultColor(), Mode.SRC_IN);
+          }
         }
+      }
     }
-
-    /**
-     * @return The summary shown when in expanded state.
-     */
-    public CharSequence getExpandedSummary() {
-        return mExpandedSummary;
-    }
-
-    /**
-     * Sets the summary text shown when the item is expanded. Corresponds to the
-     * {@code app:suwExpandedSummary} XML attribute.
-     */
-    public void setExpandedSummary(CharSequence expandedSummary) {
-        mExpandedSummary = expandedSummary;
-        if (isExpanded()) {
-            notifyChanged();
-        }
-    }
-
-    @Override
-    public void onBindView(View view) {
-        // TODO: If it is possible to detect, log a warning if this is being used with ListView.
-        super.onBindView(view);
-        View content = view.findViewById(R.id.suw_items_expandable_switch_content);
-        content.setOnClickListener(this);
-
-        if (content instanceof CheckableLinearLayout) {
-            ((CheckableLinearLayout) content).setChecked(isExpanded());
-        }
-
-        tintCompoundDrawables(view);
-
-        // Expandable switch item has focusability on the expandable layout on the left, and the
-        // switch on the right, but not the item itself.
-        view.setFocusable(false);
-    }
-
-    @Override
-    public void onClick(View v) {
-        setExpanded(!isExpanded());
-    }
-
-    // Tint the expand arrow with the text color
-    private void tintCompoundDrawables(View view) {
-        final TypedArray a = view.getContext()
-                .obtainStyledAttributes(new int[] {android.R.attr.textColorPrimary});
-        final ColorStateList tintColor = a.getColorStateList(0);
-        a.recycle();
-
-        if (tintColor != null) {
-            TextView titleView = (TextView) view.findViewById(R.id.suw_items_title);
-            for (Drawable drawable : titleView.getCompoundDrawables()) {
-                if (drawable != null) {
-                    drawable.setColorFilter(tintColor.getDefaultColor(), Mode.SRC_IN);
-                }
-            }
-            if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
-                for (Drawable drawable : titleView.getCompoundDrawablesRelative()) {
-                    if (drawable != null) {
-                        drawable.setColorFilter(tintColor.getDefaultColor(), Mode.SRC_IN);
-                    }
-                }
-            }
-
-        }
-    }
+  }
 }
diff --git a/library/gingerbread/src/com/android/setupwizardlib/items/SwitchItem.java b/library/gingerbread/src/com/android/setupwizardlib/items/SwitchItem.java
index 8d828ac..56fa742 100644
--- a/library/gingerbread/src/com/android/setupwizardlib/items/SwitchItem.java
+++ b/library/gingerbread/src/com/android/setupwizardlib/items/SwitchItem.java
@@ -18,12 +18,10 @@
 
 import android.content.Context;
 import android.content.res.TypedArray;
+import androidx.appcompat.widget.SwitchCompat;
 import android.util.AttributeSet;
 import android.view.View;
 import android.widget.CompoundButton;
-
-import androidx.appcompat.widget.SwitchCompat;
-
 import com.android.setupwizardlib.R;
 
 /**
@@ -34,101 +32,93 @@
  */
 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;
+  /** Listener for check state changes of this switch item. */
+  public interface OnCheckedChangeListener {
 
     /**
-     * Creates a default switch item.
-     */
-    public SwitchItem() {
-        super();
-    }
-
-    /**
-     * Creates a switch item. This constructor is used for inflation from XML.
+     * Callback when checked state of a {@link SwitchItem} is changed.
      *
-     * @param context The context which this item is inflated in.
-     * @param attrs The XML attributes defined on the item.
+     * @see #setOnCheckedChangeListener(OnCheckedChangeListener)
      */
-    public SwitchItem(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SuwSwitchItem);
-        mChecked = a.getBoolean(R.styleable.SuwSwitchItem_android_checked, false);
-        a.recycle();
-    }
+    void onCheckedChange(SwitchItem item, boolean isChecked);
+  }
 
-    /**
-     * Sets whether this item should be checked.
-     */
-    public void setChecked(boolean checked) {
-        if (mChecked != checked) {
-            mChecked = checked;
-            notifyItemChanged();
-            if (mListener != null) {
-                mListener.onCheckedChange(this, checked);
-            }
-        }
-    }
+  private boolean checked = false;
+  private OnCheckedChangeListener listener;
 
-    /**
-     * @return True if this switch item is currently checked.
-     */
-    public boolean isChecked() {
-        return mChecked;
-    }
+  /** Creates a default switch item. */
+  public SwitchItem() {
+    super();
+  }
 
-    @Override
-    protected int getDefaultLayoutResource() {
-        return R.layout.suw_items_switch;
-    }
+  /**
+   * 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);
+    checked = a.getBoolean(R.styleable.SuwSwitchItem_android_checked, false);
+    a.recycle();
+  }
 
-    /**
-     * Toggle the checked state of the switch, without invalidating the entire item.
-     *
-     * @param view The root view of this item, typically from the argument of onItemClick.
-     */
-    public void toggle(View view) {
-        mChecked = !mChecked;
-        final SwitchCompat switchView = (SwitchCompat) view.findViewById(R.id.suw_items_switch);
-        switchView.setChecked(mChecked);
+  /** Sets whether this item should be checked. */
+  public void setChecked(boolean checked) {
+    if (this.checked != checked) {
+      this.checked = checked;
+      notifyItemChanged();
+      if (listener != null) {
+        listener.onCheckedChange(this, checked);
+      }
     }
+  }
 
-    @Override
-    public void onBindView(View view) {
-        super.onBindView(view);
-        final SwitchCompat switchView = (SwitchCompat) view.findViewById(R.id.suw_items_switch);
-        switchView.setOnCheckedChangeListener(null);
-        switchView.setChecked(mChecked);
-        switchView.setOnCheckedChangeListener(this);
-        switchView.setEnabled(isEnabled());
-    }
+  /** @return True if this switch item is currently checked. */
+  public boolean isChecked() {
+    return checked;
+  }
 
-    /**
-     * 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;
-    }
+  @Override
+  protected int getDefaultLayoutResource() {
+    return R.layout.suw_items_switch;
+  }
 
-    @Override
-    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
-        mChecked = isChecked;
-        if (mListener != null) {
-            mListener.onCheckedChange(this, isChecked);
-        }
+  /**
+   * Toggle the checked state of the switch, without invalidating the entire item.
+   *
+   * @param view The root view of this item, typically from the argument of onItemClick.
+   */
+  public void toggle(View view) {
+    checked = !checked;
+    final SwitchCompat switchView = (SwitchCompat) view.findViewById(R.id.suw_items_switch);
+    switchView.setChecked(checked);
+  }
+
+  @Override
+  public void onBindView(View view) {
+    super.onBindView(view);
+    final SwitchCompat switchView = (SwitchCompat) view.findViewById(R.id.suw_items_switch);
+    switchView.setOnCheckedChangeListener(null);
+    switchView.setChecked(checked);
+    switchView.setOnCheckedChangeListener(this);
+    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) {
+    this.listener = listener;
+  }
+
+  @Override
+  public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+    checked = isChecked;
+    if (listener != null) {
+      listener.onCheckedChange(this, isChecked);
     }
+  }
 }
diff --git a/library/gingerbread/src/com/android/setupwizardlib/util/LinkAccessibilityHelper.java b/library/gingerbread/src/com/android/setupwizardlib/util/LinkAccessibilityHelper.java
index 2a378e8..c416a9e 100644
--- a/library/gingerbread/src/com/android/setupwizardlib/util/LinkAccessibilityHelper.java
+++ b/library/gingerbread/src/com/android/setupwizardlib/util/LinkAccessibilityHelper.java
@@ -19,6 +19,12 @@
 import android.graphics.Rect;
 import android.os.Build;
 import android.os.Bundle;
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+import androidx.core.view.AccessibilityDelegateCompat;
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
+import androidx.core.view.accessibility.AccessibilityNodeProviderCompat;
+import androidx.customview.widget.ExploreByTouchHelper;
 import android.text.Layout;
 import android.text.Spanned;
 import android.text.style.ClickableSpan;
@@ -28,14 +34,6 @@
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.VisibleForTesting;
-import androidx.core.view.AccessibilityDelegateCompat;
-import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
-import androidx.core.view.accessibility.AccessibilityNodeProviderCompat;
-import androidx.customview.widget.ExploreByTouchHelper;
-
 import java.util.List;
 
 /**
@@ -46,6 +44,7 @@
  * support for ClickableSpan accessibility.
  *
  * <p>Sample usage:
+ *
  * <pre>
  * LinkAccessibilityHelper mAccessibilityHelper;
  *
@@ -68,262 +67,260 @@
  */
 public class LinkAccessibilityHelper extends AccessibilityDelegateCompat {
 
-    private static final String TAG = "LinkAccessibilityHelper";
+  private static final String TAG = "LinkAccessibilityHelper";
 
-    private final AccessibilityDelegateCompat mDelegate;
+  private final AccessibilityDelegateCompat delegate;
 
-    public LinkAccessibilityHelper(TextView view) {
-        this(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
-                // Platform support was added in O. This helper will be no-op
-                ? new AccessibilityDelegateCompat()
-                // Pre-O, we extend ExploreByTouchHelper to expose a virtual view hierarchy
-                : new PreOLinkAccessibilityHelper(view));
-    }
+  public LinkAccessibilityHelper(TextView view) {
+    this(
+        Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
+            // Platform support was added in O. This helper will be no-op
+            ? new AccessibilityDelegateCompat()
+            // Pre-O, we extend ExploreByTouchHelper to expose a virtual view hierarchy
+            : new PreOLinkAccessibilityHelper(view));
+  }
 
-    @VisibleForTesting
-    LinkAccessibilityHelper(@NonNull AccessibilityDelegateCompat delegate) {
-        mDelegate = delegate;
+  @VisibleForTesting
+  LinkAccessibilityHelper(@NonNull AccessibilityDelegateCompat delegate) {
+    this.delegate = delegate;
+  }
+
+  @Override
+  public void sendAccessibilityEvent(View host, int eventType) {
+    delegate.sendAccessibilityEvent(host, eventType);
+  }
+
+  @Override
+  public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event) {
+    delegate.sendAccessibilityEventUnchecked(host, event);
+  }
+
+  @Override
+  public boolean dispatchPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
+    return delegate.dispatchPopulateAccessibilityEvent(host, event);
+  }
+
+  @Override
+  public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
+    delegate.onPopulateAccessibilityEvent(host, event);
+  }
+
+  @Override
+  public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
+    delegate.onInitializeAccessibilityEvent(host, event);
+  }
+
+  @Override
+  public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
+    delegate.onInitializeAccessibilityNodeInfo(host, info);
+  }
+
+  @Override
+  public boolean onRequestSendAccessibilityEvent(
+      ViewGroup host, View child, AccessibilityEvent event) {
+    return delegate.onRequestSendAccessibilityEvent(host, child, event);
+  }
+
+  @Override
+  public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(View host) {
+    return delegate.getAccessibilityNodeProvider(host);
+  }
+
+  @Override
+  public boolean performAccessibilityAction(View host, int action, Bundle args) {
+    return delegate.performAccessibilityAction(host, action, args);
+  }
+
+  /**
+   * Dispatches hover event to the virtual view hierarchy. This method should be called in {@link
+   * View#dispatchHoverEvent(MotionEvent)}.
+   *
+   * @see ExploreByTouchHelper#dispatchHoverEvent(MotionEvent)
+   */
+  public boolean dispatchHoverEvent(MotionEvent event) {
+    return delegate instanceof ExploreByTouchHelper
+        && ((ExploreByTouchHelper) delegate).dispatchHoverEvent(event);
+  }
+
+  @VisibleForTesting
+  static class PreOLinkAccessibilityHelper extends ExploreByTouchHelper {
+
+    private final Rect tempRect = new Rect();
+    private final TextView view;
+
+    PreOLinkAccessibilityHelper(TextView view) {
+      super(view);
+      this.view = view;
     }
 
     @Override
-    public void sendAccessibilityEvent(View host, int eventType) {
-        mDelegate.sendAccessibilityEvent(host, eventType);
-    }
-
-    @Override
-    public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event) {
-        mDelegate.sendAccessibilityEventUnchecked(host, event);
-    }
-
-    @Override
-    public boolean dispatchPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
-        return mDelegate.dispatchPopulateAccessibilityEvent(host, event);
-    }
-
-    @Override
-    public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
-        mDelegate.onPopulateAccessibilityEvent(host, event);
-    }
-
-    @Override
-    public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
-        mDelegate.onInitializeAccessibilityEvent(host, event);
-    }
-
-    @Override
-    public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
-        mDelegate.onInitializeAccessibilityNodeInfo(host, info);
-    }
-
-    @Override
-    public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child,
-            AccessibilityEvent event) {
-        return mDelegate.onRequestSendAccessibilityEvent(host, child, event);
-    }
-
-    @Override
-    public AccessibilityNodeProviderCompat getAccessibilityNodeProvider(View host) {
-        return mDelegate.getAccessibilityNodeProvider(host);
-    }
-
-    @Override
-    public boolean performAccessibilityAction(View host, int action, Bundle args) {
-        return mDelegate.performAccessibilityAction(host, action, args);
-    }
-
-    /**
-     * Dispatches hover event to the virtual view hierarchy. This method should be called in
-     * {@link View#dispatchHoverEvent(MotionEvent)}.
-     *
-     * @see ExploreByTouchHelper#dispatchHoverEvent(MotionEvent)
-     */
-    public final boolean dispatchHoverEvent(MotionEvent event) {
-        return mDelegate instanceof ExploreByTouchHelper
-                && ((ExploreByTouchHelper) mDelegate).dispatchHoverEvent(event);
-    }
-
-    @VisibleForTesting
-    static class PreOLinkAccessibilityHelper extends ExploreByTouchHelper {
-
-        private final Rect mTempRect = new Rect();
-        private final TextView mView;
-
-        PreOLinkAccessibilityHelper(TextView view) {
-            super(view);
-            mView = view;
+    protected int getVirtualViewAt(float x, float y) {
+      final CharSequence text = view.getText();
+      if (text instanceof Spanned) {
+        final Spanned spannedText = (Spanned) text;
+        final int offset = getOffsetForPosition(view, x, y);
+        ClickableSpan[] linkSpans = spannedText.getSpans(offset, offset, ClickableSpan.class);
+        if (linkSpans.length == 1) {
+          ClickableSpan linkSpan = linkSpans[0];
+          return spannedText.getSpanStart(linkSpan);
         }
+      }
+      return ExploreByTouchHelper.INVALID_ID;
+    }
 
-        @Override
-        protected int getVirtualViewAt(float x, float y) {
-            final CharSequence text = mView.getText();
-            if (text instanceof Spanned) {
-                final Spanned spannedText = (Spanned) text;
-                final int offset = getOffsetForPosition(mView, x, y);
-                ClickableSpan[] linkSpans =
-                        spannedText.getSpans(offset, offset, ClickableSpan.class);
-                if (linkSpans.length == 1) {
-                    ClickableSpan linkSpan = linkSpans[0];
-                    return spannedText.getSpanStart(linkSpan);
-                }
-            }
-            return ExploreByTouchHelper.INVALID_ID;
+    @Override
+    protected void getVisibleVirtualViews(List<Integer> virtualViewIds) {
+      final CharSequence text = view.getText();
+      if (text instanceof Spanned) {
+        final Spanned spannedText = (Spanned) text;
+        ClickableSpan[] linkSpans =
+            spannedText.getSpans(0, spannedText.length(), ClickableSpan.class);
+        for (ClickableSpan span : linkSpans) {
+          virtualViewIds.add(spannedText.getSpanStart(span));
         }
+      }
+    }
 
-        @Override
-        protected void getVisibleVirtualViews(List<Integer> virtualViewIds) {
-            final CharSequence text = mView.getText();
-            if (text instanceof Spanned) {
-                final Spanned spannedText = (Spanned) text;
-                ClickableSpan[] linkSpans = spannedText.getSpans(0, spannedText.length(),
-                        ClickableSpan.class);
-                for (ClickableSpan span : linkSpans) {
-                    virtualViewIds.add(spannedText.getSpanStart(span));
-                }
-            }
+    @Override
+    protected void onPopulateEventForVirtualView(int virtualViewId, AccessibilityEvent event) {
+      final ClickableSpan span = getSpanForOffset(virtualViewId);
+      if (span != null) {
+        event.setContentDescription(getTextForSpan(span));
+      } else {
+        Log.e(TAG, "LinkSpan is null for offset: " + virtualViewId);
+        event.setContentDescription(view.getText());
+      }
+    }
+
+    @Override
+    protected void onPopulateNodeForVirtualView(
+        int virtualViewId, AccessibilityNodeInfoCompat info) {
+      final ClickableSpan span = getSpanForOffset(virtualViewId);
+      if (span != null) {
+        info.setContentDescription(getTextForSpan(span));
+      } else {
+        Log.e(TAG, "LinkSpan is null for offset: " + virtualViewId);
+        info.setContentDescription(view.getText());
+      }
+      info.setFocusable(true);
+      info.setClickable(true);
+      getBoundsForSpan(span, tempRect);
+      if (tempRect.isEmpty()) {
+        Log.e(TAG, "LinkSpan bounds is empty for: " + virtualViewId);
+        tempRect.set(0, 0, 1, 1);
+      }
+      info.setBoundsInParent(tempRect);
+      info.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK);
+    }
+
+    @Override
+    protected boolean onPerformActionForVirtualView(
+        int virtualViewId, int action, Bundle arguments) {
+      if (action == AccessibilityNodeInfoCompat.ACTION_CLICK) {
+        ClickableSpan span = getSpanForOffset(virtualViewId);
+        if (span != null) {
+          span.onClick(view);
+          return true;
+        } else {
+          Log.e(TAG, "LinkSpan is null for offset: " + virtualViewId);
         }
+      }
+      return false;
+    }
 
-        @Override
-        protected void onPopulateEventForVirtualView(int virtualViewId, AccessibilityEvent event) {
-            final ClickableSpan span = getSpanForOffset(virtualViewId);
-            if (span != null) {
-                event.setContentDescription(getTextForSpan(span));
+    private ClickableSpan getSpanForOffset(int offset) {
+      CharSequence text = view.getText();
+      if (text instanceof Spanned) {
+        Spanned spannedText = (Spanned) text;
+        ClickableSpan[] spans = spannedText.getSpans(offset, offset, ClickableSpan.class);
+        if (spans.length == 1) {
+          return spans[0];
+        }
+      }
+      return null;
+    }
+
+    private CharSequence getTextForSpan(ClickableSpan span) {
+      CharSequence text = view.getText();
+      if (text instanceof Spanned) {
+        Spanned spannedText = (Spanned) text;
+        return spannedText.subSequence(
+            spannedText.getSpanStart(span), spannedText.getSpanEnd(span));
+      }
+      return text;
+    }
+
+    // Find the bounds of a span. If it spans multiple lines, it will only return the bounds for
+    // the section on the first line.
+    private Rect getBoundsForSpan(ClickableSpan span, Rect outRect) {
+      CharSequence text = view.getText();
+      outRect.setEmpty();
+      if (text instanceof Spanned) {
+        final Layout layout = view.getLayout();
+        if (layout != null) {
+          Spanned spannedText = (Spanned) text;
+          final int spanStart = spannedText.getSpanStart(span);
+          final int spanEnd = spannedText.getSpanEnd(span);
+          final float xStart = layout.getPrimaryHorizontal(spanStart);
+          final float xEnd = layout.getPrimaryHorizontal(spanEnd);
+          final int lineStart = layout.getLineForOffset(spanStart);
+          final int lineEnd = layout.getLineForOffset(spanEnd);
+          layout.getLineBounds(lineStart, outRect);
+          if (lineEnd == lineStart) {
+            // If the span is on a single line, adjust both the left and right bounds
+            // so outrect is exactly bounding the span.
+            outRect.left = (int) Math.min(xStart, xEnd);
+            outRect.right = (int) Math.max(xStart, xEnd);
+          } else {
+            // If the span wraps across multiple lines, only use the first line (as
+            // returned by layout.getLineBounds above), and adjust the "start" of
+            // outrect to where the span starts, leaving the "end" of outrect at the end
+            // of the line. ("start" being left for LTR, and right for RTL)
+            if (layout.getParagraphDirection(lineStart) == Layout.DIR_RIGHT_TO_LEFT) {
+              outRect.right = (int) xStart;
             } else {
-                Log.e(TAG, "LinkSpan is null for offset: " + virtualViewId);
-                event.setContentDescription(mView.getText());
+              outRect.left = (int) xStart;
             }
+          }
+
+          // Offset for padding
+          outRect.offset(view.getTotalPaddingLeft(), view.getTotalPaddingTop());
         }
-
-        @Override
-        protected void onPopulateNodeForVirtualView(
-                int virtualViewId,
-                AccessibilityNodeInfoCompat info) {
-            final ClickableSpan span = getSpanForOffset(virtualViewId);
-            if (span != null) {
-                info.setContentDescription(getTextForSpan(span));
-            } else {
-                Log.e(TAG, "LinkSpan is null for offset: " + virtualViewId);
-                info.setContentDescription(mView.getText());
-            }
-            info.setFocusable(true);
-            info.setClickable(true);
-            getBoundsForSpan(span, mTempRect);
-            if (mTempRect.isEmpty()) {
-                Log.e(TAG, "LinkSpan bounds is empty for: " + virtualViewId);
-                mTempRect.set(0, 0, 1, 1);
-            }
-            info.setBoundsInParent(mTempRect);
-            info.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK);
-        }
-
-        @Override
-        protected boolean onPerformActionForVirtualView(
-                int virtualViewId,
-                int action,
-                Bundle arguments) {
-            if (action == AccessibilityNodeInfoCompat.ACTION_CLICK) {
-                ClickableSpan span = getSpanForOffset(virtualViewId);
-                if (span != null) {
-                    span.onClick(mView);
-                    return true;
-                } else {
-                    Log.e(TAG, "LinkSpan is null for offset: " + virtualViewId);
-                }
-            }
-            return false;
-        }
-
-        private ClickableSpan getSpanForOffset(int offset) {
-            CharSequence text = mView.getText();
-            if (text instanceof Spanned) {
-                Spanned spannedText = (Spanned) text;
-                ClickableSpan[] spans = spannedText.getSpans(offset, offset, ClickableSpan.class);
-                if (spans.length == 1) {
-                    return spans[0];
-                }
-            }
-            return null;
-        }
-
-        private CharSequence getTextForSpan(ClickableSpan span) {
-            CharSequence text = mView.getText();
-            if (text instanceof Spanned) {
-                Spanned spannedText = (Spanned) text;
-                return spannedText.subSequence(
-                        spannedText.getSpanStart(span),
-                        spannedText.getSpanEnd(span));
-            }
-            return text;
-        }
-
-        // Find the bounds of a span. If it spans multiple lines, it will only return the bounds for
-        // the section on the first line.
-        private Rect getBoundsForSpan(ClickableSpan span, Rect outRect) {
-            CharSequence text = mView.getText();
-            outRect.setEmpty();
-            if (text instanceof Spanned) {
-                final Layout layout = mView.getLayout();
-                if (layout != null) {
-                    Spanned spannedText = (Spanned) text;
-                    final int spanStart = spannedText.getSpanStart(span);
-                    final int spanEnd = spannedText.getSpanEnd(span);
-                    final float xStart = layout.getPrimaryHorizontal(spanStart);
-                    final float xEnd = layout.getPrimaryHorizontal(spanEnd);
-                    final int lineStart = layout.getLineForOffset(spanStart);
-                    final int lineEnd = layout.getLineForOffset(spanEnd);
-                    layout.getLineBounds(lineStart, outRect);
-                    if (lineEnd == lineStart) {
-                        // If the span is on a single line, adjust both the left and right bounds
-                        // so outrect is exactly bounding the span.
-                        outRect.left = (int) Math.min(xStart, xEnd);
-                        outRect.right = (int) Math.max(xStart, xEnd);
-                    } else {
-                        // If the span wraps across multiple lines, only use the first line (as
-                        // returned by layout.getLineBounds above), and adjust the "start" of
-                        // outrect to where the span starts, leaving the "end" of outrect at the end
-                        // of the line. ("start" being left for LTR, and right for RTL)
-                        if (layout.getParagraphDirection(lineStart) == Layout.DIR_RIGHT_TO_LEFT) {
-                            outRect.right = (int) xStart;
-                        } else {
-                            outRect.left = (int) xStart;
-                        }
-                    }
-
-                    // Offset for padding
-                    outRect.offset(mView.getTotalPaddingLeft(), mView.getTotalPaddingTop());
-                }
-            }
-            return outRect;
-        }
-
-        // Compat implementation of TextView#getOffsetForPosition().
-
-        private static int getOffsetForPosition(TextView view, float x, float y) {
-            if (view.getLayout() == null) return -1;
-            final int line = getLineAtCoordinate(view, y);
-            return getOffsetAtCoordinate(view, line, x);
-        }
-
-        private static float convertToLocalHorizontalCoordinate(TextView view, float x) {
-            x -= view.getTotalPaddingLeft();
-            // Clamp the position to inside of the view.
-            x = Math.max(0.0f, x);
-            x = Math.min(view.getWidth() - view.getTotalPaddingRight() - 1, x);
-            x += view.getScrollX();
-            return x;
-        }
-
-        private static int getLineAtCoordinate(TextView view, float y) {
-            y -= view.getTotalPaddingTop();
-            // Clamp the position to inside of the view.
-            y = Math.max(0.0f, y);
-            y = Math.min(view.getHeight() - view.getTotalPaddingBottom() - 1, y);
-            y += view.getScrollY();
-            return view.getLayout().getLineForVertical((int) y);
-        }
-
-        private static int getOffsetAtCoordinate(TextView view, int line, float x) {
-            x = convertToLocalHorizontalCoordinate(view, x);
-            return view.getLayout().getOffsetForHorizontal(line, x);
-        }
+      }
+      return outRect;
     }
+
+    // Compat implementation of TextView#getOffsetForPosition().
+
+    private static int getOffsetForPosition(TextView view, float x, float y) {
+      if (view.getLayout() == null) {
+        return -1;
+      }
+      final int line = getLineAtCoordinate(view, y);
+      return getOffsetAtCoordinate(view, line, x);
+    }
+
+    private static float convertToLocalHorizontalCoordinate(TextView view, float x) {
+      x -= view.getTotalPaddingLeft();
+      // Clamp the position to inside of the view.
+      x = Math.max(0.0f, x);
+      x = Math.min(view.getWidth() - view.getTotalPaddingRight() - 1, x);
+      x += view.getScrollX();
+      return x;
+    }
+
+    private static int getLineAtCoordinate(TextView view, float y) {
+      y -= view.getTotalPaddingTop();
+      // Clamp the position to inside of the view.
+      y = Math.max(0.0f, y);
+      y = Math.min(view.getHeight() - view.getTotalPaddingBottom() - 1, y);
+      y += view.getScrollY();
+      return view.getLayout().getLineForVertical((int) y);
+    }
+
+    private static int getOffsetAtCoordinate(TextView view, int line, float x) {
+      x = convertToLocalHorizontalCoordinate(view, x);
+      return view.getLayout().getOffsetForHorizontal(line, x);
+    }
+  }
 }
diff --git a/library/gingerbread/src/com/android/setupwizardlib/view/NavigationBarButton.java b/library/gingerbread/src/com/android/setupwizardlib/view/NavigationBarButton.java
index 872cc9f..b2d9d79 100644
--- a/library/gingerbread/src/com/android/setupwizardlib/view/NavigationBarButton.java
+++ b/library/gingerbread/src/com/android/setupwizardlib/view/NavigationBarButton.java
@@ -23,11 +23,10 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.LayerDrawable;
 import android.os.Build;
+import androidx.annotation.NonNull;
 import android.util.AttributeSet;
 import android.widget.Button;
 
-import androidx.annotation.NonNull;
-
 /**
  * Button for navigation bar, which includes tinting of its compound drawables to be used for dark
  * and light themes.
@@ -35,128 +34,144 @@
 @SuppressLint("AppCompatCustomView")
 public class NavigationBarButton extends Button {
 
-    public NavigationBarButton(Context context) {
-        super(context);
-        init();
-    }
+  public NavigationBarButton(Context context) {
+    super(context);
+    init();
+  }
 
-    public NavigationBarButton(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        init();
-    }
+  public NavigationBarButton(Context context, AttributeSet attrs) {
+    super(context, attrs);
+    init();
+  }
 
-    private void init() {
-        // Unfortunately, drawableStart and drawableEnd set through XML does not call the setter,
-        // so manually getting it and wrapping it in the compat drawable.
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
-            Drawable[] drawables = getCompoundDrawablesRelative();
-            for (int i = 0; i < drawables.length; i++) {
-                if (drawables[i] != null) {
-                    drawables[i] = TintedDrawable.wrap(drawables[i]);
-                }
-            }
-            setCompoundDrawablesRelativeWithIntrinsicBounds(drawables[0], drawables[1],
-                    drawables[2], drawables[3]);
+  private void init() {
+    // Unfortunately, drawableStart and drawableEnd set through XML does not call the setter,
+    // so manually getting it and wrapping it in the compat drawable.
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+      Drawable[] drawables = getCompoundDrawablesRelative();
+      for (int i = 0; i < drawables.length; i++) {
+        if (drawables[i] != null) {
+          drawables[i] = TintedDrawable.wrap(drawables[i]);
         }
+      }
+      setCompoundDrawablesRelativeWithIntrinsicBounds(
+          drawables[0], drawables[1], drawables[2], drawables[3]);
+    }
+  }
+
+  @Override
+  public void setCompoundDrawables(Drawable left, Drawable top, Drawable right, Drawable bottom) {
+    if (left != null) {
+      left = TintedDrawable.wrap(left);
+    }
+    if (top != null) {
+      top = TintedDrawable.wrap(top);
+    }
+    if (right != null) {
+      right = TintedDrawable.wrap(right);
+    }
+    if (bottom != null) {
+      bottom = TintedDrawable.wrap(bottom);
+    }
+    super.setCompoundDrawables(left, top, right, bottom);
+    tintDrawables();
+  }
+
+  @Override
+  public void setCompoundDrawablesRelative(
+      Drawable start, Drawable top, Drawable end, Drawable bottom) {
+    if (start != null) {
+      start = TintedDrawable.wrap(start);
+    }
+    if (top != null) {
+      top = TintedDrawable.wrap(top);
+    }
+    if (end != null) {
+      end = TintedDrawable.wrap(end);
+    }
+    if (bottom != null) {
+      bottom = TintedDrawable.wrap(bottom);
+    }
+    super.setCompoundDrawablesRelative(start, top, end, bottom);
+    tintDrawables();
+  }
+
+  @Override
+  public void setTextColor(ColorStateList colors) {
+    super.setTextColor(colors);
+    tintDrawables();
+  }
+
+  private void tintDrawables() {
+    final ColorStateList textColors = getTextColors();
+    if (textColors != null) {
+      for (Drawable drawable : getAllCompoundDrawables()) {
+        if (drawable instanceof TintedDrawable) {
+          ((TintedDrawable) drawable).setTintListCompat(textColors);
+        }
+      }
+      invalidate();
+    }
+  }
+
+  private Drawable[] getAllCompoundDrawables() {
+    Drawable[] drawables = new Drawable[6];
+    Drawable[] compoundDrawables = getCompoundDrawables();
+    drawables[0] = compoundDrawables[0]; // left
+    drawables[1] = compoundDrawables[1]; // top
+    drawables[2] = compoundDrawables[2]; // right
+    drawables[3] = compoundDrawables[3]; // bottom
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+      Drawable[] compoundDrawablesRelative = getCompoundDrawablesRelative();
+      drawables[4] = compoundDrawablesRelative[0]; // start
+      drawables[5] = compoundDrawablesRelative[2]; // end
+    }
+    return drawables;
+  }
+
+  // TODO: Remove this class and use DrawableCompat.wrap() once we can use support library 22.1.0
+  // or above
+  private static class TintedDrawable extends LayerDrawable {
+
+    public static TintedDrawable wrap(Drawable drawable) {
+      if (drawable instanceof TintedDrawable) {
+        return (TintedDrawable) drawable;
+      }
+      return new TintedDrawable(drawable.mutate());
+    }
+
+    private ColorStateList tintList = null;
+
+    TintedDrawable(Drawable wrapped) {
+      super(new Drawable[] {wrapped});
     }
 
     @Override
-    public void setCompoundDrawables(Drawable left, Drawable top, Drawable right, Drawable bottom) {
-        if (left != null) left = TintedDrawable.wrap(left);
-        if (top != null) top = TintedDrawable.wrap(top);
-        if (right != null) right = TintedDrawable.wrap(right);
-        if (bottom != null) bottom = TintedDrawable.wrap(bottom);
-        super.setCompoundDrawables(left, top, right, bottom);
-        tintDrawables();
+    public boolean isStateful() {
+      return true;
     }
 
     @Override
-    public void setCompoundDrawablesRelative(Drawable start, Drawable top, Drawable end,
-            Drawable bottom) {
-        if (start != null) start = TintedDrawable.wrap(start);
-        if (top != null) top = TintedDrawable.wrap(top);
-        if (end != null) end = TintedDrawable.wrap(end);
-        if (bottom != null) bottom = TintedDrawable.wrap(bottom);
-        super.setCompoundDrawablesRelative(start, top, end, bottom);
-        tintDrawables();
+    public boolean setState(@NonNull int[] stateSet) {
+      boolean needsInvalidate = super.setState(stateSet);
+      boolean needsInvalidateForState = updateState();
+      return needsInvalidate || needsInvalidateForState;
     }
 
-    @Override
-    public void setTextColor(ColorStateList colors) {
-        super.setTextColor(colors);
-        tintDrawables();
+    public void setTintListCompat(ColorStateList colors) {
+      tintList = colors;
+      if (updateState()) {
+        invalidateSelf();
+      }
     }
 
-    private void tintDrawables() {
-        final ColorStateList textColors = getTextColors();
-        if (textColors != null) {
-            for (Drawable drawable : getAllCompoundDrawables()) {
-                if (drawable instanceof TintedDrawable) {
-                    ((TintedDrawable) drawable).setTintListCompat(textColors);
-                }
-            }
-            invalidate();
-        }
+    private boolean updateState() {
+      if (tintList != null) {
+        final int color = tintList.getColorForState(getState(), 0);
+        setColorFilter(color, PorterDuff.Mode.SRC_IN);
+        return true; // Needs invalidate
+      }
+      return false;
     }
-
-    private Drawable[] getAllCompoundDrawables() {
-        Drawable[] drawables = new Drawable[6];
-        Drawable[] compoundDrawables = getCompoundDrawables();
-        drawables[0] = compoundDrawables[0];  // left
-        drawables[1] = compoundDrawables[1];  // top
-        drawables[2] = compoundDrawables[2];  // right
-        drawables[3] = compoundDrawables[3];  // bottom
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
-            Drawable[] compoundDrawablesRelative = getCompoundDrawablesRelative();
-            drawables[4] = compoundDrawablesRelative[0];  // start
-            drawables[5] = compoundDrawablesRelative[2];  // end
-        }
-        return drawables;
-    }
-
-    // TODO: Remove this class and use DrawableCompat.wrap() once we can use support library 22.1.0
-    // or above
-    private static class TintedDrawable extends LayerDrawable {
-
-        public static TintedDrawable wrap(Drawable drawable) {
-            if (drawable instanceof TintedDrawable) {
-                return (TintedDrawable) drawable;
-            }
-            return new TintedDrawable(drawable.mutate());
-        }
-
-        private ColorStateList mTintList = null;
-
-        TintedDrawable(Drawable wrapped) {
-            super(new Drawable[] { wrapped });
-        }
-
-        @Override
-        public boolean isStateful() {
-            return true;
-        }
-
-        @Override
-        public boolean setState(@NonNull int[] stateSet) {
-            boolean needsInvalidate = super.setState(stateSet);
-            boolean needsInvalidateForState = updateState();
-            return needsInvalidate || needsInvalidateForState;
-        }
-
-        public void setTintListCompat(ColorStateList colors) {
-            mTintList = colors;
-            if (updateState()) {
-                invalidateSelf();
-            }
-        }
-
-        private boolean updateState() {
-            if (mTintList != null) {
-                final int color = mTintList.getColorForState(getState(), 0);
-                setColorFilter(color, PorterDuff.Mode.SRC_IN);
-                return true;  // Needs invalidate
-            }
-            return false;
-        }
-    }
+  }
 }
diff --git a/library/gingerbread/src/com/android/setupwizardlib/view/RichTextView.java b/library/gingerbread/src/com/android/setupwizardlib/view/RichTextView.java
index 1ee3219..5e50e7a 100644
--- a/library/gingerbread/src/com/android/setupwizardlib/view/RichTextView.java
+++ b/library/gingerbread/src/com/android/setupwizardlib/view/RichTextView.java
@@ -20,6 +20,8 @@
 import android.graphics.drawable.Drawable;
 import android.os.Build.VERSION;
 import android.os.Build.VERSION_CODES;
+import androidx.core.view.ViewCompat;
+import androidx.appcompat.widget.AppCompatTextView;
 import android.text.Annotation;
 import android.text.SpannableString;
 import android.text.Spanned;
@@ -29,10 +31,6 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.MotionEvent;
-
-import androidx.appcompat.widget.AppCompatTextView;
-import androidx.core.view.ViewCompat;
-
 import com.android.setupwizardlib.span.LinkSpan;
 import com.android.setupwizardlib.span.LinkSpan.OnLinkClickListener;
 import com.android.setupwizardlib.span.SpanHelper;
@@ -40,178 +38,178 @@
 import com.android.setupwizardlib.view.TouchableMovementMethod.TouchableLinkMovementMethod;
 
 /**
- * An extension of TextView that automatically replaces the annotation tags as specified in
- * {@link SpanHelper#replaceSpan(android.text.Spannable, Object, Object)}
+ * 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 AppCompatTextView implements OnLinkClickListener {
 
-    /* static section */
+  /* static section */
 
-    private static final String TAG = "RichTextView";
+  private static final String TAG = "RichTextView";
 
-    private static final String ANNOTATION_LINK = "link";
-    private static final String ANNOTATION_TEXT_APPEARANCE = "textAppearance";
+  private static final String ANNOTATION_LINK = "link";
+  private static final String ANNOTATION_TEXT_APPEARANCE = "textAppearance";
 
-    /**
-     * Replace &lt;annotation&gt; tags in strings to become their respective types. Currently 2
-     * types are supported:
-     * <ol>
-     *     <li>&lt;annotation link="foobar"&gt; will create a
-     *     {@link com.android.setupwizardlib.span.LinkSpan} that broadcasts with the key
-     *     "foobar"</li>
-     *     <li>&lt;annotation textAppearance="TextAppearance.FooBar"&gt; will create a
-     *     {@link android.text.style.TextAppearanceSpan} with @style/TextAppearance.FooBar</li>
-     * </ol>
-     */
-    public static CharSequence getRichText(Context context, CharSequence text) {
-        if (text instanceof Spanned) {
-            final SpannableString spannable = new SpannableString(text);
-            final Annotation[] spans = spannable.getSpans(0, spannable.length(), Annotation.class);
-            for (Annotation span : spans) {
-                final String key = span.getKey();
-                if (ANNOTATION_TEXT_APPEARANCE.equals(key)) {
-                    String textAppearance = span.getValue();
-                    final int style = context.getResources()
-                            .getIdentifier(textAppearance, "style", context.getPackageName());
-                    if (style == 0) {
-                        Log.w(TAG, "Cannot find resource: " + style);
-                    }
-                    final TextAppearanceSpan textAppearanceSpan =
-                            new TextAppearanceSpan(context, style);
-                    SpanHelper.replaceSpan(spannable, span, textAppearanceSpan);
-                } else if (ANNOTATION_LINK.equals(key)) {
-                    LinkSpan link = new LinkSpan(span.getValue());
-                    SpanHelper.replaceSpan(spannable, span, link);
-                }
-            }
-            return spannable;
+  /**
+   * Replace &lt;annotation&gt; tags in strings to become their respective types. Currently 2 types
+   * are supported:
+   *
+   * <ol>
+   *   <li>&lt;annotation link="foobar"&gt; will create a {@link
+   *       com.android.setupwizardlib.span.LinkSpan} that broadcasts with the key "foobar"
+   *   <li>&lt;annotation textAppearance="TextAppearance.FooBar"&gt; will create a {@link
+   *       android.text.style.TextAppearanceSpan} with @style/TextAppearance.FooBar
+   * </ol>
+   */
+  public static CharSequence getRichText(Context context, CharSequence text) {
+    if (text instanceof Spanned) {
+      final SpannableString spannable = new SpannableString(text);
+      final Annotation[] spans = spannable.getSpans(0, spannable.length(), Annotation.class);
+      for (Annotation span : spans) {
+        final String key = span.getKey();
+        if (ANNOTATION_TEXT_APPEARANCE.equals(key)) {
+          String textAppearance = span.getValue();
+          final int style =
+              context
+                  .getResources()
+                  .getIdentifier(textAppearance, "style", context.getPackageName());
+          if (style == 0) {
+            Log.w(TAG, "Cannot find resource: " + style);
+          }
+          final TextAppearanceSpan textAppearanceSpan = new TextAppearanceSpan(context, style);
+          SpanHelper.replaceSpan(spannable, span, textAppearanceSpan);
+        } else if (ANNOTATION_LINK.equals(key)) {
+          LinkSpan link = new LinkSpan(span.getValue());
+          SpanHelper.replaceSpan(spannable, span, link);
         }
-        return text;
+      }
+      return spannable;
     }
+    return text;
+  }
 
-    /* non-static section */
+  /* non-static section */
 
-    private LinkAccessibilityHelper mAccessibilityHelper;
-    private OnLinkClickListener mOnLinkClickListener;
+  private LinkAccessibilityHelper accessibilityHelper;
+  private OnLinkClickListener onLinkClickListener;
 
-    public RichTextView(Context context) {
-        super(context);
-        init();
+  public RichTextView(Context context) {
+    super(context);
+    init();
+  }
+
+  public RichTextView(Context context, AttributeSet attrs) {
+    super(context, attrs);
+    init();
+  }
+
+  private void init() {
+    accessibilityHelper = new LinkAccessibilityHelper(this);
+    ViewCompat.setAccessibilityDelegate(this, accessibilityHelper);
+  }
+
+  @Override
+  public void setText(CharSequence text, BufferType type) {
+    text = getRichText(getContext(), text);
+    // Set text first before doing anything else because setMovementMethod internally calls
+    // setText. This in turn ends up calling this method with mText as the first parameter
+    super.setText(text, type);
+    boolean hasLinks = hasLinks(text);
+
+    if (hasLinks) {
+      // When a TextView has a movement method, it will set the view to clickable. This makes
+      // View.onTouchEvent always return true and consumes the touch event, essentially
+      // nullifying any return values of MovementMethod.onTouchEvent.
+      // To still allow propagating touch events to the parent when this view doesn't have
+      // links, we only set the movement method here if the text contains links.
+      setMovementMethod(TouchableLinkMovementMethod.getInstance());
+    } else {
+      setMovementMethod(null);
     }
-
-    public RichTextView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        init();
+    // ExploreByTouchHelper automatically enables focus for RichTextView
+    // even though it may not have any links. Causes problems during talkback
+    // as individual TextViews consume touch events and thereby reducing the focus window
+    // shown by Talkback. Disable focus if there are no links
+    setFocusable(hasLinks);
+    // Do not "reveal" (i.e. scroll to) this view when this view is focused. Since this view is
+    // focusable in touch mode, we may be focused when the screen is first shown, and starting
+    // a screen halfway scrolled down is confusing to the user.
+    if (VERSION.SDK_INT >= VERSION_CODES.N_MR1) {
+      setRevealOnFocusHint(false);
+      // setRevealOnFocusHint is a new API added in SDK 25. For lower SDK versions, do not
+      // call setFocusableInTouchMode. We won't get touch effect on those earlier versions,
+      // but the link will still work, and will prevent the scroll view from starting halfway
+      // down the page.
+      setFocusableInTouchMode(hasLinks);
     }
+  }
 
-    private void init() {
-        mAccessibilityHelper = new LinkAccessibilityHelper(this);
-        ViewCompat.setAccessibilityDelegate(this, mAccessibilityHelper);
+  private boolean hasLinks(CharSequence text) {
+    if (text instanceof Spanned) {
+      final ClickableSpan[] spans =
+          ((Spanned) text).getSpans(0, text.length(), ClickableSpan.class);
+      return spans.length > 0;
     }
+    return false;
+  }
 
-    @Override
-    public void setText(CharSequence text, BufferType type) {
-        text = getRichText(getContext(), text);
-        // Set text first before doing anything else because setMovementMethod internally calls
-        // setText. This in turn ends up calling this method with mText as the first parameter
-        super.setText(text, type);
-        boolean hasLinks = hasLinks(text);
+  @Override
+  @SuppressWarnings("ClickableViewAccessibility") // super.onTouchEvent is called
+  public boolean onTouchEvent(MotionEvent event) {
+    // Since View#onTouchEvent always return true if the view is clickable (which is the case
+    // when a TextView has a movement method), override the implementation to allow the movement
+    // method, if it implements TouchableMovementMethod, to say that the touch is not handled,
+    // allowing the event to bubble up to the parent view.
+    boolean superResult = super.onTouchEvent(event);
+    MovementMethod movementMethod = getMovementMethod();
+    if (movementMethod instanceof TouchableMovementMethod) {
+      TouchableMovementMethod touchableMovementMethod = (TouchableMovementMethod) movementMethod;
+      if (touchableMovementMethod.getLastTouchEvent() == event) {
+        return touchableMovementMethod.isLastTouchEventHandled();
+      }
+    }
+    return superResult;
+  }
 
-        if (hasLinks) {
-            // When a TextView has a movement method, it will set the view to clickable. This makes
-            // View.onTouchEvent always return true and consumes the touch event, essentially
-            // nullifying any return values of MovementMethod.onTouchEvent.
-            // To still allow propagating touch events to the parent when this view doesn't have
-            // links, we only set the movement method here if the text contains links.
-            setMovementMethod(TouchableLinkMovementMethod.getInstance());
-        } else {
-            setMovementMethod(null);
+  @Override
+  protected boolean dispatchHoverEvent(MotionEvent event) {
+    if (accessibilityHelper != null && accessibilityHelper.dispatchHoverEvent(event)) {
+      return true;
+    }
+    return super.dispatchHoverEvent(event);
+  }
+
+  @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);
+          }
         }
-        // ExploreByTouchHelper automatically enables focus for RichTextView
-        // even though it may not have any links. Causes problems during talkback
-        // as individual TextViews consume touch events and thereby reducing the focus window
-        // shown by Talkback. Disable focus if there are no links
-        setFocusable(hasLinks);
-        // Do not "reveal" (i.e. scroll to) this view when this view is focused. Since this view is
-        // focusable in touch mode, we may be focused when the screen is first shown, and starting
-        // a screen halfway scrolled down is confusing to the user.
-        if (VERSION.SDK_INT >= VERSION_CODES.N_MR1) {
-            setRevealOnFocusHint(false);
-            // setRevealOnFocusHint is a new API added in SDK 25. For lower SDK versions, do not
-            // call setFocusableInTouchMode. We won't get touch effect on those earlier versions,
-            // but the link will still work, and will prevent the scroll view from starting halfway
-            // down the page.
-            setFocusableInTouchMode(hasLinks);
-        }
+      }
     }
+  }
 
-    private boolean hasLinks(CharSequence text) {
-        if (text instanceof Spanned) {
-            final ClickableSpan[] spans =
-                    ((Spanned) text).getSpans(0, text.length(), ClickableSpan.class);
-            return spans.length > 0;
-        }
-        return false;
+  public void setOnLinkClickListener(OnLinkClickListener listener) {
+    onLinkClickListener = listener;
+  }
+
+  public OnLinkClickListener getOnLinkClickListener() {
+    return onLinkClickListener;
+  }
+
+  @Override
+  public boolean onLinkClick(LinkSpan span) {
+    if (onLinkClickListener != null) {
+      return onLinkClickListener.onLinkClick(span);
     }
-
-    @Override
-    @SuppressWarnings("ClickableViewAccessibility")  // super.onTouchEvent is called
-    public boolean onTouchEvent(MotionEvent event) {
-        // Since View#onTouchEvent always return true if the view is clickable (which is the case
-        // when a TextView has a movement method), override the implementation to allow the movement
-        // method, if it implements TouchableMovementMethod, to say that the touch is not handled,
-        // allowing the event to bubble up to the parent view.
-        boolean superResult = super.onTouchEvent(event);
-        MovementMethod movementMethod = getMovementMethod();
-        if (movementMethod instanceof TouchableMovementMethod) {
-            TouchableMovementMethod touchableMovementMethod =
-                    (TouchableMovementMethod) movementMethod;
-            if (touchableMovementMethod.getLastTouchEvent() == event) {
-                return touchableMovementMethod.isLastTouchEventHandled();
-            }
-        }
-        return superResult;
-    }
-
-    @Override
-    protected boolean dispatchHoverEvent(MotionEvent event) {
-        if (mAccessibilityHelper != null && mAccessibilityHelper.dispatchHoverEvent(event)) {
-            return true;
-        }
-        return super.dispatchHoverEvent(event);
-    }
-
-    @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 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;
-    }
+    return false;
+  }
 }
diff --git a/library/gingerbread/test/instrumentation/src/com/android/setupwizardlib/items/ButtonItemDrawingTest.java b/library/gingerbread/test/instrumentation/src/com/android/setupwizardlib/items/ButtonItemDrawingTest.java
index f5b8253..7f51fa7 100644
--- a/library/gingerbread/test/instrumentation/src/com/android/setupwizardlib/items/ButtonItemDrawingTest.java
+++ b/library/gingerbread/test/instrumentation/src/com/android/setupwizardlib/items/ButtonItemDrawingTest.java
@@ -18,19 +18,16 @@
 
 import static org.junit.Assert.assertTrue;
 
+import androidx.annotation.StyleRes;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.LinearLayout;
 import android.support.test.annotation.UiThreadTest;
 import android.support.test.filters.SmallTest;
 import android.support.test.rule.UiThreadTestRule;
 import android.support.test.runner.AndroidJUnit4;
-import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.LinearLayout;
-
-import androidx.annotation.StyleRes;
-
 import com.android.setupwizardlib.R;
 import com.android.setupwizardlib.test.util.DrawingTestHelper;
-
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -39,69 +36,65 @@
 @RunWith(AndroidJUnit4.class)
 public class ButtonItemDrawingTest {
 
-    private static final int GLIF_ACCENT_COLOR = 0xff4285f4;
-    private static final int GLIF_V3_ACCENT_COLOR = 0xff1a73e8;
+  private static final int GLIF_ACCENT_COLOR = 0xff4285f4;
+  private static final int GLIF_V3_ACCENT_COLOR = 0xff1a73e8;
 
-    // These tests need to be run on UI thread because button uses ValueAnimator
-    @Rule
-    public UiThreadTestRule mUiThreadTestRule = new UiThreadTestRule();
+  // These tests need to be run on UI thread because button uses ValueAnimator
+  @Rule public UiThreadTestRule mUiThreadTestRule = new UiThreadTestRule();
 
-    @Test
-    @UiThreadTest
-    public void drawButton_glif_shouldHaveAccentColoredButton()
-            throws InstantiationException, IllegalAccessException {
-        Button button = createButton(R.style.SuwThemeGlif_Light);
+  @Test
+  @UiThreadTest
+  public void drawButton_glif_shouldHaveAccentColoredButton()
+      throws InstantiationException, IllegalAccessException {
+    Button button = createButton(R.style.SuwThemeGlif_Light);
 
-        DrawingTestHelper drawingTestHelper = new DrawingTestHelper(50, 50);
-        drawingTestHelper.drawView(button);
+    DrawingTestHelper drawingTestHelper = new DrawingTestHelper(50, 50);
+    drawingTestHelper.drawView(button);
 
-        int accentPixelCount =
-                countPixelsWithColor(drawingTestHelper.getPixels(), GLIF_ACCENT_COLOR);
-        assertTrue("> 10 pixels should be #4285f4. Found " + accentPixelCount,
-                accentPixelCount > 10);
+    int accentPixelCount = countPixelsWithColor(drawingTestHelper.getPixels(), GLIF_ACCENT_COLOR);
+    assertTrue("> 10 pixels should be #4285f4. Found " + accentPixelCount, accentPixelCount > 10);
+  }
+
+  @Test
+  @UiThreadTest
+  public void drawButton_glifV3_shouldHaveAccentColoredButton()
+      throws InstantiationException, IllegalAccessException {
+    Button button = createButton(R.style.SuwThemeGlifV3_Light);
+
+    DrawingTestHelper drawingTestHelper = new DrawingTestHelper(50, 50);
+    drawingTestHelper.drawView(button);
+
+    int accentPixelCount =
+        countPixelsWithColor(drawingTestHelper.getPixels(), GLIF_V3_ACCENT_COLOR);
+    assertTrue("> 10 pixels should be #1a73e8. Found " + accentPixelCount, accentPixelCount > 10);
+  }
+
+  private Button createButton(@StyleRes int theme)
+      throws InstantiationException, IllegalAccessException {
+    final ViewGroup parent = new LinearLayout(DrawingTestHelper.createCanvasActivity(theme));
+    TestButtonItem item = new TestButtonItem();
+    item.setTheme(R.style.SuwButtonItem_Colored);
+    item.setText("foobar");
+
+    return item.createButton(parent);
+  }
+
+  private int countPixelsWithColor(int[] pixels, int color) {
+    int count = 0;
+    for (int pixel : pixels) {
+      if (pixel == color) {
+        count++;
+      }
     }
+    return count;
+  }
 
-    @Test
-    @UiThreadTest
-    public void drawButton_glifV3_shouldHaveAccentColoredButton()
-            throws InstantiationException, IllegalAccessException {
-        Button button = createButton(R.style.SuwThemeGlifV3_Light);
+  private static class TestButtonItem extends ButtonItem {
 
-        DrawingTestHelper drawingTestHelper = new DrawingTestHelper(50, 50);
-        drawingTestHelper.drawView(button);
-
-        int accentPixelCount =
-                countPixelsWithColor(drawingTestHelper.getPixels(), GLIF_V3_ACCENT_COLOR);
-        assertTrue("> 10 pixels should be #1a73e8. Found " + accentPixelCount,
-                accentPixelCount > 10);
+    @Override
+    public Button createButton(ViewGroup parent) {
+      // Make this method public for testing
+      return super.createButton(parent);
     }
-
-    private Button createButton(@StyleRes int theme)
-            throws InstantiationException, IllegalAccessException {
-        final ViewGroup parent = new LinearLayout(DrawingTestHelper.createCanvasActivity(theme));
-        TestButtonItem item = new TestButtonItem();
-        item.setTheme(R.style.SuwButtonItem_Colored);
-        item.setText("foobar");
-
-        return item.createButton(parent);
-    }
-
-    private int countPixelsWithColor(int[] pixels, int color) {
-        int count = 0;
-        for (int pixel : pixels) {
-            if (pixel == color) {
-                count++;
-            }
-        }
-        return count;
-    }
-
-    private static class TestButtonItem extends ButtonItem {
-
-        @Override
-        public Button createButton(ViewGroup parent) {
-            // Make this method public for testing
-            return super.createButton(parent);
-        }
-    }
+  }
 }
diff --git a/library/gingerbread/test/instrumentation/src/com/android/setupwizardlib/test/util/DrawingTestActivity.java b/library/gingerbread/test/instrumentation/src/com/android/setupwizardlib/test/util/DrawingTestActivity.java
index d3518f4..80e23e9 100644
--- a/library/gingerbread/test/instrumentation/src/com/android/setupwizardlib/test/util/DrawingTestActivity.java
+++ b/library/gingerbread/test/instrumentation/src/com/android/setupwizardlib/test/util/DrawingTestActivity.java
@@ -26,5 +26,4 @@
  *
  * @see DrawingTestHelper
  */
-public class DrawingTestActivity extends AppCompatActivity {
-}
+public class DrawingTestActivity extends AppCompatActivity {}
diff --git a/library/gingerbread/test/instrumentation/src/com/android/setupwizardlib/util/LinkAccessibilityHelperTest.java b/library/gingerbread/test/instrumentation/src/com/android/setupwizardlib/util/LinkAccessibilityHelperTest.java
index 7b4ad4d..da5e16c 100644
--- a/library/gingerbread/test/instrumentation/src/com/android/setupwizardlib/util/LinkAccessibilityHelperTest.java
+++ b/library/gingerbread/test/instrumentation/src/com/android/setupwizardlib/util/LinkAccessibilityHelperTest.java
@@ -25,342 +25,334 @@
 
 import android.graphics.Rect;
 import android.os.Bundle;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.core.view.AccessibilityDelegateCompat;
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
+import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat;
+import androidx.customview.widget.ExploreByTouchHelper;
 import android.text.SpannableStringBuilder;
 import android.util.DisplayMetrics;
 import android.util.TypedValue;
-import android.view.MotionEvent;
 import android.view.View;
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.FrameLayout;
 import android.widget.TextView;
-
 import androidx.core.text.BidiFormatter;
-import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
-import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat;
-import androidx.customview.widget.ExploreByTouchHelper;
-
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
 import com.android.setupwizardlib.span.LinkSpan;
 import com.android.setupwizardlib.util.LinkAccessibilityHelper.PreOLinkAccessibilityHelper;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class LinkAccessibilityHelperTest {
 
-    private static final LinkSpan LINK_SPAN = new LinkSpan("foobar");
+  private static final LinkSpan LINK_SPAN = new LinkSpan("foobar");
 
-    private TextView mTextView;
-    private TestPreOLinkAccessibilityHelper mHelper;
+  private TextView mTextView;
+  private TestPreOLinkAccessibilityHelper mHelper;
 
-    private DisplayMetrics mDisplayMetrics;
+  private DisplayMetrics mDisplayMetrics;
 
-    @Test
-    public void testGetVirtualViewAt() {
-        initTextView();
-        final int virtualViewId = mHelper.getVirtualViewAt(dp2Px(15), dp2Px(10));
-        assertEquals("Virtual view ID should be 1", 1, virtualViewId);
-    }
+  @Test
+  public void testGetVirtualViewAt() {
+    initTextView();
+    final int virtualViewId = mHelper.getVirtualViewAt(dp2Px(15), dp2Px(10));
+    assertEquals("Virtual view ID should be 1", 1, virtualViewId);
+  }
 
-    @Test
-    public void testGetVirtualViewAtHost() {
-        initTextView();
-        final int virtualViewId = mHelper.getVirtualViewAt(dp2Px(100), dp2Px(100));
-        assertEquals("Virtual view ID should be INVALID_ID",
-                ExploreByTouchHelper.INVALID_ID, virtualViewId);
-    }
+  @Test
+  public void testGetVirtualViewAtHost() {
+    initTextView();
+    final int virtualViewId = mHelper.getVirtualViewAt(dp2Px(100), dp2Px(100));
+    assertEquals(
+        "Virtual view ID should be INVALID_ID", ExploreByTouchHelper.INVALID_ID, virtualViewId);
+  }
 
-    @Test
-    public void testGetVisibleVirtualViews() {
-        initTextView();
-        List<Integer> virtualViewIds = new ArrayList<>();
-        mHelper.getVisibleVirtualViews(virtualViewIds);
+  @Test
+  public void testGetVisibleVirtualViews() {
+    initTextView();
+    List<Integer> virtualViewIds = new ArrayList<>();
+    mHelper.getVisibleVirtualViews(virtualViewIds);
 
-        assertEquals("VisibleVirtualViews should be [1]",
-                Collections.singletonList(1), virtualViewIds);
-    }
+    assertEquals("VisibleVirtualViews should be [1]", Collections.singletonList(1), virtualViewIds);
+  }
 
-    @Test
-    public void testOnPopulateEventForVirtualView() {
-        initTextView();
-        AccessibilityEvent event = AccessibilityEvent.obtain();
-        mHelper.onPopulateEventForVirtualView(1, event);
+  @Test
+  public void testOnPopulateEventForVirtualView() {
+    initTextView();
+    AccessibilityEvent event = AccessibilityEvent.obtain();
+    mHelper.onPopulateEventForVirtualView(1, event);
 
-        // LinkSpan is set on substring(1, 2) of "Hello world" --> "e"
-        assertEquals("LinkSpan description should be \"e\"",
-                "e", event.getContentDescription().toString());
+    // LinkSpan is set on substring(1, 2) of "Hello world" --> "e"
+    assertEquals(
+        "LinkSpan description should be \"e\"", "e", event.getContentDescription().toString());
 
-        event.recycle();
-    }
+    event.recycle();
+  }
 
-    @Test
-    public void testOnPopulateEventForVirtualViewHost() {
-        initTextView();
-        AccessibilityEvent event = AccessibilityEvent.obtain();
-        mHelper.onPopulateEventForVirtualView(ExploreByTouchHelper.INVALID_ID, event);
+  @Test
+  public void testOnPopulateEventForVirtualViewHost() {
+    initTextView();
+    AccessibilityEvent event = AccessibilityEvent.obtain();
+    mHelper.onPopulateEventForVirtualView(ExploreByTouchHelper.INVALID_ID, event);
 
-        assertEquals("Host view description should be \"Hello world\"", "Hello world",
-                event.getContentDescription().toString());
+    assertEquals(
+        "Host view description should be \"Hello world\"",
+        "Hello world",
+        event.getContentDescription().toString());
 
-        event.recycle();
-    }
+    event.recycle();
+  }
 
-    @Test
-    public void testOnPopulateNodeForVirtualView() {
-        initTextView();
-        AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
-        mHelper.onPopulateNodeForVirtualView(1, info);
+  @Test
+  public void testOnPopulateNodeForVirtualView() {
+    initTextView();
+    AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
+    mHelper.onPopulateNodeForVirtualView(1, info);
 
-        assertEquals("LinkSpan description should be \"e\"",
-                "e", info.getContentDescription().toString());
-        assertTrue("LinkSpan should be focusable", info.isFocusable());
-        assertTrue("LinkSpan should be clickable", info.isClickable());
-        Rect bounds = new Rect();
-        info.getBoundsInParent(bounds);
-        assertEquals("LinkSpan bounds should be (10.5dp, 0dp, 18.5dp, 20.5dp)",
-                new Rect(dp2Px(10.5f), dp2Px(0f), dp2Px(18.5f), dp2Px(20.5f)), bounds);
+    assertEquals(
+        "LinkSpan description should be \"e\"", "e", info.getContentDescription().toString());
+    assertTrue("LinkSpan should be focusable", info.isFocusable());
+    assertTrue("LinkSpan should be clickable", info.isClickable());
+    Rect bounds = new Rect();
+    info.getBoundsInParent(bounds);
+    assertEquals(
+        "LinkSpan bounds should be (10.5dp, 0dp, 18.5dp, 20.5dp)",
+        new Rect(dp2Px(10.5f), dp2Px(0f), dp2Px(18.5f), dp2Px(20.5f)),
+        bounds);
 
-        info.recycle();
-    }
+    info.recycle();
+  }
 
-    @Test
-    public void testNullLayout() {
-        initTextView();
-        // Setting the padding will cause the layout to be null-ed out.
-        mTextView.setPadding(1, 1, 1, 1);
+  @Test
+  public void testNullLayout() {
+    initTextView();
+    // Setting the padding will cause the layout to be null-ed out.
+    mTextView.setPadding(1, 1, 1, 1);
 
-        AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
-        mHelper.onPopulateNodeForVirtualView(0, info);
+    AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
+    mHelper.onPopulateNodeForVirtualView(0, info);
 
-        Rect bounds = new Rect();
-        info.getBoundsInParent(bounds);
-        assertEquals("LinkSpan bounds should be (0, 0, 1, 1)",
-                new Rect(0, 0, 1, 1), bounds);
+    Rect bounds = new Rect();
+    info.getBoundsInParent(bounds);
+    assertEquals("LinkSpan bounds should be (0, 0, 1, 1)", new Rect(0, 0, 1, 1), bounds);
 
-        info.recycle();
-    }
+    info.recycle();
+  }
 
-    @Test
-    public void testRtlLayout() {
-        SpannableStringBuilder ssb = new SpannableStringBuilder("מכונה בתרגום");
-        ssb.setSpan(LINK_SPAN, 1, 2, 0 /* flags */);
-        initTextView(ssb);
+  @Test
+  public void testRtlLayout() {
+    SpannableStringBuilder ssb = new SpannableStringBuilder("מכונה בתרגום");
+    ssb.setSpan(LINK_SPAN, 1, 2, 0 /* flags */);
+    initTextView(ssb);
 
-        AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
-        mHelper.onPopulateNodeForVirtualView(1, info);
+    AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
+    mHelper.onPopulateNodeForVirtualView(1, info);
 
-        assertEquals("LinkSpan description should be \"כ\"",
-                "כ", info.getContentDescription().toString());
-        Rect bounds = new Rect();
-        info.getBoundsInParent(bounds);
-        assertEquals("LinkSpan bounds should be (481.5dp, 0dp, 489.5dp, 20.5dp)",
-                new Rect(dp2Px(481.5f), dp2Px(0f), dp2Px(489.5f), dp2Px(20.5f)), bounds);
+    assertEquals(
+        "LinkSpan description should be \"כ\"", "כ", info.getContentDescription().toString());
+    Rect bounds = new Rect();
+    info.getBoundsInParent(bounds);
+    assertEquals(
+        "LinkSpan bounds should be (481.5dp, 0dp, 489.5dp, 20.5dp)",
+        new Rect(dp2Px(481.5f), dp2Px(0f), dp2Px(489.5f), dp2Px(20.5f)),
+        bounds);
 
-        info.recycle();
-    }
+    info.recycle();
+  }
 
-    @Test
-    public void testMultilineLink() {
-        SpannableStringBuilder ssb = new SpannableStringBuilder(
-                "Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
+  @Test
+  public void testMultilineLink() {
+    SpannableStringBuilder ssb =
+        new SpannableStringBuilder(
+            "Lorem ipsum dolor sit amet, consectetur adipiscing elit. "
                 + "Praesent accumsan efficitur eros eu porttitor.");
-        ssb.setSpan(LINK_SPAN, 51, 74, 0 /* flags */);
-        initTextView(ssb);
+    ssb.setSpan(LINK_SPAN, 51, 74, 0 /* flags */);
+    initTextView(ssb);
 
-        AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
-        mHelper.onPopulateNodeForVirtualView(51, info);
+    AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
+    mHelper.onPopulateNodeForVirtualView(51, info);
 
-        assertEquals("LinkSpan description should match the span",
-                "elit. Praesent accumsan", info.getContentDescription().toString());
-        Rect bounds = new Rect();
-        info.getBoundsInParent(bounds);
-        assertEquals("LinkSpan bounds should match first line of the span",
-                new Rect(dp2Px(343f), dp2Px(0f), dp2Px(500f), dp2Px(19.5f)), bounds);
+    assertEquals(
+        "LinkSpan description should match the span",
+        "elit. Praesent accumsan",
+        info.getContentDescription().toString());
+    Rect bounds = new Rect();
+    info.getBoundsInParent(bounds);
+    assertEquals(
+        "LinkSpan bounds should match first line of the span",
+        new Rect(dp2Px(343f), dp2Px(0f), dp2Px(500f), dp2Px(19.5f)),
+        bounds);
 
-        info.recycle();
+    info.recycle();
+  }
+
+  @Test
+  public void testRtlMultilineLink() {
+    String iwLoremIpsum =
+        "אחר על רביעי אקטואליה. לוח דת אחרות המקובל רומנית, מיזמים מועמדים "
+            + "האנציקלופדיה בה צ'ט. מתן מה שנורו לערוך ייִדיש, בקר או החול אנתרופולוגיה, עוד "
+            + "דפים המחשב מיזמים ב.";
+    SpannableStringBuilder ssb = new SpannableStringBuilder(iwLoremIpsum);
+    ssb.setSpan(LINK_SPAN, 50, 100, 0 /* flags */);
+    initTextView(ssb);
+
+    AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
+    mHelper.onPopulateNodeForVirtualView(50, info);
+
+    assertEquals(
+        "LinkSpan description should match the span",
+        iwLoremIpsum.substring(50, 100),
+        info.getContentDescription().toString());
+    Rect bounds = new Rect();
+    info.getBoundsInParent(bounds);
+    assertEquals(
+        "LinkSpan bounds should match the first line of the span",
+        new Rect(dp2Px(0f), dp2Px(0f), dp2Px(150f), dp2Px(19.5f)),
+        bounds);
+
+    info.recycle();
+  }
+
+  @Test
+  public void testBidiMultilineLink() {
+    String iwLoremIpsum =
+        "אחר על רביעי אקטואליה. לוח דת אחרות המקובל רומנית, מיזמים מועמדים "
+            + "האנציקלופדיה בה צ'ט. מתן מה שנורו לערוך ייִדיש, בקר או החול אנתרופולוגיה, עוד "
+            + "דפים המחשב מיזמים ב.";
+    BidiFormatter formatter = BidiFormatter.getInstance(false /* rtlContext */);
+    SpannableStringBuilder ssb = new SpannableStringBuilder();
+    ssb.append("hello ").append(formatter.unicodeWrap(iwLoremIpsum)).append(" world");
+    ssb.setSpan(
+        LINK_SPAN,
+        "hello ".length() + 2, // Add two for the characters added by BidiFormatter
+        "hello ".length() + 2 + iwLoremIpsum.length(),
+        0 /* flags */);
+    initTextView(ssb);
+
+    AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
+    mHelper.onPopulateNodeForVirtualView("hello ".length() + 2, info);
+
+    assertEquals(
+        "LinkSpan description should match the span",
+        iwLoremIpsum,
+        info.getContentDescription().toString());
+    Rect bounds = new Rect();
+    info.getBoundsInParent(bounds);
+    assertEquals(
+        "LinkSpan bounds should match the first line of the span",
+        new Rect(dp2Px(491.5f), dp2Px(0f), dp2Px(500f), dp2Px(19.5f)),
+        bounds);
+
+    info.recycle();
+  }
+
+  @Test
+  public void testMethodDelegation() {
+    initTextView();
+    AccessibilityDelegateCompat delegate = mock(AccessibilityDelegateCompat.class);
+    LinkAccessibilityHelper helper = new LinkAccessibilityHelper(delegate);
+
+    AccessibilityEvent accessibilityEvent =
+        AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_CLICKED);
+
+    helper.sendAccessibilityEvent(mTextView, AccessibilityEvent.TYPE_VIEW_CLICKED);
+    verify(delegate)
+        .sendAccessibilityEvent(same(mTextView), eq(AccessibilityEvent.TYPE_VIEW_CLICKED));
+
+    helper.sendAccessibilityEventUnchecked(mTextView, accessibilityEvent);
+    verify(delegate).sendAccessibilityEventUnchecked(same(mTextView), same(accessibilityEvent));
+
+    helper.performAccessibilityAction(
+        mTextView, AccessibilityActionCompat.ACTION_CLICK.getId(), Bundle.EMPTY);
+    verify(delegate)
+        .performAccessibilityAction(
+            same(mTextView), eq(AccessibilityActionCompat.ACTION_CLICK.getId()), eq(Bundle.EMPTY));
+
+    helper.dispatchPopulateAccessibilityEvent(mTextView, accessibilityEvent);
+    verify(delegate).dispatchPopulateAccessibilityEvent(same(mTextView), same(accessibilityEvent));
+
+    helper.getAccessibilityNodeProvider(mTextView);
+    verify(delegate).getAccessibilityNodeProvider(same(mTextView));
+
+    helper.onInitializeAccessibilityEvent(mTextView, accessibilityEvent);
+    verify(delegate).onInitializeAccessibilityEvent(same(mTextView), eq(accessibilityEvent));
+
+    AccessibilityNodeInfoCompat accessibilityNodeInfo = AccessibilityNodeInfoCompat.obtain();
+    helper.onInitializeAccessibilityNodeInfo(mTextView, accessibilityNodeInfo);
+    verify(delegate)
+        .onInitializeAccessibilityNodeInfo(same(mTextView), same(accessibilityNodeInfo));
+
+    helper.onPopulateAccessibilityEvent(mTextView, accessibilityEvent);
+    verify(delegate).onPopulateAccessibilityEvent(same(mTextView), same(accessibilityEvent));
+
+    FrameLayout parent = new FrameLayout(InstrumentationRegistry.getTargetContext());
+    helper.onRequestSendAccessibilityEvent(parent, mTextView, accessibilityEvent);
+    verify(delegate)
+        .onRequestSendAccessibilityEvent(same(parent), same(mTextView), same(accessibilityEvent));
+  }
+
+  private void initTextView() {
+    SpannableStringBuilder ssb = new SpannableStringBuilder("Hello world");
+    ssb.setSpan(LINK_SPAN, 1, 2, 0 /* flags */);
+    initTextView(ssb);
+  }
+
+  private void initTextView(CharSequence text) {
+    mTextView = new TextView(InstrumentationRegistry.getContext());
+    mTextView.setSingleLine(false);
+    mTextView.setText(text);
+    mTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15);
+    mHelper = new TestPreOLinkAccessibilityHelper(mTextView);
+
+    int measureExactly500dp =
+        View.MeasureSpec.makeMeasureSpec(dp2Px(500), View.MeasureSpec.EXACTLY);
+    mTextView.measure(measureExactly500dp, measureExactly500dp);
+    mTextView.layout(dp2Px(0), dp2Px(0), dp2Px(500), dp2Px(500));
+  }
+
+  private int dp2Px(float dp) {
+    if (mDisplayMetrics == null) {
+      mDisplayMetrics = InstrumentationRegistry.getContext().getResources().getDisplayMetrics();
+    }
+    return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, mDisplayMetrics);
+  }
+
+  public static class TestPreOLinkAccessibilityHelper extends PreOLinkAccessibilityHelper {
+
+    TestPreOLinkAccessibilityHelper(TextView view) {
+      super(view);
     }
 
-    @Test
-    public void testRtlMultilineLink() {
-        String iwLoremIpsum = "אחר על רביעי אקטואליה. לוח דת אחרות המקובל רומנית, מיזמים מועמדים "
-                + "האנציקלופדיה בה צ'ט. מתן מה שנורו לערוך ייִדיש, בקר או החול אנתרופולוגיה, עוד "
-                + "דפים המחשב מיזמים ב.";
-        SpannableStringBuilder ssb = new SpannableStringBuilder(iwLoremIpsum);
-        ssb.setSpan(LINK_SPAN, 50, 100, 0 /* flags */);
-        initTextView(ssb);
-
-        AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
-        mHelper.onPopulateNodeForVirtualView(50, info);
-
-        assertEquals("LinkSpan description should match the span",
-                iwLoremIpsum.substring(50, 100),
-                info.getContentDescription().toString());
-        Rect bounds = new Rect();
-        info.getBoundsInParent(bounds);
-        assertEquals("LinkSpan bounds should match the first line of the span",
-                new Rect(dp2Px(0f), dp2Px(0f), dp2Px(150f), dp2Px(19.5f)), bounds);
-
-        info.recycle();
+    @Override
+    public int getVirtualViewAt(float x, float y) {
+      return super.getVirtualViewAt(x, y);
     }
 
-    @Test
-    public void testBidiMultilineLink() {
-        String iwLoremIpsum = "אחר על רביעי אקטואליה. לוח דת אחרות המקובל רומנית, מיזמים מועמדים "
-                + "האנציקלופדיה בה צ'ט. מתן מה שנורו לערוך ייִדיש, בקר או החול אנתרופולוגיה, עוד "
-                + "דפים המחשב מיזמים ב.";
-        BidiFormatter formatter = BidiFormatter.getInstance(false /* rtlContext */);
-        SpannableStringBuilder ssb = new SpannableStringBuilder();
-        ssb.append("hello ").append(formatter.unicodeWrap(iwLoremIpsum)).append(" world");
-        ssb.setSpan(LINK_SPAN,
-                "hello ".length() + 2, // Add two for the characters added by BidiFormatter
-                "hello ".length() + 2 + iwLoremIpsum.length(),
-                0 /* flags */);
-        initTextView(ssb);
-
-        AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
-        mHelper.onPopulateNodeForVirtualView("hello ".length() + 2, info);
-
-        assertEquals("LinkSpan description should match the span",
-                iwLoremIpsum,
-                info.getContentDescription().toString());
-        Rect bounds = new Rect();
-        info.getBoundsInParent(bounds);
-        assertEquals("LinkSpan bounds should match the first line of the span",
-                new Rect(dp2Px(491.5f), dp2Px(0f), dp2Px(500f), dp2Px(19.5f)), bounds);
-
-        info.recycle();
+    @Override
+    public void getVisibleVirtualViews(List<Integer> virtualViewIds) {
+      super.getVisibleVirtualViews(virtualViewIds);
     }
 
-    @Test
-    public void testMethodDelegation() {
-        initTextView();
-        ExploreByTouchHelper delegate = mock(TestPreOLinkAccessibilityHelper.class);
-        LinkAccessibilityHelper helper = new LinkAccessibilityHelper(delegate);
-
-        AccessibilityEvent accessibilityEvent =
-                AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_CLICKED);
-
-        helper.sendAccessibilityEvent(mTextView, AccessibilityEvent.TYPE_VIEW_CLICKED);
-        verify(delegate).sendAccessibilityEvent(
-                same(mTextView),
-                eq(AccessibilityEvent.TYPE_VIEW_CLICKED));
-
-        helper.sendAccessibilityEventUnchecked(mTextView, accessibilityEvent);
-        verify(delegate).sendAccessibilityEventUnchecked(same(mTextView), same(accessibilityEvent));
-
-        helper.performAccessibilityAction(
-                mTextView,
-                AccessibilityActionCompat.ACTION_CLICK.getId(),
-                Bundle.EMPTY);
-        verify(delegate).performAccessibilityAction(
-                same(mTextView),
-                eq(AccessibilityActionCompat.ACTION_CLICK.getId()),
-                eq(Bundle.EMPTY));
-
-        helper.dispatchPopulateAccessibilityEvent(
-                mTextView,
-                accessibilityEvent);
-        verify(delegate).dispatchPopulateAccessibilityEvent(
-                same(mTextView),
-                same(accessibilityEvent));
-
-        MotionEvent motionEvent = MotionEvent.obtain(0, 0, 0, 0, 0, 0);
-        helper.dispatchHoverEvent(motionEvent);
-        verify(delegate).dispatchHoverEvent(eq(motionEvent));
-
-        helper.getAccessibilityNodeProvider(mTextView);
-        verify(delegate).getAccessibilityNodeProvider(same(mTextView));
-
-        helper.onInitializeAccessibilityEvent(mTextView, accessibilityEvent);
-        verify(delegate).onInitializeAccessibilityEvent(
-                same(mTextView),
-                eq(accessibilityEvent));
-
-        AccessibilityNodeInfoCompat accessibilityNodeInfo = AccessibilityNodeInfoCompat.obtain();
-        helper.onInitializeAccessibilityNodeInfo(mTextView, accessibilityNodeInfo);
-        verify(delegate).onInitializeAccessibilityNodeInfo(
-                same(mTextView),
-                same(accessibilityNodeInfo));
-
-        helper.onPopulateAccessibilityEvent(mTextView, accessibilityEvent);
-        verify(delegate).onPopulateAccessibilityEvent(
-                same(mTextView),
-                same(accessibilityEvent));
-
-        FrameLayout parent = new FrameLayout(InstrumentationRegistry.getTargetContext());
-        helper.onRequestSendAccessibilityEvent(parent, mTextView, accessibilityEvent);
-        verify(delegate).onRequestSendAccessibilityEvent(
-                same(parent),
-                same(mTextView),
-                same(accessibilityEvent));
+    @Override
+    public void onPopulateEventForVirtualView(int virtualViewId, AccessibilityEvent event) {
+      super.onPopulateEventForVirtualView(virtualViewId, event);
     }
 
-    private void initTextView() {
-        SpannableStringBuilder ssb = new SpannableStringBuilder("Hello world");
-        ssb.setSpan(LINK_SPAN, 1, 2, 0 /* flags */);
-        initTextView(ssb);
+    @Override
+    public void onPopulateNodeForVirtualView(int virtualViewId, AccessibilityNodeInfoCompat info) {
+      super.onPopulateNodeForVirtualView(virtualViewId, info);
     }
 
-    private void initTextView(CharSequence text) {
-        mTextView = new TextView(InstrumentationRegistry.getContext());
-        mTextView.setSingleLine(false);
-        mTextView.setText(text);
-        mTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 15);
-        mHelper = new TestPreOLinkAccessibilityHelper(mTextView);
-
-        int measureExactly500dp = View.MeasureSpec.makeMeasureSpec(dp2Px(500),
-                View.MeasureSpec.EXACTLY);
-        mTextView.measure(measureExactly500dp, measureExactly500dp);
-        mTextView.layout(dp2Px(0), dp2Px(0), dp2Px(500), dp2Px(500));
+    @Override
+    public boolean onPerformActionForVirtualView(int virtualViewId, int action, Bundle arguments) {
+      return super.onPerformActionForVirtualView(virtualViewId, action, arguments);
     }
-
-    private int dp2Px(float dp) {
-        if (mDisplayMetrics == null) {
-            mDisplayMetrics =
-                    InstrumentationRegistry.getContext().getResources().getDisplayMetrics();
-        }
-        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, mDisplayMetrics);
-    }
-
-    public static class TestPreOLinkAccessibilityHelper extends PreOLinkAccessibilityHelper {
-
-        TestPreOLinkAccessibilityHelper(TextView view) {
-            super(view);
-        }
-
-        @Override
-        public int getVirtualViewAt(float x, float y) {
-            return super.getVirtualViewAt(x, y);
-        }
-
-        @Override
-        public void getVisibleVirtualViews(List<Integer> virtualViewIds) {
-            super.getVisibleVirtualViews(virtualViewIds);
-        }
-
-        @Override
-        public void onPopulateEventForVirtualView(int virtualViewId, AccessibilityEvent event) {
-            super.onPopulateEventForVirtualView(virtualViewId, event);
-        }
-
-        @Override
-        public void onPopulateNodeForVirtualView(int virtualViewId,
-                AccessibilityNodeInfoCompat info) {
-            super.onPopulateNodeForVirtualView(virtualViewId, info);
-        }
-
-        @Override
-        public boolean onPerformActionForVirtualView(int virtualViewId, int action,
-                Bundle arguments) {
-            return super.onPerformActionForVirtualView(virtualViewId, action, arguments);
-        }
-    }
+  }
 }
diff --git a/library/gingerbread/test/robotest/src/com/android/setupwizardlib/items/ExpandableSwitchItemTest.java b/library/gingerbread/test/robotest/src/com/android/setupwizardlib/items/ExpandableSwitchItemTest.java
index 6192061..c43b4ab 100644
--- a/library/gingerbread/test/robotest/src/com/android/setupwizardlib/items/ExpandableSwitchItemTest.java
+++ b/library/gingerbread/test/robotest/src/com/android/setupwizardlib/items/ExpandableSwitchItemTest.java
@@ -17,7 +17,6 @@
 package com.android.setupwizardlib.items;
 
 import static com.google.common.truth.Truth.assertThat;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -27,133 +26,139 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.TextView;
-
 import com.android.setupwizardlib.R;
-import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
 import com.android.setupwizardlib.view.CheckableLinearLayout;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.annotation.Config;
 
-@RunWith(SuwLibRobolectricTestRunner.class)
-@Config(sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
+@RunWith(RobolectricTestRunner.class)
+@Config(sdk = {Config.OLDEST_SDK, Config.NEWEST_SDK})
 public class ExpandableSwitchItemTest {
 
-    private TextView mSummaryView;
-    private ExpandableSwitchItem mItem;
+  private TextView mSummaryView;
+  private ExpandableSwitchItem mItem;
 
-    @Before
-    public void setUp() {
-        mItem = new ExpandableSwitchItem();
-        mItem.setTitle("TestTitle");
-        mItem.setCollapsedSummary("TestSummary");
-        mItem.setExpandedSummary("TestSummaryExpanded");
-    }
+  @Before
+  public void setUp() {
+    mItem = new ExpandableSwitchItem();
+    mItem.setTitle("TestTitle");
+    mItem.setCollapsedSummary("TestSummary");
+    mItem.setExpandedSummary("TestSummaryExpanded");
+  }
 
-    @Test
-    public void testInitialState() {
-        View view = createLayout();
-        mItem.onBindView(view);
+  @Test
+  public void testInitialState() {
+    View view = createLayout();
+    mItem.onBindView(view);
 
-        assertEquals("Collapsed summary should be TestSummary",
-                "TestSummary", mItem.getCollapsedSummary());
-        assertEquals("Expanded summary should be TestSummaryExpanded",
-                "TestSummaryExpanded", mItem.getExpandedSummary());
+    assertEquals(
+        "Collapsed summary should be TestSummary", "TestSummary", mItem.getCollapsedSummary());
+    assertEquals(
+        "Expanded summary should be TestSummaryExpanded",
+        "TestSummaryExpanded",
+        mItem.getExpandedSummary());
 
-        assertEquals("Should be collapsed initially", "TestSummary", mItem.getSummary());
-        assertEquals("Summary view should display collapsed summary",
-                "TestSummary", mSummaryView.getText());
+    assertEquals("Should be collapsed initially", "TestSummary", mItem.getSummary());
+    assertEquals(
+        "Summary view should display collapsed summary", "TestSummary", mSummaryView.getText());
 
-        assertFalse("Expandable switch item itself should not be focusable", view.isFocusable());
+    assertFalse("Expandable switch item itself should not be focusable", view.isFocusable());
 
-        View switchContent = view.findViewById(R.id.suw_items_expandable_switch_content);
-        assertThat(switchContent).isInstanceOf(CheckableLinearLayout.class);
-        assertThat(switchContent.isFocusable())
-                .named("expandable content focusable")
-                .isTrue();
-    }
+    View switchContent = view.findViewById(R.id.suw_items_expandable_switch_content);
+    assertThat(switchContent).isInstanceOf(CheckableLinearLayout.class);
+    assertThat(switchContent.isFocusable()).named("expandable content focusable").isTrue();
+  }
 
-    @Test
-    public void testExpanded() {
-        View view = createLayout();
-        mItem.onBindView(view);
+  @Test
+  public void testExpanded() {
+    View view = createLayout();
+    mItem.onBindView(view);
 
-        mItem.setExpanded(true);
+    mItem.setExpanded(true);
 
-        assertEquals("Collapsed summary should be TestSummary",
-                "TestSummary", mItem.getCollapsedSummary());
-        assertEquals("Expanded summary should be TestSummaryExpanded",
-                "TestSummaryExpanded", mItem.getExpandedSummary());
+    assertEquals(
+        "Collapsed summary should be TestSummary", "TestSummary", mItem.getCollapsedSummary());
+    assertEquals(
+        "Expanded summary should be TestSummaryExpanded",
+        "TestSummaryExpanded",
+        mItem.getExpandedSummary());
 
-        assertTrue("Should be expanded", mItem.isExpanded());
-        assertEquals("getSummary should be expanded summary",
-                "TestSummaryExpanded", mItem.getSummary());
-    }
+    assertTrue("Should be expanded", mItem.isExpanded());
+    assertEquals(
+        "getSummary should be expanded summary", "TestSummaryExpanded", mItem.getSummary());
+  }
 
-    @Test
-    public void testCollapsed() {
-        View view = createLayout();
-        mItem.onBindView(view);
+  @Test
+  public void testCollapsed() {
+    View view = createLayout();
+    mItem.onBindView(view);
 
-        mItem.setExpanded(true);
-        assertTrue("Should be expanded", mItem.isExpanded());
+    mItem.setExpanded(true);
+    assertTrue("Should be expanded", mItem.isExpanded());
 
-        mItem.setExpanded(false);
+    mItem.setExpanded(false);
 
-        assertEquals("Collapsed summary should be TestSummary",
-                "TestSummary", mItem.getCollapsedSummary());
-        assertEquals("Expanded summary should be TestSummaryExpanded",
-                "TestSummaryExpanded", mItem.getExpandedSummary());
+    assertEquals(
+        "Collapsed summary should be TestSummary", "TestSummary", mItem.getCollapsedSummary());
+    assertEquals(
+        "Expanded summary should be TestSummaryExpanded",
+        "TestSummaryExpanded",
+        mItem.getExpandedSummary());
 
-        assertFalse("Should be expanded", mItem.isExpanded());
-        assertEquals("getSummary should be collapsed summary",
-                "TestSummary", mItem.getSummary());
-    }
+    assertFalse("Should be expanded", mItem.isExpanded());
+    assertEquals("getSummary should be collapsed summary", "TestSummary", mItem.getSummary());
+  }
 
-    @Test
-    public void testClick() {
-        View view = createLayout();
-        mItem.onBindView(view);
+  @Test
+  public void testClick() {
+    View view = createLayout();
+    mItem.onBindView(view);
 
-        assertFalse("Should not be expanded initially", mItem.isExpanded());
+    assertFalse("Should not be expanded initially", mItem.isExpanded());
 
-        final View content = view.findViewById(R.id.suw_items_expandable_switch_content);
-        content.performClick();
-        assertTrue("Should be expanded after clicking", mItem.isExpanded());
+    final View content = view.findViewById(R.id.suw_items_expandable_switch_content);
+    content.performClick();
+    assertTrue("Should be expanded after clicking", mItem.isExpanded());
 
-        content.performClick();
-        assertFalse("Should be collapsed again after clicking", mItem.isExpanded());
-    }
+    content.performClick();
+    assertFalse("Should be collapsed again after clicking", mItem.isExpanded());
+  }
 
-    @Test
-    public void testDrawableState() {
-        final View view =
-                LayoutInflater.from(application).inflate(mItem.getLayoutResource(), null);
-        mItem.onBindView(view);
+  @Test
+  public void testDrawableState() {
+    final View view = LayoutInflater.from(application).inflate(mItem.getLayoutResource(), null);
+    mItem.onBindView(view);
 
-        final View titleView = view.findViewById(R.id.suw_items_title);
-        assertThat(titleView.getDrawableState()).asList().named("Drawable state")
-                .doesNotContain(android.R.attr.state_checked);
+    final View titleView = view.findViewById(R.id.suw_items_title);
+    assertThat(titleView.getDrawableState())
+        .asList()
+        .named("Drawable state")
+        .doesNotContain(android.R.attr.state_checked);
 
-        mItem.setExpanded(true);
-        mItem.onBindView(view);
-        assertThat(titleView.getDrawableState()).asList().named("Drawable state")
-                .contains(android.R.attr.state_checked);
+    mItem.setExpanded(true);
+    mItem.onBindView(view);
+    assertThat(titleView.getDrawableState())
+        .asList()
+        .named("Drawable state")
+        .contains(android.R.attr.state_checked);
 
-        mItem.setExpanded(false);
-        mItem.onBindView(view);
-        assertThat(titleView.getDrawableState()).asList().named("Drawable state")
-                .doesNotContain(android.R.attr.state_checked);
-    }
+    mItem.setExpanded(false);
+    mItem.onBindView(view);
+    assertThat(titleView.getDrawableState())
+        .asList()
+        .named("Drawable state")
+        .doesNotContain(android.R.attr.state_checked);
+  }
 
-    private ViewGroup createLayout() {
-        ViewGroup root =
-                (ViewGroup) LayoutInflater.from(application)
-                        .inflate(R.layout.suw_items_expandable_switch, null);
-        mSummaryView = root.findViewById(R.id.suw_items_summary);
+  private ViewGroup createLayout() {
+    ViewGroup root =
+        (ViewGroup)
+            LayoutInflater.from(application).inflate(R.layout.suw_items_expandable_switch, null);
+    mSummaryView = root.findViewById(R.id.suw_items_summary);
 
-        return root;
-    }
+    return root;
+  }
 }
diff --git a/library/gingerbread/test/robotest/src/com/android/setupwizardlib/items/SwitchItemTest.java b/library/gingerbread/test/robotest/src/com/android/setupwizardlib/items/SwitchItemTest.java
index 05d6e5b..422d979 100644
--- a/library/gingerbread/test/robotest/src/com/android/setupwizardlib/items/SwitchItemTest.java
+++ b/library/gingerbread/test/robotest/src/com/android/setupwizardlib/items/SwitchItemTest.java
@@ -17,7 +17,6 @@
 package com.android.setupwizardlib.items;
 
 import static com.google.common.truth.Truth.assertThat;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -26,219 +25,232 @@
 import android.annotation.TargetApi;
 import android.os.Build.VERSION;
 import android.os.Build.VERSION_CODES;
+import androidx.appcompat.widget.SwitchCompat;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
+import android.widget.LinearLayout;
 import android.widget.TextView;
-
-import androidx.appcompat.widget.SwitchCompat;
-
 import com.android.setupwizardlib.R;
-import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
-
 import org.junit.Assume;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.annotation.Config;
 
-@RunWith(SuwLibRobolectricTestRunner.class)
-@Config(sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
+@RunWith(RobolectricTestRunner.class)
+@Config(sdk = {Config.OLDEST_SDK, Config.NEWEST_SDK})
 public class SwitchItemTest {
 
-    private SwitchCompat mSwitch;
+  private SwitchCompat mSwitch;
 
-    @Test
-    public void testLayout() {
-        Assume.assumeTrue(VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP);
-        SwitchItem item = new SwitchItem();
-        LayoutInflater inflater = LayoutInflater.from(application);
-        ViewGroup layout = (ViewGroup) inflater.inflate(item.getDefaultLayoutResource(), null);
-        assertThat(layout.getClipToPadding()).isFalse();
+  @Test
+  public void defaultLayout_baselineAligned_shouldBeFalse() {
+    Assume.assumeTrue(VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP);
+    LayoutInflater inflater = LayoutInflater.from(application);
+    SwitchItem item = new SwitchItem();
+    LinearLayout layout = (LinearLayout) inflater.inflate(item.getDefaultLayoutResource(), null);
+    assertThat(layout.isBaselineAligned()).isFalse();
+  }
+
+  @Test
+  public void verboseLayout_clipPadding_shouldBeFalse() {
+    Assume.assumeTrue(VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP);
+    LayoutInflater inflater = LayoutInflater.from(application);
+    SwitchItem item =
+        new SwitchItem(
+            application,
+            Robolectric.buildAttributeSet()
+                .addAttribute(android.R.attr.layout, "@layout/suw_items_switch_verbose")
+                .build());
+    ViewGroup layout = (ViewGroup) inflater.inflate(item.getLayoutResource(), null);
+    assertThat(layout.getClipToPadding()).isFalse();
+  }
+
+  @Test
+  public void testChecked() {
+    SwitchItem item = new SwitchItem();
+    item.setTitle("TestTitle");
+    item.setSummary("TestSummary");
+    View view = createLayout();
+
+    item.setChecked(true);
+
+    item.onBindView(view);
+
+    assertTrue("Switch should be checked", mSwitch.isChecked());
+  }
+
+  @Test
+  public void testNotChecked() {
+    SwitchItem item = new SwitchItem();
+    item.setTitle("TestTitle");
+    item.setSummary("TestSummary");
+    View view = createLayout();
+
+    item.setChecked(false);
+
+    item.onBindView(view);
+
+    assertFalse("Switch should be unchecked", mSwitch.isChecked());
+  }
+
+  @Test
+  public void testListener() {
+    SwitchItem item = new SwitchItem();
+    item.setTitle("TestTitle");
+    item.setSummary("TestSummary");
+    View view = createLayout();
+
+    item.setChecked(true);
+
+    final TestOnCheckedChangeListener listener = new TestOnCheckedChangeListener();
+    item.setOnCheckedChangeListener(listener);
+
+    item.onBindView(view);
+
+    assertTrue("Switch should be checked", mSwitch.isChecked());
+    mSwitch.setChecked(false);
+
+    assertTrue("Listener should be called", listener.mCalled);
+    assertFalse("Listener should not be checked", listener.mChecked);
+
+    mSwitch.setChecked(true);
+
+    assertTrue("Listener should be called", listener.mCalled);
+    assertTrue("Listener should be checked", listener.mChecked);
+  }
+
+  @Test
+  public void testRebind() {
+    SwitchItem item1 = new SwitchItem();
+    item1.setTitle("TestTitle1");
+    item1.setSummary("TestSummary1");
+    item1.setChecked(false);
+
+    SwitchItem item2 = new SwitchItem();
+    item2.setTitle("TestTitle2");
+    item2.setSummary("TestSummary2");
+    item2.setChecked(true);
+
+    View view = createLayout();
+
+    item1.onBindView(view);
+    item2.onBindView(view);
+
+    // Switch should be bound to item2, and therefore checked
+    assertTrue("Switch should be checked", mSwitch.isChecked());
+
+    // Switching the switch to false should change the checked state of item 2 only
+    mSwitch.setChecked(false);
+    assertFalse("Item1 should still be unchecked", item1.isChecked());
+    assertFalse("Item2 should not be checked", item2.isChecked());
+
+    // Switching the switch to true should change the checked state of item 2 only
+    mSwitch.setChecked(true);
+    assertFalse("Item1 should still be unchecked", item1.isChecked());
+    assertTrue("Item2 should be checked", item2.isChecked());
+  }
+
+  @Test
+  public void testListenerSetChecked() {
+    // Check that calling setChecked on the item will also call the listener.
+
+    SwitchItem item = new SwitchItem();
+    item.setTitle("TestTitle");
+    item.setSummary("TestSummary");
+    View view = createLayout();
+
+    item.setChecked(true);
+
+    final TestOnCheckedChangeListener listener = new TestOnCheckedChangeListener();
+    item.setOnCheckedChangeListener(listener);
+
+    item.onBindView(view);
+
+    assertTrue("Switch should be checked", mSwitch.isChecked());
+    item.setChecked(false);
+
+    assertTrue("Listener should be called", listener.mCalled);
+    assertFalse("Listener should not be checked", listener.mChecked);
+
+    item.setChecked(true);
+
+    assertTrue("Listener should be called", listener.mCalled);
+    assertTrue("Listener should be checked", listener.mChecked);
+  }
+
+  @Test
+  public void testToggle() {
+    SwitchItem item = new SwitchItem();
+    item.setTitle("TestTitle");
+    item.setSummary("TestSummary");
+    View view = createLayout();
+
+    item.setChecked(true);
+    item.onBindView(view);
+
+    assertTrue("Switch should be checked", mSwitch.isChecked());
+
+    item.toggle(view);
+
+    assertFalse("Switch should be unchecked", mSwitch.isChecked());
+  }
+
+  @TargetApi(VERSION_CODES.JELLY_BEAN_MR1)
+  @Config(minSdk = VERSION_CODES.JELLY_BEAN_MR1)
+  @Test
+  public void testAccessibility() {
+    SwitchItem item = new SwitchItem();
+    item.setTitle("TestTitle");
+    item.setSummary("TestSummary");
+
+    View view = LayoutInflater.from(application).inflate(R.layout.suw_items_switch, null);
+    item.onBindView(view);
+
+    final View titleView = view.findViewById(R.id.suw_items_title);
+    assertEquals(
+        "Title view should label for switch", R.id.suw_items_switch, titleView.getLabelFor());
+  }
+
+  private ViewGroup createLayout() {
+    ViewGroup root = new FrameLayout(application);
+
+    TextView titleView = new TextView(application);
+    titleView.setId(R.id.suw_items_title);
+    root.addView(titleView);
+
+    TextView summaryView = new TextView(application);
+    summaryView.setId(R.id.suw_items_summary);
+    root.addView(summaryView);
+
+    FrameLayout iconContainer = new FrameLayout(application);
+    iconContainer.setId(R.id.suw_items_icon_container);
+    root.addView(iconContainer);
+
+    ImageView iconView = new ImageView(application);
+    iconView.setId(R.id.suw_items_icon);
+    iconContainer.addView(iconView);
+
+    mSwitch = new SwitchCompat(application);
+    mSwitch.setId(R.id.suw_items_switch);
+    root.addView(mSwitch);
+
+    return root;
+  }
+
+  private static class TestOnCheckedChangeListener implements SwitchItem.OnCheckedChangeListener {
+
+    boolean mCalled = false;
+    boolean mChecked = false;
+
+    @Override
+    public void onCheckedChange(SwitchItem item, boolean isChecked) {
+      mCalled = true;
+      mChecked = isChecked;
     }
-
-    @Test
-    public void testChecked() {
-        SwitchItem item = new SwitchItem();
-        item.setTitle("TestTitle");
-        item.setSummary("TestSummary");
-        View view = createLayout();
-
-        item.setChecked(true);
-
-        item.onBindView(view);
-
-        assertTrue("Switch should be checked", mSwitch.isChecked());
-    }
-
-    @Test
-    public void testNotChecked() {
-        SwitchItem item = new SwitchItem();
-        item.setTitle("TestTitle");
-        item.setSummary("TestSummary");
-        View view = createLayout();
-
-        item.setChecked(false);
-
-        item.onBindView(view);
-
-        assertFalse("Switch should be unchecked", mSwitch.isChecked());
-    }
-
-    @Test
-    public void testListener() {
-        SwitchItem item = new SwitchItem();
-        item.setTitle("TestTitle");
-        item.setSummary("TestSummary");
-        View view = createLayout();
-
-        item.setChecked(true);
-
-        final TestOnCheckedChangeListener listener = new TestOnCheckedChangeListener();
-        item.setOnCheckedChangeListener(listener);
-
-        item.onBindView(view);
-
-        assertTrue("Switch should be checked", mSwitch.isChecked());
-        mSwitch.setChecked(false);
-
-        assertTrue("Listener should be called", listener.mCalled);
-        assertFalse("Listener should not be checked", listener.mChecked);
-
-        mSwitch.setChecked(true);
-
-        assertTrue("Listener should be called", listener.mCalled);
-        assertTrue("Listener should be checked", listener.mChecked);
-    }
-
-    @Test
-    public void testRebind() {
-        SwitchItem item1 = new SwitchItem();
-        item1.setTitle("TestTitle1");
-        item1.setSummary("TestSummary1");
-        item1.setChecked(false);
-
-        SwitchItem item2 = new SwitchItem();
-        item2.setTitle("TestTitle2");
-        item2.setSummary("TestSummary2");
-        item2.setChecked(true);
-
-        View view = createLayout();
-
-        item1.onBindView(view);
-        item2.onBindView(view);
-
-        // Switch should be bound to item2, and therefore checked
-        assertTrue("Switch should be checked", mSwitch.isChecked());
-
-        // Switching the switch to false should change the checked state of item 2 only
-        mSwitch.setChecked(false);
-        assertFalse("Item1 should still be unchecked", item1.isChecked());
-        assertFalse("Item2 should not be checked", item2.isChecked());
-
-        // Switching the switch to true should change the checked state of item 2 only
-        mSwitch.setChecked(true);
-        assertFalse("Item1 should still be unchecked", item1.isChecked());
-        assertTrue("Item2 should be checked", item2.isChecked());
-    }
-
-    @Test
-    public void testListenerSetChecked() {
-        // Check that calling setChecked on the item will also call the listener.
-
-        SwitchItem item = new SwitchItem();
-        item.setTitle("TestTitle");
-        item.setSummary("TestSummary");
-        View view = createLayout();
-
-        item.setChecked(true);
-
-        final TestOnCheckedChangeListener listener = new TestOnCheckedChangeListener();
-        item.setOnCheckedChangeListener(listener);
-
-        item.onBindView(view);
-
-        assertTrue("Switch should be checked", mSwitch.isChecked());
-        item.setChecked(false);
-
-        assertTrue("Listener should be called", listener.mCalled);
-        assertFalse("Listener should not be checked", listener.mChecked);
-
-        item.setChecked(true);
-
-        assertTrue("Listener should be called", listener.mCalled);
-        assertTrue("Listener should be checked", listener.mChecked);
-    }
-
-    @Test
-    public void testToggle() {
-        SwitchItem item = new SwitchItem();
-        item.setTitle("TestTitle");
-        item.setSummary("TestSummary");
-        View view = createLayout();
-
-        item.setChecked(true);
-        item.onBindView(view);
-
-        assertTrue("Switch should be checked", mSwitch.isChecked());
-
-        item.toggle(view);
-
-        assertFalse("Switch should be unchecked", mSwitch.isChecked());
-    }
-
-    @TargetApi(VERSION_CODES.JELLY_BEAN_MR1)
-    @Config(minSdk = VERSION_CODES.JELLY_BEAN_MR1)
-    @Test
-    public void testAccessibility() {
-        SwitchItem item = new SwitchItem();
-        item.setTitle("TestTitle");
-        item.setSummary("TestSummary");
-
-        View view = LayoutInflater.from(application).inflate(R.layout.suw_items_switch, null);
-        item.onBindView(view);
-
-        final View titleView = view.findViewById(R.id.suw_items_title);
-        assertEquals("Title view should label for switch",
-                R.id.suw_items_switch, titleView.getLabelFor());
-    }
-
-    private ViewGroup createLayout() {
-        ViewGroup root = new FrameLayout(application);
-
-        TextView titleView = new TextView(application);
-        titleView.setId(R.id.suw_items_title);
-        root.addView(titleView);
-
-        TextView summaryView = new TextView(application);
-        summaryView.setId(R.id.suw_items_summary);
-        root.addView(summaryView);
-
-        FrameLayout iconContainer = new FrameLayout(application);
-        iconContainer.setId(R.id.suw_items_icon_container);
-        root.addView(iconContainer);
-
-        ImageView iconView = new ImageView(application);
-        iconView.setId(R.id.suw_items_icon);
-        iconContainer.addView(iconView);
-
-        mSwitch = new SwitchCompat(application);
-        mSwitch.setId(R.id.suw_items_switch);
-        root.addView(mSwitch);
-
-        return root;
-    }
-
-    private static class TestOnCheckedChangeListener implements SwitchItem.OnCheckedChangeListener {
-
-        boolean mCalled = false;
-        boolean mChecked = false;
-
-        @Override
-        public void onCheckedChange(SwitchItem item, boolean isChecked) {
-            mCalled = true;
-            mChecked = isChecked;
-        }
-    }
+  }
 }
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
index 7a08235..1f76d35 100644
--- a/library/gingerbread/test/robotest/src/com/android/setupwizardlib/util/DimensionConsistencyTest.java
+++ b/library/gingerbread/test/robotest/src/com/android/setupwizardlib/util/DimensionConsistencyTest.java
@@ -24,45 +24,42 @@
 import android.util.DisplayMetrics;
 import android.util.TypedValue;
 import android.view.ContextThemeWrapper;
-
 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.RobolectricTestRunner;
 import org.robolectric.annotation.Config;
 
-@RunWith(SuwLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 @Config(sdk = Config.ALL_SDKS)
 public class DimensionConsistencyTest {
 
-    // Visual height of the framework switch widget
-    private static final int SWTICH_HEIGHT_DP = 26;
+  // Visual height of the framework switch widget
+  private static final int SWTICH_HEIGHT_DP = 26;
 
-    private Context mContext;
+  private Context mContext;
 
-    @Before
-    public void setUp() {
-        mContext = new ContextThemeWrapper(application, R.style.SuwThemeGlif_Light);
-    }
+  @Before
+  public void setUp() {
+    mContext = new ContextThemeWrapper(application, R.style.SuwThemeGlif_Light);
+  }
 
-    @Test
-    public void testSwitchPaddingTop() {
-        final Resources res = mContext.getResources();
+  @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));
-    }
+    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);
-    }
+  private int dp2Px(float dp) {
+    DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();
+    return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, displayMetrics);
+  }
 }
diff --git a/library/grandfathered_lint_checks.txt b/library/grandfathered_lint_checks.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/library/grandfathered_lint_checks.txt
diff --git a/library/main/res/color-v23/suw_flat_button_highlight.xml b/library/main/res/color-v23/suw_flat_button_highlight.xml
index c5be14f..cdb1305 100644
--- a/library/main/res/color-v23/suw_flat_button_highlight.xml
+++ b/library/main/res/color-v23/suw_flat_button_highlight.xml
@@ -17,5 +17,5 @@
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:color="?android:attr/colorAccent"
-        android:alpha="0.24" />
+        android:alpha="?attr/suwButtonHighlightAlpha" />
 </selector>
diff --git a/library/main/res/values-as/strings.xml b/library/main/res/values-as/strings.xml
new file mode 100644
index 0000000..be6e06b
--- /dev/null
+++ b/library/main/res/values-as/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+    Copyright (C) 2015 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ -->
+
+<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">"পৰৱৰ্তী"</string>
+    <string name="suw_back_button_label" msgid="1460929053642711025">"উভতি যাওক"</string>
+    <string name="suw_more_button_label" msgid="7769076059705546563">"অধিক"</string>
+</resources>
diff --git a/library/main/res/values-en-rXC/strings.xml b/library/main/res/values-en-rXC/strings.xml
index 693af6b..7a7836b 100644
--- a/library/main/res/values-en-rXC/strings.xml
+++ b/library/main/res/values-en-rXC/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">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‏‎‎‎‏‎‏‏‏‎‎‏‎‏‏‎‎‎‏‎‏‏‎‎‎‎‏‎‏‎‏‏‎‎‏‎‎‎‏‏‏‎‎‎‏‎‎‎‏‏‏‎‏‎‎Next‎‏‎‎‏‎"</string>
-    <string name="suw_back_button_label" msgid="1460929053642711025">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‎‎‏‎‎‎‏‏‎‎‏‎‎‎‎‏‏‎‎‏‏‏‎‏‏‏‏‏‎‎‏‎‎‎‏‏‎‏‎‏‏‏‏‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎Back‎‏‎‎‏‎"</string>
-    <string name="suw_more_button_label" msgid="7769076059705546563">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‏‎‏‎‎‎‏‎‏‎‎‏‏‎‏‏‎‎‎‎‎‏‏‏‎‎‏‎‎‏‏‏‎‏‏‏‏‎‏‏‎‎‏‎‏‏‏‎‏‎‎‎‎‏‏‎More‎‏‎‎‏‎"</string>
+    <string name="suw_next_button_label" msgid="7269625133873553978">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‏‎‎‎‏‎‏‏‏‎‎‏‎‏‏‎‎‎‏‎‏‏‎‎‎‎‏‎‏‎‏‏‎‎‏‎‎‎‏‏‏‎‎‎‏‎‎‎‏‏‏‎‏‎‎Next‎‏‎‎‏‎"</string>
+    <string name="suw_back_button_label" msgid="1460929053642711025">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‎‎‏‎‎‎‏‏‎‎‏‎‎‎‎‏‏‎‎‏‏‏‎‏‏‏‏‏‎‎‏‎‎‎‏‏‎‏‎‏‏‏‏‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎Back‎‏‎‎‏‎"</string>
+    <string name="suw_more_button_label" msgid="7769076059705546563">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‏‎‏‎‎‎‏‎‏‎‎‏‏‎‏‏‎‎‎‎‎‏‏‏‎‎‏‎‎‏‏‏‎‏‏‏‏‎‏‏‎‎‏‎‏‏‏‎‏‎‎‎‎‏‏‎More‎‏‎‎‏‎"</string>
 </resources>
diff --git a/library/main/res/values-hi/strings.xml b/library/main/res/values-hi/strings.xml
index 3fb41d3..ec2cd77 100644
--- a/library/main/res/values-hi/strings.xml
+++ b/library/main/res/values-hi/strings.xml
@@ -19,5 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="suw_next_button_label" msgid="7269625133873553978">"आगे बढ़ें"</string>
     <string name="suw_back_button_label" msgid="1460929053642711025">"पीछे"</string>
-    <string name="suw_more_button_label" msgid="7769076059705546563">"अधिक"</string>
+    <string name="suw_more_button_label" msgid="7769076059705546563">"ज़्यादा"</string>
 </resources>
diff --git a/library/main/res/values-mr/strings.xml b/library/main/res/values-mr/strings.xml
index a529655..5c5b6c2 100644
--- a/library/main/res/values-mr/strings.xml
+++ b/library/main/res/values-mr/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">"पुढील"</string>
+    <string name="suw_next_button_label" msgid="7269625133873553978">"पुढे जा"</string>
     <string name="suw_back_button_label" msgid="1460929053642711025">"मागे"</string>
     <string name="suw_more_button_label" msgid="7769076059705546563">"अधिक"</string>
 </resources>
diff --git a/library/main/res/values-night/styles.xml b/library/main/res/values-night/styles.xml
new file mode 100644
index 0000000..912e149
--- /dev/null
+++ b/library/main/res/values-night/styles.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Copyright (C) 2018 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<resources>
+
+    <!-- DayNight themes -->
+    <style name="SuwThemeMaterial.DayNight" parent="SuwThemeMaterial" />
+    <style name="SuwThemeGlif.DayNight" parent="SuwThemeGlif" />
+    <style name="SuwThemeGlifV2.DayNight" parent="SuwThemeGlifV2" />
+    <style name="SuwThemeGlifV3.DayNight" parent="SuwThemeGlifV3" />
+
+</resources>
diff --git a/library/main/res/values-or/strings.xml b/library/main/res/values-or/strings.xml
new file mode 100644
index 0000000..c4d12ff
--- /dev/null
+++ b/library/main/res/values-or/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+    Copyright (C) 2015 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ -->
+
+<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">"ପରବର୍ତ୍ତୀ"</string>
+    <string name="suw_back_button_label" msgid="1460929053642711025">"ପଛକୁ ଫେରନ୍ତୁ"</string>
+    <string name="suw_more_button_label" msgid="7769076059705546563">"ଅଧିକ"</string>
+</resources>
diff --git a/library/main/res/values-v21/styles.xml b/library/main/res/values-v21/styles.xml
index d2c27f6..fe71289 100644
--- a/library/main/res/values-v21/styles.xml
+++ b/library/main/res/values-v21/styles.xml
@@ -38,10 +38,6 @@
 
     <!-- Button styles -->
 
-    <style name="SuwGlifButton.Tertiary" parent="SuwGlifButton.BaseTertiary">
-        <item name="android:fontFamily">sans-serif-medium</item>
-    </style>
-
     <style name="SuwBase.ProgressBarLarge" parent="@android:style/Widget.Material.ProgressBar.Large" />
 
     <style name="SuwFourColorIndeterminateProgressBar" parent="SuwBase.ProgressBarLarge">
@@ -50,6 +46,8 @@
         <item name="android:indeterminateDrawable">@drawable/suw_fourcolor_progress_bar</item>
         <item name="android:indeterminateTint">@null</item>
         <item name="android:indeterminateTintMode">@null</item>
+        <item name="android:paddingStart">@dimen/suw_glif_progress_bar_padding</item>
+        <item name="android:paddingEnd">@dimen/suw_glif_progress_bar_padding</item>
     </style>
 
     <!-- Items styles -->
diff --git a/library/main/res/values/attrs.xml b/library/main/res/values/attrs.xml
index b3fcfe9..86b27fa 100644
--- a/library/main/res/values/attrs.xml
+++ b/library/main/res/values/attrs.xml
@@ -21,6 +21,7 @@
     <attr name="suwLayoutTheme" format="reference" />
     <attr name="suwMarginSides" format="dimension|reference" />
     <attr name="suwEditTextBackgroundColor" format="color" />
+    <attr name="suwButtonHighlightAlpha" format="float" />
 
     <!-- Subset of values in "gravity" in frameworks/base/core/res/res/values/attrs.xml. Only
          horizontal values are listed here as the header does not support vertical gravity. -->
diff --git a/library/main/res/values/dimens.xml b/library/main/res/values/dimens.xml
index 1a8b516..63980ab 100644
--- a/library/main/res/values/dimens.xml
+++ b/library/main/res/values/dimens.xml
@@ -31,7 +31,7 @@
     <dimen name="suw_glif_footer_padding_vertical">8dp</dimen>
     <dimen name="suw_glif_footer_min_height">72dp</dimen>
     <dimen name="suw_glif_margin_sides">24dp</dimen>
-    <dimen name="suw_glif_margin_top">48dp</dimen>
+    <dimen name="suw_glif_margin_top">56dp</dimen>
 
     <dimen name="suw_glif_v3_button_corner_radius">4dp</dimen>
 
@@ -98,7 +98,7 @@
     <!-- This is the extra spacing required to make the leading exactly 32sp -->
     <dimen name="suw_header_title_line_spacing_extra">3.67sp</dimen>
 
-    <dimen name="suw_glif_header_title_margin_top">15dp</dimen>
+    <dimen name="suw_glif_header_title_margin_top">16dp</dimen>
     <dimen name="suw_glif_header_title_margin_bottom">2dp</dimen>
 
     <dimen name="suw_glif_icon_max_height">32dp</dimen>
@@ -139,6 +139,7 @@
     <!-- The margin to compensate for the padding built-in to the widget itself -->
     <dimen name="suw_progress_bar_margin_vertical">-7dp</dimen>
     <dimen name="suw_glif_progress_bar_margin_vertical">7dp</dimen>
+    <dimen name="suw_glif_progress_bar_padding">40dp</dimen>
 
     <!-- Edit Text dimensions -->
     <dimen name="suw_edit_text_min_height">56dp</dimen>
diff --git a/library/main/res/values/styles.xml b/library/main/res/values/styles.xml
index fa2a080..34a917f 100644
--- a/library/main/res/values/styles.xml
+++ b/library/main/res/values/styles.xml
@@ -58,6 +58,12 @@
         <item name="android:activityCloseExitAnimation">@anim/suw_slide_back_out</item>
     </style>
 
+    <!-- DayNight themes -->
+    <style name="SuwThemeMaterial.DayNight" parent="SuwThemeMaterial.Light" />
+    <style name="SuwThemeGlif.DayNight" parent="SuwThemeGlif.Light" />
+    <style name="SuwThemeGlifV2.DayNight" parent="SuwThemeGlifV2.Light" />
+    <style name="SuwThemeGlifV3.DayNight" parent="SuwThemeGlifV3.Light" />
+
     <!-- Content styles -->
 
     <!-- Ignore UnusedResources: Used by clients -->
@@ -166,7 +172,7 @@
         <!-- Before Honeycomb, layout_gravity is needed for FrameLayout to apply the margins -->
         <item name="android:layout_gravity">top</item>
         <item name="android:ellipsize">end</item>
-        <item name="android:maxLines">2</item>
+        <item name="android:maxLines">3</item>
         <item name="android:textSize">@dimen/suw_header_title_size</item>
     </style>
 
@@ -204,11 +210,6 @@
         <item name="android:textAllCaps" tools:targetApi="ice_cream_sandwich">false</item>
     </style>
 
-    <!-- Ignore UnusedResources: used by clients -->
-    <style name="SuwGlifButton.Tertiary"
-        parent="SuwGlifButton.BaseTertiary"
-        tools:ignore="UnusedResources" />
-
     <!-- The start and end paddings are asymmetric because start buttons are borderless buttons
          which aligns the text label. -->
     <style name="SuwGlifButtonBar">
diff --git a/library/main/src/com/android/setupwizardlib/GlifLayout.java b/library/main/src/com/android/setupwizardlib/GlifLayout.java
index 9b30c2f..20fb6b6 100644
--- a/library/main/src/com/android/setupwizardlib/GlifLayout.java
+++ b/library/main/src/com/android/setupwizardlib/GlifLayout.java
@@ -24,6 +24,9 @@
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.os.Build.VERSION_CODES;
+import androidx.annotation.LayoutRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -32,11 +35,6 @@
 import android.widget.ProgressBar;
 import android.widget.ScrollView;
 import android.widget.TextView;
-
-import androidx.annotation.LayoutRes;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
 import com.android.setupwizardlib.template.ButtonFooterMixin;
 import com.android.setupwizardlib.template.ColoredHeaderMixin;
 import com.android.setupwizardlib.template.HeaderMixin;
@@ -50,6 +48,7 @@
  * Layout for the GLIF theme used in Setup Wizard for N.
  *
  * <p>Example usage:
+ *
  * <pre>{@code
  * &lt;com.android.setupwizardlib.GlifLayout
  *     xmlns:android="http://schemas.android.com/apk/res/android"
@@ -66,259 +65,254 @@
  */
 public class GlifLayout extends TemplateLayout {
 
-    private static final String TAG = "GlifLayout";
+  private static final String TAG = "GlifLayout";
 
-    private ColorStateList mPrimaryColor;
+  private ColorStateList primaryColor;
 
-    private boolean mBackgroundPatterned = true;
+  private boolean backgroundPatterned = true;
 
-    /**
-     * The color of the background. If null, the color will inherit from mPrimaryColor.
-     */
-    @Nullable
-    private ColorStateList mBackgroundBaseColor;
+  /** The color of the background. If null, the color will inherit from primaryColor. */
+  @Nullable private ColorStateList backgroundBaseColor;
 
-    private boolean mLayoutFullscreen = true;
+  private boolean layoutFullscreen = true;
 
-    public GlifLayout(Context context) {
-        this(context, 0, 0);
+  public GlifLayout(Context context) {
+    this(context, 0, 0);
+  }
+
+  public GlifLayout(Context context, int template) {
+    this(context, template, 0);
+  }
+
+  public GlifLayout(Context context, int template, int containerId) {
+    super(context, template, containerId);
+    init(null, R.attr.suwLayoutTheme);
+  }
+
+  public GlifLayout(Context context, AttributeSet attrs) {
+    super(context, attrs);
+    init(attrs, R.attr.suwLayoutTheme);
+  }
+
+  @TargetApi(VERSION_CODES.HONEYCOMB)
+  public GlifLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+    super(context, attrs, defStyleAttr);
+    init(attrs, defStyleAttr);
+  }
+
+  // All the constructors delegate to this init method. The 3-argument constructor is not
+  // available in LinearLayout before v11, so call super with the exact same arguments.
+  private void init(AttributeSet attrs, int defStyleAttr) {
+    registerMixin(HeaderMixin.class, new ColoredHeaderMixin(this, attrs, defStyleAttr));
+    registerMixin(IconMixin.class, new IconMixin(this, attrs, defStyleAttr));
+    registerMixin(ProgressBarMixin.class, new ProgressBarMixin(this));
+    registerMixin(ButtonFooterMixin.class, new ButtonFooterMixin(this));
+    final RequireScrollMixin requireScrollMixin = new RequireScrollMixin(this);
+    registerMixin(RequireScrollMixin.class, requireScrollMixin);
+
+    final ScrollView scrollView = getScrollView();
+    if (scrollView != null) {
+      requireScrollMixin.setScrollHandlingDelegate(
+          new ScrollViewScrollHandlingDelegate(requireScrollMixin, scrollView));
     }
 
-    public GlifLayout(Context context, int template) {
-        this(context, template, 0);
+    TypedArray a =
+        getContext().obtainStyledAttributes(attrs, R.styleable.SuwGlifLayout, defStyleAttr, 0);
+
+    ColorStateList primaryColor = a.getColorStateList(R.styleable.SuwGlifLayout_suwColorPrimary);
+    if (primaryColor != null) {
+      setPrimaryColor(primaryColor);
     }
 
-    public GlifLayout(Context context, int template, int containerId) {
-        super(context, template, containerId);
-        init(null, R.attr.suwLayoutTheme);
+    ColorStateList backgroundColor =
+        a.getColorStateList(R.styleable.SuwGlifLayout_suwBackgroundBaseColor);
+    setBackgroundBaseColor(backgroundColor);
+
+    boolean backgroundPatterned =
+        a.getBoolean(R.styleable.SuwGlifLayout_suwBackgroundPatterned, true);
+    setBackgroundPatterned(backgroundPatterned);
+
+    final int footer = a.getResourceId(R.styleable.SuwGlifLayout_suwFooter, 0);
+    if (footer != 0) {
+      inflateFooter(footer);
     }
 
-    public GlifLayout(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        init(attrs, R.attr.suwLayoutTheme);
+    final int stickyHeader = a.getResourceId(R.styleable.SuwGlifLayout_suwStickyHeader, 0);
+    if (stickyHeader != 0) {
+      inflateStickyHeader(stickyHeader);
     }
 
-    @TargetApi(VERSION_CODES.HONEYCOMB)
-    public GlifLayout(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-        init(attrs, defStyleAttr);
+    layoutFullscreen = a.getBoolean(R.styleable.SuwGlifLayout_suwLayoutFullscreen, true);
+
+    a.recycle();
+
+    if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP && layoutFullscreen) {
+      setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
     }
+  }
 
-    // All the constructors delegate to this init method. The 3-argument constructor is not
-    // available in LinearLayout before v11, so call super with the exact same arguments.
-    private void init(AttributeSet attrs, int defStyleAttr) {
-        registerMixin(HeaderMixin.class, new ColoredHeaderMixin(this, attrs, defStyleAttr));
-        registerMixin(IconMixin.class, new IconMixin(this, attrs, defStyleAttr));
-        registerMixin(ProgressBarMixin.class, new ProgressBarMixin(this));
-        registerMixin(ButtonFooterMixin.class, new ButtonFooterMixin(this));
-        final RequireScrollMixin requireScrollMixin = new RequireScrollMixin(this);
-        registerMixin(RequireScrollMixin.class, requireScrollMixin);
-
-        final ScrollView scrollView = getScrollView();
-        if (scrollView != null) {
-            requireScrollMixin.setScrollHandlingDelegate(
-                    new ScrollViewScrollHandlingDelegate(requireScrollMixin, scrollView));
-        }
-
-        TypedArray a = getContext().obtainStyledAttributes(attrs,
-                R.styleable.SuwGlifLayout, defStyleAttr, 0);
-
-        ColorStateList primaryColor =
-                a.getColorStateList(R.styleable.SuwGlifLayout_suwColorPrimary);
-        if (primaryColor != null) {
-            setPrimaryColor(primaryColor);
-        }
-
-        ColorStateList backgroundColor =
-                a.getColorStateList(R.styleable.SuwGlifLayout_suwBackgroundBaseColor);
-        setBackgroundBaseColor(backgroundColor);
-
-        boolean backgroundPatterned =
-                a.getBoolean(R.styleable.SuwGlifLayout_suwBackgroundPatterned, true);
-        setBackgroundPatterned(backgroundPatterned);
-
-        final int footer = a.getResourceId(R.styleable.SuwGlifLayout_suwFooter, 0);
-        if (footer != 0) {
-            inflateFooter(footer);
-        }
-
-        final int stickyHeader = a.getResourceId(R.styleable.SuwGlifLayout_suwStickyHeader, 0);
-        if (stickyHeader != 0) {
-            inflateStickyHeader(stickyHeader);
-        }
-
-        mLayoutFullscreen = a.getBoolean(R.styleable.SuwGlifLayout_suwLayoutFullscreen, true);
-
-        a.recycle();
-
-        if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP && mLayoutFullscreen) {
-            setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
-        }
+  @Override
+  protected View onInflateTemplate(LayoutInflater inflater, @LayoutRes int template) {
+    if (template == 0) {
+      template = R.layout.suw_glif_template;
     }
+    return inflateTemplate(inflater, R.style.SuwThemeGlif_Light, template);
+  }
 
-    @Override
-    protected View onInflateTemplate(LayoutInflater inflater, @LayoutRes int template) {
-        if (template == 0) {
-            template = R.layout.suw_glif_template;
-        }
-        return inflateTemplate(inflater, R.style.SuwThemeGlif_Light, template);
+  @Override
+  protected ViewGroup findContainer(int containerId) {
+    if (containerId == 0) {
+      containerId = R.id.suw_layout_content;
     }
+    return super.findContainer(containerId);
+  }
 
-    @Override
-    protected ViewGroup findContainer(int containerId) {
-        if (containerId == 0) {
-            containerId = R.id.suw_layout_content;
-        }
-        return super.findContainer(containerId);
-    }
+  /**
+   * Sets the footer of the layout, which is at the bottom of the content area outside the scrolling
+   * container. The footer can only be inflated once per instance of this layout.
+   *
+   * @param footer The layout to be inflated as footer.
+   * @return The root of the inflated footer view.
+   */
+  public View inflateFooter(@LayoutRes int footer) {
+    ViewStub footerStub = findManagedViewById(R.id.suw_layout_footer);
+    footerStub.setLayoutResource(footer);
+    return footerStub.inflate();
+  }
 
-    /**
-     * Sets the footer of the layout, which is at the bottom of the content area outside the
-     * scrolling container. The footer can only be inflated once per instance of this layout.
-     *
-     * @param footer The layout to be inflated as footer.
-     * @return The root of the inflated footer view.
-     */
-    public View inflateFooter(@LayoutRes int footer) {
-        ViewStub footerStub = findManagedViewById(R.id.suw_layout_footer);
-        footerStub.setLayoutResource(footer);
-        return footerStub.inflate();
-    }
+  /**
+   * Sets the sticky header (i.e. header that doesn't scroll) of the layout, which is at the top of
+   * the content area outside of the scrolling container. The header can only be inflated once per
+   * instance of this layout.
+   *
+   * @param header The layout to be inflated as the header.
+   * @return The root of the inflated header view.
+   */
+  public View inflateStickyHeader(@LayoutRes int header) {
+    ViewStub stickyHeaderStub = findManagedViewById(R.id.suw_layout_sticky_header);
+    stickyHeaderStub.setLayoutResource(header);
+    return stickyHeaderStub.inflate();
+  }
 
-    /**
-     * Sets the sticky header (i.e. header that doesn't scroll) of the layout, which is at the top
-     * of the content area outside of the scrolling container. The header can only be inflated once
-     * per instance of this layout.
-     *
-     * @param header The layout to be inflated as the header.
-     * @return The root of the inflated header view.
-     */
-    public View inflateStickyHeader(@LayoutRes int header) {
-        ViewStub stickyHeaderStub = findManagedViewById(R.id.suw_layout_sticky_header);
-        stickyHeaderStub.setLayoutResource(header);
-        return stickyHeaderStub.inflate();
-    }
+  public ScrollView getScrollView() {
+    final View view = findManagedViewById(R.id.suw_scroll_view);
+    return view instanceof ScrollView ? (ScrollView) view : null;
+  }
 
-    public ScrollView getScrollView() {
-        final View view = findManagedViewById(R.id.suw_scroll_view);
-        return view instanceof ScrollView ? (ScrollView) view : null;
-    }
+  public TextView getHeaderTextView() {
+    return getMixin(HeaderMixin.class).getTextView();
+  }
 
-    public TextView getHeaderTextView() {
-        return getMixin(HeaderMixin.class).getTextView();
-    }
+  public void setHeaderText(int title) {
+    getMixin(HeaderMixin.class).setText(title);
+  }
 
-    public void setHeaderText(int title) {
-        getMixin(HeaderMixin.class).setText(title);
-    }
+  public void setHeaderText(CharSequence title) {
+    getMixin(HeaderMixin.class).setText(title);
+  }
 
-    public void setHeaderText(CharSequence title) {
-        getMixin(HeaderMixin.class).setText(title);
-    }
+  public CharSequence getHeaderText() {
+    return getMixin(HeaderMixin.class).getText();
+  }
 
-    public CharSequence getHeaderText() {
-        return getMixin(HeaderMixin.class).getText();
-    }
+  public void setHeaderColor(ColorStateList color) {
+    final ColoredHeaderMixin mixin = (ColoredHeaderMixin) getMixin(HeaderMixin.class);
+    mixin.setColor(color);
+  }
 
-    public void setHeaderColor(ColorStateList color) {
-        final ColoredHeaderMixin mixin = (ColoredHeaderMixin) getMixin(HeaderMixin.class);
-        mixin.setColor(color);
-    }
+  public ColorStateList getHeaderColor() {
+    final ColoredHeaderMixin mixin = (ColoredHeaderMixin) getMixin(HeaderMixin.class);
+    return mixin.getColor();
+  }
 
-    public ColorStateList getHeaderColor() {
-        final ColoredHeaderMixin mixin = (ColoredHeaderMixin) getMixin(HeaderMixin.class);
-        return mixin.getColor();
-    }
+  public void setIcon(Drawable icon) {
+    getMixin(IconMixin.class).setIcon(icon);
+  }
 
-    public void setIcon(Drawable icon) {
-        getMixin(IconMixin.class).setIcon(icon);
-    }
+  public Drawable getIcon() {
+    return getMixin(IconMixin.class).getIcon();
+  }
 
-    public Drawable getIcon() {
-        return getMixin(IconMixin.class).getIcon();
-    }
+  /**
+   * Sets the primary color of this layout, which will be used to determine the color of the
+   * progress bar and the background pattern.
+   */
+  public void setPrimaryColor(@NonNull ColorStateList color) {
+    primaryColor = color;
+    updateBackground();
+    getMixin(ProgressBarMixin.class).setColor(color);
+  }
 
-    /**
-     * Sets the primary color of this layout, which will be used to determine the color of the
-     * progress bar and the background pattern.
-     */
-    public void setPrimaryColor(@NonNull ColorStateList color) {
-        mPrimaryColor = color;
-        updateBackground();
-        getMixin(ProgressBarMixin.class).setColor(color);
-    }
+  public ColorStateList getPrimaryColor() {
+    return primaryColor;
+  }
 
-    public ColorStateList getPrimaryColor() {
-        return mPrimaryColor;
-    }
+  /**
+   * Sets the base color of the background view, which is the status bar for phones and the full-
+   * screen background for tablets. If {@link #isBackgroundPatterned()} is true, the pattern will be
+   * drawn with this color.
+   *
+   * @param color The color to use as the base color of the background. If {@code null}, {@link
+   *     #getPrimaryColor()} will be used.
+   */
+  public void setBackgroundBaseColor(@Nullable ColorStateList color) {
+    backgroundBaseColor = color;
+    updateBackground();
+  }
 
-    /**
-     * Sets the base color of the background view, which is the status bar for phones and the full-
-     * screen background for tablets. If {@link #isBackgroundPatterned()} is true, the pattern will
-     * be drawn with this color.
-     *
-     * @param color The color to use as the base color of the background. If {@code null},
-     *              {@link #getPrimaryColor()} will be used.
-     */
-    public void setBackgroundBaseColor(@Nullable ColorStateList color) {
-        mBackgroundBaseColor = color;
-        updateBackground();
-    }
+  /**
+   * @return The base color of the background. {@code null} indicates the background will be drawn
+   *     with {@link #getPrimaryColor()}.
+   */
+  @Nullable
+  public ColorStateList getBackgroundBaseColor() {
+    return backgroundBaseColor;
+  }
 
-    /**
-     * @return The base color of the background. {@code null} indicates the background will be drawn
-     *         with {@link #getPrimaryColor()}.
-     */
-    @Nullable
-    public ColorStateList getBackgroundBaseColor() {
-        return mBackgroundBaseColor;
-    }
+  /**
+   * Sets whether the background should be {@link GlifPatternDrawable}. If {@code false}, the
+   * background will be a solid color.
+   */
+  public void setBackgroundPatterned(boolean patterned) {
+    backgroundPatterned = patterned;
+    updateBackground();
+  }
 
-    /**
-     * Sets whether the background should be {@link GlifPatternDrawable}. If {@code false}, the
-     * background will be a solid color.
-     */
-    public void setBackgroundPatterned(boolean patterned) {
-        mBackgroundPatterned = patterned;
-        updateBackground();
-    }
+  /** @return True if this view uses {@link GlifPatternDrawable} as background. */
+  public boolean isBackgroundPatterned() {
+    return backgroundPatterned;
+  }
 
-    /**
-     * @return True if this view uses {@link GlifPatternDrawable} as background.
-     */
-    public boolean isBackgroundPatterned() {
-        return mBackgroundPatterned;
+  private void updateBackground() {
+    final View patternBg = findManagedViewById(R.id.suw_pattern_bg);
+    if (patternBg != null) {
+      int backgroundColor = 0;
+      if (backgroundBaseColor != null) {
+        backgroundColor = backgroundBaseColor.getDefaultColor();
+      } else if (primaryColor != null) {
+        backgroundColor = primaryColor.getDefaultColor();
+      }
+      Drawable background =
+          backgroundPatterned
+              ? new GlifPatternDrawable(backgroundColor)
+              : new ColorDrawable(backgroundColor);
+      if (patternBg instanceof StatusBarBackgroundLayout) {
+        ((StatusBarBackgroundLayout) patternBg).setStatusBarBackground(background);
+      } else {
+        patternBg.setBackgroundDrawable(background);
+      }
     }
+  }
 
-    private void updateBackground() {
-        final View patternBg = findManagedViewById(R.id.suw_pattern_bg);
-        if (patternBg != null) {
-            int backgroundColor = 0;
-            if (mBackgroundBaseColor != null) {
-                backgroundColor = mBackgroundBaseColor.getDefaultColor();
-            } else if (mPrimaryColor != null) {
-                backgroundColor = mPrimaryColor.getDefaultColor();
-            }
-            Drawable background = mBackgroundPatterned
-                    ? new GlifPatternDrawable(backgroundColor)
-                    : new ColorDrawable(backgroundColor);
-            if (patternBg instanceof StatusBarBackgroundLayout) {
-                ((StatusBarBackgroundLayout) patternBg).setStatusBarBackground(background);
-            } else {
-                patternBg.setBackgroundDrawable(background);
-            }
-        }
-    }
+  public boolean isProgressBarShown() {
+    return getMixin(ProgressBarMixin.class).isShown();
+  }
 
-    public boolean isProgressBarShown() {
-        return getMixin(ProgressBarMixin.class).isShown();
-    }
+  public void setProgressBarShown(boolean shown) {
+    getMixin(ProgressBarMixin.class).setShown(shown);
+  }
 
-    public void setProgressBarShown(boolean shown) {
-        getMixin(ProgressBarMixin.class).setShown(shown);
-    }
-
-    public ProgressBar peekProgressBar() {
-        return getMixin(ProgressBarMixin.class).peekProgressBar();
-    }
+  public ProgressBar peekProgressBar() {
+    return getMixin(ProgressBarMixin.class).peekProgressBar();
+  }
 }
diff --git a/library/main/src/com/android/setupwizardlib/GlifListLayout.java b/library/main/src/com/android/setupwizardlib/GlifListLayout.java
index 8266e5f..c4f66fa 100644
--- a/library/main/src/com/android/setupwizardlib/GlifListLayout.java
+++ b/library/main/src/com/android/setupwizardlib/GlifListLayout.java
@@ -26,7 +26,6 @@
 import android.view.ViewGroup;
 import android.widget.ListAdapter;
 import android.widget.ListView;
-
 import com.android.setupwizardlib.template.ListMixin;
 import com.android.setupwizardlib.template.ListViewScrollHandlingDelegate;
 import com.android.setupwizardlib.template.RequireScrollMixin;
@@ -37,130 +36,113 @@
  */
 public class GlifListLayout extends GlifLayout {
 
-    /* static section */
+  private ListMixin listMixin;
 
-    private static final String TAG = "GlifListLayout";
+  public GlifListLayout(Context context) {
+    this(context, 0, 0);
+  }
 
-    /* non-static section */
+  public GlifListLayout(Context context, int template) {
+    this(context, template, 0);
+  }
 
-    private ListMixin mListMixin;
+  public GlifListLayout(Context context, int template, int containerId) {
+    super(context, template, containerId);
+    init(null, 0);
+  }
 
-    public GlifListLayout(Context context) {
-        this(context, 0, 0);
+  public GlifListLayout(Context context, AttributeSet attrs) {
+    super(context, attrs);
+    init(attrs, 0);
+  }
+
+  @TargetApi(VERSION_CODES.HONEYCOMB)
+  public GlifListLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+    super(context, attrs, defStyleAttr);
+    init(attrs, defStyleAttr);
+  }
+
+  private void init(AttributeSet attrs, int defStyleAttr) {
+    listMixin = new ListMixin(this, attrs, defStyleAttr);
+    registerMixin(ListMixin.class, listMixin);
+
+    final RequireScrollMixin requireScrollMixin = getMixin(RequireScrollMixin.class);
+    requireScrollMixin.setScrollHandlingDelegate(
+        new ListViewScrollHandlingDelegate(requireScrollMixin, getListView()));
+  }
+
+  @Override
+  protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+    super.onLayout(changed, left, top, right, bottom);
+    listMixin.onLayout();
+  }
+
+  @Override
+  protected View onInflateTemplate(LayoutInflater inflater, int template) {
+    if (template == 0) {
+      template = R.layout.suw_glif_list_template;
     }
+    return super.onInflateTemplate(inflater, template);
+  }
 
-    public GlifListLayout(Context context, int template) {
-        this(context, template, 0);
+  @Override
+  protected ViewGroup findContainer(int containerId) {
+    if (containerId == 0) {
+      containerId = android.R.id.list;
     }
+    return super.findContainer(containerId);
+  }
 
-    public GlifListLayout(Context context, int template, int containerId) {
-        super(context, template, containerId);
-        init(context, null, 0);
-    }
+  public ListView getListView() {
+    return listMixin.getListView();
+  }
 
-    public GlifListLayout(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        init(context, attrs, 0);
-    }
+  public void setAdapter(ListAdapter adapter) {
+    listMixin.setAdapter(adapter);
+  }
 
-    @TargetApi(VERSION_CODES.HONEYCOMB)
-    public GlifListLayout(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-        init(context, attrs, defStyleAttr);
-    }
+  public ListAdapter getAdapter() {
+    return listMixin.getAdapter();
+  }
 
-    private void init(Context context, AttributeSet attrs, int defStyleAttr) {
-        mListMixin = new ListMixin(this, attrs, defStyleAttr);
-        registerMixin(ListMixin.class, mListMixin);
+  /** @deprecated Use {@link #setDividerInsets(int, int)} instead. */
+  @Deprecated
+  public void setDividerInset(int inset) {
+    listMixin.setDividerInset(inset);
+  }
 
-        final RequireScrollMixin requireScrollMixin = getMixin(RequireScrollMixin.class);
-        requireScrollMixin.setScrollHandlingDelegate(
-                new ListViewScrollHandlingDelegate(requireScrollMixin, getListView()));
-    }
+  /**
+   * 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) {
+    listMixin.setDividerInsets(start, end);
+  }
 
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        super.onLayout(changed, left, top, right, bottom);
-        mListMixin.onLayout();
-    }
+  /** @deprecated Use {@link #getDividerInsetStart()} instead. */
+  @Deprecated
+  public int getDividerInset() {
+    return listMixin.getDividerInset();
+  }
 
-    @Override
-    protected View onInflateTemplate(LayoutInflater inflater, int template) {
-        if (template == 0) {
-            template = R.layout.suw_glif_list_template;
-        }
-        return super.onInflateTemplate(inflater, template);
-    }
+  /** @see ListMixin#getDividerInsetStart() */
+  public int getDividerInsetStart() {
+    return listMixin.getDividerInsetStart();
+  }
 
-    @Override
-    protected ViewGroup findContainer(int containerId) {
-        if (containerId == 0) {
-            containerId = android.R.id.list;
-        }
-        return super.findContainer(containerId);
-    }
+  /** @see ListMixin#getDividerInsetEnd() */
+  public int getDividerInsetEnd() {
+    return listMixin.getDividerInsetEnd();
+  }
 
-    public ListView getListView() {
-        return mListMixin.getListView();
-    }
-
-    public void setAdapter(ListAdapter adapter) {
-        mListMixin.setAdapter(adapter);
-    }
-
-    public ListAdapter getAdapter() {
-        return mListMixin.getAdapter();
-    }
-
-    /**
-     * @deprecated Use {@link #setDividerInsets(int, int)} instead.
-     */
-    @Deprecated
-    public void setDividerInset(int inset) {
-        mListMixin.setDividerInset(inset);
-    }
-
-    /**
-     * 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() {
-        return mListMixin.getDivider();
-    }
+  /** @see ListMixin#getDivider() */
+  public Drawable getDivider() {
+    return listMixin.getDivider();
+  }
 }
diff --git a/library/main/src/com/android/setupwizardlib/GlifPatternDrawable.java b/library/main/src/com/android/setupwizardlib/GlifPatternDrawable.java
index caf92ac..f65f3ec 100644
--- a/library/main/src/com/android/setupwizardlib/GlifPatternDrawable.java
+++ b/library/main/src/com/android/setupwizardlib/GlifPatternDrawable.java
@@ -31,10 +31,8 @@
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
-
 import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
-
 import java.lang.ref.SoftReference;
 
 /**
@@ -42,263 +40,258 @@
  * tablets in GLIF layout.
  */
 public class GlifPatternDrawable extends Drawable {
-    /*
-     * This class essentially implements a simple SVG in Java code, with some special handling of
-     * scaling when given different bounds.
-     */
+  /*
+   * This class essentially implements a simple SVG in Java code, with some special handling of
+   * scaling when given different bounds.
+   */
 
-    /* static section */
+  /* static section */
 
-    @SuppressLint("InlinedApi")
-    private static final int[] ATTRS_PRIMARY_COLOR = new int[]{ android.R.attr.colorPrimary };
+  @SuppressLint("InlinedApi")
+  private static final int[] ATTRS_PRIMARY_COLOR = new int[] {android.R.attr.colorPrimary};
 
-    private static final float VIEWBOX_HEIGHT = 768f;
-    private static final float VIEWBOX_WIDTH = 1366f;
-    // X coordinate of scale focus, as a fraction of of the width. (Range is 0 - 1)
-    private static final float SCALE_FOCUS_X = .146f;
-    // Y coordinate of scale focus, as a fraction of of the height. (Range is 0 - 1)
-    private static final float SCALE_FOCUS_Y = .228f;
+  private static final float VIEWBOX_HEIGHT = 768f;
+  private static final float VIEWBOX_WIDTH = 1366f;
+  // X coordinate of scale focus, as a fraction of the width. (Range is 0 - 1)
+  private static final float SCALE_FOCUS_X = .146f;
+  // Y coordinate of scale focus, as a fraction of the height. (Range is 0 - 1)
+  private static final float SCALE_FOCUS_Y = .228f;
 
-    // Alpha component of the color to be drawn, on top of the grayscale pattern. (Range is 0 - 1)
-    private static final float COLOR_ALPHA = .8f;
-    // Int version of COLOR_ALPHA. (Range is 0 - 255)
-    private static final int COLOR_ALPHA_INT = (int) (COLOR_ALPHA * 255);
+  // Alpha component of the color to be drawn, on top of the grayscale pattern. (Range is 0 - 1)
+  private static final float COLOR_ALPHA = .8f;
+  // Int version of COLOR_ALPHA. (Range is 0 - 255)
+  private static final int COLOR_ALPHA_INT = (int) (COLOR_ALPHA * 255);
 
-    // Cap the bitmap size, such that it won't hurt the performance too much
-    // and it won't crash due to a very large scale.
-    // The drawable will look blurry above this size.
-    // This is a multiplier applied on top of the viewbox size.
-    // Resulting max cache size = (1.5 x 1366, 1.5 x 768) = (2049, 1152)
-    private static final float MAX_CACHED_BITMAP_SCALE = 1.5f;
+  // Cap the bitmap size, such that it won't hurt the performance too much
+  // and it won't crash due to a very large scale.
+  // The drawable will look blurry above this size.
+  // This is a multiplier applied on top of the viewbox size.
+  // Resulting max cache size = (1.5 x 1366, 1.5 x 768) = (2049, 1152)
+  private static final float MAX_CACHED_BITMAP_SCALE = 1.5f;
 
-    private static final int NUM_PATHS = 7;
+  private static final int NUM_PATHS = 7;
 
-    private static SoftReference<Bitmap> sBitmapCache;
-    private static Path[] sPatternPaths;
-    private static int[] sPatternLightness;
+  private static SoftReference<Bitmap> bitmapCache;
+  private static Path[] patternPaths;
+  private static int[] patternLightness;
 
-    public static GlifPatternDrawable getDefault(Context context) {
-        int colorPrimary = 0;
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
-            final TypedArray a = context.obtainStyledAttributes(ATTRS_PRIMARY_COLOR);
-            colorPrimary = a.getColor(0, Color.BLACK);
-            a.recycle();
-        }
-        return new GlifPatternDrawable(colorPrimary);
+  public static GlifPatternDrawable getDefault(Context context) {
+    int colorPrimary = 0;
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+      final TypedArray a = context.obtainStyledAttributes(ATTRS_PRIMARY_COLOR);
+      colorPrimary = a.getColor(0, Color.BLACK);
+      a.recycle();
+    }
+    return new GlifPatternDrawable(colorPrimary);
+  }
+
+  @VisibleForTesting
+  public static void invalidatePattern() {
+    bitmapCache = null;
+  }
+
+  /* non-static section */
+
+  private int color;
+  private final Paint tempPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+
+  public GlifPatternDrawable(int color) {
+    setColor(color);
+  }
+
+  @Override
+  public void draw(@NonNull Canvas canvas) {
+    final Rect bounds = getBounds();
+    int drawableWidth = bounds.width();
+    int drawableHeight = bounds.height();
+    Bitmap bitmap = null;
+    if (bitmapCache != null) {
+      bitmap = bitmapCache.get();
+    }
+    if (bitmap != null) {
+      final int bitmapWidth = bitmap.getWidth();
+      final int bitmapHeight = bitmap.getHeight();
+      // Invalidate the cache if this drawable is bigger and we can still create a bigger
+      // cache.
+      if (drawableWidth > bitmapWidth && bitmapWidth < VIEWBOX_WIDTH * MAX_CACHED_BITMAP_SCALE) {
+        bitmap = null;
+      } else if (drawableHeight > bitmapHeight
+          && bitmapHeight < VIEWBOX_HEIGHT * MAX_CACHED_BITMAP_SCALE) {
+        bitmap = null;
+      }
     }
 
-    @VisibleForTesting
-    public static void invalidatePattern() {
-        sBitmapCache = null;
+    if (bitmap == null) {
+      // Reset the paint so it can be used to draw the paths in renderOnCanvas
+      tempPaint.reset();
+
+      bitmap = createBitmapCache(drawableWidth, drawableHeight);
+      bitmapCache = new SoftReference<>(bitmap);
+
+      // Reset the paint to so it can be used to draw the bitmap
+      tempPaint.reset();
     }
 
-    /* non-static section */
+    canvas.save();
+    canvas.clipRect(bounds);
 
-    private int mColor;
-    private Paint mTempPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+    scaleCanvasToBounds(canvas, bitmap, bounds);
+    canvas.drawColor(Color.BLACK);
+    tempPaint.setColor(Color.WHITE);
+    canvas.drawBitmap(bitmap, 0, 0, tempPaint);
+    canvas.drawColor(color);
 
-    public GlifPatternDrawable(int color) {
-        setColor(color);
+    canvas.restore();
+  }
+
+  @VisibleForTesting
+  public Bitmap createBitmapCache(int drawableWidth, int drawableHeight) {
+    float scaleX = drawableWidth / VIEWBOX_WIDTH;
+    float scaleY = drawableHeight / VIEWBOX_HEIGHT;
+    float scale = Math.max(scaleX, scaleY);
+    scale = Math.min(MAX_CACHED_BITMAP_SCALE, scale);
+
+    int scaledWidth = (int) (VIEWBOX_WIDTH * scale);
+    int scaledHeight = (int) (VIEWBOX_HEIGHT * scale);
+
+    // Use ALPHA_8 mask to save memory, since the pattern is grayscale only anyway.
+    Bitmap bitmap = Bitmap.createBitmap(scaledWidth, scaledHeight, Bitmap.Config.ALPHA_8);
+    Canvas bitmapCanvas = new Canvas(bitmap);
+    renderOnCanvas(bitmapCanvas, scale);
+    return bitmap;
+  }
+
+  private void renderOnCanvas(Canvas canvas, float scale) {
+    canvas.save();
+    canvas.scale(scale, scale);
+
+    tempPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
+
+    // Draw the pattern by creating the paths, adjusting the colors and drawing them. The path
+    // values are extracted from the SVG of the pattern file.
+
+    if (patternPaths == null) {
+      patternPaths = new Path[NUM_PATHS];
+      // Lightness values of the pattern, range 0 - 255
+      patternLightness = new int[] {10, 40, 51, 66, 91, 112, 130};
+
+      Path p = patternPaths[0] = new Path();
+      p.moveTo(1029.4f, 357.5f);
+      p.lineTo(1366f, 759.1f);
+      p.lineTo(1366f, 0f);
+      p.lineTo(1137.7f, 0f);
+      p.close();
+
+      p = patternPaths[1] = new Path();
+      p.moveTo(1138.1f, 0f);
+      p.rLineTo(-144.8f, 768f);
+      p.rLineTo(372.7f, 0f);
+      p.rLineTo(0f, -524f);
+      p.cubicTo(1290.7f, 121.6f, 1219.2f, 41.1f, 1178.7f, 0f);
+      p.close();
+
+      p = patternPaths[2] = new Path();
+      p.moveTo(949.8f, 768f);
+      p.rCubicTo(92.6f, -170.6f, 213f, -440.3f, 269.4f, -768f);
+      p.lineTo(585f, 0f);
+      p.rLineTo(2.1f, 766f);
+      p.close();
+
+      p = patternPaths[3] = new Path();
+      p.moveTo(471.1f, 768f);
+      p.rMoveTo(704.5f, 0f);
+      p.cubicTo(1123.6f, 563.3f, 1027.4f, 275.2f, 856.2f, 0f);
+      p.lineTo(476.4f, 0f);
+      p.rLineTo(-5.3f, 768f);
+      p.close();
+
+      p = patternPaths[4] = new Path();
+      p.moveTo(323.1f, 768f);
+      p.moveTo(777.5f, 768f);
+      p.cubicTo(661.9f, 348.8f, 427.2f, 21.4f, 401.2f, 25.4f);
+      p.lineTo(323.1f, 768f);
+      p.close();
+
+      p = patternPaths[5] = new Path();
+      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);
+      p.close();
+
+      p = patternPaths[6] = new Path();
+      p.moveTo(146f, 0f);
+      p.lineTo(0f, 0f);
+      p.lineTo(0f, 768f);
+      p.lineTo(394.2f, 768f);
+      p.cubicTo(327.7f, 475.3f, 228.5f, 201f, 146f, 0f);
+      p.close();
     }
 
-    @Override
-    public void draw(@NonNull Canvas canvas) {
-        final Rect bounds = getBounds();
-        int drawableWidth = bounds.width();
-        int drawableHeight = bounds.height();
-        Bitmap bitmap = null;
-        if (sBitmapCache != null) {
-            bitmap = sBitmapCache.get();
-        }
-        if (bitmap != null) {
-            final int bitmapWidth = bitmap.getWidth();
-            final int bitmapHeight = bitmap.getHeight();
-            // Invalidate the cache if this drawable is bigger and we can still create a bigger
-            // cache.
-            if (drawableWidth > bitmapWidth
-                    && bitmapWidth < VIEWBOX_WIDTH * MAX_CACHED_BITMAP_SCALE) {
-                bitmap = null;
-            } else if (drawableHeight > bitmapHeight
-                    && bitmapHeight < VIEWBOX_HEIGHT * MAX_CACHED_BITMAP_SCALE) {
-                bitmap = null;
-            }
-        }
-
-        if (bitmap == null) {
-            // Reset the paint so it can be used to draw the paths in renderOnCanvas
-            mTempPaint.reset();
-
-            bitmap = createBitmapCache(drawableWidth, drawableHeight);
-            sBitmapCache = new SoftReference<>(bitmap);
-
-            // Reset the paint to so it can be used to draw the bitmap
-            mTempPaint.reset();
-        }
-
-        canvas.save();
-        canvas.clipRect(bounds);
-
-        scaleCanvasToBounds(canvas, bitmap, bounds);
-        canvas.drawColor(Color.BLACK);
-        mTempPaint.setColor(Color.WHITE);
-        canvas.drawBitmap(bitmap, 0, 0, mTempPaint);
-        canvas.drawColor(mColor);
-
-        canvas.restore();
+    for (int i = 0; i < NUM_PATHS; i++) {
+      // Color is 0xAARRGGBB, so alpha << 24 will create a color with (alpha)% black.
+      // Although the color components don't really matter, since the backing bitmap cache is
+      // ALPHA_8.
+      tempPaint.setColor(patternLightness[i] << 24);
+      canvas.drawPath(patternPaths[i], tempPaint);
     }
 
-    @VisibleForTesting
-    public Bitmap createBitmapCache(int drawableWidth, int drawableHeight) {
-        float scaleX = drawableWidth / VIEWBOX_WIDTH;
-        float scaleY = drawableHeight / VIEWBOX_HEIGHT;
-        float scale = Math.max(scaleX, scaleY);
-        scale = Math.min(MAX_CACHED_BITMAP_SCALE, scale);
+    canvas.restore();
+    tempPaint.reset();
+  }
 
+  @VisibleForTesting
+  public void scaleCanvasToBounds(Canvas canvas, Bitmap bitmap, Rect drawableBounds) {
+    int bitmapWidth = bitmap.getWidth();
+    int bitmapHeight = bitmap.getHeight();
+    float scaleX = drawableBounds.width() / (float) bitmapWidth;
+    float scaleY = drawableBounds.height() / (float) bitmapHeight;
 
-        int scaledWidth = (int) (VIEWBOX_WIDTH * scale);
-        int scaledHeight = (int) (VIEWBOX_HEIGHT * scale);
-
-        // Use ALPHA_8 mask to save memory, since the pattern is grayscale only anyway.
-        Bitmap bitmap = Bitmap.createBitmap(
-                scaledWidth,
-                scaledHeight,
-                Bitmap.Config.ALPHA_8);
-        Canvas bitmapCanvas = new Canvas(bitmap);
-        renderOnCanvas(bitmapCanvas, scale);
-        return bitmap;
+    // First scale both sides to fit independently.
+    canvas.scale(scaleX, scaleY);
+    if (scaleY > scaleX) {
+      // Adjust x-scale to maintain aspect ratio using the pivot, so that more of the texture
+      // and less of the blank space on the left edge is seen.
+      canvas.scale(scaleY / scaleX, 1f, SCALE_FOCUS_X * bitmapWidth, 0f);
+    } else if (scaleX > scaleY) {
+      // Adjust y-scale to maintain aspect ratio using the pivot, so that an intersection of
+      // two "circles" can always be seen.
+      canvas.scale(1f, scaleX / scaleY, 0f, SCALE_FOCUS_Y * bitmapHeight);
     }
+  }
 
-    private void renderOnCanvas(Canvas canvas, float scale) {
-        canvas.save();
-        canvas.scale(scale, scale);
+  @Override
+  public void setAlpha(int i) {
+    // Ignore
+  }
 
-        mTempPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
+  @Override
+  public void setColorFilter(ColorFilter colorFilter) {
+    // Ignore
+  }
 
-        // Draw the pattern by creating the paths, adjusting the colors and drawing them. The path
-        // values are extracted from the SVG of the pattern file.
+  @Override
+  public int getOpacity() {
+    return PixelFormat.UNKNOWN;
+  }
 
-        if (sPatternPaths == null) {
-            sPatternPaths = new Path[NUM_PATHS];
-            // Lightness values of the pattern, range 0 - 255
-            sPatternLightness = new int[] { 10, 40, 51, 66, 91, 112, 130 };
+  /**
+   * Sets the color used as the base color of this pattern drawable. The alpha component of the
+   * color will be ignored.
+   */
+  public void setColor(int color) {
+    final int r = Color.red(color);
+    final int g = Color.green(color);
+    final int b = Color.blue(color);
+    this.color = Color.argb(COLOR_ALPHA_INT, r, g, b);
+    invalidateSelf();
+  }
 
-            Path p = sPatternPaths[0] = new Path();
-            p.moveTo(1029.4f, 357.5f);
-            p.lineTo(1366f, 759.1f);
-            p.lineTo(1366f, 0f);
-            p.lineTo(1137.7f, 0f);
-            p.close();
-
-            p = sPatternPaths[1] = new Path();
-            p.moveTo(1138.1f, 0f);
-            p.rLineTo(-144.8f, 768f);
-            p.rLineTo(372.7f, 0f);
-            p.rLineTo(0f, -524f);
-            p.cubicTo(1290.7f, 121.6f, 1219.2f, 41.1f, 1178.7f, 0f);
-            p.close();
-
-            p = sPatternPaths[2] = new Path();
-            p.moveTo(949.8f, 768f);
-            p.rCubicTo(92.6f, -170.6f, 213f, -440.3f, 269.4f, -768f);
-            p.lineTo(585f, 0f);
-            p.rLineTo(2.1f, 766f);
-            p.close();
-
-            p = sPatternPaths[3] = new Path();
-            p.moveTo(471.1f, 768f);
-            p.rMoveTo(704.5f, 0f);
-            p.cubicTo(1123.6f, 563.3f, 1027.4f, 275.2f, 856.2f, 0f);
-            p.lineTo(476.4f, 0f);
-            p.rLineTo(-5.3f, 768f);
-            p.close();
-
-            p = sPatternPaths[4] = new Path();
-            p.moveTo(323.1f, 768f);
-            p.moveTo(777.5f, 768f);
-            p.cubicTo(661.9f, 348.8f, 427.2f, 21.4f, 401.2f, 25.4f);
-            p.lineTo(323.1f, 768f);
-            p.close();
-
-            p = sPatternPaths[5] = new Path();
-            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);
-            p.close();
-
-            p = sPatternPaths[6] = new Path();
-            p.moveTo(146f, 0f);
-            p.lineTo(0f, 0f);
-            p.lineTo(0f, 768f);
-            p.lineTo(394.2f, 768f);
-            p.cubicTo(327.7f, 475.3f, 228.5f, 201f, 146f, 0f);
-            p.close();
-        }
-
-        for (int i = 0; i < NUM_PATHS; i++) {
-            // Color is 0xAARRGGBB, so alpha << 24 will create a color with (alpha)% black.
-            // Although the color components don't really matter, since the backing bitmap cache is
-            // ALPHA_8.
-            mTempPaint.setColor(sPatternLightness[i] << 24);
-            canvas.drawPath(sPatternPaths[i], mTempPaint);
-        }
-
-        canvas.restore();
-        mTempPaint.reset();
-    }
-
-    @VisibleForTesting
-    public void scaleCanvasToBounds(Canvas canvas, Bitmap bitmap, Rect drawableBounds) {
-        int bitmapWidth = bitmap.getWidth();
-        int bitmapHeight = bitmap.getHeight();
-        float scaleX = drawableBounds.width() / (float) bitmapWidth;
-        float scaleY = drawableBounds.height() / (float) bitmapHeight;
-
-        // First scale both sides to fit independently.
-        canvas.scale(scaleX, scaleY);
-        if (scaleY > scaleX) {
-            // Adjust x-scale to maintain aspect ratio using the pivot, so that more of the texture
-            // and less of the blank space on the left edge is seen.
-            canvas.scale(scaleY / scaleX, 1f, SCALE_FOCUS_X * bitmapWidth, 0f);
-        } else if (scaleX > scaleY) {
-            // Adjust y-scale to maintain aspect ratio using the pivot, so that an intersection of
-            // two "circles" can always be seen.
-            canvas.scale(1f, scaleX / scaleY, 0f, SCALE_FOCUS_Y * bitmapHeight);
-        }
-    }
-
-    @Override
-    public void setAlpha(int i) {
-        // Ignore
-    }
-
-    @Override
-    public void setColorFilter(ColorFilter colorFilter) {
-        // Ignore
-    }
-
-    @Override
-    public int getOpacity() {
-        return PixelFormat.UNKNOWN;
-    }
-
-    /**
-     * Sets the color used as the base color of this pattern drawable. The alpha component of the
-     * color will be ignored.
-     */
-    public void setColor(int color) {
-        final int r = Color.red(color);
-        final int g = Color.green(color);
-        final int b = Color.blue(color);
-        mColor = Color.argb(COLOR_ALPHA_INT, r, g, b);
-        invalidateSelf();
-    }
-
-    /**
-     * @return The color used as the base color of this pattern drawable. The alpha component of
-     * this is always 255.
-     */
-    public int getColor() {
-        return Color.argb(255, Color.red(mColor), Color.green(mColor), Color.blue(mColor));
-    }
+  /**
+   * @return The color used as the base color of this pattern drawable. The alpha component of this
+   *     is always 255.
+   */
+  public int getColor() {
+    return Color.argb(255, Color.red(color), Color.green(color), Color.blue(color));
+  }
 }
diff --git a/library/main/src/com/android/setupwizardlib/SetupWizardItemsLayout.java b/library/main/src/com/android/setupwizardlib/SetupWizardItemsLayout.java
index d520873..6197c4c 100644
--- a/library/main/src/com/android/setupwizardlib/SetupWizardItemsLayout.java
+++ b/library/main/src/com/android/setupwizardlib/SetupWizardItemsLayout.java
@@ -17,34 +17,30 @@
 package com.android.setupwizardlib;
 
 import android.content.Context;
+import androidx.annotation.Nullable;
 import android.util.AttributeSet;
 import android.widget.ListAdapter;
-
-import androidx.annotation.Nullable;
-
 import com.android.setupwizardlib.items.ItemAdapter;
 
-/**
- * @deprecated Use {@link SetupWizardListLayout} instead.
- */
+/** @deprecated Use {@link SetupWizardListLayout} instead. */
 @Deprecated
 public class SetupWizardItemsLayout extends SetupWizardListLayout {
 
-    public SetupWizardItemsLayout(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
+  public SetupWizardItemsLayout(Context context, AttributeSet attrs) {
+    super(context, attrs);
+  }
 
-    public SetupWizardItemsLayout(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-    }
+  public SetupWizardItemsLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+    super(context, attrs, defStyleAttr);
+  }
 
-    @Override
-    @Nullable
-    public ItemAdapter getAdapter() {
-        final ListAdapter adapter = super.getAdapter();
-        if (adapter instanceof ItemAdapter) {
-            return (ItemAdapter) adapter;
-        }
-        return null;
+  @Override
+  @Nullable
+  public ItemAdapter getAdapter() {
+    final ListAdapter adapter = super.getAdapter();
+    if (adapter instanceof ItemAdapter) {
+      return (ItemAdapter) adapter;
     }
+    return null;
+  }
 }
diff --git a/library/main/src/com/android/setupwizardlib/SetupWizardLayout.java b/library/main/src/com/android/setupwizardlib/SetupWizardLayout.java
index 065d2ef..792d102 100644
--- a/library/main/src/com/android/setupwizardlib/SetupWizardLayout.java
+++ b/library/main/src/com/android/setupwizardlib/SetupWizardLayout.java
@@ -38,7 +38,6 @@
 import android.view.ViewGroup;
 import android.widget.ScrollView;
 import android.widget.TextView;
-
 import com.android.setupwizardlib.template.HeaderMixin;
 import com.android.setupwizardlib.template.NavigationBarMixin;
 import com.android.setupwizardlib.template.ProgressBarMixin;
@@ -49,385 +48,377 @@
 
 public class SetupWizardLayout extends TemplateLayout {
 
-    private static final String TAG = "SetupWizardLayout";
+  private static final String TAG = "SetupWizardLayout";
 
-    public SetupWizardLayout(Context context) {
-        super(context, 0, 0);
-        init(null, R.attr.suwLayoutTheme);
+  public SetupWizardLayout(Context context) {
+    super(context, 0, 0);
+    init(null, R.attr.suwLayoutTheme);
+  }
+
+  public SetupWizardLayout(Context context, int template) {
+    this(context, template, 0);
+  }
+
+  public SetupWizardLayout(Context context, int template, int containerId) {
+    super(context, template, containerId);
+    init(null, R.attr.suwLayoutTheme);
+  }
+
+  public SetupWizardLayout(Context context, AttributeSet attrs) {
+    super(context, attrs);
+    init(attrs, R.attr.suwLayoutTheme);
+  }
+
+  @TargetApi(VERSION_CODES.HONEYCOMB)
+  public SetupWizardLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+    super(context, attrs, defStyleAttr);
+    init(attrs, defStyleAttr);
+  }
+
+  // All the constructors delegate to this init method. The 3-argument constructor is not
+  // available in LinearLayout before v11, so call super with the exact same arguments.
+  private void init(AttributeSet attrs, int defStyleAttr) {
+    registerMixin(HeaderMixin.class, new HeaderMixin(this, attrs, defStyleAttr));
+    registerMixin(ProgressBarMixin.class, new ProgressBarMixin(this));
+    registerMixin(NavigationBarMixin.class, new NavigationBarMixin(this));
+    final RequireScrollMixin requireScrollMixin = new RequireScrollMixin(this);
+    registerMixin(RequireScrollMixin.class, requireScrollMixin);
+
+    final ScrollView scrollView = getScrollView();
+    if (scrollView != null) {
+      requireScrollMixin.setScrollHandlingDelegate(
+          new ScrollViewScrollHandlingDelegate(requireScrollMixin, scrollView));
     }
 
-    public SetupWizardLayout(Context context, int template) {
-        this(context, template, 0);
+    final TypedArray a =
+        getContext()
+            .obtainStyledAttributes(attrs, R.styleable.SuwSetupWizardLayout, defStyleAttr, 0);
+
+    // Set the background from XML, either directly or built from a bitmap tile
+    final Drawable background = a.getDrawable(R.styleable.SuwSetupWizardLayout_suwBackground);
+    if (background != null) {
+      setLayoutBackground(background);
+    } else {
+      final Drawable backgroundTile =
+          a.getDrawable(R.styleable.SuwSetupWizardLayout_suwBackgroundTile);
+      if (backgroundTile != null) {
+        setBackgroundTile(backgroundTile);
+      }
     }
 
-    public SetupWizardLayout(Context context, int template, int containerId) {
-        super(context, template, containerId);
-        init(null, R.attr.suwLayoutTheme);
+    // Set the illustration from XML, either directly or built from image + horizontal tile
+    final Drawable illustration = a.getDrawable(R.styleable.SuwSetupWizardLayout_suwIllustration);
+    if (illustration != null) {
+      setIllustration(illustration);
+    } else {
+      final Drawable illustrationImage =
+          a.getDrawable(R.styleable.SuwSetupWizardLayout_suwIllustrationImage);
+      final Drawable horizontalTile =
+          a.getDrawable(R.styleable.SuwSetupWizardLayout_suwIllustrationHorizontalTile);
+      if (illustrationImage != null && horizontalTile != null) {
+        setIllustration(illustrationImage, horizontalTile);
+      }
     }
 
-    public SetupWizardLayout(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        init(attrs, R.attr.suwLayoutTheme);
+    // Set the top padding of the illustration
+    int decorPaddingTop =
+        a.getDimensionPixelSize(R.styleable.SuwSetupWizardLayout_suwDecorPaddingTop, -1);
+    if (decorPaddingTop == -1) {
+      decorPaddingTop = getResources().getDimensionPixelSize(R.dimen.suw_decor_padding_top);
+    }
+    setDecorPaddingTop(decorPaddingTop);
+
+    // Set the illustration aspect ratio. See Illustration.setAspectRatio(float). This will
+    // override suwDecorPaddingTop if its value is not 0.
+    float illustrationAspectRatio =
+        a.getFloat(R.styleable.SuwSetupWizardLayout_suwIllustrationAspectRatio, -1f);
+    if (illustrationAspectRatio == -1f) {
+      final TypedValue out = new TypedValue();
+      getResources().getValue(R.dimen.suw_illustration_aspect_ratio, out, true);
+      illustrationAspectRatio = out.getFloat();
+    }
+    setIllustrationAspectRatio(illustrationAspectRatio);
+
+    a.recycle();
+  }
+
+  @Override
+  protected Parcelable onSaveInstanceState() {
+    final Parcelable parcelable = super.onSaveInstanceState();
+    final SavedState ss = new SavedState(parcelable);
+    ss.isProgressBarShown = isProgressBarShown();
+    return ss;
+  }
+
+  @Override
+  protected void onRestoreInstanceState(Parcelable state) {
+    if (!(state instanceof SavedState)) {
+      Log.w(TAG, "Ignoring restore instance state " + state);
+      super.onRestoreInstanceState(state);
+      return;
     }
 
-    @TargetApi(VERSION_CODES.HONEYCOMB)
-    public SetupWizardLayout(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-        init(attrs, defStyleAttr);
+    final SavedState ss = (SavedState) state;
+    super.onRestoreInstanceState(ss.getSuperState());
+    final boolean isProgressBarShown = ss.isProgressBarShown;
+    setProgressBarShown(isProgressBarShown);
+  }
+
+  @Override
+  protected View onInflateTemplate(LayoutInflater inflater, int template) {
+    if (template == 0) {
+      template = R.layout.suw_template;
+    }
+    return inflateTemplate(inflater, R.style.SuwThemeMaterial_Light, template);
+  }
+
+  @Override
+  protected ViewGroup findContainer(int containerId) {
+    if (containerId == 0) {
+      containerId = R.id.suw_layout_content;
+    }
+    return super.findContainer(containerId);
+  }
+
+  public NavigationBar getNavigationBar() {
+    return getMixin(NavigationBarMixin.class).getNavigationBar();
+  }
+
+  public ScrollView getScrollView() {
+    final View view = findManagedViewById(R.id.suw_bottom_scroll_view);
+    return view instanceof ScrollView ? (ScrollView) view : null;
+  }
+
+  public void requireScrollToBottom() {
+    final RequireScrollMixin requireScrollMixin = getMixin(RequireScrollMixin.class);
+    final NavigationBar navigationBar = getNavigationBar();
+    if (navigationBar != null) {
+      requireScrollMixin.requireScrollWithNavigationBar(navigationBar);
+    } else {
+      Log.e(TAG, "Cannot require scroll. Navigation bar is null.");
+    }
+  }
+
+  public void setHeaderText(int title) {
+    getMixin(HeaderMixin.class).setText(title);
+  }
+
+  public void setHeaderText(CharSequence title) {
+    getMixin(HeaderMixin.class).setText(title);
+  }
+
+  public CharSequence getHeaderText() {
+    return getMixin(HeaderMixin.class).getText();
+  }
+
+  public TextView getHeaderTextView() {
+    return getMixin(HeaderMixin.class).getTextView();
+  }
+
+  /**
+   * Set the illustration of the layout. The drawable will be applied as is, and the bounds will be
+   * set as implemented in {@link com.android.setupwizardlib.view.Illustration}. To create a
+   * suitable drawable from an asset and a horizontal repeating tile, use {@link
+   * #setIllustration(int, int)} instead.
+   *
+   * @param drawable The drawable specifying the illustration.
+   */
+  public void setIllustration(Drawable drawable) {
+    final View view = findManagedViewById(R.id.suw_layout_decor);
+    if (view instanceof Illustration) {
+      final Illustration illustration = (Illustration) view;
+      illustration.setIllustration(drawable);
+    }
+  }
+
+  /**
+   * Set the illustration of the layout, which will be created asset and the horizontal tile as
+   * suitable. On phone layouts (not sw600dp), the asset will be scaled, maintaining aspect ratio.
+   * On tablets (sw600dp), the assets will always have 256dp height and the rest of the illustration
+   * area that the asset doesn't fill will be covered by the horizontalTile.
+   *
+   * @param asset Resource ID of the illustration asset.
+   * @param horizontalTile Resource ID of the horizontally repeating tile for tablet layout.
+   */
+  public void setIllustration(int asset, int horizontalTile) {
+    final View view = findManagedViewById(R.id.suw_layout_decor);
+    if (view instanceof Illustration) {
+      final Illustration illustration = (Illustration) view;
+      final Drawable illustrationDrawable = getIllustration(asset, horizontalTile);
+      illustration.setIllustration(illustrationDrawable);
+    }
+  }
+
+  private void setIllustration(Drawable asset, Drawable horizontalTile) {
+    final View view = findManagedViewById(R.id.suw_layout_decor);
+    if (view instanceof Illustration) {
+      final Illustration illustration = (Illustration) view;
+      final Drawable illustrationDrawable = getIllustration(asset, horizontalTile);
+      illustration.setIllustration(illustrationDrawable);
+    }
+  }
+
+  /**
+   * Sets the aspect ratio of the illustration. This will be the space (padding top) reserved above
+   * the header text. This will override the padding top of the illustration.
+   *
+   * @param aspectRatio The aspect ratio
+   * @see com.android.setupwizardlib.view.Illustration#setAspectRatio(float)
+   */
+  public void setIllustrationAspectRatio(float aspectRatio) {
+    final View view = findManagedViewById(R.id.suw_layout_decor);
+    if (view instanceof Illustration) {
+      final Illustration illustration = (Illustration) view;
+      illustration.setAspectRatio(aspectRatio);
+    }
+  }
+
+  /**
+   * Set the top padding of the decor view. If the decor is an Illustration and the aspect ratio is
+   * set, this value will be overridden.
+   *
+   * <p>Note: Currently the default top padding for tablet landscape is 128dp, which is the offset
+   * of the card from the top. This is likely to change in future versions so this value aligns with
+   * the height of the illustration instead.
+   *
+   * @param paddingTop The top padding in pixels.
+   */
+  public void setDecorPaddingTop(int paddingTop) {
+    final View view = findManagedViewById(R.id.suw_layout_decor);
+    if (view != null) {
+      view.setPadding(
+          view.getPaddingLeft(), paddingTop, view.getPaddingRight(), view.getPaddingBottom());
+    }
+  }
+
+  /**
+   * Set the background of the layout, which is expected to be able to extend infinitely. If it is a
+   * bitmap tile and you want it to repeat, use {@link #setBackgroundTile(int)} instead.
+   */
+  public void setLayoutBackground(Drawable background) {
+    final View view = findManagedViewById(R.id.suw_layout_decor);
+    if (view != null) {
+      //noinspection deprecation
+      view.setBackgroundDrawable(background);
+    }
+  }
+
+  /**
+   * Set the background of the layout to a repeating bitmap tile. To use a different kind of
+   * drawable, use {@link #setLayoutBackground(android.graphics.drawable.Drawable)} instead.
+   */
+  public void setBackgroundTile(int backgroundTile) {
+    final Drawable backgroundTileDrawable = getContext().getResources().getDrawable(backgroundTile);
+    setBackgroundTile(backgroundTileDrawable);
+  }
+
+  private void setBackgroundTile(Drawable backgroundTile) {
+    if (backgroundTile instanceof BitmapDrawable) {
+      ((BitmapDrawable) backgroundTile).setTileModeXY(TileMode.REPEAT, TileMode.REPEAT);
+    }
+    setLayoutBackground(backgroundTile);
+  }
+
+  private Drawable getIllustration(int asset, int horizontalTile) {
+    final Context context = getContext();
+    final Drawable assetDrawable = context.getResources().getDrawable(asset);
+    final Drawable tile = context.getResources().getDrawable(horizontalTile);
+    return getIllustration(assetDrawable, tile);
+  }
+
+  @SuppressLint("RtlHardcoded")
+  private Drawable getIllustration(Drawable asset, Drawable horizontalTile) {
+    final Context context = getContext();
+    if (context.getResources().getBoolean(R.bool.suwUseTabletLayout)) {
+      // If it is a "tablet" (sw600dp), create a LayerDrawable with the horizontal tile.
+      if (horizontalTile instanceof BitmapDrawable) {
+        ((BitmapDrawable) horizontalTile).setTileModeX(TileMode.REPEAT);
+        ((BitmapDrawable) horizontalTile).setGravity(Gravity.TOP);
+      }
+      if (asset instanceof BitmapDrawable) {
+        // Always specify TOP | LEFT, Illustration will flip the entire LayerDrawable.
+        ((BitmapDrawable) asset).setGravity(Gravity.TOP | Gravity.LEFT);
+      }
+      final LayerDrawable layers = new LayerDrawable(new Drawable[] {horizontalTile, asset});
+      if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
+        layers.setAutoMirrored(true);
+      }
+      return layers;
+    } else {
+      // If it is a "phone" (not sw600dp), simply return the illustration
+      if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
+        asset.setAutoMirrored(true);
+      }
+      return asset;
+    }
+  }
+
+  public boolean isProgressBarShown() {
+    return getMixin(ProgressBarMixin.class).isShown();
+  }
+
+  /**
+   * Sets whether the progress bar below the header text is shown or not. The progress bar is a
+   * lazily inflated ViewStub, which means the progress bar will not actually be part of the view
+   * hierarchy until the first time this is set to {@code true}.
+   */
+  public void setProgressBarShown(boolean shown) {
+    getMixin(ProgressBarMixin.class).setShown(shown);
+  }
+
+  /** @deprecated Use {@link #setProgressBarShown(boolean)} */
+  @Deprecated
+  public void showProgressBar() {
+    setProgressBarShown(true);
+  }
+
+  /** @deprecated Use {@link #setProgressBarShown(boolean)} */
+  @Deprecated
+  public void hideProgressBar() {
+    setProgressBarShown(false);
+  }
+
+  public void setProgressBarColor(ColorStateList color) {
+    getMixin(ProgressBarMixin.class).setColor(color);
+  }
+
+  public ColorStateList getProgressBarColor() {
+    return getMixin(ProgressBarMixin.class).getColor();
+  }
+
+  /* Misc */
+
+  protected static class SavedState extends BaseSavedState {
+
+    boolean isProgressBarShown = false;
+
+    public SavedState(Parcelable parcelable) {
+      super(parcelable);
     }
 
-    // All the constructors delegate to this init method. The 3-argument constructor is not
-    // available in LinearLayout before v11, so call super with the exact same arguments.
-    private void init(AttributeSet attrs, int defStyleAttr) {
-        registerMixin(HeaderMixin.class, new HeaderMixin(this, attrs, defStyleAttr));
-        registerMixin(ProgressBarMixin.class, new ProgressBarMixin(this));
-        registerMixin(NavigationBarMixin.class, new NavigationBarMixin(this));
-        final RequireScrollMixin requireScrollMixin = new RequireScrollMixin(this);
-        registerMixin(RequireScrollMixin.class, requireScrollMixin);
-
-        final ScrollView scrollView = getScrollView();
-        if (scrollView != null) {
-            requireScrollMixin.setScrollHandlingDelegate(
-                    new ScrollViewScrollHandlingDelegate(requireScrollMixin, scrollView));
-        }
-
-        final TypedArray a = getContext().obtainStyledAttributes(attrs,
-                R.styleable.SuwSetupWizardLayout, defStyleAttr, 0);
-
-        // Set the background from XML, either directly or built from a bitmap tile
-        final Drawable background =
-                a.getDrawable(R.styleable.SuwSetupWizardLayout_suwBackground);
-        if (background != null) {
-            setLayoutBackground(background);
-        } else {
-            final Drawable backgroundTile =
-                    a.getDrawable(R.styleable.SuwSetupWizardLayout_suwBackgroundTile);
-            if (backgroundTile != null) {
-                setBackgroundTile(backgroundTile);
-            }
-        }
-
-        // Set the illustration from XML, either directly or built from image + horizontal tile
-        final Drawable illustration =
-                a.getDrawable(R.styleable.SuwSetupWizardLayout_suwIllustration);
-        if (illustration != null) {
-            setIllustration(illustration);
-        } else {
-            final Drawable illustrationImage =
-                    a.getDrawable(R.styleable.SuwSetupWizardLayout_suwIllustrationImage);
-            final Drawable horizontalTile = a.getDrawable(
-                    R.styleable.SuwSetupWizardLayout_suwIllustrationHorizontalTile);
-            if (illustrationImage != null && horizontalTile != null) {
-                setIllustration(illustrationImage, horizontalTile);
-            }
-        }
-
-        // Set the top padding of the illustration
-        int decorPaddingTop = a.getDimensionPixelSize(
-                R.styleable.SuwSetupWizardLayout_suwDecorPaddingTop, -1);
-        if (decorPaddingTop == -1) {
-            decorPaddingTop = getResources().getDimensionPixelSize(R.dimen.suw_decor_padding_top);
-        }
-        setDecorPaddingTop(decorPaddingTop);
-
-
-        // Set the illustration aspect ratio. See Illustration.setAspectRatio(float). This will
-        // override suwDecorPaddingTop if its value is not 0.
-        float illustrationAspectRatio = a.getFloat(
-                R.styleable.SuwSetupWizardLayout_suwIllustrationAspectRatio, -1f);
-        if (illustrationAspectRatio == -1f) {
-            final TypedValue out = new TypedValue();
-            getResources().getValue(R.dimen.suw_illustration_aspect_ratio, out, true);
-            illustrationAspectRatio = out.getFloat();
-        }
-        setIllustrationAspectRatio(illustrationAspectRatio);
-
-        a.recycle();
+    public SavedState(Parcel source) {
+      super(source);
+      isProgressBarShown = source.readInt() != 0;
     }
 
     @Override
-    protected Parcelable onSaveInstanceState() {
-        final Parcelable parcelable = super.onSaveInstanceState();
-        final SavedState ss = new SavedState(parcelable);
-        ss.mIsProgressBarShown = isProgressBarShown();
-        return ss;
+    public void writeToParcel(Parcel dest, int flags) {
+      super.writeToParcel(dest, flags);
+      dest.writeInt(isProgressBarShown ? 1 : 0);
     }
 
-    @Override
-    protected void onRestoreInstanceState(Parcelable state) {
-        if (!(state instanceof SavedState)) {
-            Log.w(TAG, "Ignoring restore instance state " + state);
-            super.onRestoreInstanceState(state);
-            return;
-        }
+    public static final Parcelable.Creator<SavedState> CREATOR =
+        new Parcelable.Creator<SavedState>() {
 
-        final SavedState ss = (SavedState) state;
-        super.onRestoreInstanceState(ss.getSuperState());
-        final boolean isProgressBarShown = ss.mIsProgressBarShown;
-        setProgressBarShown(isProgressBarShown);
-    }
+          @Override
+          public SavedState createFromParcel(Parcel parcel) {
+            return new SavedState(parcel);
+          }
 
-    @Override
-    protected View onInflateTemplate(LayoutInflater inflater, int template) {
-        if (template == 0) {
-            template = R.layout.suw_template;
-        }
-        return inflateTemplate(inflater, R.style.SuwThemeMaterial_Light, template);
-    }
-
-    @Override
-    protected ViewGroup findContainer(int containerId) {
-        if (containerId == 0) {
-            containerId = R.id.suw_layout_content;
-        }
-        return super.findContainer(containerId);
-    }
-
-    public NavigationBar getNavigationBar() {
-        return getMixin(NavigationBarMixin.class).getNavigationBar();
-    }
-
-    public ScrollView getScrollView() {
-        final View view = findManagedViewById(R.id.suw_bottom_scroll_view);
-        return view instanceof ScrollView ? (ScrollView) view : null;
-    }
-
-    public void requireScrollToBottom() {
-        final RequireScrollMixin requireScrollMixin = getMixin(RequireScrollMixin.class);
-        final NavigationBar navigationBar = getNavigationBar();
-        if (navigationBar != null) {
-            requireScrollMixin.requireScrollWithNavigationBar(navigationBar);
-        } else {
-            Log.e(TAG, "Cannot require scroll. Navigation bar is null.");
-        }
-    }
-
-    public void setHeaderText(int title) {
-        getMixin(HeaderMixin.class).setText(title);
-    }
-
-    public void setHeaderText(CharSequence title) {
-        getMixin(HeaderMixin.class).setText(title);
-    }
-
-    public CharSequence getHeaderText() {
-        return getMixin(HeaderMixin.class).getText();
-    }
-
-    public TextView getHeaderTextView() {
-        return getMixin(HeaderMixin.class).getTextView();
-    }
-
-    /**
-     * Set the illustration of the layout. The drawable will be applied as is, and the bounds will
-     * be set as implemented in {@link com.android.setupwizardlib.view.Illustration}. To create
-     * a suitable drawable from an asset and a horizontal repeating tile, use
-     * {@link #setIllustration(int, int)} instead.
-     *
-     * @param drawable The drawable specifying the illustration.
-     */
-    public void setIllustration(Drawable drawable) {
-        final View view = findManagedViewById(R.id.suw_layout_decor);
-        if (view instanceof Illustration) {
-            final Illustration illustration = (Illustration) view;
-            illustration.setIllustration(drawable);
-        }
-    }
-
-    /**
-     * Set the illustration of the layout, which will be created asset and the horizontal tile as
-     * suitable. On phone layouts (not sw600dp), the asset will be scaled, maintaining aspect ratio.
-     * On tablets (sw600dp), the assets will always have 256dp height and the rest of the
-     * illustration area that the asset doesn't fill will be covered by the horizontalTile.
-     *
-     * @param asset Resource ID of the illustration asset.
-     * @param horizontalTile Resource ID of the horizontally repeating tile for tablet layout.
-     */
-    public void setIllustration(int asset, int horizontalTile) {
-        final View view = findManagedViewById(R.id.suw_layout_decor);
-        if (view instanceof Illustration) {
-            final Illustration illustration = (Illustration) view;
-            final Drawable illustrationDrawable = getIllustration(asset, horizontalTile);
-            illustration.setIllustration(illustrationDrawable);
-        }
-    }
-
-    private void setIllustration(Drawable asset, Drawable horizontalTile) {
-        final View view = findManagedViewById(R.id.suw_layout_decor);
-        if (view instanceof Illustration) {
-            final Illustration illustration = (Illustration) view;
-            final Drawable illustrationDrawable = getIllustration(asset, horizontalTile);
-            illustration.setIllustration(illustrationDrawable);
-        }
-    }
-
-    /**
-     * Sets the aspect ratio of the illustration. This will be the space (padding top) reserved
-     * above the header text. This will override the padding top of the illustration.
-     *
-     * @param aspectRatio The aspect ratio
-     * @see com.android.setupwizardlib.view.Illustration#setAspectRatio(float)
-     */
-    public void setIllustrationAspectRatio(float aspectRatio) {
-        final View view = findManagedViewById(R.id.suw_layout_decor);
-        if (view instanceof Illustration) {
-            final Illustration illustration = (Illustration) view;
-            illustration.setAspectRatio(aspectRatio);
-        }
-    }
-
-    /**
-     * Set the top padding of the decor view. If the decor is an Illustration and the aspect ratio
-     * is set, this value will be overridden.
-     *
-     * <p>Note: Currently the default top padding for tablet landscape is 128dp, which is the offset
-     * of the card from the top. This is likely to change in future versions so this value aligns
-     * with the height of the illustration instead.
-     *
-     * @param paddingTop The top padding in pixels.
-     */
-    public void setDecorPaddingTop(int paddingTop) {
-        final View view = findManagedViewById(R.id.suw_layout_decor);
-        if (view != null) {
-            view.setPadding(view.getPaddingLeft(), paddingTop, view.getPaddingRight(),
-                    view.getPaddingBottom());
-        }
-    }
-
-    /**
-     * Set the background of the layout, which is expected to be able to extend infinitely. If it is
-     * a bitmap tile and you want it to repeat, use {@link #setBackgroundTile(int)} instead.
-     */
-    public void setLayoutBackground(Drawable background) {
-        final View view = findManagedViewById(R.id.suw_layout_decor);
-        if (view != null) {
-            //noinspection deprecation
-            view.setBackgroundDrawable(background);
-        }
-    }
-
-    /**
-     * Set the background of the layout to a repeating bitmap tile. To use a different kind of
-     * drawable, use {@link #setLayoutBackground(android.graphics.drawable.Drawable)} instead.
-     */
-    public void setBackgroundTile(int backgroundTile) {
-        final Drawable backgroundTileDrawable =
-                getContext().getResources().getDrawable(backgroundTile);
-        setBackgroundTile(backgroundTileDrawable);
-    }
-
-    private void setBackgroundTile(Drawable backgroundTile) {
-        if (backgroundTile instanceof BitmapDrawable) {
-            ((BitmapDrawable) backgroundTile).setTileModeXY(TileMode.REPEAT, TileMode.REPEAT);
-        }
-        setLayoutBackground(backgroundTile);
-    }
-
-    private Drawable getIllustration(int asset, int horizontalTile) {
-        final Context context = getContext();
-        final Drawable assetDrawable = context.getResources().getDrawable(asset);
-        final Drawable tile = context.getResources().getDrawable(horizontalTile);
-        return getIllustration(assetDrawable, tile);
-    }
-
-    @SuppressLint("RtlHardcoded")
-    private Drawable getIllustration(Drawable asset, Drawable horizontalTile) {
-        final Context context = getContext();
-        if (context.getResources().getBoolean(R.bool.suwUseTabletLayout)) {
-            // If it is a "tablet" (sw600dp), create a LayerDrawable with the horizontal tile.
-            if (horizontalTile instanceof BitmapDrawable) {
-                ((BitmapDrawable) horizontalTile).setTileModeX(TileMode.REPEAT);
-                ((BitmapDrawable) horizontalTile).setGravity(Gravity.TOP);
-            }
-            if (asset instanceof BitmapDrawable) {
-                // Always specify TOP | LEFT, Illustration will flip the entire LayerDrawable.
-                ((BitmapDrawable) asset).setGravity(Gravity.TOP | Gravity.LEFT);
-            }
-            final LayerDrawable layers =
-                    new LayerDrawable(new Drawable[] { horizontalTile, asset });
-            if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
-                layers.setAutoMirrored(true);
-            }
-            return layers;
-        } else {
-            // If it is a "phone" (not sw600dp), simply return the illustration
-            if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
-                asset.setAutoMirrored(true);
-            }
-            return asset;
-        }
-    }
-
-    public boolean isProgressBarShown() {
-        return getMixin(ProgressBarMixin.class).isShown();
-    }
-
-    /**
-     * Sets whether the progress bar below the header text is shown or not. The progress bar is
-     * a lazily inflated ViewStub, which means the progress bar will not actually be part of the
-     * view hierarchy until the first time this is set to {@code true}.
-     */
-    public void setProgressBarShown(boolean shown) {
-        getMixin(ProgressBarMixin.class).setShown(shown);
-    }
-
-    /**
-     * @deprecated Use {@link #setProgressBarShown(boolean)}
-     */
-    @Deprecated
-    public void showProgressBar() {
-        setProgressBarShown(true);
-    }
-
-    /**
-     * @deprecated Use {@link #setProgressBarShown(boolean)}
-     */
-    @Deprecated
-    public void hideProgressBar() {
-        setProgressBarShown(false);
-    }
-
-    public void setProgressBarColor(ColorStateList color) {
-        getMixin(ProgressBarMixin.class).setColor(color);
-    }
-
-    public ColorStateList getProgressBarColor() {
-        return getMixin(ProgressBarMixin.class).getColor();
-    }
-
-    /* Misc */
-
-    protected static class SavedState extends BaseSavedState {
-
-        boolean mIsProgressBarShown = false;
-
-        public SavedState(Parcelable parcelable) {
-            super(parcelable);
-        }
-
-        public SavedState(Parcel source) {
-            super(source);
-            mIsProgressBarShown = source.readInt() != 0;
-        }
-
-        @Override
-        public void writeToParcel(Parcel dest, int flags) {
-            super.writeToParcel(dest, flags);
-            dest.writeInt(mIsProgressBarShown ? 1 : 0);
-        }
-
-        public static final Parcelable.Creator<SavedState> CREATOR =
-                new Parcelable.Creator<SavedState>() {
-
-                    @Override
-                    public SavedState createFromParcel(Parcel parcel) {
-                        return new SavedState(parcel);
-                    }
-
-                    @Override
-                    public SavedState[] newArray(int size) {
-                        return new SavedState[size];
-                    }
-                };
-    }
+          @Override
+          public SavedState[] newArray(int size) {
+            return new SavedState[size];
+          }
+        };
+  }
 }
diff --git a/library/main/src/com/android/setupwizardlib/SetupWizardListLayout.java b/library/main/src/com/android/setupwizardlib/SetupWizardListLayout.java
index 050d566..f1c7e11 100644
--- a/library/main/src/com/android/setupwizardlib/SetupWizardListLayout.java
+++ b/library/main/src/com/android/setupwizardlib/SetupWizardListLayout.java
@@ -26,141 +26,128 @@
 import android.view.ViewGroup;
 import android.widget.ListAdapter;
 import android.widget.ListView;
-
 import com.android.setupwizardlib.template.ListMixin;
 import com.android.setupwizardlib.template.ListViewScrollHandlingDelegate;
 import com.android.setupwizardlib.template.RequireScrollMixin;
 
 public class SetupWizardListLayout extends SetupWizardLayout {
 
-    private static final String TAG = "SetupWizardListLayout";
+  private ListMixin listMixin;
 
-    private ListMixin mListMixin;
+  public SetupWizardListLayout(Context context) {
+    this(context, 0, 0);
+  }
 
-    public SetupWizardListLayout(Context context) {
-        this(context, 0, 0);
+  public SetupWizardListLayout(Context context, int template) {
+    this(context, template, 0);
+  }
+
+  public SetupWizardListLayout(Context context, int template, int containerId) {
+    super(context, template, containerId);
+    init(null, 0);
+  }
+
+  public SetupWizardListLayout(Context context, AttributeSet attrs) {
+    super(context, attrs);
+    init(attrs, 0);
+  }
+
+  @TargetApi(VERSION_CODES.HONEYCOMB)
+  public SetupWizardListLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+    super(context, attrs, defStyleAttr);
+    init(attrs, defStyleAttr);
+  }
+
+  private void init(AttributeSet attrs, int defStyleAttr) {
+    listMixin = new ListMixin(this, attrs, defStyleAttr);
+    registerMixin(ListMixin.class, listMixin);
+
+    final RequireScrollMixin requireScrollMixin = getMixin(RequireScrollMixin.class);
+    requireScrollMixin.setScrollHandlingDelegate(
+        new ListViewScrollHandlingDelegate(requireScrollMixin, getListView()));
+  }
+
+  @Override
+  protected View onInflateTemplate(LayoutInflater inflater, int template) {
+    if (template == 0) {
+      template = R.layout.suw_list_template;
     }
+    return super.onInflateTemplate(inflater, template);
+  }
 
-    public SetupWizardListLayout(Context context, int template) {
-        this(context, template, 0);
+  @Override
+  protected ViewGroup findContainer(int containerId) {
+    if (containerId == 0) {
+      containerId = android.R.id.list;
     }
+    return super.findContainer(containerId);
+  }
 
-    public SetupWizardListLayout(Context context, int template, int containerId) {
-        super(context, template, containerId);
-        init(context, null, 0);
-    }
+  @Override
+  protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+    super.onLayout(changed, left, top, right, bottom);
+    listMixin.onLayout();
+  }
 
-    public SetupWizardListLayout(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        init(context, attrs, 0);
-    }
+  public ListView getListView() {
+    return listMixin.getListView();
+  }
 
-    @TargetApi(VERSION_CODES.HONEYCOMB)
-    public SetupWizardListLayout(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-        init(context, attrs, defStyleAttr);
-    }
+  public void setAdapter(ListAdapter adapter) {
+    listMixin.setAdapter(adapter);
+  }
 
-    private void init(Context context, AttributeSet attrs, int defStyleAttr) {
-        mListMixin = new ListMixin(this, attrs, defStyleAttr);
-        registerMixin(ListMixin.class, mListMixin);
+  public ListAdapter getAdapter() {
+    return listMixin.getAdapter();
+  }
 
-        final RequireScrollMixin requireScrollMixin = getMixin(RequireScrollMixin.class);
-        requireScrollMixin.setScrollHandlingDelegate(
-                new ListViewScrollHandlingDelegate(requireScrollMixin, getListView()));
-    }
+  /**
+   * 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 ListMixin#setDividerInset(int)
+   * @deprecated Use {@link #setDividerInsets(int, int)} instead.
+   */
+  @Deprecated
+  public void setDividerInset(int inset) {
+    listMixin.setDividerInset(inset);
+  }
 
-    @Override
-    protected View onInflateTemplate(LayoutInflater inflater, int template) {
-        if (template == 0) {
-            template = R.layout.suw_list_template;
-        }
-        return super.onInflateTemplate(inflater, template);
-    }
+  /**
+   * 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) {
+    listMixin.setDividerInsets(start, end);
+  }
 
-    @Override
-    protected ViewGroup findContainer(int containerId) {
-        if (containerId == 0) {
-            containerId = android.R.id.list;
-        }
-        return super.findContainer(containerId);
-    }
+  /** @deprecated Use {@link #getDividerInsetStart()} instead. */
+  @Deprecated
+  public int getDividerInset() {
+    return listMixin.getDividerInset();
+  }
 
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        super.onLayout(changed, left, top, right, bottom);
-        mListMixin.onLayout();
-    }
+  /** @see ListMixin#getDividerInsetStart() */
+  public int getDividerInsetStart() {
+    return listMixin.getDividerInsetStart();
+  }
 
-    public ListView getListView() {
-        return mListMixin.getListView();
-    }
+  /** @see ListMixin#getDividerInsetEnd() */
+  public int getDividerInsetEnd() {
+    return listMixin.getDividerInsetEnd();
+  }
 
-    public void setAdapter(ListAdapter adapter) {
-        mListMixin.setAdapter(adapter);
-    }
-
-    public ListAdapter getAdapter() {
-        return mListMixin.getAdapter();
-    }
-
-    /**
-     * 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 ListMixin#setDividerInset(int)
-     * @deprecated Use {@link #setDividerInsets(int, int)} instead.
-     */
-    @Deprecated
-    public void setDividerInset(int inset) {
-        mListMixin.setDividerInset(inset);
-    }
-
-    /**
-     * 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() {
-        return mListMixin.getDivider();
-    }
+  /** @see ListMixin#getDivider() */
+  public Drawable getDivider() {
+    return listMixin.getDivider();
+  }
 }
diff --git a/library/main/src/com/android/setupwizardlib/TemplateLayout.java b/library/main/src/com/android/setupwizardlib/TemplateLayout.java
index 0108880..c53e176 100644
--- a/library/main/src/com/android/setupwizardlib/TemplateLayout.java
+++ b/library/main/src/com/android/setupwizardlib/TemplateLayout.java
@@ -20,256 +20,249 @@
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.os.Build.VERSION_CODES;
+import androidx.annotation.Keep;
+import androidx.annotation.LayoutRes;
+import androidx.annotation.StyleRes;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
 import android.widget.FrameLayout;
-
-import androidx.annotation.Keep;
-import androidx.annotation.LayoutRes;
-import androidx.annotation.StyleRes;
-
 import com.android.setupwizardlib.template.Mixin;
 import com.android.setupwizardlib.util.FallbackThemeWrapper;
-
 import java.util.HashMap;
 import java.util.Map;
 
 /**
- * A generic template class that inflates a template, provided in the constructor or in
- * {@code android:layout} through XML, and adds its children to a "container" in the template. When
+ * A generic template class that inflates a template, provided in the constructor or in {@code
+ * android:layout} through XML, and adds its children to a "container" in the template. When
  * inflating this layout from XML, the {@code android:layout} and {@code suwContainer} attributes
  * are required.
  */
 public class TemplateLayout extends FrameLayout {
 
-    /**
-     * The container of the actual content. This will be a view in the template, which child views
-     * will be added to when {@link #addView(View)} is called.
-     */
-    private ViewGroup mContainer;
+  /**
+   * The container of the actual content. This will be a view in the template, which child views
+   * will be added to when {@link #addView(View)} is called.
+   */
+  private ViewGroup container;
 
-    private Map<Class<? extends Mixin>, Mixin> mMixins = new HashMap<>();
+  private final Map<Class<? extends Mixin>, Mixin> mixins = new HashMap<>();
 
-    public TemplateLayout(Context context, int template, int containerId) {
-        super(context);
-        init(template, containerId, null, R.attr.suwLayoutTheme);
+  public TemplateLayout(Context context, int template, int containerId) {
+    super(context);
+    init(template, containerId, null, R.attr.suwLayoutTheme);
+  }
+
+  public TemplateLayout(Context context, AttributeSet attrs) {
+    super(context, attrs);
+    init(0, 0, attrs, R.attr.suwLayoutTheme);
+  }
+
+  @TargetApi(VERSION_CODES.HONEYCOMB)
+  public TemplateLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+    super(context, attrs, defStyleAttr);
+    init(0, 0, attrs, defStyleAttr);
+  }
+
+  // All the constructors delegate to this init method. The 3-argument constructor is not
+  // available in LinearLayout before v11, so call super with the exact same arguments.
+  private void init(int template, int containerId, AttributeSet attrs, int defStyleAttr) {
+    final TypedArray a =
+        getContext().obtainStyledAttributes(attrs, R.styleable.SuwTemplateLayout, defStyleAttr, 0);
+    if (template == 0) {
+      template = a.getResourceId(R.styleable.SuwTemplateLayout_android_layout, 0);
     }
-
-    public TemplateLayout(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        init(0, 0, attrs, R.attr.suwLayoutTheme);
+    if (containerId == 0) {
+      containerId = a.getResourceId(R.styleable.SuwTemplateLayout_suwContainer, 0);
     }
+    inflateTemplate(template, containerId);
 
-    @TargetApi(VERSION_CODES.HONEYCOMB)
-    public TemplateLayout(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-        init(0, 0, attrs, defStyleAttr);
+    a.recycle();
+  }
+
+  /**
+   * Registers a mixin with a given class. This method should be called in the constructor.
+   *
+   * @param cls The class to register the mixin. In most cases, {@code cls} is the same as {@code
+   *     mixin.getClass()}, but {@code cls} can also be a super class of that. In the latter case
+   *     the mixin must be retrieved using {@code cls} in {@link #getMixin(Class)}, not the
+   *     subclass.
+   * @param mixin The mixin to be registered.
+   * @param <M> The class of the mixin to register. This is the same as {@code cls}
+   */
+  protected <M extends Mixin> void registerMixin(Class<M> cls, M mixin) {
+    mixins.put(cls, mixin);
+  }
+
+  /**
+   * Same as {@link android.view.View#findViewById(int)}, but may include views that are managed by
+   * this view but not currently added to the view hierarchy. e.g. recycler view or list view
+   * headers that are not currently shown.
+   */
+  // Returning generic type is the common pattern used for findViewBy* methods
+  @SuppressWarnings("TypeParameterUnusedInFormals")
+  public <T extends View> T findManagedViewById(int id) {
+    return findViewById(id);
+  }
+
+  /**
+   * Get a {@link Mixin} from this template registered earlier in {@link #registerMixin(Class,
+   * Mixin)}.
+   *
+   * @param cls The class marker of Mixin being requested. The actual Mixin returned may be a
+   *     subclass of this marker. Note that this must be the same class as registered in {@link
+   *     #registerMixin(Class, Mixin)}, which is not necessarily the same as the concrete class of
+   *     the instance returned by this method.
+   * @param <M> The type of the class marker.
+   * @return The mixin marked by {@code cls}, or null if the template does not have a matching
+   *     mixin.
+   */
+  @SuppressWarnings("unchecked")
+  public <M extends Mixin> M getMixin(Class<M> cls) {
+    return (M) mixins.get(cls);
+  }
+
+  @Override
+  public void addView(View child, int index, ViewGroup.LayoutParams params) {
+    container.addView(child, index, params);
+  }
+
+  private void addViewInternal(View child) {
+    super.addView(child, -1, generateDefaultLayoutParams());
+  }
+
+  private void inflateTemplate(int templateResource, int containerId) {
+    final LayoutInflater inflater = LayoutInflater.from(getContext());
+    final View templateRoot = onInflateTemplate(inflater, templateResource);
+    addViewInternal(templateRoot);
+
+    container = findContainer(containerId);
+    if (container == null) {
+      throw new IllegalArgumentException("Container cannot be null in TemplateLayout");
     }
+    onTemplateInflated();
+  }
 
-    // All the constructors delegate to this init method. The 3-argument constructor is not
-    // available in LinearLayout before v11, so call super with the exact same arguments.
-    private void init(int template, int containerId, AttributeSet attrs, int defStyleAttr) {
-        final TypedArray a = getContext().obtainStyledAttributes(attrs,
-                R.styleable.SuwTemplateLayout, defStyleAttr, 0);
-        if (template == 0) {
-            template = a.getResourceId(R.styleable.SuwTemplateLayout_android_layout, 0);
-        }
-        if (containerId == 0) {
-            containerId = a.getResourceId(R.styleable.SuwTemplateLayout_suwContainer, 0);
-        }
-        inflateTemplate(template, containerId);
-
-        a.recycle();
+  /**
+   * Inflate the template using the given inflater and theme. The fallback theme will be applied to
+   * the theme without overriding the values already defined in the theme, but simply providing
+   * default values for values which have not been defined. This allows templates to add additional
+   * required theme attributes without breaking existing clients.
+   *
+   * <p>In general, clients should still set the activity theme to the corresponding theme in setup
+   * wizard lib, so that the content area gets the correct styles as well.
+   *
+   * @param inflater A LayoutInflater to inflate the template.
+   * @param fallbackTheme A fallback theme to apply to the template. If the values defined in the
+   *     fallback theme is already defined in the original theme, the value in the original theme
+   *     takes precedence.
+   * @param template The layout template to be inflated.
+   * @return Root of the inflated layout.
+   * @see FallbackThemeWrapper
+   */
+  protected final View inflateTemplate(
+      LayoutInflater inflater, @StyleRes int fallbackTheme, @LayoutRes int template) {
+    if (template == 0) {
+      throw new IllegalArgumentException("android:layout not specified for TemplateLayout");
     }
-
-    /**
-     * Registers a mixin with a given class. This method should be called in the constructor.
-     *
-     * @param cls The class to register the mixin. In most cases, {@code cls} is the same as
-     *            {@code mixin.getClass()}, but {@code cls} can also be a super class of that. In
-     *            the latter case the the mixin must be retrieved using {@code cls} in
-     *            {@link #getMixin(Class)}, not the subclass.
-     * @param mixin The mixin to be registered.
-     * @param <M> The class of the mixin to register. This is the same as {@code cls}
-     */
-    protected <M extends Mixin> void registerMixin(Class<M> cls, M mixin) {
-        mMixins.put(cls, mixin);
+    if (fallbackTheme != 0) {
+      inflater =
+          LayoutInflater.from(new FallbackThemeWrapper(inflater.getContext(), fallbackTheme));
     }
+    return inflater.inflate(template, this, false);
+  }
 
-    /**
-     * Same as {@link android.view.View#findViewById(int)}, but may include views that are managed
-     * by this view but not currently added to the view hierarchy. e.g. recycler view or list view
-     * headers that are not currently shown.
-     */
-    // Returning generic type is the common pattern used for findViewBy* methods
-    @SuppressWarnings("TypeParameterUnusedInFormals")
-    public <T extends View> T findManagedViewById(int id) {
-        return findViewById(id);
+  /**
+   * This method inflates the template. Subclasses can override this method to customize the
+   * template inflation, or change to a different default template. The root of the inflated layout
+   * should be returned, and not added to the view hierarchy.
+   *
+   * @param inflater A LayoutInflater to inflate the template.
+   * @param template The resource ID of the template to be inflated, or 0 if no template is
+   *     specified.
+   * @return Root of the inflated layout.
+   */
+  protected View onInflateTemplate(LayoutInflater inflater, @LayoutRes int template) {
+    return inflateTemplate(inflater, 0, template);
+  }
+
+  protected ViewGroup findContainer(int containerId) {
+    if (containerId == 0) {
+      // Maintain compatibility with the deprecated way of specifying container ID.
+      containerId = getContainerId();
     }
+    return (ViewGroup) findViewById(containerId);
+  }
 
-    /**
-     * Get a {@link Mixin} from this template registered earlier in
-     * {@link #registerMixin(Class, Mixin)}.
-     *
-     * @param cls The class marker of Mixin being requested. The actual Mixin returned may be a
-     *            subclass of this marker. Note that this must be the same class as registered in
-     *            {@link #registerMixin(Class, Mixin)}, which is not necessarily the
-     *            same as the concrete class of the instance returned by this method.
-     * @param <M> The type of the class marker.
-     * @return The mixin marked by {@code cls}, or null if the template does not have a matching
-     *         mixin.
-     */
-    @SuppressWarnings("unchecked")
-    public <M extends Mixin> M getMixin(Class<M> cls) {
-        return (M) mMixins.get(cls);
+  /**
+   * This is called after the template has been inflated and added to the view hierarchy. Subclasses
+   * can implement this method to modify the template as necessary, such as caching views retrieved
+   * from findViewById, or other view operations that need to be done in code. You can think of this
+   * as {@link View#onFinishInflate()} but for inflation of the template instead of for child views.
+   */
+  protected void onTemplateInflated() {}
+
+  /**
+   * @return ID of the default container for this layout. This will be used to find the container
+   *     ViewGroup, which all children views of this layout will be placed in.
+   * @deprecated Override {@link #findContainer(int)} instead.
+   */
+  @Deprecated
+  protected int getContainerId() {
+    return 0;
+  }
+
+  /* Animator support */
+
+  private float xFraction;
+  private ViewTreeObserver.OnPreDrawListener preDrawListener;
+
+  /**
+   * Set the X translation as a fraction of the width of this view. Make sure this method is not
+   * stripped out by proguard when using this with {@link android.animation.ObjectAnimator}. You may
+   * need to add <code>
+   *     -keep @androidx.annotation.Keep class *
+   * </code> to your proguard configuration if you are seeing mysterious {@link NoSuchMethodError}
+   * at runtime.
+   */
+  @Keep
+  @TargetApi(VERSION_CODES.HONEYCOMB)
+  public void setXFraction(float fraction) {
+    xFraction = fraction;
+    final int width = getWidth();
+    if (width != 0) {
+      setTranslationX(width * fraction);
+    } else {
+      // If we haven't done a layout pass yet, wait for one and then set the fraction before
+      // the draw occurs using an OnPreDrawListener. Don't call translationX until we know
+      // getWidth() has a reliable, non-zero value or else we will see the fragment flicker on
+      // screen.
+      if (preDrawListener == null) {
+        preDrawListener =
+            new ViewTreeObserver.OnPreDrawListener() {
+              @Override
+              public boolean onPreDraw() {
+                getViewTreeObserver().removeOnPreDrawListener(preDrawListener);
+                setXFraction(xFraction);
+                return true;
+              }
+            };
+        getViewTreeObserver().addOnPreDrawListener(preDrawListener);
+      }
     }
+  }
 
-    @Override
-    public void addView(View child, int index, ViewGroup.LayoutParams params) {
-        mContainer.addView(child, index, params);
-    }
-
-    private void addViewInternal(View child) {
-        super.addView(child, -1, generateDefaultLayoutParams());
-    }
-
-    private void inflateTemplate(int templateResource, int containerId) {
-        final LayoutInflater inflater = LayoutInflater.from(getContext());
-        final View templateRoot = onInflateTemplate(inflater, templateResource);
-        addViewInternal(templateRoot);
-
-        mContainer = findContainer(containerId);
-        if (mContainer == null) {
-            throw new IllegalArgumentException("Container cannot be null in TemplateLayout");
-        }
-        onTemplateInflated();
-    }
-
-    /**
-     * This method inflates the template. Subclasses can override this method to customize the
-     * template inflation, or change to a different default template. The root of the inflated
-     * layout should be returned, and not added to the view hierarchy.
-     *
-     * @param inflater A LayoutInflater to inflate the template.
-     * @param template The resource ID of the template to be inflated, or 0 if no template is
-     *                 specified.
-     * @return Root of the inflated layout.
-     */
-    protected View onInflateTemplate(LayoutInflater inflater, @LayoutRes int template) {
-        return inflateTemplate(inflater, 0, template);
-    }
-
-    /**
-     * Inflate the template using the given inflater and theme. The fallback theme will be applied
-     * to the theme without overriding the values already defined in the theme, but simply providing
-     * default values for values which have not been defined. This allows templates to add
-     * additional required theme attributes without breaking existing clients.
-     *
-     * <p>In general, clients should still set the activity theme to the corresponding theme in
-     * setup wizard lib, so that the content area gets the correct styles as well.
-     *
-     * @param inflater A LayoutInflater to inflate the template.
-     * @param fallbackTheme A fallback theme to apply to the template. If the values defined in the
-     *                      fallback theme is already defined in the original theme, the value in
-     *                      the original theme takes precedence.
-     * @param template The layout template to be inflated.
-     * @return Root of the inflated layout.
-     *
-     * @see FallbackThemeWrapper
-     */
-    protected final View inflateTemplate(LayoutInflater inflater, @StyleRes int fallbackTheme,
-            @LayoutRes int template) {
-        if (template == 0) {
-            throw new IllegalArgumentException("android:layout not specified for TemplateLayout");
-        }
-        if (fallbackTheme != 0) {
-            inflater = LayoutInflater.from(
-                    new FallbackThemeWrapper(inflater.getContext(), fallbackTheme));
-        }
-        return inflater.inflate(template, this, false);
-    }
-
-    protected ViewGroup findContainer(int containerId) {
-        if (containerId == 0) {
-            // Maintain compatibility with the deprecated way of specifying container ID.
-            containerId = getContainerId();
-        }
-        return (ViewGroup) findViewById(containerId);
-    }
-
-    /**
-     * This is called after the template has been inflated and added to the view hierarchy.
-     * Subclasses can implement this method to modify the template as necessary, such as caching
-     * views retrieved from findViewById, or other view operations that need to be done in code.
-     * You can think of this as {@link View#onFinishInflate()} but for inflation of the
-     * template instead of for child views.
-     */
-    protected void onTemplateInflated() {
-    }
-
-    /**
-     * @return ID of the default container for this layout. This will be used to find the container
-     * ViewGroup, which all children views of this layout will be placed in.
-     * @deprecated Override {@link #findContainer(int)} instead.
-     */
-    @Deprecated
-    protected int getContainerId() {
-        return 0;
-    }
-
-    /* Animator support */
-
-    private float mXFraction;
-    private ViewTreeObserver.OnPreDrawListener mPreDrawListener;
-
-    /**
-     * Set the X translation as a fraction of the width of this view. Make sure this method is not
-     * stripped out by proguard when using this with {@link android.animation.ObjectAnimator}. You
-     * may need to add
-     * <code>
-     *     -keep @androidx.annotation.Keep class *
-     * </code>
-     * to your proguard configuration if you are seeing mysterious {@link NoSuchMethodError} at
-     * runtime.
-     */
-    @Keep
-    @TargetApi(VERSION_CODES.HONEYCOMB)
-    public void setXFraction(float fraction) {
-        mXFraction = fraction;
-        final int width = getWidth();
-        if (width != 0) {
-            setTranslationX(width * fraction);
-        } else {
-            // If we haven't done a layout pass yet, wait for one and then set the fraction before
-            // the draw occurs using an OnPreDrawListener. Don't call translationX until we know
-            // getWidth() has a reliable, non-zero value or else we will see the fragment flicker on
-            // screen.
-            if (mPreDrawListener == null) {
-                mPreDrawListener = new ViewTreeObserver.OnPreDrawListener() {
-                    @Override
-                    public boolean onPreDraw() {
-                        getViewTreeObserver().removeOnPreDrawListener(mPreDrawListener);
-                        setXFraction(mXFraction);
-                        return true;
-                    }
-                };
-                getViewTreeObserver().addOnPreDrawListener(mPreDrawListener);
-            }
-        }
-    }
-
-    /**
-     * Return the X translation as a fraction of the width, as previously set in
-     * {@link #setXFraction(float)}.
-     *
-     * @see #setXFraction(float)
-     */
-    @Keep
-    @TargetApi(VERSION_CODES.HONEYCOMB)
-    public float getXFraction() {
-        return mXFraction;
-    }
+  /**
+   * Return the X translation as a fraction of the width, as previously set in {@link
+   * #setXFraction(float)}.
+   *
+   * @see #setXFraction(float)
+   */
+  @Keep
+  @TargetApi(VERSION_CODES.HONEYCOMB)
+  public float getXFraction() {
+    return xFraction;
+  }
 }
diff --git a/library/main/src/com/android/setupwizardlib/gesture/ConsecutiveTapsGestureDetector.java b/library/main/src/com/android/setupwizardlib/gesture/ConsecutiveTapsGestureDetector.java
index f438691..2ea5288 100644
--- a/library/main/src/com/android/setupwizardlib/gesture/ConsecutiveTapsGestureDetector.java
+++ b/library/main/src/com/android/setupwizardlib/gesture/ConsecutiveTapsGestureDetector.java
@@ -24,95 +24,86 @@
 /**
  * Helper class to detect the consective-tap gestures on a view.
  *
- * <p/>This class is instantiated and used similar to a GestureDetector, where onTouchEvent should
- * be called when there are MotionEvents this detector should know about.
+ * <p>This class is instantiated and used similar to a GestureDetector, where onTouchEvent should be
+ * called when there are MotionEvents this detector should know about.
  */
 public final class ConsecutiveTapsGestureDetector {
 
-    public interface OnConsecutiveTapsListener {
-        /**
-         * Callback method when the user tapped on the target view X number of times.
-         */
-        void onConsecutiveTaps(int numOfConsecutiveTaps);
-    }
+  public interface OnConsecutiveTapsListener {
+    /** Callback method when the user tapped on the target view X number of times. */
+    void onConsecutiveTaps(int numOfConsecutiveTaps);
+  }
 
-    private final View mView;
-    private final OnConsecutiveTapsListener mListener;
-    private final int mConsecutiveTapTouchSlopSquare;
-    private final int mConsecutiveTapTimeout;
+  private final View view;
+  private final OnConsecutiveTapsListener listener;
+  private final int consecutiveTapTouchSlopSquare;
+  private final int consecutiveTapTimeout;
 
-    private int mConsecutiveTapsCounter = 0;
-    private MotionEvent mPreviousTapEvent;
+  private int consecutiveTapsCounter = 0;
+  private MotionEvent previousTapEvent;
 
-    /**
-     * @param listener The listener that responds to the gesture.
-     * @param view  The target view that associated with consecutive-tap gesture.
-     */
-    public ConsecutiveTapsGestureDetector(
-            OnConsecutiveTapsListener listener,
-            View view) {
-        mListener = listener;
-        mView = view;
-        int doubleTapSlop = ViewConfiguration.get(mView.getContext()).getScaledDoubleTapSlop();
-        mConsecutiveTapTouchSlopSquare = doubleTapSlop * doubleTapSlop;
-        mConsecutiveTapTimeout = ViewConfiguration.getDoubleTapTimeout();
-    }
+  /**
+   * @param listener The listener that responds to the gesture.
+   * @param view The target view that associated with consecutive-tap gesture.
+   */
+  public ConsecutiveTapsGestureDetector(OnConsecutiveTapsListener listener, View view) {
+    this.listener = listener;
+    this.view = view;
+    int doubleTapSlop = ViewConfiguration.get(this.view.getContext()).getScaledDoubleTapSlop();
+    consecutiveTapTouchSlopSquare = doubleTapSlop * doubleTapSlop;
+    consecutiveTapTimeout = ViewConfiguration.getDoubleTapTimeout();
+  }
 
-    /**
-     * This method should be called from the relevant activity or view, typically in
-     * onTouchEvent, onInterceptTouchEvent or dispatchTouchEvent.
-     *
-     * @param ev The motion event
-     */
-    public void onTouchEvent(MotionEvent ev) {
-        if (ev.getAction() == MotionEvent.ACTION_UP) {
-            Rect viewRect = new Rect();
-            int[] leftTop = new int[2];
-            mView.getLocationOnScreen(leftTop);
-            viewRect.set(
-                    leftTop[0],
-                    leftTop[1],
-                    leftTop[0] + mView.getWidth(),
-                    leftTop[1] + mView.getHeight());
-            if (viewRect.contains((int) ev.getX(), (int) ev.getY())) {
-                if (isConsecutiveTap(ev)) {
-                    mConsecutiveTapsCounter++;
-                } else {
-                    mConsecutiveTapsCounter = 1;
-                }
-                mListener.onConsecutiveTaps(mConsecutiveTapsCounter);
-            } else {
-                // Touch outside the target view. Reset counter.
-                mConsecutiveTapsCounter = 0;
-            }
-
-            if (mPreviousTapEvent != null) {
-                mPreviousTapEvent.recycle();
-            }
-            mPreviousTapEvent = MotionEvent.obtain(ev);
+  /**
+   * This method should be called from the relevant activity or view, typically in onTouchEvent,
+   * onInterceptTouchEvent or dispatchTouchEvent.
+   *
+   * @param ev The motion event
+   */
+  public void onTouchEvent(MotionEvent ev) {
+    if (ev.getAction() == MotionEvent.ACTION_UP) {
+      Rect viewRect = new Rect();
+      int[] leftTop = new int[2];
+      view.getLocationOnScreen(leftTop);
+      viewRect.set(
+          leftTop[0], leftTop[1], leftTop[0] + view.getWidth(), leftTop[1] + view.getHeight());
+      if (viewRect.contains((int) ev.getX(), (int) ev.getY())) {
+        if (isConsecutiveTap(ev)) {
+          consecutiveTapsCounter++;
+        } else {
+          consecutiveTapsCounter = 1;
         }
+        listener.onConsecutiveTaps(consecutiveTapsCounter);
+      } else {
+        // Touch outside the target view. Reset counter.
+        consecutiveTapsCounter = 0;
+      }
+
+      if (previousTapEvent != null) {
+        previousTapEvent.recycle();
+      }
+      previousTapEvent = MotionEvent.obtain(ev);
+    }
+  }
+
+  /** Resets the consecutive-tap counter to zero. */
+  public void resetCounter() {
+    consecutiveTapsCounter = 0;
+  }
+
+  /**
+   * Returns true if the distance between consecutive tap is within {@link
+   * #consecutiveTapTouchSlopSquare}. False, otherwise.
+   */
+  private boolean isConsecutiveTap(MotionEvent currentTapEvent) {
+    if (previousTapEvent == null) {
+      return false;
     }
 
-    /**
-     * Resets the consecutive-tap counter to zero.
-     */
-    public void resetCounter() {
-        mConsecutiveTapsCounter = 0;
-    }
-
-    /**
-     * Returns true if the distance between consecutive tap is within
-     * {@link #mConsecutiveTapTouchSlopSquare}. False, otherwise.
-     */
-    private boolean isConsecutiveTap(MotionEvent currentTapEvent) {
-        if (mPreviousTapEvent == null) {
-            return false;
-        }
-
-        double deltaX = mPreviousTapEvent.getX() - currentTapEvent.getX();
-        double deltaY = mPreviousTapEvent.getY() - currentTapEvent.getY();
-        long deltaTime = currentTapEvent.getEventTime() - mPreviousTapEvent.getEventTime();
-        return (deltaX * deltaX + deltaY * deltaY <= mConsecutiveTapTouchSlopSquare)
-                && deltaTime < mConsecutiveTapTimeout;
-    }
+    double deltaX = previousTapEvent.getX() - currentTapEvent.getX();
+    double deltaY = previousTapEvent.getY() - currentTapEvent.getY();
+    long deltaTime = currentTapEvent.getEventTime() - previousTapEvent.getEventTime();
+    return (deltaX * deltaX + deltaY * deltaY <= consecutiveTapTouchSlopSquare)
+        && deltaTime < consecutiveTapTimeout;
+  }
 }
diff --git a/library/main/src/com/android/setupwizardlib/items/AbstractItem.java b/library/main/src/com/android/setupwizardlib/items/AbstractItem.java
index 11a9939..88f9294 100644
--- a/library/main/src/com/android/setupwizardlib/items/AbstractItem.java
+++ b/library/main/src/com/android/setupwizardlib/items/AbstractItem.java
@@ -25,42 +25,42 @@
  */
 public abstract class AbstractItem extends AbstractItemHierarchy implements IItem {
 
-    public AbstractItem() {
-        super();
-    }
+  public AbstractItem() {
+    super();
+  }
 
-    public AbstractItem(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
+  public AbstractItem(Context context, AttributeSet attrs) {
+    super(context, attrs);
+  }
 
-    @Override
-    public int getCount() {
-        return 1;
-    }
+  @Override
+  public int getCount() {
+    return 1;
+  }
 
-    @Override
-    public IItem getItemAt(int position) {
-        return this;
-    }
+  @Override
+  public IItem getItemAt(int position) {
+    return this;
+  }
 
-    @Override
-    public ItemHierarchy findItemById(int id) {
-        if (id == getId()) {
-            return this;
-        }
-        return null;
+  @Override
+  public ItemHierarchy findItemById(int id) {
+    if (id == getId()) {
+      return this;
     }
+    return null;
+  }
 
-    /**
-     * Convenience method to notify the adapter that the contents of this item has changed. This
-     * only includes non-structural changes. Changes that causes the item to be removed should use
-     * the other notification methods.
-     *
-     * @see #notifyItemRangeChanged(int, int)
-     * @see #notifyItemRangeInserted(int, int)
-     * @see #notifyItemRangeRemoved(int, int)
-     */
-    public void notifyItemChanged() {
-        notifyItemRangeChanged(0, 1);
-    }
+  /**
+   * Convenience method to notify the adapter that the contents of this item has changed. This only
+   * includes non-structural changes. Changes that causes the item to be removed should use the
+   * other notification methods.
+   *
+   * @see #notifyItemRangeChanged(int, int)
+   * @see #notifyItemRangeInserted(int, int)
+   * @see #notifyItemRangeRemoved(int, int)
+   */
+  public void notifyItemChanged() {
+    notifyItemRangeChanged(0, 1);
+  }
 }
diff --git a/library/main/src/com/android/setupwizardlib/items/AbstractItemHierarchy.java b/library/main/src/com/android/setupwizardlib/items/AbstractItemHierarchy.java
index 805e7af..e33cc2f 100644
--- a/library/main/src/com/android/setupwizardlib/items/AbstractItemHierarchy.java
+++ b/library/main/src/com/android/setupwizardlib/items/AbstractItemHierarchy.java
@@ -20,138 +20,123 @@
 import android.content.res.TypedArray;
 import android.util.AttributeSet;
 import android.util.Log;
-
 import com.android.setupwizardlib.R;
-
 import java.util.ArrayList;
 
-/**
- * An abstract item hierarchy; provides default implementation for ID and observers.
- */
+/** An abstract item hierarchy; provides default implementation for ID and observers. */
 public abstract class AbstractItemHierarchy implements ItemHierarchy {
 
-    /* static section */
+  /* static section */
 
-    private static final String TAG = "AbstractItemHierarchy";
+  private static final String TAG = "AbstractItemHierarchy";
 
-    /* non-static section */
+  /* non-static section */
 
-    private ArrayList<Observer> mObservers = new ArrayList<>();
-    private int mId = 0;
+  private final ArrayList<Observer> observers = new ArrayList<>();
+  private int id = 0;
 
-    public AbstractItemHierarchy() {
+  public AbstractItemHierarchy() {}
+
+  public AbstractItemHierarchy(Context context, AttributeSet attrs) {
+    TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SuwAbstractItem);
+    id = a.getResourceId(R.styleable.SuwAbstractItem_android_id, 0);
+    a.recycle();
+  }
+
+  public void setId(int id) {
+    this.id = id;
+  }
+
+  public int getId() {
+    return id;
+  }
+
+  public int getViewId() {
+    return getId();
+  }
+
+  @Override
+  public void registerObserver(Observer observer) {
+    observers.add(observer);
+  }
+
+  @Override
+  public void unregisterObserver(Observer observer) {
+    observers.remove(observer);
+  }
+
+  /** @see Observer#onChanged(ItemHierarchy) */
+  public void notifyChanged() {
+    for (Observer observer : observers) {
+      observer.onChanged(this);
+    }
+  }
+
+  /** @see Observer#onItemRangeChanged(ItemHierarchy, int, int) */
+  public void notifyItemRangeChanged(int position, int itemCount) {
+    if (position < 0) {
+      Log.w(TAG, "notifyItemRangeChanged: Invalid position=" + position);
+      return;
+    }
+    if (itemCount < 0) {
+      Log.w(TAG, "notifyItemRangeChanged: Invalid itemCount=" + itemCount);
+      return;
     }
 
-    public AbstractItemHierarchy(Context context, AttributeSet attrs) {
-        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SuwAbstractItem);
-        mId = a.getResourceId(R.styleable.SuwAbstractItem_android_id, 0);
-        a.recycle();
+    for (Observer observer : observers) {
+      observer.onItemRangeChanged(this, position, itemCount);
+    }
+  }
+
+  /** @see Observer#onItemRangeInserted(ItemHierarchy, int, int) */
+  public void notifyItemRangeInserted(int position, int itemCount) {
+    if (position < 0) {
+      Log.w(TAG, "notifyItemRangeInserted: Invalid position=" + position);
+      return;
+    }
+    if (itemCount < 0) {
+      Log.w(TAG, "notifyItemRangeInserted: Invalid itemCount=" + itemCount);
+      return;
     }
 
-    public void setId(int id) {
-        mId = id;
+    for (Observer observer : observers) {
+      observer.onItemRangeInserted(this, position, itemCount);
+    }
+  }
+
+  /** @see Observer#onItemRangeMoved(ItemHierarchy, int, int, int) */
+  public void notifyItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
+    if (fromPosition < 0) {
+      Log.w(TAG, "notifyItemRangeMoved: Invalid fromPosition=" + fromPosition);
+      return;
+    }
+    if (toPosition < 0) {
+      Log.w(TAG, "notifyItemRangeMoved: Invalid toPosition=" + toPosition);
+      return;
+    }
+    if (itemCount < 0) {
+      Log.w(TAG, "notifyItemRangeMoved: Invalid itemCount=" + itemCount);
+      return;
     }
 
-    public int getId() {
-        return mId;
+    for (Observer observer : observers) {
+      observer.onItemRangeMoved(this, fromPosition, toPosition, itemCount);
+    }
+  }
+
+  /** @see Observer#onItemRangeRemoved(ItemHierarchy, int, int) */
+  public void notifyItemRangeRemoved(int position, int itemCount) {
+    if (position < 0) {
+      Log.w(TAG, "notifyItemRangeInserted: Invalid position=" + position);
+      return;
+    }
+    if (itemCount < 0) {
+      Log.w(TAG, "notifyItemRangeInserted: Invalid itemCount=" + itemCount);
+      return;
     }
 
-    public int getViewId() {
-        return getId();
+    for (Observer observer : observers) {
+      observer.onItemRangeRemoved(this, position, itemCount);
     }
-
-    @Override
-    public void registerObserver(Observer observer) {
-        mObservers.add(observer);
-    }
-
-    @Override
-    public void unregisterObserver(Observer observer) {
-        mObservers.remove(observer);
-    }
-
-    /**
-     * @see Observer#onChanged(ItemHierarchy)
-     */
-    public void notifyChanged() {
-        for (Observer observer : mObservers) {
-            observer.onChanged(this);
-        }
-    }
-
-    /**
-     * @see Observer#onItemRangeChanged(ItemHierarchy, int, int)
-     */
-    public void notifyItemRangeChanged(int position, int itemCount) {
-        if (position < 0) {
-            Log.w(TAG, "notifyItemRangeChanged: Invalid position=" + position);
-            return;
-        }
-        if (itemCount < 0) {
-            Log.w(TAG, "notifyItemRangeChanged: Invalid itemCount=" + itemCount);
-            return;
-        }
-
-        for (Observer observer : mObservers) {
-            observer.onItemRangeChanged(this, position, itemCount);
-        }
-    }
-
-    /**
-     * @see Observer#onItemRangeInserted(ItemHierarchy, int, int)
-     */
-    public void notifyItemRangeInserted(int position, int itemCount) {
-        if (position < 0) {
-            Log.w(TAG, "notifyItemRangeInserted: Invalid position=" + position);
-            return;
-        }
-        if (itemCount < 0) {
-            Log.w(TAG, "notifyItemRangeInserted: Invalid itemCount=" + itemCount);
-            return;
-        }
-
-        for (Observer observer : mObservers) {
-            observer.onItemRangeInserted(this, position, itemCount);
-        }
-    }
-
-    /**
-     * @see Observer#onItemRangeMoved(ItemHierarchy, int, int, int)
-     */
-    public void notifyItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
-        if (fromPosition < 0) {
-            Log.w(TAG, "notifyItemRangeMoved: Invalid fromPosition=" + fromPosition);
-            return;
-        }
-        if (toPosition < 0) {
-            Log.w(TAG, "notifyItemRangeMoved: Invalid toPosition=" + toPosition);
-            return;
-        }
-        if (itemCount < 0) {
-            Log.w(TAG, "notifyItemRangeMoved: Invalid itemCount=" + itemCount);
-            return;
-        }
-
-        for (Observer observer : mObservers) {
-            observer.onItemRangeMoved(this, fromPosition, toPosition, itemCount);
-        }
-    }
-
-    /**
-     * @see Observer#onItemRangeRemoved(ItemHierarchy, int, int)
-     */
-    public void notifyItemRangeRemoved(int position, int itemCount) {
-        if (position < 0) {
-            Log.w(TAG, "notifyItemRangeInserted: Invalid position=" + position);
-            return;
-        }
-        if (itemCount < 0) {
-            Log.w(TAG, "notifyItemRangeInserted: Invalid itemCount=" + itemCount);
-            return;
-        }
-
-        for (Observer observer : mObservers) {
-            observer.onItemRangeRemoved(this, position, itemCount);
-        }
-    }
+  }
 }
diff --git a/library/main/src/com/android/setupwizardlib/items/ButtonBarItem.java b/library/main/src/com/android/setupwizardlib/items/ButtonBarItem.java
index 06ce4ac..80b9453 100644
--- a/library/main/src/com/android/setupwizardlib/items/ButtonBarItem.java
+++ b/library/main/src/com/android/setupwizardlib/items/ButtonBarItem.java
@@ -21,16 +21,15 @@
 import android.view.View;
 import android.widget.Button;
 import android.widget.LinearLayout;
-
 import com.android.setupwizardlib.R;
-
 import java.util.ArrayList;
 
 /**
- * A list item with one or more buttons, declared as
- * {@link com.android.setupwizardlib.items.ButtonItem}.
+ * A list item with one or more buttons, declared as {@link
+ * com.android.setupwizardlib.items.ButtonItem}.
  *
  * <p>Example usage:
+ *
  * <pre>{@code
  * &lt;ButtonBarItem&gt;
  *
@@ -48,81 +47,81 @@
  */
 public class ButtonBarItem extends AbstractItem implements ItemInflater.ItemParent {
 
-    private final ArrayList<ButtonItem> mButtons = new ArrayList<>();
-    private boolean mVisible = true;
+  private final ArrayList<ButtonItem> buttons = new ArrayList<>();
+  private boolean visible = true;
 
-    public ButtonBarItem() {
-        super();
+  public ButtonBarItem() {
+    super();
+  }
+
+  public ButtonBarItem(Context context, AttributeSet attrs) {
+    super(context, attrs);
+  }
+
+  @Override
+  public int getCount() {
+    return isVisible() ? 1 : 0;
+  }
+
+  @Override
+  public boolean isEnabled() {
+    // The children buttons are enabled and clickable, but the item itself is not
+    return false;
+  }
+
+  @Override
+  public int getLayoutResource() {
+    return R.layout.suw_items_button_bar;
+  }
+
+  public void setVisible(boolean visible) {
+    this.visible = visible;
+  }
+
+  public boolean isVisible() {
+    return visible;
+  }
+
+  @Override
+  public int getViewId() {
+    return getId();
+  }
+
+  @Override
+  public void onBindView(View view) {
+    // Note: The efficiency could be improved by trying to recycle the buttons created by
+    // ButtonItem
+    final LinearLayout layout = (LinearLayout) view;
+    layout.removeAllViews();
+
+    for (ButtonItem buttonItem : buttons) {
+      Button button = buttonItem.createButton(layout);
+      layout.addView(button);
     }
 
-    public ButtonBarItem(Context context, AttributeSet attrs) {
-        super(context, attrs);
+    view.setId(getViewId());
+  }
+
+  @Override
+  public void addChild(ItemHierarchy child) {
+    if (child instanceof ButtonItem) {
+      buttons.add((ButtonItem) child);
+    } else {
+      throw new UnsupportedOperationException("Cannot add non-button item to Button Bar");
     }
+  }
 
-    @Override
-    public int getCount() {
-        return isVisible() ? 1 : 0;
+  @Override
+  public ItemHierarchy findItemById(int id) {
+    if (getId() == id) {
+      return this;
     }
-
-    @Override
-    public boolean isEnabled() {
-        // The children buttons are enabled and clickable, but the item itself is not
-        return false;
+    for (ButtonItem button : buttons) {
+      final ItemHierarchy item = button.findItemById(id);
+      if (item != null) {
+        return item;
+      }
     }
-
-    @Override
-    public int getLayoutResource() {
-        return R.layout.suw_items_button_bar;
-    }
-
-    public void setVisible(boolean visible) {
-        mVisible = visible;
-    }
-
-    public boolean isVisible() {
-        return mVisible;
-    }
-
-    @Override
-    public int getViewId() {
-        return getId();
-    }
-
-    @Override
-    public void onBindView(View view) {
-        // Note: The efficiency could be improved by trying to recycle the buttons created by
-        // ButtonItem
-        final LinearLayout layout = (LinearLayout) view;
-        layout.removeAllViews();
-
-        for (ButtonItem buttonItem : mButtons) {
-            Button button = buttonItem.createButton(layout);
-            layout.addView(button);
-        }
-
-        view.setId(getViewId());
-    }
-
-    @Override
-    public void addChild(ItemHierarchy child) {
-        if (child instanceof ButtonItem) {
-            mButtons.add((ButtonItem) child);
-        } else {
-            throw new UnsupportedOperationException("Cannot add non-button item to Button Bar");
-        }
-    }
-
-    @Override
-    public ItemHierarchy findItemById(int id) {
-        if (getId() == id) {
-            return this;
-        }
-        for (ButtonItem button : mButtons) {
-            final ItemHierarchy item = button.findItemById(id);
-            if (item != null) {
-                return item;
-            }
-        }
-        return null;
-    }
+    return null;
+  }
 }
diff --git a/library/main/src/com/android/setupwizardlib/items/ButtonItem.java b/library/main/src/com/android/setupwizardlib/items/ButtonItem.java
index 07802ae..b398f4d 100644
--- a/library/main/src/com/android/setupwizardlib/items/ButtonItem.java
+++ b/library/main/src/com/android/setupwizardlib/items/ButtonItem.java
@@ -25,7 +25,6 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.Button;
-
 import com.android.setupwizardlib.R;
 
 /**
@@ -34,128 +33,123 @@
  */
 public class ButtonItem extends AbstractItem implements View.OnClickListener {
 
-    public interface OnClickListener {
-        void onClick(ButtonItem item);
+  public interface OnClickListener {
+    void onClick(ButtonItem item);
+  }
+
+  private boolean enabled = true;
+  private CharSequence text;
+  private int theme = R.style.SuwButtonItem;
+  private OnClickListener listener;
+
+  private Button button;
+
+  public ButtonItem() {
+    super();
+  }
+
+  public ButtonItem(Context context, AttributeSet attrs) {
+    super(context, attrs);
+    TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SuwButtonItem);
+    enabled = a.getBoolean(R.styleable.SuwButtonItem_android_enabled, true);
+    text = a.getText(R.styleable.SuwButtonItem_android_text);
+    theme = a.getResourceId(R.styleable.SuwButtonItem_android_theme, R.style.SuwButtonItem);
+    a.recycle();
+  }
+
+  public void setOnClickListener(OnClickListener listener) {
+    this.listener = listener;
+  }
+
+  public void setText(CharSequence text) {
+    this.text = text;
+  }
+
+  public CharSequence getText() {
+    return text;
+  }
+
+  /**
+   * The theme to use for this button. This can be used to create button of a particular style (e.g.
+   * a colored or borderless button). Typically {@code android:buttonStyle} will be set in the theme
+   * to change the style applied by the button.
+   *
+   * @param theme Resource ID of the theme
+   */
+  public void setTheme(int theme) {
+    this.theme = theme;
+    button = null;
+  }
+
+  /** @return Resource ID of the theme used by this button. */
+  public int getTheme() {
+    return theme;
+  }
+
+  public void setEnabled(boolean enabled) {
+    this.enabled = enabled;
+  }
+
+  @Override
+  public int getCount() {
+    return 0;
+  }
+
+  @Override
+  public boolean isEnabled() {
+    return enabled;
+  }
+
+  @Override
+  public int getLayoutResource() {
+    return 0;
+  }
+
+  /** Do not use this since ButtonItem is not directly part of a list. */
+  @Override
+  public final void onBindView(View view) {
+    throw new UnsupportedOperationException("Cannot bind to ButtonItem's view");
+  }
+
+  /**
+   * Create a button according to this button item.
+   *
+   * @param parent The parent of the button, used to retrieve the theme and context for this button.
+   * @return A button that can be added to the parent.
+   */
+  protected Button createButton(ViewGroup parent) {
+    if (button == null) {
+      Context context = parent.getContext();
+      if (theme != 0) {
+        context = new ContextThemeWrapper(context, theme);
+      }
+      button = createButton(context);
+      button.setOnClickListener(this);
+    } else {
+      if (button.getParent() instanceof ViewGroup) {
+        // A view cannot be added to a different parent if one already exists. Remove this
+        // button from its parent before returning.
+        ((ViewGroup) button.getParent()).removeView(button);
+      }
     }
+    button.setEnabled(enabled);
+    button.setText(text);
+    button.setId(getViewId());
+    return button;
+  }
 
-    private boolean mEnabled = true;
-    private CharSequence mText;
-    private int mTheme = R.style.SuwButtonItem;
-    private OnClickListener mListener;
+  @SuppressLint("InflateParams") // This is used similar to Button(Context), so it's OK to not
+  // specify the parent.
+  private Button createButton(Context context) {
+    // Inflate a single button from XML, so that when using support lib, it will take advantage
+    // of the injected layout inflater and give us AppCompatButton instead.
+    return (Button) LayoutInflater.from(context).inflate(R.layout.suw_button, null, false);
+  }
 
-    private Button mButton;
-
-    public ButtonItem() {
-        super();
+  @Override
+  public void onClick(View v) {
+    if (listener != null) {
+      listener.onClick(this);
     }
-
-    public ButtonItem(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SuwButtonItem);
-        mEnabled = a.getBoolean(R.styleable.SuwButtonItem_android_enabled, true);
-        mText = a.getText(R.styleable.SuwButtonItem_android_text);
-        mTheme = a.getResourceId(R.styleable.SuwButtonItem_android_theme, R.style.SuwButtonItem);
-        a.recycle();
-    }
-
-    public void setOnClickListener(OnClickListener listener) {
-        mListener = listener;
-    }
-
-    public void setText(CharSequence text) {
-        mText = text;
-    }
-
-    public CharSequence getText() {
-        return mText;
-    }
-
-    /**
-     * The theme to use for this button. This can be used to create button of a particular style
-     * (e.g. a colored or borderless button). Typically {@code android:buttonStyle} will be set in
-     * the theme to change the style applied by the button.
-     *
-     * @param theme Resource ID of the theme
-     */
-    public void setTheme(int theme) {
-        mTheme = theme;
-        mButton = null;
-    }
-
-    /**
-     * @return Resource ID of the theme used by this button.
-     */
-    public int getTheme() {
-        return mTheme;
-    }
-
-    public void setEnabled(boolean enabled) {
-        mEnabled = enabled;
-    }
-
-    @Override
-    public int getCount() {
-        return 0;
-    }
-
-    @Override
-    public boolean isEnabled() {
-        return mEnabled;
-    }
-
-    @Override
-    public int getLayoutResource() {
-        return 0;
-    }
-
-    /**
-     * Do not use this since ButtonItem is not directly part of a list.
-     */
-    @Override
-    public final void onBindView(View view) {
-        throw new UnsupportedOperationException("Cannot bind to ButtonItem's view");
-    }
-
-    /**
-     * Create a button according to this button item.
-     *
-     * @param parent The parent of the button, used to retrieve the theme and context for this
-     *               button.
-     * @return A button that can be added to the parent.
-     */
-    protected Button createButton(ViewGroup parent) {
-        if (mButton == null) {
-            Context context = parent.getContext();
-            if (mTheme != 0) {
-                context = new ContextThemeWrapper(context, mTheme);
-            }
-            mButton = createButton(context);
-            mButton.setOnClickListener(this);
-        } else {
-            if (mButton.getParent() instanceof ViewGroup) {
-                // A view cannot be added to a different parent if one already exists. Remove this
-                // button from its parent before returning.
-                ((ViewGroup) mButton.getParent()).removeView(mButton);
-            }
-        }
-        mButton.setEnabled(mEnabled);
-        mButton.setText(mText);
-        mButton.setId(getViewId());
-        return mButton;
-    }
-
-    @Override
-    public void onClick(View v) {
-        if (mListener != null) {
-            mListener.onClick(this);
-        }
-    }
-
-    @SuppressLint("InflateParams")  // This is used similar to Button(Context), so it's OK to not
-                                    // specify the parent.
-    private Button createButton(Context context) {
-        // Inflate a single button from XML, so that when using support lib, it will take advantage
-        // of the injected layout inflater and give us AppCompatButton instead.
-        return (Button) LayoutInflater.from(context).inflate(R.layout.suw_button, null, false);
-    }
+  }
 }
diff --git a/library/main/src/com/android/setupwizardlib/items/IItem.java b/library/main/src/com/android/setupwizardlib/items/IItem.java
index 26391dc..cd29ec2 100644
--- a/library/main/src/com/android/setupwizardlib/items/IItem.java
+++ b/library/main/src/com/android/setupwizardlib/items/IItem.java
@@ -18,31 +18,27 @@
 
 import android.view.View;
 
-/**
- * Representation of an item in an {@link ItemHierarchy}.
- */
+/** Representation of an item in an {@link ItemHierarchy}. */
 public interface IItem {
 
-    /**
-     * Get the Android resource ID for locating the layout for this item.
-     *
-     * @return Resource ID for the layout of this item. This layout will be used to inflate the View
-     *         passed to {@link #onBindView(android.view.View)}.
-     */
-    int getLayoutResource();
+  /**
+   * Get the Android resource ID for locating the layout for this item.
+   *
+   * @return Resource ID for the layout of this item. This layout will be used to inflate the View
+   *     passed to {@link #onBindView(android.view.View)}.
+   */
+  int getLayoutResource();
 
-    /**
-     * Called by items framework to display the data specified by this item. This method should
-     * update {@code view} to reflect its data.
-     *
-     * @param view A view inflated from {@link #getLayoutResource()}, which should be updated to
-     *             display data from this item. This view may be recycled from other items with the
-     *             same layout resource.
-     */
-    void onBindView(View view);
+  /**
+   * Called by items framework to display the data specified by this item. This method should update
+   * {@code view} to reflect its data.
+   *
+   * @param view A view inflated from {@link #getLayoutResource()}, which should be updated to
+   *     display data from this item. This view may be recycled from other items with the same
+   *     layout resource.
+   */
+  void onBindView(View view);
 
-    /**
-     * @return True if this item is enabled.
-     */
-    boolean isEnabled();
+  /** @return True if this item is enabled. */
+  boolean isEnabled();
 }
diff --git a/library/main/src/com/android/setupwizardlib/items/Item.java b/library/main/src/com/android/setupwizardlib/items/Item.java
index fc8823e..c0d49d3 100644
--- a/library/main/src/com/android/setupwizardlib/items/Item.java
+++ b/library/main/src/com/android/setupwizardlib/items/Item.java
@@ -23,7 +23,6 @@
 import android.view.View;
 import android.widget.ImageView;
 import android.widget.TextView;
-
 import com.android.setupwizardlib.R;
 
 /**
@@ -32,146 +31,145 @@
  */
 public class Item extends AbstractItem {
 
-    private boolean mEnabled = true;
-    private Drawable mIcon;
-    private int mLayoutRes;
-    private CharSequence mSummary;
-    private CharSequence mTitle;
-    private boolean mVisible = true;
+  private boolean enabled = true;
+  private Drawable icon;
+  private int layoutRes;
+  private CharSequence summary;
+  private CharSequence title;
+  private boolean visible = true;
 
-    public Item() {
-        super();
-        mLayoutRes = getDefaultLayoutResource();
+  public Item() {
+    super();
+    layoutRes = getDefaultLayoutResource();
+  }
+
+  public Item(Context context, AttributeSet attrs) {
+    super(context, attrs);
+    TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SuwItem);
+    enabled = a.getBoolean(R.styleable.SuwItem_android_enabled, true);
+    icon = a.getDrawable(R.styleable.SuwItem_android_icon);
+    title = a.getText(R.styleable.SuwItem_android_title);
+    summary = a.getText(R.styleable.SuwItem_android_summary);
+    layoutRes = a.getResourceId(R.styleable.SuwItem_android_layout, getDefaultLayoutResource());
+    visible = a.getBoolean(R.styleable.SuwItem_android_visible, true);
+    a.recycle();
+  }
+
+  protected int getDefaultLayoutResource() {
+    return R.layout.suw_items_default;
+  }
+
+  public void setEnabled(boolean enabled) {
+    this.enabled = enabled;
+    notifyItemChanged();
+  }
+
+  @Override
+  public int getCount() {
+    return isVisible() ? 1 : 0;
+  }
+
+  @Override
+  public boolean isEnabled() {
+    return enabled;
+  }
+
+  public void setIcon(Drawable icon) {
+    this.icon = icon;
+    notifyItemChanged();
+  }
+
+  public Drawable getIcon() {
+    return icon;
+  }
+
+  public void setLayoutResource(int layoutResource) {
+    layoutRes = layoutResource;
+    notifyItemChanged();
+  }
+
+  @Override
+  public int getLayoutResource() {
+    return layoutRes;
+  }
+
+  public void setSummary(CharSequence summary) {
+    this.summary = summary;
+    notifyItemChanged();
+  }
+
+  public CharSequence getSummary() {
+    return summary;
+  }
+
+  public void setTitle(CharSequence title) {
+    this.title = title;
+    notifyItemChanged();
+  }
+
+  public CharSequence getTitle() {
+    return title;
+  }
+
+  public void setVisible(boolean visible) {
+    if (this.visible == visible) {
+      return;
+    }
+    this.visible = visible;
+    if (!visible) {
+      notifyItemRangeRemoved(0, 1);
+    } else {
+      notifyItemRangeInserted(0, 1);
+    }
+  }
+
+  public boolean isVisible() {
+    return visible;
+  }
+
+  @Override
+  public int getViewId() {
+    return getId();
+  }
+
+  @Override
+  public void onBindView(View view) {
+    TextView label = (TextView) view.findViewById(R.id.suw_items_title);
+    label.setText(getTitle());
+
+    TextView summaryView = (TextView) view.findViewById(R.id.suw_items_summary);
+    CharSequence summary = getSummary();
+    if (summary != null && summary.length() > 0) {
+      summaryView.setText(summary);
+      summaryView.setVisibility(View.VISIBLE);
+    } else {
+      summaryView.setVisibility(View.GONE);
     }
 
-    public Item(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SuwItem);
-        mEnabled = a.getBoolean(R.styleable.SuwItem_android_enabled, true);
-        mIcon = a.getDrawable(R.styleable.SuwItem_android_icon);
-        mTitle = a.getText(R.styleable.SuwItem_android_title);
-        mSummary = a.getText(R.styleable.SuwItem_android_summary);
-        mLayoutRes = a.getResourceId(R.styleable.SuwItem_android_layout,
-                getDefaultLayoutResource());
-        mVisible = a.getBoolean(R.styleable.SuwItem_android_visible, true);
-        a.recycle();
+    final View iconContainer = view.findViewById(R.id.suw_items_icon_container);
+    final Drawable icon = getIcon();
+    if (icon != null) {
+      final ImageView iconView = (ImageView) view.findViewById(R.id.suw_items_icon);
+      // Set the image drawable to null before setting the state and level to avoid affecting
+      // any recycled drawable in the ImageView
+      iconView.setImageDrawable(null);
+      onMergeIconStateAndLevels(iconView, icon);
+      iconView.setImageDrawable(icon);
+      iconContainer.setVisibility(View.VISIBLE);
+    } else {
+      iconContainer.setVisibility(View.GONE);
     }
 
-    protected int getDefaultLayoutResource() {
-        return R.layout.suw_items_default;
-    }
+    view.setId(getViewId());
+  }
 
-    public void setEnabled(boolean enabled) {
-        mEnabled = enabled;
-        notifyItemChanged();
-    }
-
-    @Override
-    public int getCount() {
-        return isVisible() ? 1 : 0;
-    }
-
-    @Override
-    public boolean isEnabled() {
-        return mEnabled;
-    }
-
-    public void setIcon(Drawable icon) {
-        mIcon = icon;
-        notifyItemChanged();
-    }
-
-    public Drawable getIcon() {
-        return mIcon;
-    }
-
-    public void setLayoutResource(int layoutResource) {
-        mLayoutRes = layoutResource;
-        notifyItemChanged();
-    }
-
-    @Override
-    public int getLayoutResource() {
-        return mLayoutRes;
-    }
-
-    public void setSummary(CharSequence summary) {
-        mSummary = summary;
-        notifyItemChanged();
-    }
-
-    public CharSequence getSummary() {
-        return mSummary;
-    }
-
-    public void setTitle(CharSequence title) {
-        mTitle = title;
-        notifyItemChanged();
-    }
-
-    public CharSequence getTitle() {
-        return mTitle;
-    }
-
-    public void setVisible(boolean visible) {
-        if (mVisible == visible) {
-            return;
-        }
-        mVisible = visible;
-        if (!visible) {
-            notifyItemRangeRemoved(0, 1);
-        } else {
-            notifyItemRangeInserted(0, 1);
-        }
-    }
-
-    public boolean isVisible() {
-        return mVisible;
-    }
-
-    @Override
-    public int getViewId() {
-        return getId();
-    }
-
-    @Override
-    public void onBindView(View view) {
-        TextView label = (TextView) view.findViewById(R.id.suw_items_title);
-        label.setText(getTitle());
-
-        TextView summaryView = (TextView) view.findViewById(R.id.suw_items_summary);
-        CharSequence summary = getSummary();
-        if (summary != null && summary.length() > 0) {
-            summaryView.setText(summary);
-            summaryView.setVisibility(View.VISIBLE);
-        } else {
-            summaryView.setVisibility(View.GONE);
-        }
-
-        final View iconContainer = view.findViewById(R.id.suw_items_icon_container);
-        final Drawable icon = getIcon();
-        if (icon != null) {
-            final ImageView iconView = (ImageView) view.findViewById(R.id.suw_items_icon);
-            // Set the image drawable to null before setting the state and level to avoid affecting
-            // any recycled drawable in the ImageView
-            iconView.setImageDrawable(null);
-            onMergeIconStateAndLevels(iconView, icon);
-            iconView.setImageDrawable(icon);
-            iconContainer.setVisibility(View.VISIBLE);
-        } else {
-            iconContainer.setVisibility(View.GONE);
-        }
-
-        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());
-    }
+  /**
+   * 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/items/ItemAdapter.java b/library/main/src/com/android/setupwizardlib/items/ItemAdapter.java
index 53285e7..851736c 100644
--- a/library/main/src/com/android/setupwizardlib/items/ItemAdapter.java
+++ b/library/main/src/com/android/setupwizardlib/items/ItemAdapter.java
@@ -23,130 +23,130 @@
 import android.widget.BaseAdapter;
 
 /**
- * An adapter typically used with ListView to display an
- * {@link com.android.setupwizardlib.items.ItemHierarchy}. The item hierarchy used to create this
- * adapter can be inflated by {@link ItemInflater} from XML.
+ * An adapter typically used with ListView to display an {@link
+ * com.android.setupwizardlib.items.ItemHierarchy}. The item hierarchy used to create this adapter
+ * can be inflated by {@link ItemInflater} from XML.
  */
 public class ItemAdapter extends BaseAdapter implements ItemHierarchy.Observer {
 
-    private final ItemHierarchy mItemHierarchy;
-    private ViewTypes mViewTypes = new ViewTypes();
+  private final ItemHierarchy itemHierarchy;
+  private final ViewTypes viewTypes = new ViewTypes();
 
-    public ItemAdapter(ItemHierarchy hierarchy) {
-        mItemHierarchy = hierarchy;
-        mItemHierarchy.registerObserver(this);
-        refreshViewTypes();
+  public ItemAdapter(ItemHierarchy hierarchy) {
+    itemHierarchy = hierarchy;
+    itemHierarchy.registerObserver(this);
+    refreshViewTypes();
+  }
+
+  @Override
+  public int getCount() {
+    return itemHierarchy.getCount();
+  }
+
+  @Override
+  public IItem getItem(int position) {
+    return itemHierarchy.getItemAt(position);
+  }
+
+  @Override
+  public long getItemId(int position) {
+    return position;
+  }
+
+  @Override
+  public int getItemViewType(int position) {
+    IItem item = getItem(position);
+    int layoutRes = item.getLayoutResource();
+    return viewTypes.get(layoutRes);
+  }
+
+  @Override
+  public int getViewTypeCount() {
+    return viewTypes.size();
+  }
+
+  private void refreshViewTypes() {
+    for (int i = 0; i < getCount(); i++) {
+      IItem item = getItem(i);
+      viewTypes.add(item.getLayoutResource());
+    }
+  }
+
+  @Override
+  public View getView(int position, View convertView, ViewGroup parent) {
+    IItem item = getItem(position);
+    if (convertView == null) {
+      LayoutInflater inflater = LayoutInflater.from(parent.getContext());
+      convertView = inflater.inflate(item.getLayoutResource(), parent, false);
+    }
+    item.onBindView(convertView);
+    return convertView;
+  }
+
+  @Override
+  public void onChanged(ItemHierarchy hierarchy) {
+    refreshViewTypes();
+    notifyDataSetChanged();
+  }
+
+  @Override
+  public void onItemRangeChanged(ItemHierarchy itemHierarchy, int positionStart, int itemCount) {
+    onChanged(itemHierarchy);
+  }
+
+  @Override
+  public void onItemRangeInserted(ItemHierarchy itemHierarchy, int positionStart, int itemCount) {
+    onChanged(itemHierarchy);
+  }
+
+  @Override
+  public void onItemRangeMoved(
+      ItemHierarchy itemHierarchy, int fromPosition, int toPosition, int itemCount) {
+    onChanged(itemHierarchy);
+  }
+
+  @Override
+  public void onItemRangeRemoved(ItemHierarchy itemHierarchy, int positionStart, int itemCount) {
+    onChanged(itemHierarchy);
+  }
+
+  @Override
+  public boolean isEnabled(int position) {
+    return getItem(position).isEnabled();
+  }
+
+  public ItemHierarchy findItemById(int id) {
+    return itemHierarchy.findItemById(id);
+  }
+
+  public ItemHierarchy getRootItemHierarchy() {
+    return itemHierarchy;
+  }
+
+  /**
+   * A helper class to pack a sparse set of integers (e.g. resource IDs) to a contiguous list of
+   * integers (e.g. adapter positions), providing mapping to retrieve the original ID from a given
+   * position. This is used to pack the view types of the adapter into contiguous integers from a
+   * given layout resource.
+   */
+  private static class ViewTypes {
+    private final SparseIntArray positionMap = new SparseIntArray();
+    private int nextPosition = 0;
+
+    public int add(int id) {
+      if (positionMap.indexOfKey(id) < 0) {
+        positionMap.put(id, nextPosition);
+        nextPosition++;
+      }
+      return positionMap.get(id);
     }
 
-    @Override
-    public int getCount() {
-        return mItemHierarchy.getCount();
+    public int size() {
+      return positionMap.size();
     }
 
-    @Override
-    public IItem getItem(int position) {
-        return mItemHierarchy.getItemAt(position);
+    public int get(int id) {
+      return positionMap.get(id);
     }
-
-    @Override
-    public long getItemId(int position) {
-        return position;
-    }
-
-    @Override
-    public int getItemViewType(int position) {
-        IItem item = getItem(position);
-        int layoutRes = item.getLayoutResource();
-        return mViewTypes.get(layoutRes);
-    }
-
-    @Override
-    public int getViewTypeCount() {
-        return mViewTypes.size();
-    }
-
-    private void refreshViewTypes() {
-        for (int i = 0; i < getCount(); i++) {
-            IItem item = getItem(i);
-            mViewTypes.add(item.getLayoutResource());
-        }
-    }
-
-    @Override
-    public View getView(int position, View convertView, ViewGroup parent) {
-        IItem item = getItem(position);
-        if (convertView == null) {
-            LayoutInflater inflater = LayoutInflater.from(parent.getContext());
-            convertView = inflater.inflate(item.getLayoutResource(), parent, false);
-        }
-        item.onBindView(convertView);
-        return convertView;
-    }
-
-    @Override
-    public void onChanged(ItemHierarchy hierarchy) {
-        refreshViewTypes();
-        notifyDataSetChanged();
-    }
-
-    @Override
-    public void onItemRangeChanged(ItemHierarchy itemHierarchy, int positionStart, int itemCount) {
-        onChanged(itemHierarchy);
-    }
-
-    @Override
-    public void onItemRangeInserted(ItemHierarchy itemHierarchy, int positionStart, int itemCount) {
-        onChanged(itemHierarchy);
-    }
-
-    @Override
-    public void onItemRangeMoved(ItemHierarchy itemHierarchy, int fromPosition, int toPosition,
-            int itemCount) {
-        onChanged(itemHierarchy);
-    }
-
-    @Override
-    public void onItemRangeRemoved(ItemHierarchy itemHierarchy, int positionStart, int itemCount) {
-        onChanged(itemHierarchy);
-    }
-
-    @Override
-    public boolean isEnabled(int position) {
-        return getItem(position).isEnabled();
-    }
-
-    public ItemHierarchy findItemById(int id) {
-        return mItemHierarchy.findItemById(id);
-    }
-
-    public ItemHierarchy getRootItemHierarchy() {
-        return mItemHierarchy;
-    }
-
-    /**
-     * A helper class to pack a sparse set of integers (e.g. resource IDs) to a contiguous list of
-     * integers (e.g. adapter positions), providing mapping to retrieve the original ID from a given
-     * position. This is used to pack the view types of the adapter into contiguous integers from
-     * a given layout resource.
-     */
-    private static class ViewTypes {
-        private SparseIntArray mPositionMap = new SparseIntArray();
-        private int nextPosition = 0;
-
-        public int add(int id) {
-            if (mPositionMap.indexOfKey(id) < 0) {
-                mPositionMap.put(id, nextPosition);
-                nextPosition++;
-            }
-            return mPositionMap.get(id);
-        }
-
-        public int size() {
-            return mPositionMap.size();
-        }
-
-        public int get(int id) {
-            return mPositionMap.get(id);
-        }
-    }
+  }
 }
diff --git a/library/main/src/com/android/setupwizardlib/items/ItemGroup.java b/library/main/src/com/android/setupwizardlib/items/ItemGroup.java
index 97b3199..246469f 100644
--- a/library/main/src/com/android/setupwizardlib/items/ItemGroup.java
+++ b/library/main/src/com/android/setupwizardlib/items/ItemGroup.java
@@ -20,301 +20,288 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.SparseIntArray;
-
 import java.util.ArrayList;
 import java.util.List;
 
-public class ItemGroup extends AbstractItemHierarchy implements ItemInflater.ItemParent,
-        ItemHierarchy.Observer {
+public class ItemGroup extends AbstractItemHierarchy
+    implements ItemInflater.ItemParent, ItemHierarchy.Observer {
 
-    /* static section */
+  /* static section */
 
-    private static final String TAG = "ItemGroup";
+  private static final String TAG = "ItemGroup";
 
-    /**
-     * Binary search for the closest value that's smaller than or equal to {@code value}, and
-     * return the corresponding key.
-     */
-    private static int binarySearch(SparseIntArray array, int value) {
-        final int size = array.size();
-        int lo = 0;
-        int hi = size - 1;
+  /**
+   * Binary search for the closest value that's smaller than or equal to {@code value}, and return
+   * the corresponding key.
+   */
+  private static int binarySearch(SparseIntArray array, int value) {
+    final int size = array.size();
+    int lo = 0;
+    int hi = size - 1;
 
-        while (lo <= hi) {
-            final int mid = (lo + hi) >>> 1;
-            final int midVal = array.valueAt(mid);
+    while (lo <= hi) {
+      final int mid = (lo + hi) >>> 1;
+      final int midVal = array.valueAt(mid);
 
-            if (midVal < value) {
-                lo = mid + 1;
-            } else if (midVal > value) {
-                hi = mid - 1;
-            } else {
-                return array.keyAt(mid);  // value found
-            }
+      if (midVal < value) {
+        lo = mid + 1;
+      } else if (midVal > value) {
+        hi = mid - 1;
+      } else {
+        return array.keyAt(mid); // value found
+      }
+    }
+    // Value not found. Return the last item before our search range, which is the closest
+    // value smaller than the value we are looking for.
+    return array.keyAt(lo - 1);
+  }
+
+  /**
+   * Same as {@link List#indexOf(Object)}, but using identity comparison rather than {@link
+   * Object#equals(Object)}.
+   */
+  private static <T> int identityIndexOf(List<T> list, T object) {
+    final int count = list.size();
+    for (int i = 0; i < count; i++) {
+      if (list.get(i) == object) {
+        return i;
+      }
+    }
+    return -1;
+  }
+
+  /* non-static section */
+
+  private final List<ItemHierarchy> children = new ArrayList<>();
+
+  /**
+   * A mapping from the index of an item hierarchy in children, to the first position in which the
+   * corresponding child hierarchy represents. For example:
+   *
+   * <p>ItemHierarchy Item Item Position Index
+   *
+   * <p>0 [ Wi-Fi AP 1 ] 0 | Wi-Fi AP 2 | 1 | Wi-Fi AP 3 | 2 | Wi-Fi AP 4 | 3 [ Wi-Fi AP 5 ] 4
+   *
+   * <p>1 [ <Empty Item Hierarchy> ]
+   *
+   * <p>2 [ Use cellular data ] 5
+   *
+   * <p>3 [ Don't connect ] 6
+   *
+   * <p>For this example of Wi-Fi screen, the following mapping will be produced: [ 0 -> 0 | 2 -> 5
+   * | 3 -> 6 ]
+   *
+   * <p>Also note how ItemHierarchy index 1 is not present in the map, because it is empty.
+   *
+   * <p>ItemGroup uses this map to look for which ItemHierarchy an item at a given position belongs
+   * to.
+   */
+  private final SparseIntArray hierarchyStart = new SparseIntArray();
+
+  private int count = 0;
+  private boolean dirty = false;
+
+  public ItemGroup() {
+    super();
+  }
+
+  public ItemGroup(Context context, AttributeSet attrs) {
+    // Constructor for XML inflation
+    super(context, attrs);
+  }
+
+  /** Add a child hierarchy to this item group. */
+  @Override
+  public void addChild(ItemHierarchy child) {
+    dirty = true;
+    children.add(child);
+    child.registerObserver(this);
+
+    final int count = child.getCount();
+    if (count > 0) {
+      notifyItemRangeInserted(getChildPosition(child), count);
+    }
+  }
+
+  /**
+   * Remove a previously added child from this item group.
+   *
+   * @return True if there is a match for the child and it is removed. False if the child could not
+   *     be found in our list of child hierarchies.
+   */
+  public boolean removeChild(ItemHierarchy child) {
+    final int childIndex = identityIndexOf(children, child);
+    final int childPosition = getChildPosition(childIndex);
+    dirty = true;
+    if (childIndex != -1) {
+      final int childCount = child.getCount();
+      children.remove(childIndex);
+      child.unregisterObserver(this);
+      if (childCount > 0) {
+        notifyItemRangeRemoved(childPosition, childCount);
+      }
+      return true;
+    }
+    return false;
+  }
+
+  /** Remove all children from this hierarchy. */
+  public void clear() {
+    if (children.isEmpty()) {
+      return;
+    }
+
+    final int numRemoved = getCount();
+
+    for (ItemHierarchy item : children) {
+      item.unregisterObserver(this);
+    }
+    dirty = true;
+    children.clear();
+    notifyItemRangeRemoved(0, numRemoved);
+  }
+
+  @Override
+  public int getCount() {
+    updateDataIfNeeded();
+    return count;
+  }
+
+  @Override
+  public IItem getItemAt(int position) {
+    int itemIndex = getItemIndex(position);
+    ItemHierarchy item = children.get(itemIndex);
+    int subpos = position - hierarchyStart.get(itemIndex);
+    return item.getItemAt(subpos);
+  }
+
+  @Override
+  public void onChanged(ItemHierarchy hierarchy) {
+    // Need to set dirty, because our children may have gotten more items.
+    dirty = true;
+    notifyChanged();
+  }
+
+  /**
+   * @return The "Item Position" of the given child, or -1 if the child is not found. If the given
+   *     child is empty, position of the next visible item is returned.
+   */
+  private int getChildPosition(ItemHierarchy child) {
+    // Check the identity of the child rather than using .equals(), because here we want
+    // to find the index of the instance itself rather than something that equals to it.
+    return getChildPosition(identityIndexOf(children, child));
+  }
+
+  private int getChildPosition(int childIndex) {
+    updateDataIfNeeded();
+    if (childIndex != -1) {
+      int childPos = -1;
+      int childCount = children.size();
+      for (int i = childIndex; childPos < 0 && i < childCount; i++) {
+        // Find the position of the first visible child after childIndex. This is required
+        // when removing the last item from a nested ItemGroup.
+        childPos = hierarchyStart.get(i, -1);
+      }
+      if (childPos < 0) {
+        // If the last item in a group is being removed, there will be no visible item.
+        // In that case return the count instead, since that is where the item would have
+        // been if the child is not empty.
+        childPos = getCount();
+      }
+      return childPos;
+    }
+    return -1;
+  }
+
+  @Override
+  public void onItemRangeChanged(ItemHierarchy itemHierarchy, int positionStart, int itemCount) {
+    // No need to set dirty because onItemRangeChanged does not include any structural changes.
+    final int childPosition = getChildPosition(itemHierarchy);
+    if (childPosition >= 0) {
+      notifyItemRangeChanged(childPosition + positionStart, itemCount);
+    } else {
+      Log.e(TAG, "Unexpected child change " + itemHierarchy);
+    }
+  }
+
+  @Override
+  public void onItemRangeInserted(ItemHierarchy itemHierarchy, int positionStart, int itemCount) {
+    dirty = true;
+    final int childPosition = getChildPosition(itemHierarchy);
+    if (childPosition >= 0) {
+      notifyItemRangeInserted(childPosition + positionStart, itemCount);
+    } else {
+      Log.e(TAG, "Unexpected child insert " + itemHierarchy);
+    }
+  }
+
+  @Override
+  public void onItemRangeMoved(
+      ItemHierarchy itemHierarchy, int fromPosition, int toPosition, int itemCount) {
+    dirty = true;
+    final int childPosition = getChildPosition(itemHierarchy);
+    if (childPosition >= 0) {
+      notifyItemRangeMoved(childPosition + fromPosition, childPosition + toPosition, itemCount);
+    } else {
+      Log.e(TAG, "Unexpected child move " + itemHierarchy);
+    }
+  }
+
+  @Override
+  public void onItemRangeRemoved(ItemHierarchy itemHierarchy, int positionStart, int itemCount) {
+    dirty = true;
+    final int childPosition = getChildPosition(itemHierarchy);
+    if (childPosition >= 0) {
+      notifyItemRangeRemoved(childPosition + positionStart, itemCount);
+    } else {
+      Log.e(TAG, "Unexpected child remove " + itemHierarchy);
+    }
+  }
+
+  @Override
+  public ItemHierarchy findItemById(int id) {
+    if (id == getId()) {
+      return this;
+    }
+    for (ItemHierarchy child : children) {
+      ItemHierarchy childFindItem = child.findItemById(id);
+      if (childFindItem != null) {
+        return childFindItem;
+      }
+    }
+    return null;
+  }
+
+  /** If dirty, this method will recalculate the number of items and hierarchyStart. */
+  private void updateDataIfNeeded() {
+    if (dirty) {
+      count = 0;
+      hierarchyStart.clear();
+      for (int itemIndex = 0; itemIndex < children.size(); itemIndex++) {
+        ItemHierarchy item = children.get(itemIndex);
+        if (item.getCount() > 0) {
+          hierarchyStart.put(itemIndex, count);
         }
-        // Value not found. Return the last item before our search range, which is the closest
-        // value smaller than the value we are looking for.
-        return array.keyAt(lo - 1);
+        count += item.getCount();
+      }
+      dirty = false;
     }
+  }
 
-    /**
-     * Same as {@link List#indexOf(Object)}, but using identity comparison rather than
-     * {@link Object#equals(Object)}.
-     */
-    private static <T> int identityIndexOf(List<T> list, T object) {
-        final int count = list.size();
-        for (int i = 0; i < count; i++) {
-            if (list.get(i) == object) {
-                return i;
-            }
-        }
-        return -1;
+  /**
+   * Use binary search to locate the item hierarchy a position is contained in.
+   *
+   * @return Index of the item hierarchy which is responsible for the item at {@code position}.
+   */
+  private int getItemIndex(int position) {
+    updateDataIfNeeded();
+    if (position < 0 || position >= count) {
+      throw new IndexOutOfBoundsException("size=" + count + "; index=" + position);
     }
-
-    /* non-static section */
-
-    private List<ItemHierarchy> mChildren = new ArrayList<>();
-
-    /**
-     * A mapping from the index of an item hierarchy in mChildren, to the first position in which
-     * the corresponding child hierarchy represents. For example:
-     *
-     *   ItemHierarchy                 Item               Item Position
-     *       Index
-     *
-     *         0            [         Wi-Fi AP 1       ]        0
-     *                      |         Wi-Fi AP 2       |        1
-     *                      |         Wi-Fi AP 3       |        2
-     *                      |         Wi-Fi AP 4       |        3
-     *                      [         Wi-Fi AP 5       ]        4
-     *
-     *         1            [  <Empty Item Hierarchy>  ]
-     *
-     *         2            [     Use cellular data    ]        5
-     *
-     *         3            [       Don't connect      ]        6
-     *
-     * For this example of Wi-Fi screen, the following mapping will be produced:
-     *     [ 0 -> 0 | 2 -> 5 | 3 -> 6 ]
-     *
-     * Also note how ItemHierarchy index 1 is not present in the map, because it is empty.
-     *
-     * ItemGroup uses this map to look for which ItemHierarchy an item at a given position belongs
-     * to.
-     */
-    private SparseIntArray mHierarchyStart = new SparseIntArray();
-
-    private int mCount = 0;
-    private boolean mDirty = false;
-
-    public ItemGroup() {
-        super();
+    int result = binarySearch(hierarchyStart, position);
+    if (result < 0) {
+      throw new IllegalStateException("Cannot have item start index < 0");
     }
-
-    public ItemGroup(Context context, AttributeSet attrs) {
-        // Constructor for XML inflation
-        super(context, attrs);
-    }
-
-    /**
-     * Add a child hierarchy to this item group.
-     */
-    @Override
-    public void addChild(ItemHierarchy child) {
-        mDirty = true;
-        mChildren.add(child);
-        child.registerObserver(this);
-
-        final int count = child.getCount();
-        if (count > 0) {
-            notifyItemRangeInserted(getChildPosition(child), count);
-        }
-    }
-
-    /**
-     * Remove a previously added child from this item group.
-     *
-     * @return True if there is a match for the child and it is removed. False if the child could
-     *         not be found in our list of child hierarchies.
-     */
-    public boolean removeChild(ItemHierarchy child) {
-        final int childIndex = identityIndexOf(mChildren, child);
-        final int childPosition = getChildPosition(childIndex);
-        mDirty = true;
-        if (childIndex != -1) {
-            final int childCount = child.getCount();
-            mChildren.remove(childIndex);
-            child.unregisterObserver(this);
-            if (childCount > 0) {
-                notifyItemRangeRemoved(childPosition, childCount);
-            }
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Remove all children from this hierarchy.
-     */
-    public void clear() {
-        if (mChildren.size() == 0) {
-            return;
-        }
-
-        final int numRemoved = getCount();
-
-        for (ItemHierarchy item : mChildren) {
-            item.unregisterObserver(this);
-        }
-        mDirty = true;
-        mChildren.clear();
-        notifyItemRangeRemoved(0, numRemoved);
-    }
-
-    @Override
-    public int getCount() {
-        updateDataIfNeeded();
-        return mCount;
-    }
-
-    @Override
-    public IItem getItemAt(int position) {
-        int itemIndex = getItemIndex(position);
-        ItemHierarchy item = mChildren.get(itemIndex);
-        int subpos = position - mHierarchyStart.get(itemIndex);
-        return item.getItemAt(subpos);
-    }
-
-    @Override
-    public void onChanged(ItemHierarchy hierarchy) {
-        // Need to set dirty, because our children may have gotten more items.
-        mDirty = true;
-        notifyChanged();
-    }
-
-    /**
-     * @return The "Item Position" of the given child, or -1 if the child is not found. If the given
-     *         child is empty, position of the next visible item is returned.
-     */
-    private int getChildPosition(ItemHierarchy child) {
-        // Check the identity of the child rather than using .equals(), because here we want
-        // to find the index of the instance itself rather than something that equals to it.
-        return getChildPosition(identityIndexOf(mChildren, child));
-    }
-
-    private int getChildPosition(int childIndex) {
-        updateDataIfNeeded();
-        if (childIndex != -1) {
-            int childPos = -1;
-            int childCount = mChildren.size();
-            for (int i = childIndex; childPos < 0 && i < childCount; i++) {
-                // Find the position of the first visible child after childIndex. This is required
-                // when removing the last item from a nested ItemGroup.
-                childPos = mHierarchyStart.get(i, -1);
-            }
-            if (childPos < 0) {
-                // If the last item in a group is being removed, there will be no visible item.
-                // In that case return the count instead, since that is where the item would have
-                // been if the child is not empty.
-                childPos = getCount();
-            }
-            return childPos;
-        }
-        return -1;
-    }
-
-    @Override
-    public void onItemRangeChanged(ItemHierarchy itemHierarchy, int positionStart, int itemCount) {
-        // No need to set dirty because onItemRangeChanged does not include any structural changes.
-        final int childPosition = getChildPosition(itemHierarchy);
-        if (childPosition >= 0) {
-            notifyItemRangeChanged(childPosition + positionStart, itemCount);
-        } else {
-            Log.e(TAG, "Unexpected child change " + itemHierarchy);
-        }
-    }
-
-    @Override
-    public void onItemRangeInserted(ItemHierarchy itemHierarchy, int positionStart, int itemCount) {
-        mDirty = true;
-        final int childPosition = getChildPosition(itemHierarchy);
-        if (childPosition >= 0) {
-            notifyItemRangeInserted(childPosition + positionStart, itemCount);
-        } else {
-            Log.e(TAG, "Unexpected child insert " + itemHierarchy);
-        }
-    }
-
-    @Override
-    public void onItemRangeMoved(ItemHierarchy itemHierarchy, int fromPosition, int toPosition,
-            int itemCount) {
-        mDirty = true;
-        final int childPosition = getChildPosition(itemHierarchy);
-        if (childPosition >= 0) {
-            notifyItemRangeMoved(childPosition + fromPosition, childPosition + toPosition,
-                    itemCount);
-        } else {
-            Log.e(TAG, "Unexpected child move " + itemHierarchy);
-        }
-    }
-
-    @Override
-    public void onItemRangeRemoved(ItemHierarchy itemHierarchy, int positionStart, int itemCount) {
-        mDirty = true;
-        final int childPosition = getChildPosition(itemHierarchy);
-        if (childPosition >= 0) {
-            notifyItemRangeRemoved(childPosition + positionStart, itemCount);
-        } else {
-            Log.e(TAG, "Unexpected child remove " + itemHierarchy);
-        }
-    }
-
-    @Override
-    public ItemHierarchy findItemById(int id) {
-        if (id == getId()) {
-            return this;
-        }
-        for (ItemHierarchy child : mChildren) {
-            ItemHierarchy childFindItem = child.findItemById(id);
-            if (childFindItem != null) {
-                return childFindItem;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * If dirty, this method will recalculate the number of items and mHierarchyStart.
-     */
-    private void updateDataIfNeeded() {
-        if (mDirty) {
-            mCount = 0;
-            mHierarchyStart.clear();
-            for (int itemIndex = 0; itemIndex < mChildren.size(); itemIndex++) {
-                ItemHierarchy item = mChildren.get(itemIndex);
-                if (item.getCount() > 0) {
-                    mHierarchyStart.put(itemIndex, mCount);
-                }
-                mCount += item.getCount();
-            }
-            mDirty = false;
-        }
-    }
-
-    /**
-     * Use binary search to locate the item hierarchy a position is contained in.
-     *
-     * @return Index of the item hierarchy which is responsible for the item at {@code position}.
-     */
-    private int getItemIndex(int position) {
-        updateDataIfNeeded();
-        if (position < 0 || position >= mCount) {
-            throw new IndexOutOfBoundsException("size=" + mCount + "; index=" + position);
-        }
-        int result = binarySearch(mHierarchyStart, position);
-        if (result < 0) {
-            throw new IllegalStateException("Cannot have item start index < 0");
-        }
-        return result;
-    }
+    return result;
+  }
 }
diff --git a/library/main/src/com/android/setupwizardlib/items/ItemHierarchy.java b/library/main/src/com/android/setupwizardlib/items/ItemHierarchy.java
index 627b6f0..85e0870 100644
--- a/library/main/src/com/android/setupwizardlib/items/ItemHierarchy.java
+++ b/library/main/src/com/android/setupwizardlib/items/ItemHierarchy.java
@@ -20,82 +20,69 @@
  * Representation of zero or more items in a list. Each instance of ItemHierarchy should be capable
  * of being wrapped in ItemAdapter and be displayed.
  *
- * For example, {@link com.android.setupwizardlib.items.Item} is a representation of a single item,
- * typically with data provided from XML. {@link com.android.setupwizardlib.items.ItemGroup}
+ * <p>For example, {@link com.android.setupwizardlib.items.Item} is a representation of a single
+ * item, typically with data provided from XML. {@link com.android.setupwizardlib.items.ItemGroup}
  * represents a list of child item hierarchies it contains, but itself does not do any display.
  */
 public interface ItemHierarchy {
 
+  /**
+   * Observer for any changes in this hierarchy. If anything updated that causes this hierarchy to
+   * show different content, this observer should be called.
+   */
+  interface Observer {
     /**
-     * Observer for any changes in this hierarchy. If anything updated that causes this hierarchy to
-     * show different content, this observer should be called.
-     */
-    interface Observer {
-        /**
-         * Called when an underlying data update that can cause this hierarchy to show different
-         * content has occurred.
-         *
-         * <p>Note: This is a catch-all notification, but recycler view will have a harder time
-         * figuring out the animations for the change, and might even not animate the change at all.
-         */
-        void onChanged(ItemHierarchy itemHierarchy);
-
-        /**
-         * Called when an underlying data update that can cause changes that are local to the given
-         * items. This method indicates that there are no structural changes like inserting or
-         * removing items.
-         */
-        void onItemRangeChanged(ItemHierarchy itemHierarchy, int positionStart, int itemCount);
-
-        /**
-         * Called when items are inserted at the given position.
-         */
-        void onItemRangeInserted(ItemHierarchy itemHierarchy, int positionStart, int itemCount);
-
-        /**
-         * Called when the given items are moved to a different position.
-         */
-        void onItemRangeMoved(ItemHierarchy itemHierarchy, int fromPosition, int toPosition,
-                int itemCount);
-
-        /**
-         * Called when the given items are removed from the item hierarchy.
-         */
-        void onItemRangeRemoved(ItemHierarchy itemHierarchy, int positionStart, int itemCount);
-    }
-
-    /**
-     * Register an observer to observe changes for this item hierarchy.
-     */
-    void registerObserver(Observer observer);
-
-    /**
-     * Unregister a previously registered observer.
-     */
-    void unregisterObserver(Observer observer);
-
-    /**
-     * @return the number of items this item hierarchy represent.
-     */
-    int getCount();
-
-    /**
-     * Get the item at position.
+     * Called when an underlying data update that can cause this hierarchy to show different content
+     * has occurred.
      *
-     * @param position An integer from 0 to {@link #getCount()}}, which indicates the position in
-     *                 this item hierarchy to get the child item.
-     * @return A representation of the item at {@code position}. Must not be {@code null}.
+     * <p>Note: This is a catch-all notification, but recycler view will have a harder time figuring
+     * out the animations for the change, and might even not animate the change at all.
      */
-    IItem getItemAt(int position);
+    void onChanged(ItemHierarchy itemHierarchy);
 
     /**
-     * Find an item hierarchy within this hierarchy which has the given ID. Or null if no match is
-     * found. This hierarchy will be returned if our ID matches. Same restrictions for Android
-     * resource IDs apply to this ID. In fact, typically this ID is a resource ID generated from
-     * XML.
-     *
-     * @param id An ID to search for in this item hierarchy.
-     * @return An ItemHierarchy which matches the given ID.
+     * Called when an underlying data update that can cause changes that are local to the given
+     * items. This method indicates that there are no structural changes like inserting or removing
+     * items.
      */
-    ItemHierarchy findItemById(int id);
+    void onItemRangeChanged(ItemHierarchy itemHierarchy, int positionStart, int itemCount);
+
+    /** Called when items are inserted at the given position. */
+    void onItemRangeInserted(ItemHierarchy itemHierarchy, int positionStart, int itemCount);
+
+    /** Called when the given items are moved to a different position. */
+    void onItemRangeMoved(
+        ItemHierarchy itemHierarchy, int fromPosition, int toPosition, int itemCount);
+
+    /** Called when the given items are removed from the item hierarchy. */
+    void onItemRangeRemoved(ItemHierarchy itemHierarchy, int positionStart, int itemCount);
+  }
+
+  /** Register an observer to observe changes for this item hierarchy. */
+  void registerObserver(Observer observer);
+
+  /** Unregister a previously registered observer. */
+  void unregisterObserver(Observer observer);
+
+  /** @return the number of items this item hierarchy represent. */
+  int getCount();
+
+  /**
+   * Get the item at position.
+   *
+   * @param position An integer from 0 to {@link #getCount()}}, which indicates the position in this
+   *     item hierarchy to get the child item.
+   * @return A representation of the item at {@code position}. Must not be {@code null}.
+   */
+  IItem getItemAt(int position);
+
+  /**
+   * Find an item hierarchy within this hierarchy which has the given ID. Or null if no match is
+   * found. This hierarchy will be returned if our ID matches. Same restrictions for Android
+   * resource IDs apply to this ID. In fact, typically this ID is a resource ID generated from XML.
+   *
+   * @param id An ID to search for in this item hierarchy.
+   * @return An ItemHierarchy which matches the given ID.
+   */
+  ItemHierarchy findItemById(int id);
 }
diff --git a/library/main/src/com/android/setupwizardlib/items/ItemInflater.java b/library/main/src/com/android/setupwizardlib/items/ItemInflater.java
index 618d785..26cdbbd 100644
--- a/library/main/src/com/android/setupwizardlib/items/ItemInflater.java
+++ b/library/main/src/com/android/setupwizardlib/items/ItemInflater.java
@@ -18,26 +18,24 @@
 
 import android.content.Context;
 
-/**
- * Inflate {@link Item} hierarchies from XML files.
- */
+/** Inflate {@link Item} hierarchies from XML files. */
 public class ItemInflater extends ReflectionInflater<ItemHierarchy> {
 
-    public interface ItemParent {
-        void addChild(ItemHierarchy child);
-    }
+  public interface ItemParent {
+    void addChild(ItemHierarchy child);
+  }
 
-    public ItemInflater(Context context) {
-        super(context);
-        setDefaultPackage(Item.class.getPackage().getName() + ".");
-    }
+  public ItemInflater(Context context) {
+    super(context);
+    setDefaultPackage(Item.class.getPackage().getName() + ".");
+  }
 
-    @Override
-    protected void onAddChildItem(ItemHierarchy parent, ItemHierarchy child) {
-        if (parent instanceof ItemParent) {
-            ((ItemParent) parent).addChild(child);
-        } else {
-            throw new IllegalArgumentException("Cannot add child item to " + parent);
-        }
+  @Override
+  protected void onAddChildItem(ItemHierarchy parent, ItemHierarchy child) {
+    if (parent instanceof ItemParent) {
+      ((ItemParent) parent).addChild(child);
+    } else {
+      throw new IllegalArgumentException("Cannot add child item to " + parent);
     }
+  }
 }
diff --git a/library/main/src/com/android/setupwizardlib/items/ReflectionInflater.java b/library/main/src/com/android/setupwizardlib/items/ReflectionInflater.java
index 8ffa943..3382f56 100644
--- a/library/main/src/com/android/setupwizardlib/items/ReflectionInflater.java
+++ b/library/main/src/com/android/setupwizardlib/items/ReflectionInflater.java
@@ -17,12 +17,10 @@
 package com.android.setupwizardlib.items;
 
 import android.content.Context;
-import android.util.AttributeSet;
-import android.view.InflateException;
-
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-
+import android.util.AttributeSet;
+import android.view.InflateException;
 import java.lang.reflect.Constructor;
 import java.util.HashMap;
 
@@ -38,107 +36,104 @@
  */
 public abstract class ReflectionInflater<T> extends SimpleInflater<T> {
 
-    /* static section */
+  /* static section */
 
-    private static final Class<?>[] CONSTRUCTOR_SIGNATURE =
-            new Class<?>[] {Context.class, AttributeSet.class};
+  private static final Class<?>[] CONSTRUCTOR_SIGNATURE =
+      new Class<?>[] {Context.class, AttributeSet.class};
 
-    private static final HashMap<String, Constructor<?>> sConstructorMap = new HashMap<>();
+  private static final HashMap<String, Constructor<?>> constructorMap = new HashMap<>();
 
-    /* non-static section */
+  /* non-static section */
 
-    // Array used to contain the constructor arguments (Context, AttributeSet), to avoid allocating
-    // a new array for creation of every item.
-    private final Object[] mTempConstructorArgs = new Object[2];
+  // Array used to contain the constructor arguments (Context, AttributeSet), to avoid allocating
+  // a new array for creation of every item.
+  private final Object[] tempConstructorArgs = new Object[2];
 
-    @Nullable
-    private String mDefaultPackage;
+  @Nullable private String defaultPackage;
 
-    @NonNull
-    private final Context mContext;
+  @NonNull private final Context context;
 
-    /**
-     * Create a new inflater instance associated with a particular Context.
-     *
-     * @param context The context used to resolve resource IDs. This context is also passed to the
-     *     constructor of the items created as the first argument.
-     */
-    protected ReflectionInflater(@NonNull Context context) {
-        super(context.getResources());
-        mContext = context;
+  /**
+   * Create a new inflater instance associated with a particular Context.
+   *
+   * @param context The context used to resolve resource IDs. This context is also passed to the
+   *     constructor of the items created as the first argument.
+   */
+  protected ReflectionInflater(@NonNull Context context) {
+    super(context.getResources());
+    this.context = context;
+  }
+
+  @NonNull
+  public Context getContext() {
+    return context;
+  }
+
+  /**
+   * Instantiate the class by name. This attempts to instantiate class of the given {@code name}
+   * found in this inflater's ClassLoader.
+   *
+   * @param tagName The full name of the class to be instantiated.
+   * @param attrs The XML attributes supplied for this instance.
+   * @return The newly instantiated item.
+   */
+  @NonNull
+  public final T createItem(String tagName, String prefix, AttributeSet attrs) {
+    String qualifiedName = tagName;
+    if (prefix != null && qualifiedName.indexOf('.') == -1) {
+      qualifiedName = prefix.concat(qualifiedName);
     }
+    @SuppressWarnings("unchecked") // qualifiedName should correspond to a subclass of T
+    Constructor<? extends T> constructor =
+        (Constructor<? extends T>) constructorMap.get(qualifiedName);
 
-    @NonNull
-    public Context getContext() {
-        return mContext;
-    }
-
-    /**
-     * Instantiate the class by name. This attempts to instantiate class of the given {@code name}
-     * found in this inflater's ClassLoader.
-     *
-     * @param tagName The full name of the class to be instantiated.
-     * @param attrs The XML attributes supplied for this instance.
-     *
-     * @return The newly instantiated item.
-     */
-    @NonNull
-    public final T createItem(String tagName, String prefix, AttributeSet attrs) {
-        String qualifiedName = tagName;
-        if (prefix != null && qualifiedName.indexOf('.') == -1) {
-            qualifiedName = prefix.concat(qualifiedName);
-        }
+    try {
+      if (constructor == null) {
+        // Class not found in the cache, see if it's real, and try to add it
         @SuppressWarnings("unchecked") // qualifiedName should correspond to a subclass of T
-        Constructor<? extends T> constructor =
-                (Constructor<? extends T>) sConstructorMap.get(qualifiedName);
+        Class<? extends T> clazz =
+            (Class<? extends T>) context.getClassLoader().loadClass(qualifiedName);
+        constructor = clazz.getConstructor(CONSTRUCTOR_SIGNATURE);
+        constructor.setAccessible(true);
+        constructorMap.put(tagName, constructor);
+      }
 
-        try {
-            if (constructor == null) {
-                // Class not found in the cache, see if it's real, and try to add it
-                @SuppressWarnings("unchecked") // qualifiedName should correspond to a subclass of T
-                Class<? extends T> clazz =
-                        (Class<? extends T>) mContext.getClassLoader().loadClass(qualifiedName);
-                constructor = clazz.getConstructor(CONSTRUCTOR_SIGNATURE);
-                constructor.setAccessible(true);
-                sConstructorMap.put(tagName, constructor);
-            }
-
-            mTempConstructorArgs[0] = mContext;
-            mTempConstructorArgs[1] = attrs;
-            final T item = constructor.newInstance(mTempConstructorArgs);
-            mTempConstructorArgs[0] = null;
-            mTempConstructorArgs[1] = null;
-            return item;
-        } catch (Exception e) {
-            throw new InflateException(attrs.getPositionDescription()
-                    + ": Error inflating class " + qualifiedName, e);
-        }
+      tempConstructorArgs[0] = context;
+      tempConstructorArgs[1] = attrs;
+      final T item = constructor.newInstance(tempConstructorArgs);
+      tempConstructorArgs[0] = null;
+      tempConstructorArgs[1] = null;
+      return item;
+    } catch (Exception e) {
+      throw new InflateException(
+          attrs.getPositionDescription() + ": Error inflating class " + qualifiedName, e);
     }
+  }
 
-    @Override
-    protected T onCreateItem(String tagName, AttributeSet attrs) {
-        return createItem(tagName, mDefaultPackage, attrs);
-    }
+  @Override
+  protected T onCreateItem(String tagName, AttributeSet attrs) {
+    return createItem(tagName, defaultPackage, attrs);
+  }
 
-    /**
-     * Sets the default package that will be searched for classes to construct for tag names that
-     * have no explicit package.
-     *
-     * @param defaultPackage The default package. This will be prepended to the tag name, so it
-     *     should end with a period.
-     */
-    public void setDefaultPackage(@Nullable String defaultPackage) {
-        mDefaultPackage = defaultPackage;
-    }
+  /**
+   * Sets the default package that will be searched for classes to construct for tag names that have
+   * no explicit package.
+   *
+   * @param defaultPackage The default package. This will be prepended to the tag name, so it should
+   *     end with a period.
+   */
+  public void setDefaultPackage(@Nullable String defaultPackage) {
+    this.defaultPackage = defaultPackage;
+  }
 
-    /**
-     * Returns the default package, or null if it is not set.
-     *
-     * @see #setDefaultPackage(String)
-     * @return The default package.
-     */
-    @Nullable
-    public String getDefaultPackage() {
-        return mDefaultPackage;
-    }
+  /**
+   * Returns the default package, or null if it is not set.
+   *
+   * @see #setDefaultPackage(String)
+   * @return The default package.
+   */
+  @Nullable
+  public String getDefaultPackage() {
+    return defaultPackage;
+  }
 }
diff --git a/library/main/src/com/android/setupwizardlib/items/SimpleInflater.java b/library/main/src/com/android/setupwizardlib/items/SimpleInflater.java
index 0b12aca..767362c 100644
--- a/library/main/src/com/android/setupwizardlib/items/SimpleInflater.java
+++ b/library/main/src/com/android/setupwizardlib/items/SimpleInflater.java
@@ -18,18 +18,15 @@
 
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
+import androidx.annotation.NonNull;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Xml;
 import android.view.InflateException;
-
-import androidx.annotation.NonNull;
-
+import java.io.IOException;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
-import java.io.IOException;
-
 /**
  * A simple XML inflater, which takes care of moving the parser to the correct position. Subclasses
  * need to implement {@link #onCreateItem(String, AttributeSet)} to create an object representation
@@ -40,154 +37,154 @@
  */
 public abstract class SimpleInflater<T> {
 
-    private static final String TAG = "SimpleInflater";
-    private static final boolean DEBUG = false;
+  private static final String TAG = "SimpleInflater";
+  private static final boolean DEBUG = false;
 
-    protected final Resources mResources;
+  protected final Resources mResources;
 
-    /**
-     * Create a new inflater instance associated with a particular Resources bundle.
-     *
-     * @param resources The Resources class used to resolve given resource IDs.
-     */
-    protected SimpleInflater(@NonNull Resources resources) {
-        mResources = resources;
+  /**
+   * Create a new inflater instance associated with a particular Resources bundle.
+   *
+   * @param resources The Resources class used to resolve given resource IDs.
+   */
+  protected SimpleInflater(@NonNull Resources resources) {
+    mResources = resources;
+  }
+
+  public Resources getResources() {
+    return mResources;
+  }
+
+  /**
+   * Inflate a new hierarchy from the specified XML resource. Throws InflaterException if there is
+   * an error.
+   *
+   * @param resId ID for an XML resource to load (e.g. <code>R.xml.my_xml</code>)
+   * @return The root of the inflated hierarchy.
+   */
+  public T inflate(int resId) {
+    XmlResourceParser parser = getResources().getXml(resId);
+    try {
+      return inflate(parser);
+    } finally {
+      parser.close();
+    }
+  }
+
+  /**
+   * Inflate a new hierarchy from the specified XML node. Throws InflaterException if there is an
+   * error.
+   *
+   * <p><em><strong>Important</strong></em>&nbsp;&nbsp;&nbsp;For performance reasons, inflation
+   * relies heavily on pre-processing of XML files that is done at build time. Therefore, it is not
+   * currently possible to use inflater with an XmlPullParser over a plain XML file at runtime.
+   *
+   * @param parser XML dom node containing the description of the hierarchy.
+   * @return The root of the inflated hierarchy.
+   */
+  public T inflate(XmlPullParser parser) {
+    final AttributeSet attrs = Xml.asAttributeSet(parser);
+    T createdItem;
+
+    try {
+      // Look for the root node.
+      int type;
+      while ((type = parser.next()) != XmlPullParser.START_TAG
+          && type != XmlPullParser.END_DOCUMENT) {
+        // continue
+      }
+
+      if (type != XmlPullParser.START_TAG) {
+        throw new InflateException(parser.getPositionDescription() + ": No start tag found!");
+      }
+
+      createdItem = createItemFromTag(parser.getName(), attrs);
+
+      rInflate(parser, createdItem, attrs);
+    } catch (XmlPullParserException e) {
+      throw new InflateException(e.getMessage(), e);
+    } catch (IOException e) {
+      throw new InflateException(parser.getPositionDescription() + ": " + e.getMessage(), e);
     }
 
-    public Resources getResources() {
-        return mResources;
+    return createdItem;
+  }
+
+  /**
+   * This routine is responsible for creating the correct subclass of item given the xml element
+   * name.
+   *
+   * @param tagName The XML tag name for the item to be created.
+   * @param attrs An AttributeSet of attributes to apply to the item.
+   * @return The item created.
+   */
+  protected abstract T onCreateItem(String tagName, AttributeSet attrs);
+
+  private T createItemFromTag(String name, AttributeSet attrs) {
+    try {
+      T item = onCreateItem(name, attrs);
+      if (DEBUG) {
+        Log.v(TAG, item + " created for <" + name + ">");
+      }
+      return item;
+    } catch (InflateException e) {
+      throw e;
+    } catch (Exception e) {
+      throw new InflateException(
+          attrs.getPositionDescription() + ": Error inflating class " + name, e);
     }
+  }
 
-    /**
-     * Inflate a new hierarchy from the specified XML resource. Throws InflaterException if there is
-     * an error.
-     *
-     * @param resId ID for an XML resource to load (e.g. <code>R.xml.my_xml</code>)
-     * @return The root of the inflated hierarchy.
-     */
-    public T inflate(int resId) {
-        XmlResourceParser parser = getResources().getXml(resId);
-        try {
-            return inflate(parser);
-        } finally {
-            parser.close();
-        }
+  /**
+   * Recursive method used to descend down the xml hierarchy and instantiate items, instantiate
+   * their children, and then call onFinishInflate().
+   */
+  private void rInflate(XmlPullParser parser, T parent, final AttributeSet attrs)
+      throws XmlPullParserException, IOException {
+    final int depth = parser.getDepth();
+
+    int type;
+    while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
+        && type != XmlPullParser.END_DOCUMENT) {
+
+      if (type != XmlPullParser.START_TAG) {
+        continue;
+      }
+
+      if (onInterceptCreateItem(parser, parent, attrs)) {
+        continue;
+      }
+
+      String name = parser.getName();
+      T item = createItemFromTag(name, attrs);
+
+      onAddChildItem(parent, item);
+
+      rInflate(parser, item, attrs);
     }
+  }
 
-    /**
-     * Inflate a new hierarchy from the specified XML node. Throws InflaterException if there is an
-     * error.
-     * <p>
-     * <em><strong>Important</strong></em>&nbsp;&nbsp;&nbsp;For performance
-     * reasons, inflation relies heavily on pre-processing of XML files
-     * that is done at build time. Therefore, it is not currently possible to
-     * use inflater with an XmlPullParser over a plain XML file at runtime.
-     *
-     * @param parser XML dom node containing the description of the hierarchy.
-     * @return The root of the inflated hierarchy.
-     */
-    public T inflate(XmlPullParser parser) {
-        final AttributeSet attrs = Xml.asAttributeSet(parser);
-        T createdItem;
+  /**
+   * Whether item creation should be intercepted to perform custom handling on the parser rather
+   * than creating an object from it. This is used in rare cases where a tag doesn't correspond to
+   * creation of an object.
+   *
+   * <p>The parser will be pointing to the start of a tag, you must stop parsing and return when you
+   * reach the end of this element. That is, this method is responsible for parsing the element at
+   * the given position together with all of its child tags.
+   *
+   * <p>Note that parsing of the root tag cannot be intercepted.
+   *
+   * @param parser XML dom node containing the description of the hierarchy.
+   * @param parent The item that should be the parent of whatever you create.
+   * @param attrs An AttributeSet of attributes to apply to the item.
+   * @return True to continue parsing without calling {@link #onCreateItem(String, AttributeSet)},
+   *     or false if this inflater should proceed to create an item.
+   */
+  protected boolean onInterceptCreateItem(XmlPullParser parser, T parent, AttributeSet attrs)
+      throws XmlPullParserException {
+    return false;
+  }
 
-        try {
-            // Look for the root node.
-            int type;
-            while ((type = parser.next()) != XmlPullParser.START_TAG
-                    && type != XmlPullParser.END_DOCUMENT) {
-                // continue
-            }
-
-            if (type != XmlPullParser.START_TAG) {
-                throw new InflateException(parser.getPositionDescription()
-                        + ": No start tag found!");
-            }
-
-            createdItem = createItemFromTag(parser.getName(), attrs);
-
-            rInflate(parser, createdItem, attrs);
-        } catch (XmlPullParserException e) {
-            throw new InflateException(e.getMessage(), e);
-        } catch (IOException e) {
-            throw new InflateException(parser.getPositionDescription() + ": " + e.getMessage(), e);
-        }
-
-        return createdItem;
-    }
-
-    /**
-     * This routine is responsible for creating the correct subclass of item
-     * given the xml element name.
-     *
-     * @param tagName The XML tag name for the item to be created.
-     * @param attrs An AttributeSet of attributes to apply to the item.
-     * @return The item created.
-     */
-    protected abstract T onCreateItem(String tagName, AttributeSet attrs);
-
-    private T createItemFromTag(String name, AttributeSet attrs) {
-        try {
-            T item = onCreateItem(name, attrs);
-            if (DEBUG) Log.v(TAG, item + " created for <" + name + ">");
-            return item;
-        } catch (InflateException e) {
-            throw e;
-        } catch (Exception e) {
-            throw new InflateException(attrs.getPositionDescription()
-                    + ": Error inflating class " + name, e);
-        }
-    }
-
-    /**
-     * Recursive method used to descend down the xml hierarchy and instantiate
-     * items, instantiate their children, and then call onFinishInflate().
-     */
-    private void rInflate(XmlPullParser parser, T parent, final AttributeSet attrs)
-            throws XmlPullParserException, IOException {
-        final int depth = parser.getDepth();
-
-        int type;
-        while (((type = parser.next()) != XmlPullParser.END_TAG
-                || parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
-
-            if (type != XmlPullParser.START_TAG) {
-                continue;
-            }
-
-            if (onInterceptCreateItem(parser, parent, attrs)) {
-                continue;
-            }
-
-            String name = parser.getName();
-            T item = createItemFromTag(name, attrs);
-
-            onAddChildItem(parent, item);
-
-            rInflate(parser, item, attrs);
-        }
-    }
-
-    /**
-     * Whether item creation should be intercepted to perform custom handling on the parser rather
-     * than creating an object from it. This is used in rare cases where a tag doesn't correspond
-     * to creation of an object.
-     *
-     * The parser will be pointing to the start of a tag, you must stop parsing and return when you
-     * reach the end of this element. That is, this method is responsible for parsing the element
-     * at the given position together with all of its child tags.
-     *
-     * Note that parsing of the root tag cannot be intercepted.
-     *
-     * @param parser XML dom node containing the description of the hierarchy.
-     * @param parent The item that should be the parent of whatever you create.
-     * @param attrs An AttributeSet of attributes to apply to the item.
-     * @return True to continue parsing without calling {@link #onCreateItem(String, AttributeSet)},
-     *     or false if this inflater should proceed to create an item.
-     */
-    protected boolean onInterceptCreateItem(XmlPullParser parser, T parent, AttributeSet attrs)
-            throws XmlPullParserException {
-        return false;
-    }
-
-    protected abstract void onAddChildItem(T parent, T child);
+  protected abstract void onAddChildItem(T parent, T child);
 }
diff --git a/library/main/src/com/android/setupwizardlib/span/LinkSpan.java b/library/main/src/com/android/setupwizardlib/span/LinkSpan.java
index 3dd783b..49ce1b9 100644
--- a/library/main/src/com/android/setupwizardlib/span/LinkSpan.java
+++ b/library/main/src/com/android/setupwizardlib/span/LinkSpan.java
@@ -20,6 +20,7 @@
 import android.content.ContextWrapper;
 import android.graphics.Typeface;
 import android.os.Build;
+import androidx.annotation.Nullable;
 import android.text.Selection;
 import android.text.Spannable;
 import android.text.TextPaint;
@@ -28,126 +29,120 @@
 import android.view.View;
 import android.widget.TextView;
 
-import androidx.annotation.Nullable;
-
 /**
  * A clickable span that will listen for click events and send it back to the context. To use this
- * class, implement {@link OnLinkClickListener} in your TextView, or use
- * {@link com.android.setupwizardlib.view.RichTextView#setOnClickListener(View.OnClickListener)}.
+ * 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
+ * <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
  * also use {@code RichTextView}, which includes link support.
  */
 public class LinkSpan extends ClickableSpan {
 
-    /*
-     * Implementation note: When the orientation changes, TextView retains a reference to this span
-     * instead of writing it to a parcel (ClickableSpan is not Parcelable). If this class has any
-     * reference to the containing Activity (i.e. the activity context, or any views in the
-     * activity), it will cause memory leak.
-     */
+  /*
+   * Implementation note: When the orientation changes, TextView retains a reference to this span
+   * instead of writing it to a parcel (ClickableSpan is not Parcelable). If this class has any
+   * reference to the containing Activity (i.e. the activity context, or any views in the
+   * activity), it will cause memory leak.
+   */
 
-    /* static section */
+  /* static section */
 
-    private static final String TAG = "LinkSpan";
+  private static final String TAG = "LinkSpan";
 
-    private static final Typeface TYPEFACE_MEDIUM =
-            Typeface.create("sans-serif-medium", Typeface.NORMAL);
+  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 {
 
     /**
-     * @deprecated Use {@link 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.
      */
-    @Deprecated
-    public interface OnClickListener {
-        void onClick(LinkSpan span);
+    boolean onLinkClick(LinkSpan span);
+  }
+
+  /* non-static section */
+
+  private final String id;
+
+  public LinkSpan(String id) {
+    this.id = id;
+  }
+
+  @Override
+  public void onClick(View view) {
+    if (dispatchClick(view)) {
+      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+        // Prevent the touch event from bubbling up to the parent views.
+        view.cancelPendingInputEvents();
+      }
+    } else {
+      Log.w(TAG, "Dropping click event. No listener attached.");
     }
-
-    /**
-     * 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);
+    if (view instanceof TextView) {
+      // Remove the highlight effect when the click happens by clearing the selection
+      CharSequence text = ((TextView) view).getText();
+      if (text instanceof Spannable) {
+        Selection.setSelection((Spannable) text, 0);
+      }
     }
+  }
 
-    /* non-static section */
-
-    private final String mId;
-
-    public LinkSpan(String id) {
-        mId = id;
+  private boolean dispatchClick(View view) {
+    boolean handled = false;
+    if (view instanceof OnLinkClickListener) {
+      handled = ((OnLinkClickListener) view).onLinkClick(this);
     }
-
-    @Override
-    public void onClick(View view) {
-        if (dispatchClick(view)) {
-            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
-                // Prevent the touch event from bubbling up to the parent views.
-                view.cancelPendingInputEvents();
-            }
-        } else {
-            Log.w(TAG, "Dropping click event. No listener attached.");
-        }
-        if (view instanceof TextView) {
-            // Remove the highlight effect when the click happens by clearing the selection
-            CharSequence text = ((TextView) view).getText();
-            if (text instanceof Spannable) {
-                Selection.setSelection((Spannable) text, 0);
-            }
-        }
+    if (!handled) {
+      final OnClickListener listener = getLegacyListenerFromContext(view.getContext());
+      if (listener != null) {
+        listener.onClick(this);
+        handled = true;
+      }
     }
+    return handled;
+  }
 
-    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;
+      }
     }
+  }
 
-    /**
-     * @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);
+    drawState.setUnderlineText(false);
+    drawState.setTypeface(TYPEFACE_MEDIUM);
+  }
 
-    @Override
-    public void updateDrawState(TextPaint drawState) {
-        super.updateDrawState(drawState);
-        drawState.setUnderlineText(false);
-        drawState.setTypeface(TYPEFACE_MEDIUM);
-    }
-
-    public String getId() {
-        return mId;
-    }
+  public String getId() {
+    return id;
+  }
 }
diff --git a/library/main/src/com/android/setupwizardlib/span/SpanHelper.java b/library/main/src/com/android/setupwizardlib/span/SpanHelper.java
index d75e338..4037e6d 100644
--- a/library/main/src/com/android/setupwizardlib/span/SpanHelper.java
+++ b/library/main/src/com/android/setupwizardlib/span/SpanHelper.java
@@ -23,14 +23,14 @@
  */
 public class SpanHelper {
 
-    /**
-     * Add {@code newSpan} at the same start and end indices as {@code oldSpan} and remove
-     * {@code oldSpan} from the {@code spannable}.
-     */
-    public static void replaceSpan(Spannable spannable, Object oldSpan, Object newSpan) {
-        final int spanStart = spannable.getSpanStart(oldSpan);
-        final int spanEnd = spannable.getSpanEnd(oldSpan);
-        spannable.removeSpan(oldSpan);
-        spannable.setSpan(newSpan, spanStart, spanEnd, 0);
-    }
+  /**
+   * Add {@code newSpan} at the same start and end indices as {@code oldSpan} and remove {@code
+   * oldSpan} from the {@code spannable}.
+   */
+  public static void replaceSpan(Spannable spannable, Object oldSpan, Object newSpan) {
+    final int spanStart = spannable.getSpanStart(oldSpan);
+    final int spanEnd = spannable.getSpanEnd(oldSpan);
+    spannable.removeSpan(oldSpan);
+    spannable.setSpan(newSpan, spanStart, spanEnd, 0);
+  }
 }
diff --git a/library/main/src/com/android/setupwizardlib/template/ButtonFooterMixin.java b/library/main/src/com/android/setupwizardlib/template/ButtonFooterMixin.java
index a8580a3..c872f6b 100644
--- a/library/main/src/com/android/setupwizardlib/template/ButtonFooterMixin.java
+++ b/library/main/src/com/android/setupwizardlib/template/ButtonFooterMixin.java
@@ -18,6 +18,10 @@
 
 import android.annotation.SuppressLint;
 import android.content.Context;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
+import androidx.annotation.StyleRes;
 import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -25,12 +29,6 @@
 import android.widget.Button;
 import android.widget.LinearLayout;
 import android.widget.LinearLayout.LayoutParams;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.StringRes;
-import androidx.annotation.StyleRes;
-
 import com.android.setupwizardlib.R;
 import com.android.setupwizardlib.TemplateLayout;
 
@@ -41,134 +39,131 @@
  */
 public class ButtonFooterMixin implements Mixin {
 
-    private final Context mContext;
+  private final Context context;
 
-    @Nullable
-    private final ViewStub mFooterStub;
+  @Nullable private final ViewStub footerStub;
 
-    private LinearLayout mButtonContainer;
+  private LinearLayout buttonContainer;
 
-    /**
-     * Create a mixin for managing buttons on the footer.
-     *
-     * @param layout The {@link TemplateLayout} containing this mixin.
-     */
-    public ButtonFooterMixin(TemplateLayout layout) {
-        mContext = layout.getContext();
-        mFooterStub = (ViewStub) layout.findManagedViewById(R.id.suw_layout_footer);
+  /**
+   * Create a mixin for managing buttons on the footer.
+   *
+   * @param layout The {@link TemplateLayout} containing this mixin.
+   */
+  public ButtonFooterMixin(TemplateLayout layout) {
+    context = layout.getContext();
+    footerStub = (ViewStub) layout.findManagedViewById(R.id.suw_layout_footer);
+  }
+
+  /**
+   * Add a button with the given text and style. Common style for GLIF are {@code
+   * SuwGlifButton.Primary} and {@code SuwGlifButton.Secondary}.
+   *
+   * @param text The label for the button.
+   * @param theme Theme resource to be used for this button. Since this is applied as a theme, the
+   *     resource will typically apply {@code android:buttonStyle} so it will be applied to the
+   *     button as a style as well.
+   * @return The button that was created.
+   */
+  public Button addButton(CharSequence text, @StyleRes int theme) {
+    Button button = createThemedButton(context, theme);
+    button.setText(text);
+    return addButton(button);
+  }
+
+  /**
+   * Add a button with the given text and style. Common style for GLIF are {@code
+   * SuwGlifButton.Primary} and {@code SuwGlifButton.Secondary}.
+   *
+   * @param text The label for the button.
+   * @param theme Theme resource to be used for this button. Since this is applied as a theme, the
+   *     resource will typically apply {@code android:buttonStyle} so it will be applied to the
+   *     button as a style as well.
+   * @return The button that was created.
+   */
+  public Button addButton(@StringRes int text, @StyleRes int theme) {
+    Button button = createThemedButton(context, theme);
+    button.setText(text);
+    return addButton(button);
+  }
+
+  /**
+   * Add a button to the footer.
+   *
+   * @param button The button to be added to the footer.
+   * @return The button that was added.
+   */
+  public Button addButton(Button button) {
+    final LinearLayout buttonContainer = ensureFooterInflated();
+    buttonContainer.addView(button);
+    return button;
+  }
+
+  /**
+   * Add a space to the footer. Spaces will share the remaining space of footer, so for example,
+   * [Button] [space] [Button] [space] [Button] will give you 3 buttons, left, center, and right
+   * aligned.
+   *
+   * @return The view that was used as space.
+   */
+  public View addSpace() {
+    final LinearLayout buttonContainer = ensureFooterInflated();
+    View space = new View(buttonContainer.getContext());
+    space.setLayoutParams(new LayoutParams(0, 0, 1.0f));
+    space.setVisibility(View.INVISIBLE);
+    buttonContainer.addView(space);
+    return space;
+  }
+
+  /**
+   * Remove a previously added button.
+   *
+   * @param button The button to be removed.
+   */
+  public void removeButton(Button button) {
+    if (buttonContainer != null) {
+      buttonContainer.removeView(button);
     }
+  }
 
-    /**
-     * Add a button with the given text and style. Common style for GLIF are
-     * {@code SuwGlifButton.Primary} and {@code SuwGlifButton.Secondary}.
-     *
-     * @param text The label for the button.
-     * @param theme Theme resource to be used for this button. Since this is applied as a theme,
-     *              the resource will typically apply {@code android:buttonStyle} so it will be
-     *              applied to the button as a style as well.
-     *
-     * @return The button that was created.
-     */
-    public Button addButton(CharSequence text, @StyleRes int theme) {
-        Button button = createThemedButton(mContext, theme);
-        button.setText(text);
-        return addButton(button);
+  /**
+   * Remove a previously added space.
+   *
+   * @param space The space to be removed.
+   */
+  public void removeSpace(View space) {
+    if (buttonContainer != null) {
+      buttonContainer.removeView(space);
     }
+  }
 
-    /**
-     * Add a button with the given text and style. Common style for GLIF are
-     * {@code SuwGlifButton.Primary} and {@code SuwGlifButton.Secondary}.
-     *
-     * @param text The label for the button.
-     * @param theme Theme resource to be used for this button. Since this is applied as a theme,
-     *              the resource will typically apply {@code android:buttonStyle} so it will be
-     *              applied to the button as a style as well.
-     *
-     * @return The button that was created.
-     */
-    public Button addButton(@StringRes int text, @StyleRes int theme) {
-        Button button = createThemedButton(mContext, theme);
-        button.setText(text);
-        return addButton(button);
+  /**
+   * Remove all views, including spaces, from the footer. Note that if the footer container is
+   * already inflated, this will not remove the container itself.
+   */
+  public void removeAllViews() {
+    if (buttonContainer != null) {
+      buttonContainer.removeAllViews();
     }
+  }
 
-    /**
-     * Add a button to the footer.
-     *
-     * @param button The button to be added to the footer.
-     * @return The button that was added.
-     */
-    public Button addButton(Button button) {
-        final LinearLayout buttonContainer = ensureFooterInflated();
-        buttonContainer.addView(button);
-        return button;
+  @NonNull
+  private LinearLayout ensureFooterInflated() {
+    if (buttonContainer == null) {
+      if (footerStub == null) {
+        throw new IllegalStateException("Footer stub is not found in this template");
+      }
+      footerStub.setLayoutResource(R.layout.suw_glif_footer_button_bar);
+      buttonContainer = (LinearLayout) footerStub.inflate();
     }
+    return buttonContainer;
+  }
 
-    /**
-     * Add a space to the footer. Spaces will share the remaining space of footer, so for example,
-     * [Button] [space] [Button] [space] [Button] will give you 3 buttons, left, center, and right
-     * aligned.
-     *
-     * @return The view that was used as space.
-     */
-    public View addSpace() {
-        final LinearLayout buttonContainer = ensureFooterInflated();
-        View space = new View(buttonContainer.getContext());
-        space.setLayoutParams(new LayoutParams(0, 0, 1.0f));
-        space.setVisibility(View.INVISIBLE);
-        buttonContainer.addView(space);
-        return space;
-    }
-
-    /**
-     * Remove a previously added button.
-     *
-     * @param button The button to be removed.
-     */
-    public void removeButton(Button button) {
-        if (mButtonContainer != null) {
-            mButtonContainer.removeView(button);
-        }
-    }
-
-    /**
-     * Remove a previously added space.
-     *
-     * @param space The space to be removed.
-     */
-    public void removeSpace(View space) {
-        if (mButtonContainer != null) {
-            mButtonContainer.removeView(space);
-        }
-    }
-
-    /**
-     * Remove all views, including spaces, from the footer. Note that if the footer container is
-     * already inflated, this will not remove the container itself.
-     */
-    public void removeAllViews() {
-        if (mButtonContainer != null) {
-            mButtonContainer.removeAllViews();
-        }
-    }
-
-    @NonNull
-    private LinearLayout ensureFooterInflated() {
-        if (mButtonContainer == null) {
-            if (mFooterStub == null) {
-                throw new IllegalStateException("Footer stub is not found in this template");
-            }
-            mFooterStub.setLayoutResource(R.layout.suw_glif_footer_button_bar);
-            mButtonContainer = (LinearLayout) mFooterStub.inflate();
-        }
-        return mButtonContainer;
-    }
-
-    @SuppressLint("InflateParams")
-    private Button createThemedButton(Context context, @StyleRes int theme) {
-        // Inflate a single button from XML, which when using support lib, will take advantage of
-        // the injected layout inflater and give us AppCompatButton instead.
-        LayoutInflater inflater = LayoutInflater.from(new ContextThemeWrapper(context, theme));
-        return (Button) inflater.inflate(R.layout.suw_button, null, false);
-    }
+  @SuppressLint("InflateParams")
+  private Button createThemedButton(Context context, @StyleRes int theme) {
+    // Inflate a single button from XML, which when using support lib, will take advantage of
+    // the injected layout inflater and give us AppCompatButton instead.
+    LayoutInflater inflater = LayoutInflater.from(new ContextThemeWrapper(context, theme));
+    return (Button) inflater.inflate(R.layout.suw_button, null, false);
+  }
 }
diff --git a/library/main/src/com/android/setupwizardlib/template/ColoredHeaderMixin.java b/library/main/src/com/android/setupwizardlib/template/ColoredHeaderMixin.java
index ccc5aad..acdaa67 100644
--- a/library/main/src/com/android/setupwizardlib/template/ColoredHeaderMixin.java
+++ b/library/main/src/com/android/setupwizardlib/template/ColoredHeaderMixin.java
@@ -20,54 +20,51 @@
 import android.content.res.TypedArray;
 import android.util.AttributeSet;
 import android.widget.TextView;
-
 import com.android.setupwizardlib.R;
 import com.android.setupwizardlib.TemplateLayout;
 
 /**
  * A {@link Mixin} displaying a header text that can be set to different colors. This Mixin is
- * registered to the tempalte using HeaderMixin.class, and can be retrieved using:
- * {@code (ColoredHeaderMixin) templateLayout.getMixin(HeaderMixin.class}.
+ * registered to the template using HeaderMixin.class, and can be retrieved using: {@code
+ * (ColoredHeaderMixin) templateLayout.getMixin(HeaderMixin.class}.
  */
 public class ColoredHeaderMixin extends HeaderMixin {
 
-    /**
-     * {@inheritDoc}
-     */
-    public ColoredHeaderMixin(TemplateLayout layout, AttributeSet attrs, int defStyleAttr) {
-        super(layout, attrs, defStyleAttr);
+  /** {@inheritDoc} */
+  public ColoredHeaderMixin(TemplateLayout layout, AttributeSet attrs, int defStyleAttr) {
+    super(layout, attrs, defStyleAttr);
 
-        final TypedArray a = layout.getContext().obtainStyledAttributes(
-                attrs, R.styleable.SuwColoredHeaderMixin, defStyleAttr, 0);
+    final TypedArray a =
+        layout
+            .getContext()
+            .obtainStyledAttributes(attrs, R.styleable.SuwColoredHeaderMixin, defStyleAttr, 0);
 
-        // Set the header color
-        final ColorStateList headerColor =
-                a.getColorStateList(R.styleable.SuwColoredHeaderMixin_suwHeaderColor);
-        if (headerColor != null) {
-            setColor(headerColor);
-        }
-
-        a.recycle();
+    // Set the header color
+    final ColorStateList headerColor =
+        a.getColorStateList(R.styleable.SuwColoredHeaderMixin_suwHeaderColor);
+    if (headerColor != null) {
+      setColor(headerColor);
     }
 
-    /**
-     * Sets the color of the header text. This can also be set via XML using
-     * {@code app:suwHeaderColor}.
-     *
-     * @param color The text color of the header.
-     */
-    public void setColor(ColorStateList color) {
-        final TextView titleView = getTextView();
-        if (titleView != null) {
-            titleView.setTextColor(color);
-        }
-    }
+    a.recycle();
+  }
 
-    /**
-     * @return The current text color of the header.
-     */
-    public ColorStateList getColor() {
-        final TextView titleView = getTextView();
-        return titleView != null ? titleView.getTextColors() : null;
+  /**
+   * Sets the color of the header text. This can also be set via XML using {@code
+   * app:suwHeaderColor}.
+   *
+   * @param color The text color of the header.
+   */
+  public void setColor(ColorStateList color) {
+    final TextView titleView = getTextView();
+    if (titleView != null) {
+      titleView.setTextColor(color);
     }
+  }
+
+  /** @return The current text color of the header. */
+  public ColorStateList getColor() {
+    final TextView titleView = getTextView();
+    return titleView != null ? titleView.getTextColors() : null;
+  }
 }
diff --git a/library/main/src/com/android/setupwizardlib/template/HeaderMixin.java b/library/main/src/com/android/setupwizardlib/template/HeaderMixin.java
index 604de9a..7d7fb4a 100644
--- a/library/main/src/com/android/setupwizardlib/template/HeaderMixin.java
+++ b/library/main/src/com/android/setupwizardlib/template/HeaderMixin.java
@@ -17,80 +17,74 @@
 package com.android.setupwizardlib.template;
 
 import android.content.res.TypedArray;
-import android.util.AttributeSet;
-import android.widget.TextView;
-
 import androidx.annotation.AttrRes;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-
+import android.util.AttributeSet;
+import android.widget.TextView;
 import com.android.setupwizardlib.R;
 import com.android.setupwizardlib.TemplateLayout;
 
-/**
- * A {@link Mixin} for setting and getting the header text.
- */
+/** A {@link Mixin} for setting and getting the header text. */
 public class HeaderMixin implements Mixin {
 
-    private TemplateLayout mTemplateLayout;
+  private final TemplateLayout templateLayout;
 
-    /**
-     * @param layout The layout this Mixin belongs to.
-     * @param attrs XML attributes given to the layout.
-     * @param defStyleAttr The default style attribute as given to the constructor of the layout.
-     */
-    public HeaderMixin(@NonNull TemplateLayout layout, @Nullable AttributeSet attrs,
-            @AttrRes int defStyleAttr) {
-        mTemplateLayout = layout;
+  /**
+   * @param layout The layout this Mixin belongs to.
+   * @param attrs XML attributes given to the layout.
+   * @param defStyleAttr The default style attribute as given to the constructor of the layout.
+   */
+  public HeaderMixin(
+      @NonNull TemplateLayout layout, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
+    templateLayout = layout;
 
-        final TypedArray a = layout.getContext().obtainStyledAttributes(
-                attrs, R.styleable.SuwHeaderMixin, defStyleAttr, 0);
+    final TypedArray a =
+        layout
+            .getContext()
+            .obtainStyledAttributes(attrs, R.styleable.SuwHeaderMixin, defStyleAttr, 0);
 
-        // Set the header text
-        final CharSequence headerText = a.getText(R.styleable.SuwHeaderMixin_suwHeaderText);
-        if (headerText != null) {
-            setText(headerText);
-        }
-
-        a.recycle();
+    // Set the header text
+    final CharSequence headerText = a.getText(R.styleable.SuwHeaderMixin_suwHeaderText);
+    if (headerText != null) {
+      setText(headerText);
     }
 
-    /**
-     * @return The TextView displaying the header.
-     */
-    public TextView getTextView() {
-        return (TextView) mTemplateLayout.findManagedViewById(R.id.suw_layout_title);
-    }
+    a.recycle();
+  }
 
-    /**
-     * Sets the header text. This can also be set via the XML attribute {@code app:suwHeaderText}.
-     *
-     * @param title The resource ID of the text to be set as header.
-     */
-    public void setText(int title) {
-        final TextView titleView = getTextView();
-        if (titleView != null) {
-            titleView.setText(title);
-        }
-    }
+  /** @return The TextView displaying the header. */
+  public TextView getTextView() {
+    return (TextView) templateLayout.findManagedViewById(R.id.suw_layout_title);
+  }
 
-    /**
-     * Sets the header text. This can also be set via the XML attribute {@code app:suwHeaderText}.
-     *
-     * @param title The text to be set as header.
-     */
-    public void setText(CharSequence title) {
-        final TextView titleView = getTextView();
-        if (titleView != null) {
-            titleView.setText(title);
-        }
+  /**
+   * Sets the header text. This can also be set via the XML attribute {@code app:suwHeaderText}.
+   *
+   * @param title The resource ID of the text to be set as header.
+   */
+  public void setText(int title) {
+    final TextView titleView = getTextView();
+    if (titleView != null) {
+      titleView.setText(title);
     }
+  }
 
-    /**
-     * @return The current header text.
-     */
-    public CharSequence getText() {
-        final TextView titleView = getTextView();
-        return titleView != null ? titleView.getText() : null;
+  /**
+   * Sets the header text. This can also be set via the XML attribute {@code app:suwHeaderText}.
+   *
+   * @param title The text to be set as header.
+   */
+  public void setText(CharSequence title) {
+    final TextView titleView = getTextView();
+    if (titleView != null) {
+      titleView.setText(title);
     }
+  }
+
+  /** @return The current header text. */
+  public CharSequence getText() {
+    final TextView titleView = getTextView();
+    return titleView != null ? titleView.getText() : null;
+  }
 }
diff --git a/library/main/src/com/android/setupwizardlib/template/IconMixin.java b/library/main/src/com/android/setupwizardlib/template/IconMixin.java
index 5f5c915..e28f67d 100644
--- a/library/main/src/com/android/setupwizardlib/template/IconMixin.java
+++ b/library/main/src/com/android/setupwizardlib/template/IconMixin.java
@@ -19,100 +19,88 @@
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.drawable.Drawable;
+import androidx.annotation.DrawableRes;
 import android.util.AttributeSet;
 import android.view.View;
 import android.widget.ImageView;
-
-import androidx.annotation.DrawableRes;
-
 import com.android.setupwizardlib.R;
 import com.android.setupwizardlib.TemplateLayout;
 
-/**
- * A {@link Mixin} for setting an icon on the template layout.
- */
+/** A {@link Mixin} for setting an icon on the template layout. */
 public class IconMixin implements Mixin {
 
-    private TemplateLayout mTemplateLayout;
+  private final TemplateLayout templateLayout;
 
-    /**
-     * @param layout The template layout that this Mixin is a part of.
-     * @param attrs XML attributes given to the layout.
-     * @param defStyleAttr The default style attribute as given to the constructor of the layout.
-     */
-    public IconMixin(TemplateLayout layout, AttributeSet attrs, int defStyleAttr) {
-        mTemplateLayout = layout;
-        final Context context = layout.getContext();
+  /**
+   * @param layout The template layout that this Mixin is a part of.
+   * @param attrs XML attributes given to the layout.
+   * @param defStyleAttr The default style attribute as given to the constructor of the layout.
+   */
+  public IconMixin(TemplateLayout layout, AttributeSet attrs, int defStyleAttr) {
+    templateLayout = layout;
+    final Context context = layout.getContext();
 
-        final TypedArray a =
-                context.obtainStyledAttributes(attrs, R.styleable.SuwIconMixin, defStyleAttr, 0);
+    final TypedArray a =
+        context.obtainStyledAttributes(attrs, R.styleable.SuwIconMixin, defStyleAttr, 0);
 
-        final @DrawableRes int icon = a.getResourceId(R.styleable.SuwIconMixin_android_icon, 0);
-        if (icon != 0) {
-            setIcon(icon);
-        }
-
-        a.recycle();
+    final @DrawableRes int icon = a.getResourceId(R.styleable.SuwIconMixin_android_icon, 0);
+    if (icon != 0) {
+      setIcon(icon);
     }
 
-    /**
-     * Sets the icon on this layout. The icon can also be set in XML using {@code android:icon}.
-     *
-     * @param icon A drawable icon.
-     */
-    public void setIcon(Drawable icon) {
-        final ImageView iconView = getView();
-        if (iconView != null) {
-            iconView.setImageDrawable(icon);
-            iconView.setVisibility(icon != null ? View.VISIBLE : View.GONE);
-        }
-    }
+    a.recycle();
+  }
 
-    /**
-     * Sets the icon on this layout. The icon can also be set in XML using {@code android:icon}.
-     *
-     * @param icon A drawable icon resource.
-     */
-    public void setIcon(@DrawableRes int icon) {
-        final ImageView iconView = getView();
-        if (iconView != null) {
-            // Note: setImageResource on the ImageView is overridden in AppCompatImageView for
-            // support lib users, which enables vector drawable compat to work on versions pre-L.
-            iconView.setImageResource(icon);
-            iconView.setVisibility(icon != 0 ? View.VISIBLE : View.GONE);
-        }
+  /**
+   * Sets the icon on this layout. The icon can also be set in XML using {@code android:icon}.
+   *
+   * @param icon A drawable icon.
+   */
+  public void setIcon(Drawable icon) {
+    final ImageView iconView = getView();
+    if (iconView != null) {
+      iconView.setImageDrawable(icon);
+      iconView.setVisibility(icon != null ? View.VISIBLE : View.GONE);
     }
+  }
 
-    /**
-     * @return The icon previously set in {@link #setIcon(Drawable)} or {@code android:icon}
-     */
-    public Drawable getIcon() {
-        final ImageView iconView = getView();
-        return iconView != null ? iconView.getDrawable() : null;
+  /**
+   * Sets the icon on this layout. The icon can also be set in XML using {@code android:icon}.
+   *
+   * @param icon A drawable icon resource.
+   */
+  public void setIcon(@DrawableRes int icon) {
+    final ImageView iconView = getView();
+    if (iconView != null) {
+      // Note: setImageResource on the ImageView is overridden in AppCompatImageView for
+      // support lib users, which enables vector drawable compat to work on versions pre-L.
+      iconView.setImageResource(icon);
+      iconView.setVisibility(icon != 0 ? View.VISIBLE : View.GONE);
     }
+  }
 
-    /**
-     * Sets the content description of the icon view
-     */
-    public void setContentDescription(CharSequence description) {
-        final ImageView iconView = getView();
-        if (iconView != null) {
-            iconView.setContentDescription(description);
-        }
-    }
+  /** @return The icon previously set in {@link #setIcon(Drawable)} or {@code android:icon} */
+  public Drawable getIcon() {
+    final ImageView iconView = getView();
+    return iconView != null ? iconView.getDrawable() : null;
+  }
 
-    /**
-     * @return The content description of the icon view
-     */
-    public CharSequence getContentDescription() {
-        final ImageView iconView = getView();
-        return iconView != null ? iconView.getContentDescription() : null;
+  /** Sets the content description of the icon view */
+  public void setContentDescription(CharSequence description) {
+    final ImageView iconView = getView();
+    if (iconView != null) {
+      iconView.setContentDescription(description);
     }
+  }
 
-    /**
-     * @return The ImageView responsible for displaying the icon.
-     */
-    protected ImageView getView() {
-        return (ImageView) mTemplateLayout.findManagedViewById(R.id.suw_layout_icon);
-    }
+  /** @return The content description of the icon view */
+  public CharSequence getContentDescription() {
+    final ImageView iconView = getView();
+    return iconView != null ? iconView.getContentDescription() : null;
+  }
+
+  /** @return The ImageView responsible for displaying the icon. */
+  protected ImageView getView() {
+    return (ImageView) templateLayout.findManagedViewById(R.id.suw_layout_icon);
+  }
 }
diff --git a/library/main/src/com/android/setupwizardlib/template/ListMixin.java b/library/main/src/com/android/setupwizardlib/template/ListMixin.java
index cbc29b5..066141e 100644
--- a/library/main/src/com/android/setupwizardlib/template/ListMixin.java
+++ b/library/main/src/com/android/setupwizardlib/template/ListMixin.java
@@ -21,16 +21,14 @@
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.os.Build.VERSION_CODES;
+import androidx.annotation.AttrRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import android.util.AttributeSet;
 import android.view.View;
 import android.widget.HeaderViewListAdapter;
 import android.widget.ListAdapter;
 import android.widget.ListView;
-
-import androidx.annotation.AttrRes;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
 import com.android.setupwizardlib.R;
 import com.android.setupwizardlib.TemplateLayout;
 import com.android.setupwizardlib.items.ItemAdapter;
@@ -38,188 +36,170 @@
 import com.android.setupwizardlib.items.ItemInflater;
 import com.android.setupwizardlib.util.DrawableLayoutDirectionHelper;
 
-/**
- * A {@link Mixin} for interacting with ListViews.
- */
+/** A {@link Mixin} for interacting with ListViews. */
 public class ListMixin implements Mixin {
 
-    private TemplateLayout mTemplateLayout;
+  private final TemplateLayout templateLayout;
 
-    @Nullable
-    private ListView mListView;
+  @Nullable private ListView listView;
 
-    private Drawable mDivider;
-    private Drawable mDefaultDivider;
+  private Drawable divider;
+  private Drawable defaultDivider;
 
-    private int mDividerInsetStart;
-    private int mDividerInsetEnd;
+  private int dividerInsetStart;
+  private int dividerInsetEnd;
 
-    /**
-     * @param layout The layout this mixin belongs to.
-     */
-    public ListMixin(@NonNull TemplateLayout layout, @Nullable AttributeSet attrs,
-            @AttrRes int defStyleAttr) {
-        mTemplateLayout = layout;
+  /** @param layout The layout this mixin belongs to. */
+  public ListMixin(
+      @NonNull TemplateLayout layout, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
+    templateLayout = layout;
 
-        final Context context = layout.getContext();
-        final TypedArray a = context.obtainStyledAttributes(
-                attrs, R.styleable.SuwListMixin, defStyleAttr, 0);
+    final Context context = layout.getContext();
+    final TypedArray a =
+        context.obtainStyledAttributes(attrs, R.styleable.SuwListMixin, defStyleAttr, 0);
 
-        final int entries = a.getResourceId(R.styleable.SuwListMixin_android_entries, 0);
-        if (entries != 0) {
-            final ItemGroup inflated =
-                    (ItemGroup) new ItemInflater(context).inflate(entries);
-            setAdapter(new ItemAdapter(inflated));
-        }
-        int 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();
+    final int entries = a.getResourceId(R.styleable.SuwListMixin_android_entries, 0);
+    if (entries != 0) {
+      final ItemGroup inflated = (ItemGroup) new ItemInflater(context).inflate(entries);
+      setAdapter(new ItemAdapter(inflated));
     }
-
-    /**
-     * @return The list view contained in the layout, as marked by {@code @android:id/list}. This
-     *         will return {@code null} if the list doesn't exist in the layout.
-     */
-    public ListView getListView() {
-        return getListViewInternal();
+    int 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();
+  }
 
-    // Client code can assume getListView() will not be null if they know their template contains
-    // the list, but this mixin cannot. Any usages of getListView in this mixin needs null checks.
-    @Nullable
-    private ListView getListViewInternal() {
-        if (mListView == null) {
-            final View list = mTemplateLayout.findManagedViewById(android.R.id.list);
-            if (list instanceof ListView) {
-                mListView = (ListView) list;
-            }
-        }
-        return mListView;
-    }
+  /**
+   * @return The list view contained in the layout, as marked by {@code @android:id/list}. This will
+   *     return {@code null} if the list doesn't exist in the layout.
+   */
+  public ListView getListView() {
+    return getListViewInternal();
+  }
 
-    /**
-     * List mixin needs to update the dividers if the layout direction has changed. This method
-     * should be called when {@link View#onLayout(boolean, int, int, int, int)} of the template
-     * is called.
-     */
-    public void onLayout() {
-        if (mDivider == null) {
-            // Update divider in case layout direction has just been resolved
-            updateDivider();
-        }
+  // Client code can assume getListView() will not be null if they know their template contains
+  // the list, but this mixin cannot. Any usages of getListView in this mixin needs null checks.
+  @Nullable
+  private ListView getListViewInternal() {
+    if (listView == null) {
+      final View list = templateLayout.findManagedViewById(android.R.id.list);
+      if (list instanceof ListView) {
+        listView = (ListView) list;
+      }
     }
+    return listView;
+  }
 
-    /**
-     * Gets the adapter of the list view in this layout. If the adapter is a HeaderViewListAdapter,
-     * this method will unwrap it and return the underlying adapter.
-     *
-     * @return The adapter, or {@code null} if there is no list, or if the list has no adapter.
-     */
-    public ListAdapter getAdapter() {
-        final ListView listView = getListViewInternal();
-        if (listView != null) {
-            final ListAdapter adapter = listView.getAdapter();
-            if (adapter instanceof HeaderViewListAdapter) {
-                return ((HeaderViewListAdapter) adapter).getWrappedAdapter();
-            }
-            return adapter;
-        }
-        return null;
+  /**
+   * List mixin needs to update the dividers if the layout direction has changed. This method should
+   * be called when {@link View#onLayout(boolean, int, int, int, int)} of the template is called.
+   */
+  public void onLayout() {
+    if (divider == null) {
+      // Update divider in case layout direction has just been resolved
+      updateDivider();
     }
+  }
 
-    /**
-     * Sets the adapter on the list view in this layout.
-     */
-    public void setAdapter(ListAdapter adapter) {
-        final ListView listView = getListViewInternal();
-        if (listView != null) {
-            listView.setAdapter(adapter);
-        }
+  /**
+   * Gets the adapter of the list view in this layout. If the adapter is a HeaderViewListAdapter,
+   * this method will unwrap it and return the underlying adapter.
+   *
+   * @return The adapter, or {@code null} if there is no list, or if the list has no adapter.
+   */
+  public ListAdapter getAdapter() {
+    final ListView listView = getListViewInternal();
+    if (listView != null) {
+      final ListAdapter adapter = listView.getAdapter();
+      if (adapter instanceof HeaderViewListAdapter) {
+        return ((HeaderViewListAdapter) adapter).getWrappedAdapter();
+      }
+      return adapter;
     }
+    return null;
+  }
 
-    /**
-     * @deprecated Use {@link #setDividerInsets(int, int)} instead.
-     */
-    @Deprecated
-    public void setDividerInset(int inset) {
-        setDividerInsets(inset, 0);
+  /** Sets the adapter on the list view in this layout. */
+  public void setAdapter(ListAdapter adapter) {
+    final ListView listView = getListViewInternal();
+    if (listView != null) {
+      listView.setAdapter(adapter);
     }
+  }
 
-    /**
-     * 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.
-     */
-    public void setDividerInsets(int start, int end) {
-        mDividerInsetStart = start;
-        mDividerInsetEnd = end;
-        updateDivider();
-    }
+  /** @deprecated Use {@link #setDividerInsets(int, int)} instead. */
+  @Deprecated
+  public void setDividerInset(int inset) {
+    setDividerInsets(inset, 0);
+  }
 
-    /**
-     * @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 getDividerInsetStart();
-    }
+  /**
+   * 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.
+   */
+  public void setDividerInsets(int start, int end) {
+    dividerInsetStart = start;
+    dividerInsetEnd = end;
+    updateDivider();
+  }
 
-    /**
-     * @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 start side of the divider.
+   * @deprecated This is the same as {@link #getDividerInsetStart()}. Use that instead.
+   */
+  @Deprecated
+  public int getDividerInset() {
+    return getDividerInsetStart();
+  }
 
-    /**
-     * @return The number of pixels inset on the end side of the divider.
-     */
-    public int getDividerInsetEnd() {
-        return mDividerInsetEnd;
-    }
+  /** @return The number of pixels inset on the start side of the divider. */
+  public int getDividerInsetStart() {
+    return dividerInsetStart;
+  }
 
-    private void updateDivider() {
-        final ListView listView = getListViewInternal();
-        if (listView == null) {
-            return;
-        }
-        boolean shouldUpdate = true;
-        if (Build.VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
-            shouldUpdate = mTemplateLayout.isLayoutDirectionResolved();
-        }
-        if (shouldUpdate) {
-            if (mDefaultDivider == null) {
-                mDefaultDivider = listView.getDivider();
-            }
-            mDivider = DrawableLayoutDirectionHelper.createRelativeInsetDrawable(
-                    mDefaultDivider,
-                    mDividerInsetStart /* start */,
-                    0 /* top */,
-                    mDividerInsetEnd /* end */,
-                    0 /* bottom */,
-                    mTemplateLayout);
-            listView.setDivider(mDivider);
-        }
-    }
+  /** @return The number of pixels inset on the end side of the divider. */
+  public int getDividerInsetEnd() {
+    return dividerInsetEnd;
+  }
 
-    /**
-     * @return The drawable used as the divider.
-     */
-    public Drawable getDivider() {
-        return mDivider;
+  private void updateDivider() {
+    final ListView listView = getListViewInternal();
+    if (listView == null) {
+      return;
     }
+    boolean shouldUpdate = true;
+    if (Build.VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
+      shouldUpdate = templateLayout.isLayoutDirectionResolved();
+    }
+    if (shouldUpdate) {
+      if (defaultDivider == null) {
+        defaultDivider = listView.getDivider();
+      }
+      divider =
+          DrawableLayoutDirectionHelper.createRelativeInsetDrawable(
+              defaultDivider,
+              dividerInsetStart /* start */,
+              0 /* top */,
+              dividerInsetEnd /* end */,
+              0 /* bottom */,
+              templateLayout);
+      listView.setDivider(divider);
+    }
+  }
+
+  /** @return The drawable used as the divider. */
+  public Drawable getDivider() {
+    return divider;
+  }
 }
diff --git a/library/main/src/com/android/setupwizardlib/template/ListViewScrollHandlingDelegate.java b/library/main/src/com/android/setupwizardlib/template/ListViewScrollHandlingDelegate.java
index faea305..8614ff4 100644
--- a/library/main/src/com/android/setupwizardlib/template/ListViewScrollHandlingDelegate.java
+++ b/library/main/src/com/android/setupwizardlib/template/ListViewScrollHandlingDelegate.java
@@ -16,73 +16,67 @@
 
 package com.android.setupwizardlib.template;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import android.util.Log;
 import android.widget.AbsListView;
 import android.widget.ListAdapter;
 import android.widget.ListView;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
 import com.android.setupwizardlib.template.RequireScrollMixin.ScrollHandlingDelegate;
 
 /**
- * {@link ScrollHandlingDelegate} which analyzes scroll events from {@link ListView} and
- * notifies {@link RequireScrollMixin} about scrollability changes.
+ * {@link ScrollHandlingDelegate} which analyzes scroll events from {@link ListView} and notifies
+ * {@link RequireScrollMixin} about scrollability changes.
  */
-public class ListViewScrollHandlingDelegate implements ScrollHandlingDelegate,
-        AbsListView.OnScrollListener {
+public class ListViewScrollHandlingDelegate
+    implements ScrollHandlingDelegate, AbsListView.OnScrollListener {
 
-    private static final String TAG = "ListViewDelegate";
+  private static final String TAG = "ListViewDelegate";
 
-    private static final int SCROLL_DURATION = 500;
+  private static final int SCROLL_DURATION = 500;
 
-    @NonNull
-    private final RequireScrollMixin mRequireScrollMixin;
+  @NonNull private final RequireScrollMixin requireScrollMixin;
 
-    @Nullable
-    private final ListView mListView;
+  @Nullable private final ListView listView;
 
-    public ListViewScrollHandlingDelegate(
-            @NonNull RequireScrollMixin requireScrollMixin,
-            @Nullable ListView listView) {
-        mRequireScrollMixin = requireScrollMixin;
-        mListView = listView;
+  public ListViewScrollHandlingDelegate(
+      @NonNull RequireScrollMixin requireScrollMixin, @Nullable ListView listView) {
+    this.requireScrollMixin = requireScrollMixin;
+    this.listView = listView;
+  }
+
+  @Override
+  public void startListening() {
+    if (listView != null) {
+      listView.setOnScrollListener(this);
+
+      final ListAdapter adapter = listView.getAdapter();
+      if (listView.getLastVisiblePosition() < adapter.getCount()) {
+        requireScrollMixin.notifyScrollabilityChange(true);
+      }
+    } else {
+      Log.w(TAG, "Cannot require scroll. List view is null");
     }
+  }
 
-    @Override
-    public void startListening() {
-        if (mListView != null) {
-            mListView.setOnScrollListener(this);
-
-            final ListAdapter adapter = mListView.getAdapter();
-            if (mListView.getLastVisiblePosition() < adapter.getCount()) {
-                mRequireScrollMixin.notifyScrollabilityChange(true);
-            }
-        } else {
-            Log.w(TAG, "Cannot require scroll. List view is null");
-        }
+  @Override
+  public void pageScrollDown() {
+    if (listView != null) {
+      final int height = listView.getHeight();
+      listView.smoothScrollBy(height, SCROLL_DURATION);
     }
+  }
 
-    @Override
-    public void pageScrollDown() {
-        if (mListView != null) {
-            final int height = mListView.getHeight();
-            mListView.smoothScrollBy(height, SCROLL_DURATION);
-        }
-    }
+  @Override
+  public void onScrollStateChanged(AbsListView view, int scrollState) {}
 
-    @Override
-    public void onScrollStateChanged(AbsListView view, int scrollState) {
+  @Override
+  public void onScroll(
+      AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
+    if (firstVisibleItem + visibleItemCount >= totalItemCount) {
+      requireScrollMixin.notifyScrollabilityChange(false);
+    } else {
+      requireScrollMixin.notifyScrollabilityChange(true);
     }
-
-    @Override
-    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
-            int totalItemCount) {
-        if (firstVisibleItem + visibleItemCount >= totalItemCount) {
-            mRequireScrollMixin.notifyScrollabilityChange(false);
-        } else {
-            mRequireScrollMixin.notifyScrollabilityChange(true);
-        }
-    }
+  }
 }
diff --git a/library/main/src/com/android/setupwizardlib/template/Mixin.java b/library/main/src/com/android/setupwizardlib/template/Mixin.java
index b7b8893..b460777 100644
--- a/library/main/src/com/android/setupwizardlib/template/Mixin.java
+++ b/library/main/src/com/android/setupwizardlib/template/Mixin.java
@@ -22,5 +22,4 @@
  * @see com.android.setupwizardlib.TemplateLayout#registerMixin(Class, Mixin)
  * @see com.android.setupwizardlib.TemplateLayout#getMixin(Class)
  */
-public interface Mixin {
-}
+public interface Mixin {}
diff --git a/library/main/src/com/android/setupwizardlib/template/NavigationBarMixin.java b/library/main/src/com/android/setupwizardlib/template/NavigationBarMixin.java
index df35017..2412eda 100644
--- a/library/main/src/com/android/setupwizardlib/template/NavigationBarMixin.java
+++ b/library/main/src/com/android/setupwizardlib/template/NavigationBarMixin.java
@@ -17,67 +17,60 @@
 package com.android.setupwizardlib.template;
 
 import android.view.View;
-
 import com.android.setupwizardlib.R;
 import com.android.setupwizardlib.TemplateLayout;
 import com.android.setupwizardlib.view.NavigationBar;
 import com.android.setupwizardlib.view.NavigationBar.NavigationBarListener;
 
-/**
- * A {@link Mixin} for interacting with a {@link NavigationBar}.
- */
+/** A {@link Mixin} for interacting with a {@link NavigationBar}. */
 public class NavigationBarMixin implements Mixin {
 
-    private TemplateLayout mTemplateLayout;
+  private final TemplateLayout templateLayout;
 
-    /**
-     * @param layout The layout this mixin belongs to.
-     */
-    public NavigationBarMixin(TemplateLayout layout) {
-        mTemplateLayout = layout;
-    }
+  /** @param layout The layout this mixin belongs to. */
+  public NavigationBarMixin(TemplateLayout layout) {
+    templateLayout = layout;
+  }
 
-    /**
-     * @return The navigation bar instance in the layout, or null if the layout does not have a
-     *         navigation bar.
-     */
-    public NavigationBar getNavigationBar() {
-        final View view = mTemplateLayout.findManagedViewById(R.id.suw_layout_navigation_bar);
-        return view instanceof NavigationBar ? (NavigationBar) view : null;
-    }
+  /**
+   * @return The navigation bar instance in the layout, or null if the layout does not have a
+   *     navigation bar.
+   */
+  public NavigationBar getNavigationBar() {
+    final View view = templateLayout.findManagedViewById(R.id.suw_layout_navigation_bar);
+    return view instanceof NavigationBar ? (NavigationBar) view : null;
+  }
 
-    /**
-     * Sets the label of the next button.
-     *
-     * @param text Label of the next button.
-     */
-    public void setNextButtonText(int text) {
-        getNavigationBar().getNextButton().setText(text);
-    }
+  /**
+   * Sets the label of the next button.
+   *
+   * @param text Label of the next button.
+   */
+  public void setNextButtonText(int text) {
+    getNavigationBar().getNextButton().setText(text);
+  }
 
-    /**
-     * Sets the label of the next button.
-     *
-     * @param text Label of the next button.
-     */
-    public void setNextButtonText(CharSequence text) {
-        getNavigationBar().getNextButton().setText(text);
-    }
+  /**
+   * Sets the label of the next button.
+   *
+   * @param text Label of the next button.
+   */
+  public void setNextButtonText(CharSequence text) {
+    getNavigationBar().getNextButton().setText(text);
+  }
 
-    /**
-     * @return The current label of the next button.
-     */
-    public CharSequence getNextButtonText() {
-        return getNavigationBar().getNextButton().getText();
-    }
+  /** @return The current label of the next button. */
+  public CharSequence getNextButtonText() {
+    return getNavigationBar().getNextButton().getText();
+  }
 
-    /**
-     * Sets the listener to handle back and next button clicks in the navigation bar.
-     *
-     * @see NavigationBar#setNavigationBarListener(NavigationBarListener)
-     * @see NavigationBarListener
-     */
-    public void setNavigationBarListener(NavigationBarListener listener) {
-        getNavigationBar().setNavigationBarListener(listener);
-    }
+  /**
+   * Sets the listener to handle back and next button clicks in the navigation bar.
+   *
+   * @see NavigationBar#setNavigationBarListener(NavigationBarListener)
+   * @see NavigationBarListener
+   */
+  public void setNavigationBarListener(NavigationBarListener listener) {
+    getNavigationBar().setNavigationBarListener(listener);
+  }
 }
diff --git a/library/main/src/com/android/setupwizardlib/template/ProgressBarMixin.java b/library/main/src/com/android/setupwizardlib/template/ProgressBarMixin.java
index 504b2f0..0e128c4 100644
--- a/library/main/src/com/android/setupwizardlib/template/ProgressBarMixin.java
+++ b/library/main/src/com/android/setupwizardlib/template/ProgressBarMixin.java
@@ -19,121 +19,109 @@
 import android.content.res.ColorStateList;
 import android.os.Build;
 import android.os.Build.VERSION_CODES;
+import androidx.annotation.Nullable;
 import android.view.View;
 import android.view.ViewStub;
 import android.widget.ProgressBar;
-
-import androidx.annotation.Nullable;
-
 import com.android.setupwizardlib.R;
 import com.android.setupwizardlib.TemplateLayout;
 
-/**
- * A {@link Mixin} for showing a progress bar.
- */
+/** A {@link Mixin} for showing a progress bar. */
 public class ProgressBarMixin implements Mixin {
 
-    private TemplateLayout mTemplateLayout;
+  private final TemplateLayout templateLayout;
 
-    @Nullable
-    private ColorStateList mColor;
+  @Nullable private ColorStateList color;
 
-    /**
-     * @param layout The layout this mixin belongs to.
-     */
-    public ProgressBarMixin(TemplateLayout layout) {
-        mTemplateLayout = layout;
+  /** @param layout The layout this mixin belongs to. */
+  public ProgressBarMixin(TemplateLayout layout) {
+    templateLayout = layout;
+  }
+
+  /** @return True if the progress bar is currently shown. */
+  public boolean isShown() {
+    final View progressBar = templateLayout.findManagedViewById(R.id.suw_layout_progress);
+    return progressBar != null && progressBar.getVisibility() == View.VISIBLE;
+  }
+
+  /**
+   * Sets whether the progress bar is shown. If the progress bar has not been inflated from the
+   * stub, this method will inflate the progress bar.
+   *
+   * @param shown True to show the progress bar, false to hide it.
+   */
+  public void setShown(boolean shown) {
+    if (shown) {
+      View progressBar = getProgressBar();
+      if (progressBar != null) {
+        progressBar.setVisibility(View.VISIBLE);
+      }
+    } else {
+      View progressBar = peekProgressBar();
+      if (progressBar != null) {
+        progressBar.setVisibility(View.GONE);
+      }
     }
+  }
 
-    /**
-     * @return True if the progress bar is currently shown.
-     */
-    public boolean isShown() {
-        final View progressBar = mTemplateLayout.findManagedViewById(R.id.suw_layout_progress);
-        return progressBar != null && progressBar.getVisibility() == View.VISIBLE;
+  /**
+   * Gets the progress bar in the layout. If the progress bar has not been used before, it will be
+   * installed (i.e. inflated from its view stub).
+   *
+   * @return The progress bar of this layout. May be null only if the template used doesn't have a
+   *     progress bar built-in.
+   */
+  private ProgressBar getProgressBar() {
+    final View progressBar = peekProgressBar();
+    if (progressBar == null) {
+      final ViewStub progressBarStub =
+          (ViewStub) templateLayout.findManagedViewById(R.id.suw_layout_progress_stub);
+      if (progressBarStub != null) {
+        progressBarStub.inflate();
+      }
+      setColor(color);
     }
+    return peekProgressBar();
+  }
 
-    /**
-     * Sets whether the progress bar is shown. If the progress bar has not been inflated from the
-     * stub, this method will inflate the progress bar.
-     *
-     * @param shown True to show the progress bar, false to hide it.
-     */
-    public void setShown(boolean shown) {
-        if (shown) {
-            View progressBar = getProgressBar();
-            if (progressBar != null) {
-                progressBar.setVisibility(View.VISIBLE);
-            }
-        } else {
-            View progressBar = peekProgressBar();
-            if (progressBar != null) {
-                progressBar.setVisibility(View.GONE);
-            }
+  /**
+   * Gets the progress bar in the layout only if it has been installed. {@link #setShown(boolean)}
+   * should be called before this to ensure the progress bar is set up correctly.
+   *
+   * @return The progress bar of this layout, or null if the progress bar is not installed. The null
+   *     case can happen either if {@link #setShown(boolean)} with true was not called before this,
+   *     or if the template does not contain a progress bar.
+   */
+  public ProgressBar peekProgressBar() {
+    return (ProgressBar) templateLayout.findManagedViewById(R.id.suw_layout_progress);
+  }
+
+  /** Sets the color of the indeterminate progress bar. This method is a no-op on SDK < 21. */
+  public void setColor(@Nullable ColorStateList color) {
+    this.color = color;
+    if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+      final ProgressBar bar = peekProgressBar();
+      if (bar != null) {
+        bar.setIndeterminateTintList(color);
+        if (Build.VERSION.SDK_INT >= VERSION_CODES.M || color != null) {
+          // There is a bug in Lollipop where setting the progress tint color to null
+          // will crash with "java.lang.NullPointerException: Attempt to invoke virtual
+          // method 'int android.graphics.Paint.getAlpha()' on a null object reference"
+          // at android.graphics.drawable.NinePatchDrawable.draw(:250)
+          // The bug doesn't affect ProgressBar on M because it uses ShapeDrawable instead
+          // of NinePatchDrawable. (commit 6a8253fdc9f4574c28b4beeeed90580ffc93734a)
+          bar.setProgressBackgroundTintList(color);
         }
+      }
     }
+  }
 
-    /**
-     * Gets the progress bar in the layout. If the progress bar has not been used before, it will be
-     * installed (i.e. inflated from its view stub).
-     *
-     * @return The progress bar of this layout. May be null only if the template used doesn't have a
-     *         progress bar built-in.
-     */
-    private ProgressBar getProgressBar() {
-        final View progressBar = peekProgressBar();
-        if (progressBar == null) {
-            final ViewStub progressBarStub =
-                    (ViewStub) mTemplateLayout.findManagedViewById(R.id.suw_layout_progress_stub);
-            if (progressBarStub != null) {
-                progressBarStub.inflate();
-            }
-            setColor(mColor);
-        }
-        return peekProgressBar();
-    }
-
-    /**
-     * Gets the progress bar in the layout only if it has been installed.
-     * {@link #setShown(boolean)} should be called before this to ensure the progress bar
-     * is set up correctly.
-     *
-     * @return The progress bar of this layout, or null if the progress bar is not installed. The
-     *         null case can happen either if {@link #setShown(boolean)} with true was
-     *         not called before this, or if the template does not contain a progress bar.
-     */
-    public ProgressBar peekProgressBar() {
-        return (ProgressBar) mTemplateLayout.findManagedViewById(R.id.suw_layout_progress);
-    }
-
-    /**
-     * Sets the color of the indeterminate progress bar. This method is a no-op on SDK < 21.
-     */
-    public void setColor(@Nullable ColorStateList color) {
-        mColor = color;
-        if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
-            final ProgressBar bar = peekProgressBar();
-            if (bar != null) {
-                bar.setIndeterminateTintList(color);
-                if (Build.VERSION.SDK_INT >= VERSION_CODES.M || color != null) {
-                    // There is a bug in Lollipop where setting the progress tint color to null
-                    // will crash with "java.lang.NullPointerException: Attempt to invoke virtual
-                    // method 'int android.graphics.Paint.getAlpha()' on a null object reference"
-                    // at android.graphics.drawable.NinePatchDrawable.draw(:250)
-                    // The bug doesn't affect ProgressBar on M because it uses ShapeDrawable instead
-                    // of NinePatchDrawable. (commit 6a8253fdc9f4574c28b4beeeed90580ffc93734a)
-                    bar.setProgressBackgroundTintList(color);
-                }
-            }
-        }
-    }
-
-    /**
-     * @return The color previously set in {@link #setColor(ColorStateList)}, or null if the color
-     * is not set. In case of null, the color of the progress bar will be inherited from the theme.
-     */
-    @Nullable
-    public ColorStateList getColor() {
-        return mColor;
-    }
+  /**
+   * @return The color previously set in {@link #setColor(ColorStateList)}, or null if the color is
+   *     not set. In case of null, the color of the progress bar will be inherited from the theme.
+   */
+  @Nullable
+  public ColorStateList getColor() {
+    return color;
+  }
 }
diff --git a/library/main/src/com/android/setupwizardlib/template/RequireScrollMixin.java b/library/main/src/com/android/setupwizardlib/template/RequireScrollMixin.java
index fd3303b..02bcc1c 100644
--- a/library/main/src/com/android/setupwizardlib/template/RequireScrollMixin.java
+++ b/library/main/src/com/android/setupwizardlib/template/RequireScrollMixin.java
@@ -18,244 +18,222 @@
 
 import android.os.Handler;
 import android.os.Looper;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.Button;
-
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.StringRes;
-
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
 import com.android.setupwizardlib.TemplateLayout;
 import com.android.setupwizardlib.view.NavigationBar;
 
 /**
- * A mixin to require the a scrollable container (BottomScrollView, RecyclerView or ListView) to
- * be scrolled to bottom, making sure that the user sees all content above and below the fold.
+ * A mixin to require the a scrollable container (BottomScrollView, RecyclerView or ListView) to be
+ * scrolled to bottom, making sure that the user sees all content above and below the fold.
  */
 public class RequireScrollMixin implements Mixin {
 
-    /* static section */
+  /* static section */
+
+  /**
+   * Listener for when the require-scroll state changes. Note that this only requires the user to
+   * scroll to the bottom once - if the user scrolled to the bottom and back-up, scrolling to bottom
+   * is not required again.
+   */
+  public interface OnRequireScrollStateChangedListener {
 
     /**
-     * Listener for when the require-scroll state changes. Note that this only requires the user to
-     * scroll to the bottom once - if the user scrolled to the bottom and back-up, scrolling to
-     * bottom is not required again.
-     */
-    public interface OnRequireScrollStateChangedListener {
-
-        /**
-         * Called when require-scroll state changed.
-         *
-         * @param scrollNeeded True if the user should be required to scroll to bottom.
-         */
-        void onRequireScrollStateChanged(boolean scrollNeeded);
-    }
-
-    /**
-     * A delegate to detect scrollability changes and to scroll the page. This provides a layer
-     * of abstraction for BottomScrollView, RecyclerView and ListView. The delegate should call
-     * {@link #notifyScrollabilityChange(boolean)} when the view scrollability is changed.
-     */
-    interface ScrollHandlingDelegate {
-
-        /**
-         * Starts listening to scrollability changes at the target scrollable container.
-         */
-        void startListening();
-
-        /**
-         * Scroll the page content down by one page.
-         */
-        void pageScrollDown();
-    }
-
-    /* non-static section */
-
-    @NonNull
-    private final TemplateLayout mTemplateLayout;
-
-    private final Handler mHandler = new Handler(Looper.getMainLooper());
-
-    private boolean mRequiringScrollToBottom = false;
-
-    // Whether the user have seen the more button yet.
-    private boolean mEverScrolledToBottom = false;
-
-    private ScrollHandlingDelegate mDelegate;
-
-    @Nullable
-    private OnRequireScrollStateChangedListener mListener;
-
-    /**
-     * @param templateLayout The template containing this mixin
-     */
-    public RequireScrollMixin(@NonNull TemplateLayout templateLayout) {
-        mTemplateLayout = templateLayout;
-    }
-
-    /**
-     * Sets the delegate to handle scrolling. The type of delegate should depend on whether the
-     * scrolling view is a BottomScrollView, RecyclerView or ListView.
-     */
-    public void setScrollHandlingDelegate(@NonNull ScrollHandlingDelegate delegate) {
-        mDelegate = delegate;
-    }
-
-    /**
-     * Listen to require scroll state changes. When scroll is required,
-     * {@link OnRequireScrollStateChangedListener#onRequireScrollStateChanged(boolean)} is called
-     * with {@code true}, and vice versa.
-     */
-    public void setOnRequireScrollStateChangedListener(
-            @Nullable OnRequireScrollStateChangedListener listener) {
-        mListener = listener;
-    }
-
-    /**
-     * @return The scroll state listener previously set, or {@code null} if none is registered.
-     */
-    public OnRequireScrollStateChangedListener getOnRequireScrollStateChangedListener() {
-        return mListener;
-    }
-
-    /**
-     * Creates an {@link OnClickListener} which if scrolling is required, will scroll the page down,
-     * and if scrolling is not required, delegates to the wrapped {@code listener}. Note that you
-     * should call {@link #requireScroll()} as well in order to start requiring scrolling.
+     * Called when require-scroll state changed.
      *
-     * @param listener The listener to be invoked when scrolling is not needed and the user taps on
-     *                 the button. If {@code null}, the click listener will be a no-op when scroll
-     *                 is not required.
-     * @return A new {@link OnClickListener} which will scroll the page down or delegate to the
-     *         given listener depending on the current require-scroll state.
+     * @param scrollNeeded True if the user should be required to scroll to bottom.
      */
-    public OnClickListener createOnClickListener(@Nullable final OnClickListener listener) {
-        return new OnClickListener() {
-            @Override
-            public void onClick(View view) {
-                if (mRequiringScrollToBottom) {
-                    mDelegate.pageScrollDown();
-                } else if (listener != null) {
-                    listener.onClick(view);
-                }
-            }
-        };
-    }
+    void onRequireScrollStateChanged(boolean scrollNeeded);
+  }
 
-    /**
-     * Coordinate with the given navigation bar to require scrolling on the page. The more button
-     * will be shown instead of the next button while scrolling is required.
-     */
-    public void requireScrollWithNavigationBar(@NonNull final NavigationBar navigationBar) {
-        setOnRequireScrollStateChangedListener(
-                new OnRequireScrollStateChangedListener() {
-                    @Override
-                    public void onRequireScrollStateChanged(boolean scrollNeeded) {
-                        navigationBar.getMoreButton()
-                                .setVisibility(scrollNeeded ? View.VISIBLE : View.GONE);
-                        navigationBar.getNextButton()
-                                .setVisibility(scrollNeeded ? View.GONE : View.VISIBLE);
-                    }
-                });
-        navigationBar.getMoreButton().setOnClickListener(createOnClickListener(null));
-        requireScroll();
-    }
+  /**
+   * A delegate to detect scrollability changes and to scroll the page. This provides a layer of
+   * abstraction for BottomScrollView, RecyclerView and ListView. The delegate should call {@link
+   * #notifyScrollabilityChange(boolean)} when the view scrollability is changed.
+   */
+  interface ScrollHandlingDelegate {
 
-    /**
-     * @see #requireScrollWithButton(Button, CharSequence, OnClickListener)
-     */
-    public void requireScrollWithButton(
-            @NonNull Button button,
-            @StringRes int moreText,
-            @Nullable OnClickListener onClickListener) {
-        requireScrollWithButton(button, button.getContext().getText(moreText), onClickListener);
-    }
+    /** Starts listening to scrollability changes at the target scrollable container. */
+    void startListening();
 
-    /**
-     * Use the given {@code button} to require scrolling. When scrolling is required, the button
-     * label will change to {@code moreText}, and tapping the button will cause the page to scroll
-     * down.
-     *
-     * <p>Note: Calling {@link View#setOnClickListener} on the button after this method will remove
-     * its link to the require-scroll mechanism. If you need to do that, obtain the click listener
-     * from {@link #createOnClickListener(OnClickListener)}.
-     *
-     * <p>Note: The normal button label is taken from the button's text at the time of calling this
-     * method. Calling {@link android.widget.TextView#setText} after calling this method causes
-     * undefined behavior.
-     *
-     * @param button The button to use for require scroll. The button's "normal" label is taken from
-     *               the text at the time of calling this method, and the click listener of it will
-     *               be replaced.
-     * @param moreText The button label when scroll is required.
-     * @param onClickListener The listener for clicks when scrolling is not required.
-     */
-    public void requireScrollWithButton(
-            @NonNull final Button button,
-            final CharSequence moreText,
-            @Nullable OnClickListener onClickListener) {
-        final CharSequence nextText = button.getText();
-        button.setOnClickListener(createOnClickListener(onClickListener));
-        setOnRequireScrollStateChangedListener(new OnRequireScrollStateChangedListener() {
-            @Override
-            public void onRequireScrollStateChanged(boolean scrollNeeded) {
-                button.setText(scrollNeeded ? moreText : nextText);
-            }
-        });
-        requireScroll();
-    }
+    /** Scroll the page content down by one page. */
+    void pageScrollDown();
+  }
 
-    /**
-     * @return True if scrolling is required. Note that this mixin only requires the user to
-     * scroll to the bottom once - if the user scrolled to the bottom and back-up, scrolling to
-     * bottom is not required again.
-     */
-    public boolean isScrollingRequired() {
-        return mRequiringScrollToBottom;
-    }
+  /* non-static section */
 
-    /**
-     * Start requiring scrolling on the layout. After calling this method, this mixin will start
-     * listening to scroll events from the scrolling container, and call
-     * {@link OnRequireScrollStateChangedListener} when the scroll state changes.
-     */
-    public void requireScroll() {
-        mDelegate.startListening();
-    }
+  private final Handler handler = new Handler(Looper.getMainLooper());
 
-    /**
-     * {@link ScrollHandlingDelegate} should call this method when the scrollability of the
-     * scrolling container changed, so this mixin can recompute whether scrolling should be
-     * required.
-     *
-     * @param canScrollDown True if the view can scroll down further.
-     */
-    void notifyScrollabilityChange(boolean canScrollDown) {
-        if (canScrollDown == mRequiringScrollToBottom) {
-            // Already at the desired require-scroll state
-            return;
+  private boolean requiringScrollToBottom = false;
+
+  // Whether the user have seen the more button yet.
+  private boolean everScrolledToBottom = false;
+
+  private ScrollHandlingDelegate delegate;
+
+  @Nullable private OnRequireScrollStateChangedListener listener;
+
+  /** @param templateLayout The template containing this mixin */
+  public RequireScrollMixin(@NonNull TemplateLayout templateLayout) {
+  }
+
+  /**
+   * Sets the delegate to handle scrolling. The type of delegate should depend on whether the
+   * scrolling view is a BottomScrollView, RecyclerView or ListView.
+   */
+  public void setScrollHandlingDelegate(@NonNull ScrollHandlingDelegate delegate) {
+    this.delegate = delegate;
+  }
+
+  /**
+   * Listen to require scroll state changes. When scroll is required, {@link
+   * OnRequireScrollStateChangedListener#onRequireScrollStateChanged(boolean)} is called with {@code
+   * true}, and vice versa.
+   */
+  public void setOnRequireScrollStateChangedListener(
+      @Nullable OnRequireScrollStateChangedListener listener) {
+    this.listener = listener;
+  }
+
+  /** @return The scroll state listener previously set, or {@code null} if none is registered. */
+  public OnRequireScrollStateChangedListener getOnRequireScrollStateChangedListener() {
+    return listener;
+  }
+
+  /**
+   * Creates an {@link OnClickListener} which if scrolling is required, will scroll the page down,
+   * and if scrolling is not required, delegates to the wrapped {@code listener}. Note that you
+   * should call {@link #requireScroll()} as well in order to start requiring scrolling.
+   *
+   * @param listener The listener to be invoked when scrolling is not needed and the user taps on
+   *     the button. If {@code null}, the click listener will be a no-op when scroll is not
+   *     required.
+   * @return A new {@link OnClickListener} which will scroll the page down or delegate to the given
+   *     listener depending on the current require-scroll state.
+   */
+  public OnClickListener createOnClickListener(@Nullable final OnClickListener listener) {
+    return new OnClickListener() {
+      @Override
+      public void onClick(View view) {
+        if (requiringScrollToBottom) {
+          delegate.pageScrollDown();
+        } else if (listener != null) {
+          listener.onClick(view);
         }
-        if (canScrollDown) {
-            if (!mEverScrolledToBottom) {
-                postScrollStateChange(true);
-                mRequiringScrollToBottom = true;
-            }
-        } else {
-            postScrollStateChange(false);
-            mRequiringScrollToBottom = false;
-            mEverScrolledToBottom = true;
-        }
-    }
+      }
+    };
+  }
 
-    private void postScrollStateChange(final boolean scrollNeeded) {
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                if (mListener != null) {
-                    mListener.onRequireScrollStateChanged(scrollNeeded);
-                }
-            }
+  /**
+   * Coordinate with the given navigation bar to require scrolling on the page. The more button will
+   * be shown instead of the next button while scrolling is required.
+   */
+  public void requireScrollWithNavigationBar(@NonNull final NavigationBar navigationBar) {
+    setOnRequireScrollStateChangedListener(
+        new OnRequireScrollStateChangedListener() {
+          @Override
+          public void onRequireScrollStateChanged(boolean scrollNeeded) {
+            navigationBar.getMoreButton().setVisibility(scrollNeeded ? View.VISIBLE : View.GONE);
+            navigationBar.getNextButton().setVisibility(scrollNeeded ? View.GONE : View.VISIBLE);
+          }
         });
+    navigationBar.getMoreButton().setOnClickListener(createOnClickListener(null));
+    requireScroll();
+  }
+
+  /** @see #requireScrollWithButton(Button, CharSequence, OnClickListener) */
+  public void requireScrollWithButton(
+      @NonNull Button button, @StringRes int moreText, @Nullable OnClickListener onClickListener) {
+    requireScrollWithButton(button, button.getContext().getText(moreText), onClickListener);
+  }
+
+  /**
+   * Use the given {@code button} to require scrolling. When scrolling is required, the button label
+   * will change to {@code moreText}, and tapping the button will cause the page to scroll down.
+   *
+   * <p>Note: Calling {@link View#setOnClickListener} on the button after this method will remove
+   * its link to the require-scroll mechanism. If you need to do that, obtain the click listener
+   * from {@link #createOnClickListener(OnClickListener)}.
+   *
+   * <p>Note: The normal button label is taken from the button's text at the time of calling this
+   * method. Calling {@link android.widget.TextView#setText} after calling this method causes
+   * undefined behavior.
+   *
+   * @param button The button to use for require scroll. The button's "normal" label is taken from
+   *     the text at the time of calling this method, and the click listener of it will be replaced.
+   * @param moreText The button label when scroll is required.
+   * @param onClickListener The listener for clicks when scrolling is not required.
+   */
+  public void requireScrollWithButton(
+      @NonNull final Button button,
+      final CharSequence moreText,
+      @Nullable OnClickListener onClickListener) {
+    final CharSequence nextText = button.getText();
+    button.setOnClickListener(createOnClickListener(onClickListener));
+    setOnRequireScrollStateChangedListener(
+        new OnRequireScrollStateChangedListener() {
+          @Override
+          public void onRequireScrollStateChanged(boolean scrollNeeded) {
+            button.setText(scrollNeeded ? moreText : nextText);
+          }
+        });
+    requireScroll();
+  }
+
+  /**
+   * @return True if scrolling is required. Note that this mixin only requires the user to scroll to
+   *     the bottom once - if the user scrolled to the bottom and back-up, scrolling to bottom is
+   *     not required again.
+   */
+  public boolean isScrollingRequired() {
+    return requiringScrollToBottom;
+  }
+
+  /**
+   * Start requiring scrolling on the layout. After calling this method, this mixin will start
+   * listening to scroll events from the scrolling container, and call {@link
+   * OnRequireScrollStateChangedListener} when the scroll state changes.
+   */
+  public void requireScroll() {
+    delegate.startListening();
+  }
+
+  /**
+   * {@link ScrollHandlingDelegate} should call this method when the scrollability of the scrolling
+   * container changed, so this mixin can recompute whether scrolling should be required.
+   *
+   * @param canScrollDown True if the view can scroll down further.
+   */
+  void notifyScrollabilityChange(boolean canScrollDown) {
+    if (canScrollDown == requiringScrollToBottom) {
+      // Already at the desired require-scroll state
+      return;
     }
+    if (canScrollDown) {
+      if (!everScrolledToBottom) {
+        postScrollStateChange(true);
+        requiringScrollToBottom = true;
+      }
+    } else {
+      postScrollStateChange(false);
+      requiringScrollToBottom = false;
+      everScrolledToBottom = true;
+    }
+  }
+
+  private void postScrollStateChange(final boolean scrollNeeded) {
+    handler.post(
+        new Runnable() {
+          @Override
+          public void run() {
+            if (listener != null) {
+              listener.onRequireScrollStateChanged(scrollNeeded);
+            }
+          }
+        });
+  }
 }
diff --git a/library/main/src/com/android/setupwizardlib/template/ScrollViewScrollHandlingDelegate.java b/library/main/src/com/android/setupwizardlib/template/ScrollViewScrollHandlingDelegate.java
index 9e4d1cf..dcaa379 100644
--- a/library/main/src/com/android/setupwizardlib/template/ScrollViewScrollHandlingDelegate.java
+++ b/library/main/src/com/android/setupwizardlib/template/ScrollViewScrollHandlingDelegate.java
@@ -16,12 +16,10 @@
 
 package com.android.setupwizardlib.template;
 
-import android.util.Log;
-import android.widget.ScrollView;
-
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-
+import android.util.Log;
+import android.widget.ScrollView;
 import com.android.setupwizardlib.template.RequireScrollMixin.ScrollHandlingDelegate;
 import com.android.setupwizardlib.view.BottomScrollView;
 import com.android.setupwizardlib.view.BottomScrollView.BottomScrollListener;
@@ -31,51 +29,48 @@
  * notifies {@link RequireScrollMixin} about scrollability changes.
  */
 public class ScrollViewScrollHandlingDelegate
-        implements ScrollHandlingDelegate, BottomScrollListener {
+    implements ScrollHandlingDelegate, BottomScrollListener {
 
-    private static final String TAG = "ScrollViewDelegate";
+  private static final String TAG = "ScrollViewDelegate";
 
-    @NonNull
-    private final RequireScrollMixin mRequireScrollMixin;
+  @NonNull private final RequireScrollMixin requireScrollMixin;
 
-    @Nullable
-    private final BottomScrollView mScrollView;
+  @Nullable private final BottomScrollView scrollView;
 
-    public ScrollViewScrollHandlingDelegate(
-            @NonNull RequireScrollMixin requireScrollMixin,
-            @Nullable ScrollView scrollView) {
-        mRequireScrollMixin = requireScrollMixin;
-        if (scrollView instanceof BottomScrollView) {
-            mScrollView = (BottomScrollView) scrollView;
-        } else {
-            Log.w(TAG, "Cannot set non-BottomScrollView. Found=" + scrollView);
-            mScrollView = null;
-        }
+  public ScrollViewScrollHandlingDelegate(
+      @NonNull RequireScrollMixin requireScrollMixin, @Nullable ScrollView scrollView) {
+    this.requireScrollMixin = requireScrollMixin;
+    if (scrollView instanceof BottomScrollView) {
+      this.scrollView = (BottomScrollView) scrollView;
+    } else {
+      Log.w(TAG, "Cannot set non-BottomScrollView. Found=" + scrollView);
+      this.scrollView = null;
     }
+  }
 
-    @Override
-    public void onScrolledToBottom() {
-        mRequireScrollMixin.notifyScrollabilityChange(false);
-    }
+  @Override
+  public void onScrolledToBottom() {
+    requireScrollMixin.notifyScrollabilityChange(false);
+  }
 
-    @Override
-    public void onRequiresScroll() {
-        mRequireScrollMixin.notifyScrollabilityChange(true);
-    }
+  @Override
+  public void onRequiresScroll() {
+    requireScrollMixin.notifyScrollabilityChange(true);
+  }
 
-    @Override
-    public void startListening() {
-        if (mScrollView != null) {
-            mScrollView.setBottomScrollListener(this);
-        } else {
-            Log.w(TAG, "Cannot require scroll. Scroll view is null.");
-        }
+  @Override
+  public void startListening() {
+    if (scrollView != null) {
+      scrollView.setBottomScrollListener(this);
+    } else {
+      Log.w(TAG, "Cannot require scroll. Scroll view is null.");
     }
+  }
 
-    @Override
-    public void pageScrollDown() {
-        if (mScrollView != null) {
-            mScrollView.pageScroll(ScrollView.FOCUS_DOWN);
-        }
+  @Override
+  public void pageScrollDown() {
+    if (scrollView != null) {
+      scrollView.pageScroll(ScrollView.FOCUS_DOWN);
     }
+  }
 }
diff --git a/library/main/src/com/android/setupwizardlib/util/DrawableLayoutDirectionHelper.java b/library/main/src/com/android/setupwizardlib/util/DrawableLayoutDirectionHelper.java
index b0afaba..8214415 100644
--- a/library/main/src/com/android/setupwizardlib/util/DrawableLayoutDirectionHelper.java
+++ b/library/main/src/com/android/setupwizardlib/util/DrawableLayoutDirectionHelper.java
@@ -23,59 +23,77 @@
 import android.os.Build;
 import android.view.View;
 
-/**
- * Provides convenience methods to handle drawable layout directions in different SDK versions.
- */
+/** Provides convenience methods to handle drawable layout directions in different SDK versions. */
 public class DrawableLayoutDirectionHelper {
 
-    /**
-     * Creates an {@link android.graphics.drawable.InsetDrawable} according to the layout direction
-     * of {@code view}.
-     */
-    @SuppressLint("InlinedApi")  // Use of View.LAYOUT_DIRECTION_RTL is guarded by version check
-    public static InsetDrawable createRelativeInsetDrawable(Drawable drawable,
-            int insetStart, int insetTop, int insetEnd, int insetBottom, View view) {
-        boolean isRtl = Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1
-                && view.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
-        return createRelativeInsetDrawable(drawable, insetStart, insetTop, insetEnd, insetBottom,
-                isRtl);
-    }
+  /**
+   * Creates an {@link android.graphics.drawable.InsetDrawable} according to the layout direction of
+   * {@code view}.
+   */
+  @SuppressLint("InlinedApi") // Use of View.LAYOUT_DIRECTION_RTL is guarded by version check
+  public static InsetDrawable createRelativeInsetDrawable(
+      Drawable drawable, int insetStart, int insetTop, int insetEnd, int insetBottom, View view) {
+    boolean isRtl =
+        Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1
+            && view.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+    return createRelativeInsetDrawable(
+        drawable, insetStart, insetTop, insetEnd, insetBottom, isRtl);
+  }
 
-    /**
-     * Creates an {@link android.graphics.drawable.InsetDrawable} according to the layout direction
-     * of {@code context}.
-     */
-    @SuppressLint("InlinedApi")  // Use of View.LAYOUT_DIRECTION_RTL is guarded by version check
-    public static InsetDrawable createRelativeInsetDrawable(Drawable drawable,
-            int insetStart, int insetTop, int insetEnd, int insetBottom, Context context) {
-        boolean isRtl = false;
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
-            final int layoutDirection =
-                    context.getResources().getConfiguration().getLayoutDirection();
-            isRtl = layoutDirection == View.LAYOUT_DIRECTION_RTL;
-        }
-        return createRelativeInsetDrawable(drawable, insetStart, insetTop, insetEnd, insetBottom,
-                isRtl);
+  /**
+   * Creates an {@link android.graphics.drawable.InsetDrawable} according to the layout direction of
+   * {@code context}.
+   */
+  @SuppressLint("InlinedApi") // Use of View.LAYOUT_DIRECTION_RTL is guarded by version check
+  public static InsetDrawable createRelativeInsetDrawable(
+      Drawable drawable,
+      int insetStart,
+      int insetTop,
+      int insetEnd,
+      int insetBottom,
+      Context context) {
+    boolean isRtl = false;
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+      final int layoutDirection = context.getResources().getConfiguration().getLayoutDirection();
+      isRtl = layoutDirection == View.LAYOUT_DIRECTION_RTL;
     }
+    return createRelativeInsetDrawable(
+        drawable, insetStart, insetTop, insetEnd, insetBottom, isRtl);
+  }
 
-    /**
-     * Creates an {@link android.graphics.drawable.InsetDrawable} according to
-     * {@code layoutDirection}.
-     */
-    @SuppressLint("InlinedApi")  // Given layoutDirection will not be View.LAYOUT_DIRECTION_RTL if
-                                 // SDK version doesn't support it.
-    public static InsetDrawable createRelativeInsetDrawable(Drawable drawable,
-            int insetStart, int insetTop, int insetEnd, int insetBottom, int layoutDirection) {
-        return createRelativeInsetDrawable(drawable, insetStart, insetTop, insetEnd, insetBottom,
-                layoutDirection == View.LAYOUT_DIRECTION_RTL);
-    }
+  /**
+   * Creates an {@link android.graphics.drawable.InsetDrawable} according to {@code
+   * layoutDirection}.
+   */
+  @SuppressLint("InlinedApi") // Given layoutDirection will not be View.LAYOUT_DIRECTION_RTL if
+  // SDK version doesn't support it.
+  public static InsetDrawable createRelativeInsetDrawable(
+      Drawable drawable,
+      int insetStart,
+      int insetTop,
+      int insetEnd,
+      int insetBottom,
+      int layoutDirection) {
+    return createRelativeInsetDrawable(
+        drawable,
+        insetStart,
+        insetTop,
+        insetEnd,
+        insetBottom,
+        layoutDirection == View.LAYOUT_DIRECTION_RTL);
+  }
 
-    private static InsetDrawable createRelativeInsetDrawable(Drawable drawable,
-            int insetStart, int insetTop, int insetEnd, int insetBottom, boolean isRtl) {
-        if (isRtl) {
-            return new InsetDrawable(drawable, insetEnd, insetTop, insetStart, insetBottom);
-        } else {
-            return new InsetDrawable(drawable, insetStart, insetTop, insetEnd, insetBottom);
-        }
+  private static InsetDrawable createRelativeInsetDrawable(
+      Drawable drawable,
+      int insetStart,
+      int insetTop,
+      int insetEnd,
+      int insetBottom,
+      boolean isRtl) {
+    if (isRtl) {
+      return new InsetDrawable(drawable, insetEnd, insetTop, insetStart, insetBottom);
+    } else {
+      return new InsetDrawable(drawable, insetStart, insetTop, insetEnd, insetBottom);
     }
+  }
 }
diff --git a/library/main/src/com/android/setupwizardlib/util/FallbackThemeWrapper.java b/library/main/src/com/android/setupwizardlib/util/FallbackThemeWrapper.java
index 2ec4f84..96f9162 100644
--- a/library/main/src/com/android/setupwizardlib/util/FallbackThemeWrapper.java
+++ b/library/main/src/com/android/setupwizardlib/util/FallbackThemeWrapper.java
@@ -18,37 +18,33 @@
 
 import android.content.Context;
 import android.content.res.Resources.Theme;
+import androidx.annotation.StyleRes;
 import android.view.ContextThemeWrapper;
 
-import androidx.annotation.StyleRes;
-
 /**
- * Same as {@link ContextThemeWrapper}, but the base context's theme attributes take precedence
- * over the wrapper context's. This is used to provide default values for theme attributes
- * referenced in layouts, to remove the risk of crashing the client because of using the wrong
- * theme.
+ * Same as {@link ContextThemeWrapper}, but the base context's theme attributes take precedence over
+ * the wrapper context's. This is used to provide default values for theme attributes referenced in
+ * layouts, to remove the risk of crashing the client because of using the wrong theme.
  */
 public class FallbackThemeWrapper extends ContextThemeWrapper {
 
-    /**
-     * Creates a new context wrapper with the specified theme.
-     *
-     * The specified theme will be applied as fallbacks to the base context's theme. Any attributes
-     * defined in the base context's theme will retain their original values. Otherwise values in
-     * {@code themeResId} will be used.
-     *
-     * @param base The base context.
-     * @param themeResId The theme to use as fallback.
-     */
-    public FallbackThemeWrapper(Context base, @StyleRes int themeResId) {
-        super(base, themeResId);
-    }
+  /**
+   * Creates a new context wrapper with the specified theme.
+   *
+   * <p>The specified theme will be applied as fallbacks to the base context's theme. Any attributes
+   * defined in the base context's theme will retain their original values. Otherwise values in
+   * {@code themeResId} will be used.
+   *
+   * @param base The base context.
+   * @param themeResId The theme to use as fallback.
+   */
+  public FallbackThemeWrapper(Context base, @StyleRes int themeResId) {
+    super(base, themeResId);
+  }
 
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    protected void onApplyThemeResource(Theme theme, int resId, boolean first) {
-        theme.applyStyle(resId, false /* force */);
-    }
+  /** {@inheritDoc} */
+  @Override
+  protected void onApplyThemeResource(Theme theme, int resId, boolean first) {
+    theme.applyStyle(resId, false /* force */);
+  }
 }
diff --git a/library/main/src/com/android/setupwizardlib/util/Partner.java b/library/main/src/com/android/setupwizardlib/util/Partner.java
index 9eaedc3..ce782cb 100644
--- a/library/main/src/com/android/setupwizardlib/util/Partner.java
+++ b/library/main/src/com/android/setupwizardlib/util/Partner.java
@@ -26,14 +26,13 @@
 import android.graphics.drawable.Drawable;
 import android.os.Build.VERSION;
 import android.os.Build.VERSION_CODES;
-import android.util.Log;
-
 import androidx.annotation.AnyRes;
 import androidx.annotation.ColorRes;
 import androidx.annotation.DrawableRes;
+import androidx.annotation.Nullable;
 import androidx.annotation.StringRes;
 import androidx.annotation.VisibleForTesting;
-
+import android.util.Log;
 import java.util.List;
 
 /**
@@ -46,157 +45,156 @@
  */
 public class Partner {
 
-    private static final String TAG = "(SUW) Partner";
+  private static final String TAG = "(SUW) Partner";
 
-    /** Marker action used to discover partner */
-    private static final String ACTION_PARTNER_CUSTOMIZATION =
-            "com.android.setupwizard.action.PARTNER_CUSTOMIZATION";
+  /** Marker action used to discover partner. */
+  private static final String ACTION_PARTNER_CUSTOMIZATION =
+      "com.android.setupwizard.action.PARTNER_CUSTOMIZATION";
 
-    private static boolean sSearched = false;
-    private static Partner sPartner;
+  private static boolean searched = false;
+  @Nullable private static Partner partner;
 
-    /**
-     * Convenience to get a drawable from partner overlay, or if not available, the drawable from
-     * the original context.
-     *
-     * @see #getResourceEntry(android.content.Context, int)
-     */
-    public static Drawable getDrawable(Context context, @DrawableRes int id) {
-        final ResourceEntry entry = getResourceEntry(context, id);
-        return entry.resources.getDrawable(entry.id);
+  /**
+   * Gets a drawable from partner overlay, or if not available, the drawable from the original
+   * context.
+   *
+   * @see #getResourceEntry(android.content.Context, int)
+   */
+  public static Drawable getDrawable(Context context, @DrawableRes int id) {
+    final ResourceEntry entry = getResourceEntry(context, id);
+    return entry.resources.getDrawable(entry.id);
+  }
+
+  /**
+   * Gets a string from partner overlay, or if not available, the string from the original context.
+   *
+   * @see #getResourceEntry(android.content.Context, int)
+   */
+  public static String getString(Context context, @StringRes int id) {
+    final ResourceEntry entry = getResourceEntry(context, id);
+    return entry.resources.getString(entry.id);
+  }
+
+  /**
+   * Gets a color from partner overlay, or if not available, the color from the original context.
+   */
+  public static int getColor(Context context, @ColorRes int id) {
+    final ResourceEntry resourceEntry = getResourceEntry(context, id);
+    return resourceEntry.resources.getColor(resourceEntry.id);
+  }
+
+  /**
+   * Gets a CharSequence from partner overlay, or if not available, the text from the original
+   * context.
+   */
+  public static CharSequence getText(Context context, @StringRes int id) {
+    final ResourceEntry entry = getResourceEntry(context, id);
+    return entry.resources.getText(entry.id);
+  }
+
+  /**
+   * Finds an entry of resource in the overlay package provided by partners. It will first look for
+   * the resource in the overlay package, and if not available, will return the one in the original
+   * context.
+   *
+   * @return a ResourceEntry in the partner overlay's resources, if one is defined. Otherwise the
+   *     resources from the original context is returned. Clients can then get the resource by
+   *     {@code entry.resources.getString(entry.id)}, or other methods available in {@link
+   *     android.content.res.Resources}.
+   */
+  public static ResourceEntry getResourceEntry(Context context, @AnyRes int id) {
+    final Partner partner = Partner.get(context);
+    if (partner != null) {
+      final Resources ourResources = context.getResources();
+      final String name = ourResources.getResourceEntryName(id);
+      final String type = ourResources.getResourceTypeName(id);
+      final int partnerId = partner.getIdentifier(name, type);
+      if (partnerId != 0) {
+        return new ResourceEntry(partner.resources, partnerId, true);
+      }
     }
+    return new ResourceEntry(context.getResources(), id, false);
+  }
 
-    /**
-     * Convenience to get a string from partner overlay, or if not available, the string from the
-     * original context.
-     *
-     * @see #getResourceEntry(android.content.Context, int)
-     */
-    public static String getString(Context context, @StringRes int id) {
-        final ResourceEntry entry = getResourceEntry(context, id);
-        return entry.resources.getString(entry.id);
+  public static class ResourceEntry {
+    public Resources resources;
+    public int id;
+    public boolean isOverlay;
+
+    ResourceEntry(Resources resources, int id, boolean isOverlay) {
+      this.resources = resources;
+      this.id = id;
+      this.isOverlay = isOverlay;
     }
+  }
 
-    /**
-     * Convenience method to get color from partner overlay, or if not available, the color from
-     * the original context.
-     */
-    public static int getColor(Context context, @ColorRes int id) {
-        final ResourceEntry resourceEntry = getResourceEntry(context, id);
-        return resourceEntry.resources.getColor(resourceEntry.id);
-    }
+  /**
+   * Finds and returns partner details, or {@code null} if none exists. A partner package is marked
+   * by a broadcast receiver declared in the manifest that handles the {@code
+   * com.android.setupwizard.action.PARTNER_CUSTOMIZATION} intent action. The overlay package must
+   * also be a system package.
+   */
+  public static synchronized Partner get(Context context) {
+    if (!searched) {
+      PackageManager pm = context.getPackageManager();
+      final Intent intent = new Intent(ACTION_PARTNER_CUSTOMIZATION);
+      List<ResolveInfo> receivers;
+      if (VERSION.SDK_INT >= VERSION_CODES.N) {
+        receivers =
+            pm.queryBroadcastReceivers(
+                intent,
+                PackageManager.MATCH_SYSTEM_ONLY
+                    | PackageManager.MATCH_DIRECT_BOOT_AWARE
+                    | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
+      } else {
+        // On versions before N, direct boot doesn't exist. And the MATCH_SYSTEM_ONLY flag
+        // doesn't exist so we filter for system apps in code below.
+        receivers = pm.queryBroadcastReceivers(intent, 0);
+      }
 
-    /**
-     * Convenience method to get a CharSequence from partner overlay, or if not available, the text
-     * from the original context.
-     */
-    public static CharSequence getText(Context context, @StringRes int id) {
-        final ResourceEntry entry = getResourceEntry(context, id);
-        return entry.resources.getText(entry.id);
-    }
-
-    /**
-     * Find an entry of resource in the overlay package provided by partners. It will first look for
-     * the resource in the overlay package, and if not available, will return the one in the
-     * original context.
-     *
-     * @return a ResourceEntry in the partner overlay's resources, if one is defined. Otherwise the
-     * resources from the original context is returned. Clients can then get the resource by
-     * {@code entry.resources.getString(entry.id)}, or other methods available in
-     * {@link android.content.res.Resources}.
-     */
-    public static ResourceEntry getResourceEntry(Context context, @AnyRes int id) {
-        final Partner partner = Partner.get(context);
-        if (partner != null) {
-            final Resources ourResources = context.getResources();
-            final String name = ourResources.getResourceEntryName(id);
-            final String type = ourResources.getResourceTypeName(id);
-            final int partnerId = partner.getIdentifier(name, type);
-            if (partnerId != 0) {
-                return new ResourceEntry(partner.mResources, partnerId, true);
-            }
+      for (ResolveInfo info : receivers) {
+        if (info.activityInfo == null) {
+          continue;
         }
-        return new ResourceEntry(context.getResources(), id, false);
-    }
-
-    public static class ResourceEntry {
-        public Resources resources;
-        public int id;
-        public boolean isOverlay;
-
-        ResourceEntry(Resources resources, int id, boolean isOverlay) {
-            this.resources = resources;
-            this.id = id;
-            this.isOverlay = isOverlay;
+        final ApplicationInfo appInfo = info.activityInfo.applicationInfo;
+        if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+          try {
+            final Resources res = pm.getResourcesForApplication(appInfo);
+            partner = new Partner(appInfo.packageName, res);
+            break;
+          } catch (NameNotFoundException e) {
+            Log.w(TAG, "Failed to find resources for " + appInfo.packageName);
+          }
         }
+      }
+      searched = true;
     }
+    return partner;
+  }
 
-    /**
-     * Find and return partner details, or {@code null} if none exists. A partner package is marked
-     * by a broadcast receiver declared in the manifest that handles the
-     * {@code com.android.setupwizard.action.PARTNER_CUSTOMIZATION} intent action. The overlay
-     * package must also be a system package.
-     */
-    public static synchronized Partner get(Context context) {
-        if (!sSearched) {
-            PackageManager pm = context.getPackageManager();
-            final Intent intent = new Intent(ACTION_PARTNER_CUSTOMIZATION);
-            List<ResolveInfo> receivers;
-            if (VERSION.SDK_INT >= VERSION_CODES.N) {
-                receivers = pm.queryBroadcastReceivers(
-                        intent,
-                        PackageManager.MATCH_SYSTEM_ONLY
-                                | PackageManager.MATCH_DIRECT_BOOT_AWARE
-                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
-            } else {
-                // On versions before N, direct boot doesn't exist. And the MATCH_SYSTEM_ONLY flag
-                // doesn't exist so we filter for system apps in code below.
-                receivers = pm.queryBroadcastReceivers(intent, 0);
-            }
+  @VisibleForTesting
+  public static synchronized void resetForTesting() {
+    searched = false;
+    partner = null;
+  }
 
-            for (ResolveInfo info : receivers) {
-                if (info.activityInfo == null) {
-                    continue;
-                }
-                final ApplicationInfo appInfo = info.activityInfo.applicationInfo;
-                if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
-                    try {
-                        final Resources res = pm.getResourcesForApplication(appInfo);
-                        sPartner = new Partner(appInfo.packageName, res);
-                        break;
-                    } catch (NameNotFoundException e) {
-                        Log.w(TAG, "Failed to find resources for " + appInfo.packageName);
-                    }
-                }
-            }
-            sSearched = true;
-        }
-        return sPartner;
-    }
+  private final String packageName;
+  private final Resources resources;
 
-    @VisibleForTesting
-    public static synchronized void resetForTesting() {
-        sSearched = false;
-        sPartner = null;
-    }
+  private Partner(String packageName, Resources res) {
+    this.packageName = packageName;
+    resources = res;
+  }
 
-    private final String mPackageName;
-    private final Resources mResources;
+  public String getPackageName() {
+    return packageName;
+  }
 
-    private Partner(String packageName, Resources res) {
-        mPackageName = packageName;
-        mResources = res;
-    }
+  public Resources getResources() {
+    return resources;
+  }
 
-    public String getPackageName() {
-        return mPackageName;
-    }
-
-    public Resources getResources() {
-        return mResources;
-    }
-
-    public int getIdentifier(String name, String defType) {
-        return mResources.getIdentifier(name, defType, mPackageName);
-    }
+  public int getIdentifier(String name, String defType) {
+    return resources.getIdentifier(name, defType, packageName);
+  }
 }
diff --git a/library/main/src/com/android/setupwizardlib/util/ResultCodes.java b/library/main/src/com/android/setupwizardlib/util/ResultCodes.java
index a429e73..ea20139 100644
--- a/library/main/src/com/android/setupwizardlib/util/ResultCodes.java
+++ b/library/main/src/com/android/setupwizardlib/util/ResultCodes.java
@@ -20,9 +20,9 @@
 
 public final class ResultCodes {
 
-    public static final int RESULT_SKIP = RESULT_FIRST_USER;
-    public static final int RESULT_RETRY = RESULT_FIRST_USER + 1;
-    public static final int RESULT_ACTIVITY_NOT_FOUND = RESULT_FIRST_USER + 2;
+  public static final int RESULT_SKIP = RESULT_FIRST_USER;
+  public static final int RESULT_RETRY = RESULT_FIRST_USER + 1;
+  public static final int RESULT_ACTIVITY_NOT_FOUND = RESULT_FIRST_USER + 2;
 
-    public static final int RESULT_FIRST_SETUP_USER = RESULT_FIRST_USER + 100;
+  public static final int RESULT_FIRST_SETUP_USER = RESULT_FIRST_USER + 100;
 }
diff --git a/library/main/src/com/android/setupwizardlib/util/SystemBarHelper.java b/library/main/src/com/android/setupwizardlib/util/SystemBarHelper.java
index 7e3e885..42350cc 100644
--- a/library/main/src/com/android/setupwizardlib/util/SystemBarHelper.java
+++ b/library/main/src/com/android/setupwizardlib/util/SystemBarHelper.java
@@ -24,6 +24,7 @@
 import android.os.Build.VERSION;
 import android.os.Build.VERSION_CODES;
 import android.os.Handler;
+import androidx.annotation.RequiresPermission;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
@@ -31,331 +32,327 @@
 import android.view.WindowInsets;
 import android.view.WindowManager;
 
-import androidx.annotation.RequiresPermission;
-
 /**
  * A helper class to manage the system navigation bar and status bar. This will add various
  * systemUiVisibility flags to the given Window or View to make them follow the Setup Wizard style.
  *
- * When the useImmersiveMode intent extra is true, a screen in Setup Wizard should hide the system
- * bars using methods from this class. For Lollipop, {@link #hideSystemBars(android.view.Window)}
- * will completely hide the system navigation bar and change the status bar to transparent, and
- * layout the screen contents (usually the illustration) behind it.
+ * <p>When the useImmersiveMode intent extra is true, a screen in Setup Wizard should hide the
+ * system bars using methods from this class. For Lollipop, {@link
+ * #hideSystemBars(android.view.Window)} will completely hide the system navigation bar and change
+ * the status bar to transparent, and layout the screen contents (usually the illustration) behind
+ * it.
  */
 public class SystemBarHelper {
 
-    private static final String TAG = "SystemBarHelper";
+  private static final String TAG = "SystemBarHelper";
 
-    @SuppressLint("InlinedApi")
-    private static final int DEFAULT_IMMERSIVE_FLAGS =
-            View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
-            | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
-            | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
-            | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
+  @SuppressLint("InlinedApi")
+  private static final int DEFAULT_IMMERSIVE_FLAGS =
+      View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+          | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
+          | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+          | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
 
-    @SuppressLint("InlinedApi")
-    private static final int DIALOG_IMMERSIVE_FLAGS =
-            View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
-            | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
+  @SuppressLint("InlinedApi")
+  private static final int DIALOG_IMMERSIVE_FLAGS =
+      View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
 
-    /**
-     * Needs to be equal to View.STATUS_BAR_DISABLE_BACK
-     */
-    private static final int STATUS_BAR_DISABLE_BACK = 0x00400000;
+  /** Needs to be equal to View.STATUS_BAR_DISABLE_BACK */
+  private static final int STATUS_BAR_DISABLE_BACK = 0x00400000;
 
-    /**
-     * The maximum number of retries when peeking the decor view. When polling for the decor view,
-     * waiting it to be installed, set a maximum number of retries.
-     */
-    private static final int PEEK_DECOR_VIEW_RETRIES = 3;
+  /**
+   * The maximum number of retries when peeking the decor view. When polling for the decor view,
+   * waiting it to be installed, set a maximum number of retries.
+   */
+  private static final int PEEK_DECOR_VIEW_RETRIES = 3;
 
-    /**
-     * Hide the navigation bar for a dialog.
-     *
-     * <p>This will only take effect in versions Lollipop or above. Otherwise this is a no-op.
-     */
-    public static void hideSystemBars(final Dialog dialog) {
-        if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
-            final Window window = dialog.getWindow();
-            temporarilyDisableDialogFocus(window);
-            addVisibilityFlag(window, DIALOG_IMMERSIVE_FLAGS);
-            addImmersiveFlagsToDecorView(window, DIALOG_IMMERSIVE_FLAGS);
+  /**
+   * Hide the navigation bar for a dialog.
+   *
+   * <p>This will only take effect in versions Lollipop or above. Otherwise this is a no-op.
+   */
+  public static void hideSystemBars(final Dialog dialog) {
+    if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+      final Window window = dialog.getWindow();
+      temporarilyDisableDialogFocus(window);
+      addVisibilityFlag(window, DIALOG_IMMERSIVE_FLAGS);
+      addImmersiveFlagsToDecorView(window, DIALOG_IMMERSIVE_FLAGS);
 
-            // Also set the navigation bar and status bar to transparent color. Note that this
-            // doesn't work if android.R.boolean.config_enableTranslucentDecor is false.
-            window.setNavigationBarColor(0);
-            window.setStatusBarColor(0);
-        }
+      // Also set the navigation bar and status bar to transparent color. Note that this
+      // doesn't work if android.R.boolean.config_enableTranslucentDecor is false.
+      window.setNavigationBarColor(0);
+      window.setStatusBarColor(0);
     }
+  }
 
-    /**
-     * Hide the navigation bar, make the color of the status and navigation bars transparent, and
-     * specify {@link View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN} flag so that the content is laid-out
-     * behind the transparent status bar. This is commonly used with
-     * {@link android.app.Activity#getWindow()} to make the navigation and status bars follow the
-     * Setup Wizard style.
-     *
-     * <p>This will only take effect in versions Lollipop or above. Otherwise this is a no-op.
-     */
-    public static void hideSystemBars(final Window window) {
-        if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
-            addVisibilityFlag(window, DEFAULT_IMMERSIVE_FLAGS);
-            addImmersiveFlagsToDecorView(window, DEFAULT_IMMERSIVE_FLAGS);
+  /**
+   * Hide the navigation bar, make the color of the status and navigation bars transparent, and
+   * specify {@link View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN} flag so that the content is laid-out
+   * behind the transparent status bar. This is commonly used with {@link
+   * android.app.Activity#getWindow()} to make the navigation and status bars follow the Setup
+   * Wizard style.
+   *
+   * <p>This will only take effect in versions Lollipop or above. Otherwise this is a no-op.
+   */
+  public static void hideSystemBars(final Window window) {
+    if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+      addVisibilityFlag(window, DEFAULT_IMMERSIVE_FLAGS);
+      addImmersiveFlagsToDecorView(window, DEFAULT_IMMERSIVE_FLAGS);
 
-            // Also set the navigation bar and status bar to transparent color. Note that this
-            // doesn't work if android.R.boolean.config_enableTranslucentDecor is false.
-            window.setNavigationBarColor(0);
-            window.setStatusBarColor(0);
-        }
+      // Also set the navigation bar and status bar to transparent color. Note that this
+      // doesn't work if android.R.boolean.config_enableTranslucentDecor is false.
+      window.setNavigationBarColor(0);
+      window.setStatusBarColor(0);
     }
+  }
 
-    /**
-     * Revert the actions of hideSystemBars. Note that this will remove the system UI visibility
-     * flags regardless of whether it is originally present. You should also manually reset the
-     * navigation bar and status bar colors, as this method doesn't know what value to revert it to.
-     */
-    public static void showSystemBars(final Dialog dialog, final Context context) {
-        showSystemBars(dialog.getWindow(), context);
+  /**
+   * Revert the actions of hideSystemBars. Note that this will remove the system UI visibility flags
+   * regardless of whether it is originally present. You should also manually reset the navigation
+   * bar and status bar colors, as this method doesn't know what value to revert it to.
+   */
+  public static void showSystemBars(final Dialog dialog, final Context context) {
+    showSystemBars(dialog.getWindow(), context);
+  }
+
+  /**
+   * Revert the actions of hideSystemBars. Note that this will remove the system UI visibility flags
+   * regardless of whether it is originally present. You should also manually reset the navigation
+   * bar and status bar colors, as this method doesn't know what value to revert it to.
+   */
+  public static void showSystemBars(final Window window, final Context context) {
+    if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+      removeVisibilityFlag(window, DEFAULT_IMMERSIVE_FLAGS);
+      removeImmersiveFlagsFromDecorView(window, DEFAULT_IMMERSIVE_FLAGS);
+
+      if (context != null) {
+        //noinspection AndroidLintInlinedApi
+        final TypedArray typedArray =
+            context.obtainStyledAttributes(
+                new int[] {android.R.attr.statusBarColor, android.R.attr.navigationBarColor});
+        final int statusBarColor = typedArray.getColor(0, 0);
+        final int navigationBarColor = typedArray.getColor(1, 0);
+        window.setStatusBarColor(statusBarColor);
+        window.setNavigationBarColor(navigationBarColor);
+        typedArray.recycle();
+      }
     }
+  }
 
-    /**
-     * Revert the actions of hideSystemBars. Note that this will remove the system UI visibility
-     * flags regardless of whether it is originally present. You should also manually reset the
-     * navigation bar and status bar colors, as this method doesn't know what value to revert it to.
-     */
-    public static void showSystemBars(final Window window, final Context context) {
-        if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
-            removeVisibilityFlag(window, DEFAULT_IMMERSIVE_FLAGS);
-            removeImmersiveFlagsFromDecorView(window, DEFAULT_IMMERSIVE_FLAGS);
-
-            if (context != null) {
-                //noinspection AndroidLintInlinedApi
-                final TypedArray typedArray = context.obtainStyledAttributes(new int[]{
-                        android.R.attr.statusBarColor, android.R.attr.navigationBarColor});
-                final int statusBarColor = typedArray.getColor(0, 0);
-                final int navigationBarColor = typedArray.getColor(1, 0);
-                window.setStatusBarColor(statusBarColor);
-                window.setNavigationBarColor(navigationBarColor);
-                typedArray.recycle();
-            }
-        }
+  /** Convenience method to add a visibility flag in addition to the existing ones. */
+  public static void addVisibilityFlag(final View view, final int flag) {
+    if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {
+      final int vis = view.getSystemUiVisibility();
+      view.setSystemUiVisibility(vis | flag);
     }
+  }
 
-    /**
-     * Convenience method to add a visibility flag in addition to the existing ones.
-     */
-    public static void addVisibilityFlag(final View view, final int flag) {
-        if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {
-            final int vis = view.getSystemUiVisibility();
-            view.setSystemUiVisibility(vis | flag);
-        }
+  /** Convenience method to add a visibility flag in addition to the existing ones. */
+  public static void addVisibilityFlag(final Window window, final int flag) {
+    if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {
+      WindowManager.LayoutParams attrs = window.getAttributes();
+      attrs.systemUiVisibility |= flag;
+      window.setAttributes(attrs);
     }
+  }
 
-    /**
-     * Convenience method to add a visibility flag in addition to the existing ones.
-     */
-    public static void addVisibilityFlag(final Window window, final int flag) {
-        if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {
-            WindowManager.LayoutParams attrs = window.getAttributes();
-            attrs.systemUiVisibility |= flag;
-            window.setAttributes(attrs);
-        }
+  /**
+   * Convenience method to remove a visibility flag from the view, leaving other flags that are not
+   * specified intact.
+   */
+  public static void removeVisibilityFlag(final View view, final int flag) {
+    if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {
+      final int vis = view.getSystemUiVisibility();
+      view.setSystemUiVisibility(vis & ~flag);
     }
+  }
 
-    /**
-     * Convenience method to remove a visibility flag from the view, leaving other flags that are
-     * not specified intact.
-     */
-    public static void removeVisibilityFlag(final View view, final int flag) {
-        if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {
-            final int vis = view.getSystemUiVisibility();
-            view.setSystemUiVisibility(vis & ~flag);
-        }
+  /**
+   * Convenience method to remove a visibility flag from the window, leaving other flags that are
+   * not specified intact.
+   */
+  public static void removeVisibilityFlag(final Window window, final int flag) {
+    if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {
+      WindowManager.LayoutParams attrs = window.getAttributes();
+      attrs.systemUiVisibility &= ~flag;
+      window.setAttributes(attrs);
     }
+  }
 
-    /**
-     * Convenience method to remove a visibility flag from the window, leaving other flags that are
-     * not specified intact.
-     */
-    public static void removeVisibilityFlag(final Window window, final int flag) {
-        if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {
-            WindowManager.LayoutParams attrs = window.getAttributes();
-            attrs.systemUiVisibility &= ~flag;
-            window.setAttributes(attrs);
-        }
+  /**
+   * Sets whether the back button on the software navigation bar is visible. This only works if you
+   * have the STATUS_BAR permission. Otherwise framework will filter out this flag and this method
+   * call will not have any effect.
+   *
+   * <p>IMPORTANT: Do not assume that users have no way to go back when the back button is hidden.
+   * Many devices have physical back buttons, and accessibility services like TalkBack may have
+   * gestures mapped to back. Please use onBackPressed, onKeyDown, or other similar ways to make
+   * sure back button events are still handled (or ignored) properly.
+   */
+  @RequiresPermission("android.permission.STATUS_BAR")
+  public static void setBackButtonVisible(final Window window, final boolean visible) {
+    if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {
+      if (visible) {
+        removeVisibilityFlag(window, STATUS_BAR_DISABLE_BACK);
+        removeImmersiveFlagsFromDecorView(window, STATUS_BAR_DISABLE_BACK);
+      } else {
+        addVisibilityFlag(window, STATUS_BAR_DISABLE_BACK);
+        addImmersiveFlagsToDecorView(window, STATUS_BAR_DISABLE_BACK);
+      }
     }
+  }
 
-    /**
-     * Sets whether the back button on the software navigation bar is visible. This only works if
-     * you have the STATUS_BAR permission. Otherwise framework will filter out this flag and this
-     * method call will not have any effect.
-     *
-     * <p>IMPORTANT: Do not assume that users have no way to go back when the back button is hidden.
-     * Many devices have physical back buttons, and accessibility services like TalkBack may have
-     * gestures mapped to back. Please use onBackPressed, onKeyDown, or other similar ways to
-     * make sure back button events are still handled (or ignored) properly.
-     */
-    @RequiresPermission("android.permission.STATUS_BAR")
-    public static void setBackButtonVisible(final Window window, final boolean visible) {
-        if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {
-            if (visible) {
-                removeVisibilityFlag(window, STATUS_BAR_DISABLE_BACK);
-                removeImmersiveFlagsFromDecorView(window, STATUS_BAR_DISABLE_BACK);
+  /**
+   * Set a view to be resized when the keyboard is shown. This will set the bottom margin of the
+   * view to be immediately above the keyboard, and assumes that the view sits immediately above the
+   * navigation bar.
+   *
+   * <p>Note that you must set {@link android.R.attr#windowSoftInputMode} to {@code adjustResize}
+   * for this class to work. Otherwise window insets are not dispatched and this method will have no
+   * effect.
+   *
+   * <p>This will only take effect in versions Lollipop or above. Otherwise this is a no-op.
+   *
+   * @param view The view to be resized when the keyboard is shown.
+   */
+  public static void setImeInsetView(final View view) {
+    if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+      view.setOnApplyWindowInsetsListener(new WindowInsetsListener());
+    }
+  }
+
+  /**
+   * Add the specified immersive flags to the decor view of the window, because {@link
+   * View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN} only takes effect when it is added to a view instead of
+   * the window.
+   */
+  @TargetApi(VERSION_CODES.HONEYCOMB)
+  private static void addImmersiveFlagsToDecorView(final Window window, final int vis) {
+    getDecorView(
+        window,
+        new OnDecorViewInstalledListener() {
+          @Override
+          public void onDecorViewInstalled(View decorView) {
+            addVisibilityFlag(decorView, vis);
+          }
+        });
+  }
+
+  @TargetApi(VERSION_CODES.HONEYCOMB)
+  private static void removeImmersiveFlagsFromDecorView(final Window window, final int vis) {
+    getDecorView(
+        window,
+        new OnDecorViewInstalledListener() {
+          @Override
+          public void onDecorViewInstalled(View decorView) {
+            removeVisibilityFlag(decorView, vis);
+          }
+        });
+  }
+
+  private static void getDecorView(Window window, OnDecorViewInstalledListener callback) {
+    new DecorViewFinder().getDecorView(window, callback, PEEK_DECOR_VIEW_RETRIES);
+  }
+
+  private static class DecorViewFinder {
+
+    private final Handler handler = new Handler();
+    private Window window;
+    private int retries;
+    private OnDecorViewInstalledListener callback;
+
+    private final Runnable checkDecorViewRunnable =
+        new Runnable() {
+          @Override
+          public void run() {
+            // Use peekDecorView instead of getDecorView so that clients can still set window
+            // features after calling this method.
+            final View decorView = window.peekDecorView();
+            if (decorView != null) {
+              callback.onDecorViewInstalled(decorView);
             } else {
-                addVisibilityFlag(window, STATUS_BAR_DISABLE_BACK);
-                addImmersiveFlagsToDecorView(window, STATUS_BAR_DISABLE_BACK);
+              retries--;
+              if (retries >= 0) {
+                // If the decor view is not installed yet, try again in the next loop.
+                handler.post(checkDecorViewRunnable);
+              } else {
+                Log.w(TAG, "Cannot get decor view of window: " + window);
+              }
             }
-        }
-    }
-
-    /**
-     * Set a view to be resized when the keyboard is shown. This will set the bottom margin of the
-     * view to be immediately above the keyboard, and assumes that the view sits immediately above
-     * the navigation bar.
-     *
-     * <p>Note that you must set {@link android.R.attr#windowSoftInputMode} to {@code adjustResize}
-     * for this class to work. Otherwise window insets are not dispatched and this method will have
-     * no effect.
-     *
-     * <p>This will only take effect in versions Lollipop or above. Otherwise this is a no-op.
-     *
-     * @param view The view to be resized when the keyboard is shown.
-     */
-    public static void setImeInsetView(final View view) {
-        if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
-            view.setOnApplyWindowInsetsListener(new WindowInsetsListener());
-        }
-    }
-
-    /**
-     * Add the specified immersive flags to the decor view of the window, because
-     * {@link View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN} only takes effect when it is added to a view
-     * instead of the window.
-     */
-    @TargetApi(VERSION_CODES.HONEYCOMB)
-    private static void addImmersiveFlagsToDecorView(final Window window, final int vis) {
-        getDecorView(window, new OnDecorViewInstalledListener() {
-            @Override
-            public void onDecorViewInstalled(View decorView) {
-                addVisibilityFlag(decorView, vis);
-            }
-        });
-    }
-
-    @TargetApi(VERSION_CODES.HONEYCOMB)
-    private static void removeImmersiveFlagsFromDecorView(final Window window, final int vis) {
-        getDecorView(window, new OnDecorViewInstalledListener() {
-            @Override
-            public void onDecorViewInstalled(View decorView) {
-                removeVisibilityFlag(decorView, vis);
-            }
-        });
-    }
-
-    private static void getDecorView(Window window, OnDecorViewInstalledListener callback) {
-        new DecorViewFinder().getDecorView(window, callback, PEEK_DECOR_VIEW_RETRIES);
-    }
-
-    private static class DecorViewFinder {
-
-        private final Handler mHandler = new Handler();
-        private Window mWindow;
-        private int mRetries;
-        private OnDecorViewInstalledListener mCallback;
-
-        private Runnable mCheckDecorViewRunnable = new Runnable() {
-            @Override
-            public void run() {
-                // Use peekDecorView instead of getDecorView so that clients can still set window
-                // features after calling this method.
-                final View decorView = mWindow.peekDecorView();
-                if (decorView != null) {
-                    mCallback.onDecorViewInstalled(decorView);
-                } else {
-                    mRetries--;
-                    if (mRetries >= 0) {
-                        // If the decor view is not installed yet, try again in the next loop.
-                        mHandler.post(mCheckDecorViewRunnable);
-                    } else {
-                        Log.w(TAG, "Cannot get decor view of window: " + mWindow);
-                    }
-                }
-            }
+          }
         };
 
-        public void getDecorView(Window window, OnDecorViewInstalledListener callback,
-                int retries) {
-            mWindow = window;
-            mRetries = retries;
-            mCallback = callback;
-            mCheckDecorViewRunnable.run();
-        }
+    public void getDecorView(Window window, OnDecorViewInstalledListener callback, int retries) {
+      this.window = window;
+      this.retries = retries;
+      this.callback = callback;
+      checkDecorViewRunnable.run();
     }
+  }
 
-    private interface OnDecorViewInstalledListener {
+  private interface OnDecorViewInstalledListener {
 
-        void onDecorViewInstalled(View decorView);
-    }
+    void onDecorViewInstalled(View decorView);
+  }
 
-    /**
-     * Apply a hack to temporarily set the window to not focusable, so that the navigation bar
-     * will not show up during the transition.
-     */
-    private static void temporarilyDisableDialogFocus(final Window window) {
-        window.setFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
-                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
-        // Add the SOFT_INPUT_IS_FORWARD_NAVIGATION_FLAG. This is normally done by the system when
-        // FLAG_NOT_FOCUSABLE is not set. Setting this flag allows IME to be shown automatically
-        // if the dialog has editable text fields.
-        window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION);
-        new Handler().post(new Runnable() {
-            @Override
-            public void run() {
+  /**
+   * Apply a hack to temporarily set the window to not focusable, so that the navigation bar will
+   * not show up during the transition.
+   */
+  private static void temporarilyDisableDialogFocus(final Window window) {
+    window.setFlags(
+        WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+        WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
+    // Add the SOFT_INPUT_IS_FORWARD_NAVIGATION_FLAG. This is normally done by the system when
+    // FLAG_NOT_FOCUSABLE is not set. Setting this flag allows IME to be shown automatically
+    // if the dialog has editable text fields.
+    window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION);
+    new Handler()
+        .post(
+            new Runnable() {
+              @Override
+              public void run() {
                 window.clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
-            }
-        });
+              }
+            });
+  }
+
+  @TargetApi(VERSION_CODES.LOLLIPOP)
+  private static class WindowInsetsListener implements View.OnApplyWindowInsetsListener {
+    private int bottomOffset;
+    private boolean hasCalculatedBottomOffset = false;
+
+    @Override
+    public WindowInsets onApplyWindowInsets(View view, WindowInsets insets) {
+      if (!hasCalculatedBottomOffset) {
+        bottomOffset = getBottomDistance(view);
+        hasCalculatedBottomOffset = true;
+      }
+
+      int bottomInset = insets.getSystemWindowInsetBottom();
+
+      final int bottomMargin = Math.max(insets.getSystemWindowInsetBottom() - bottomOffset, 0);
+
+      final ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) view.getLayoutParams();
+      // Check that we have enough space to apply the bottom margins before applying it.
+      // Otherwise the framework may think that the view is empty and exclude it from layout.
+      if (bottomMargin < lp.bottomMargin + view.getHeight()) {
+        lp.setMargins(lp.leftMargin, lp.topMargin, lp.rightMargin, bottomMargin);
+        view.setLayoutParams(lp);
+        bottomInset = 0;
+      }
+
+      return insets.replaceSystemWindowInsets(
+          insets.getSystemWindowInsetLeft(),
+          insets.getSystemWindowInsetTop(),
+          insets.getSystemWindowInsetRight(),
+          bottomInset);
     }
+  }
 
-    @TargetApi(VERSION_CODES.LOLLIPOP)
-    private static class WindowInsetsListener implements View.OnApplyWindowInsetsListener {
-        private int mBottomOffset;
-        private boolean mHasCalculatedBottomOffset = false;
-
-        @Override
-        public WindowInsets onApplyWindowInsets(View view, WindowInsets insets) {
-            if (!mHasCalculatedBottomOffset) {
-                mBottomOffset = getBottomDistance(view);
-                mHasCalculatedBottomOffset = true;
-            }
-
-            int bottomInset = insets.getSystemWindowInsetBottom();
-
-            final int bottomMargin = Math.max(
-                    insets.getSystemWindowInsetBottom() - mBottomOffset, 0);
-
-            final ViewGroup.MarginLayoutParams lp =
-                    (ViewGroup.MarginLayoutParams) view.getLayoutParams();
-            // Check that we have enough space to apply the bottom margins before applying it.
-            // Otherwise the framework may think that the view is empty and exclude it from layout.
-            if (bottomMargin < lp.bottomMargin + view.getHeight()) {
-                lp.setMargins(lp.leftMargin, lp.topMargin, lp.rightMargin, bottomMargin);
-                view.setLayoutParams(lp);
-                bottomInset = 0;
-            }
-
-
-            return insets.replaceSystemWindowInsets(
-                    insets.getSystemWindowInsetLeft(),
-                    insets.getSystemWindowInsetTop(),
-                    insets.getSystemWindowInsetRight(),
-                    bottomInset
-            );
-        }
-    }
-
-    private static int getBottomDistance(View view) {
-        int[] coords = new int[2];
-        view.getLocationInWindow(coords);
-        return view.getRootView().getHeight() - coords[1] - view.getHeight();
-    }
+  private static int getBottomDistance(View view) {
+    int[] coords = new int[2];
+    view.getLocationInWindow(coords);
+    return view.getRootView().getHeight() - coords[1] - view.getHeight();
+  }
 }
diff --git a/library/main/src/com/android/setupwizardlib/util/ThemeResolver.java b/library/main/src/com/android/setupwizardlib/util/ThemeResolver.java
new file mode 100644
index 0000000..14fdc85
--- /dev/null
+++ b/library/main/src/com/android/setupwizardlib/util/ThemeResolver.java
@@ -0,0 +1,219 @@
+package com.android.setupwizardlib.util;
+
+import android.app.Activity;
+import android.content.Intent;
+import androidx.annotation.Nullable;
+import androidx.annotation.StyleRes;
+import com.android.setupwizardlib.R;
+
+/**
+ * A resolver to resolve the theme from a string or an activity intent, setting options like the
+ * default theme and the oldest supported theme. Apps can share the resolver across the entire
+ * process by calling {@link #setDefault(ThemeResolver)} in {@link
+ * android.app.Application#onCreate()}. If an app needs more granular sharing of the theme default
+ * values, additional instances of {@link ThemeResolver} can be created using the builder.
+ */
+public class ThemeResolver {
+  @StyleRes private final int defaultTheme;
+  @Nullable private final String oldestSupportedTheme;
+  private final boolean useDayNight;
+
+  @Nullable private static ThemeResolver defaultResolver;
+
+  /**
+   * Sets the default instance used for the whole process. Can be null to reset the default to the
+   * preset one.
+   */
+  public static void setDefault(@Nullable ThemeResolver resolver) {
+    defaultResolver = resolver;
+  }
+
+  /**
+   * Returns the default instance, which can be changed using {@link #setDefault(ThemeResolver)}.
+   */
+  public static ThemeResolver getDefault() {
+    if (defaultResolver == null) {
+      defaultResolver =
+          new ThemeResolver.Builder()
+              .setDefaultTheme(R.style.SuwThemeGlif_DayNight)
+              .setUseDayNight(true)
+              .build();
+    }
+    return defaultResolver;
+  }
+
+  private ThemeResolver(
+      int defaultTheme, @Nullable String oldestSupportedTheme, boolean useDayNight) {
+    this.defaultTheme = defaultTheme;
+    this.oldestSupportedTheme = oldestSupportedTheme;
+    this.useDayNight = useDayNight;
+  }
+
+  /**
+   * Returns the style for the theme specified in the intent extra. If the specified string theme is
+   * older than the oldest supported theme, the default will be returned instead. Note that the
+   * default theme is returned without processing -- it may not be a DayNight theme even if {@link
+   * #useDayNight} is true.
+   */
+  @StyleRes
+  public int resolve(Intent intent) {
+    return resolve(
+        intent.getStringExtra(WizardManagerHelper.EXTRA_THEME),
+        /* suppressDayNight= */ WizardManagerHelper.isSetupWizardIntent(intent));
+  }
+
+  /**
+   * Returns the style for the given string theme. If the specified string theme is older than the
+   * oldest supported theme, the default will be returned instead. Note that the default theme is
+   * returned without processing -- it may not be a DayNight theme even if {@link #useDayNight} is
+   * true.
+   */
+  @StyleRes
+  public int resolve(@Nullable String theme) {
+    return resolve(theme, /* suppressDayNight= */ false);
+  }
+
+  @StyleRes
+  private int resolve(@Nullable String theme, boolean suppressDayNight) {
+    int themeResource =
+        useDayNight && !suppressDayNight ? getDayNightThemeRes(theme) : getThemeRes(theme);
+    if (themeResource == 0) {
+      return defaultTheme;
+    }
+
+    if (oldestSupportedTheme != null && compareThemes(theme, oldestSupportedTheme) < 0) {
+      return defaultTheme;
+    }
+    return themeResource;
+  }
+
+  /** Reads the theme from the intent, and applies the resolved theme to the activity. */
+  public void applyTheme(Activity activity) {
+    activity.setTheme(resolve(activity.getIntent()));
+  }
+
+  /**
+   * Returns the corresponding DayNight theme resource ID for the given string theme. DayNight
+   * themes are themes that will be either light or dark depending on the system setting. For
+   * example, the string {@link WizardManagerHelper#THEME_GLIF_LIGHT} will return
+   * {@code @style/SuwThemeGlif.DayNight}.
+   */
+  @StyleRes
+  private static int getDayNightThemeRes(@Nullable String theme) {
+    if (theme != null) {
+      switch (theme) {
+        case WizardManagerHelper.THEME_GLIF_V3_LIGHT:
+        case WizardManagerHelper.THEME_GLIF_V3:
+          return R.style.SuwThemeGlifV3_DayNight;
+        case WizardManagerHelper.THEME_GLIF_V2_LIGHT:
+        case WizardManagerHelper.THEME_GLIF_V2:
+          return R.style.SuwThemeGlifV2_DayNight;
+        case WizardManagerHelper.THEME_GLIF_LIGHT:
+        case WizardManagerHelper.THEME_GLIF:
+          return R.style.SuwThemeGlif_DayNight;
+        case WizardManagerHelper.THEME_MATERIAL_LIGHT:
+        case WizardManagerHelper.THEME_MATERIAL:
+          return R.style.SuwThemeMaterial_DayNight;
+        default:
+          // fall through
+      }
+    }
+    return 0;
+  }
+
+  /**
+   * Returns the theme resource ID for the given string theme. For example, the string {@link
+   * WizardManagerHelper#THEME_GLIF_LIGHT} will return {@code @style/SuwThemeGlif.Light}.
+   */
+  @StyleRes
+  private static int getThemeRes(@Nullable String theme) {
+    if (theme != null) {
+      switch (theme) {
+        case WizardManagerHelper.THEME_GLIF_V3_LIGHT:
+          return R.style.SuwThemeGlifV3_Light;
+        case WizardManagerHelper.THEME_GLIF_V3:
+          return R.style.SuwThemeGlifV3;
+        case WizardManagerHelper.THEME_GLIF_V2_LIGHT:
+          return R.style.SuwThemeGlifV2_Light;
+        case WizardManagerHelper.THEME_GLIF_V2:
+          return R.style.SuwThemeGlifV2;
+        case WizardManagerHelper.THEME_GLIF_LIGHT:
+          return R.style.SuwThemeGlif_Light;
+        case WizardManagerHelper.THEME_GLIF:
+          return R.style.SuwThemeGlif;
+        case WizardManagerHelper.THEME_MATERIAL_LIGHT:
+          return R.style.SuwThemeMaterial_Light;
+        case WizardManagerHelper.THEME_MATERIAL:
+          return R.style.SuwThemeMaterial;
+        default:
+          // fall through
+      }
+    }
+    return 0;
+  }
+
+  /** Compares whether the versions of {@code theme1} and {@code theme2} to check which is newer. */
+  private static int compareThemes(String theme1, String theme2) {
+    return Integer.valueOf(getThemeVersion(theme1)).compareTo(getThemeVersion(theme2));
+  }
+
+  /**
+   * Returns the version of the theme. The absolute number of the theme version is not defined, but
+   * a larger number in the version indicates a newer theme.
+   */
+  private static int getThemeVersion(String theme) {
+    if (theme != null) {
+      switch (theme) {
+        case WizardManagerHelper.THEME_GLIF_V3_LIGHT:
+        case WizardManagerHelper.THEME_GLIF_V3:
+          return 4;
+        case WizardManagerHelper.THEME_GLIF_V2_LIGHT:
+        case WizardManagerHelper.THEME_GLIF_V2:
+          return 3;
+        case WizardManagerHelper.THEME_GLIF_LIGHT:
+        case WizardManagerHelper.THEME_GLIF:
+          return 2;
+        case WizardManagerHelper.THEME_MATERIAL_LIGHT:
+        case WizardManagerHelper.THEME_MATERIAL:
+          return 1;
+        default:
+          // fall through
+      }
+    }
+    return -1;
+  }
+
+  /** Builder class for {@link ThemeResolver}. */
+  public static class Builder {
+    @StyleRes private int defaultTheme = R.style.SuwThemeGlif_DayNight;
+    @Nullable private String oldestSupportedTheme = null;
+    private boolean useDayNight = true;
+
+    public Builder() {}
+
+    public Builder(ThemeResolver themeResolver) {
+      this.defaultTheme = themeResolver.defaultTheme;
+      this.oldestSupportedTheme = themeResolver.oldestSupportedTheme;
+      this.useDayNight = themeResolver.useDayNight;
+    }
+
+    public Builder setDefaultTheme(@StyleRes int defaultTheme) {
+      this.defaultTheme = defaultTheme;
+      return this;
+    }
+
+    public Builder setOldestSupportedTheme(String oldestSupportedTheme) {
+      this.oldestSupportedTheme = oldestSupportedTheme;
+      return this;
+    }
+
+    public Builder setUseDayNight(boolean useDayNight) {
+      this.useDayNight = useDayNight;
+      return this;
+    }
+
+    public ThemeResolver build() {
+      return new ThemeResolver(defaultTheme, oldestSupportedTheme, useDayNight);
+    }
+  }
+}
diff --git a/library/main/src/com/android/setupwizardlib/util/WizardManagerHelper.java b/library/main/src/com/android/setupwizardlib/util/WizardManagerHelper.java
index 0628192..4d75c78 100644
--- a/library/main/src/com/android/setupwizardlib/util/WizardManagerHelper.java
+++ b/library/main/src/com/android/setupwizardlib/util/WizardManagerHelper.java
@@ -16,314 +16,390 @@
 
 package com.android.setupwizardlib.util;
 
+import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources.Theme;
 import android.os.Build.VERSION;
 import android.os.Build.VERSION_CODES;
 import android.provider.Settings;
-
+import androidx.annotation.Nullable;
 import androidx.annotation.StyleRes;
 import androidx.annotation.VisibleForTesting;
-
-import com.android.setupwizardlib.R;
-
 import java.util.Arrays;
 
+/**
+ * Helper to interact with Wizard Manager in setup wizard, which should be used when a screen is
+ * shown inside the setup flow. This includes things like parsing extras passed by Wizard Manager,
+ * and invoking Wizard Manager to start the next action.
+ */
 public class WizardManagerHelper {
 
-    private static final String ACTION_NEXT = "com.android.wizard.NEXT";
+  private static final String ACTION_NEXT = "com.android.wizard.NEXT";
 
-    // EXTRA_SCRIPT_URI and EXTRA_ACTION_ID are used in setup wizard in versions before M and are
-    // kept for backwards compatibility.
-    @VisibleForTesting
-    static final String EXTRA_SCRIPT_URI = "scriptUri";
-    @VisibleForTesting
-    static final String EXTRA_ACTION_ID = "actionId";
+  // EXTRA_SCRIPT_URI and EXTRA_ACTION_ID are used in setup wizard in versions before M and are
+  // kept for backwards compatibility.
+  @VisibleForTesting static final String EXTRA_SCRIPT_URI = "scriptUri";
+  @VisibleForTesting static final String EXTRA_ACTION_ID = "actionId";
 
-    @VisibleForTesting
-    static final String EXTRA_WIZARD_BUNDLE = "wizardBundle";
-    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";
-    @VisibleForTesting
-    static final String EXTRA_IS_PRE_DEFERRED_SETUP = "preDeferredSetup";
+  @VisibleForTesting static final String EXTRA_WIZARD_BUNDLE = "wizardBundle";
+  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";
+  @VisibleForTesting static final String EXTRA_IS_PRE_DEFERRED_SETUP = "preDeferredSetup";
+  @VisibleForTesting public static final String EXTRA_IS_SETUP_FLOW = "isSetupFlow";
 
-    public static final String EXTRA_THEME = "theme";
-    public static final String EXTRA_USE_IMMERSIVE_MODE = "useImmersiveMode";
+  public static final String EXTRA_THEME = "theme";
+  public static final String EXTRA_USE_IMMERSIVE_MODE = "useImmersiveMode";
 
-    public static final String SETTINGS_GLOBAL_DEVICE_PROVISIONED = "device_provisioned";
-    public static final String SETTINGS_SECURE_USER_SETUP_COMPLETE = "user_setup_complete";
+  public static final String SETTINGS_GLOBAL_DEVICE_PROVISIONED = "device_provisioned";
+  public static final String SETTINGS_SECURE_USER_SETUP_COMPLETE = "user_setup_complete";
 
-    public static final String THEME_HOLO = "holo";
-    public static final String THEME_HOLO_LIGHT = "holo_light";
-    public static final String THEME_MATERIAL = "material";
-    public static final String THEME_MATERIAL_LIGHT = "material_light";
+  public static final String THEME_HOLO = "holo";
+  public static final String THEME_HOLO_LIGHT = "holo_light";
+  public static final String THEME_MATERIAL = "material";
+  public static final String THEME_MATERIAL_LIGHT = "material_light";
 
-    /**
-     * Passed in a setup wizard intent as {@link #EXTRA_THEME}. This is the dark variant of the
-     * theme used in setup wizard for Nougat MR1.
-     */
-    public static final String THEME_GLIF = "glif";
+  /**
+   * Passed in a setup wizard intent as {@link #EXTRA_THEME}. This is the dark variant of the theme
+   * used in setup wizard for Nougat MR1.
+   */
+  public static final String THEME_GLIF = "glif";
 
-    /**
-     * Passed in a setup wizard intent as {@link #EXTRA_THEME}. This is the default theme used in
-     * setup wizard for Nougat MR1.
-     */
-    public static final String THEME_GLIF_LIGHT = "glif_light";
+  /**
+   * Passed in a setup wizard intent as {@link #EXTRA_THEME}. This is the default theme used in
+   * setup wizard for Nougat MR1.
+   */
+  public static final String THEME_GLIF_LIGHT = "glif_light";
 
-    /**
-     * 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_V2 = "glif_v2";
+  /**
+   * 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_V2 = "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_V2_LIGHT = "glif_v2_light";
+  /**
+   * 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_V2_LIGHT = "glif_v2_light";
 
-    /**
-     * Passed in a setup wizard intent as {@link #EXTRA_THEME}. This is the dark variant of the
-     * theme used in setup wizard for P.
-     */
-    public static final String THEME_GLIF_V3 = "glif_v3";
+  /**
+   * Passed in a setup wizard intent as {@link #EXTRA_THEME}. This is the dark variant of the theme
+   * used in setup wizard for P.
+   */
+  public static final String THEME_GLIF_V3 = "glif_v3";
 
-    /**
-     * Passed in a setup wizard intent as {@link #EXTRA_THEME}. This is the default theme used in
-     * setup wizard for P.
-     */
-    public static final String THEME_GLIF_V3_LIGHT = "glif_v3_light";
+  /**
+   * Passed in a setup wizard intent as {@link #EXTRA_THEME}. This is the default theme used in
+   * setup wizard for P.
+   */
+  public static final String THEME_GLIF_V3_LIGHT = "glif_v3_light";
 
-    /**
-     * Get an intent that will invoke the next step of setup wizard.
-     *
-     * @param originalIntent The original intent that was used to start the step, usually via
-     *                       {@link android.app.Activity#getIntent()}.
-     * @param resultCode The result code of the step. See {@link ResultCodes}.
-     * @return A new intent that can be used with
-     *         {@link android.app.Activity#startActivityForResult(Intent, int)} to start the next
-     *         step of the setup flow.
-     */
-    public static Intent getNextIntent(Intent originalIntent, int resultCode) {
-        return getNextIntent(originalIntent, resultCode, null);
+  /**
+   * Get an intent that will invoke the next step of setup wizard.
+   *
+   * @param originalIntent The original intent that was used to start the step, usually via {@link
+   *     android.app.Activity#getIntent()}.
+   * @param resultCode The result code of the step. See {@link ResultCodes}.
+   * @return A new intent that can be used with {@link
+   *     android.app.Activity#startActivityForResult(Intent, int)} to start the next step of the
+   *     setup flow.
+   */
+  public static Intent getNextIntent(Intent originalIntent, int resultCode) {
+    return getNextIntent(originalIntent, resultCode, null);
+  }
+
+  /**
+   * Get an intent that will invoke the next step of setup wizard.
+   *
+   * @param originalIntent The original intent that was used to start the step, usually via {@link
+   *     android.app.Activity#getIntent()}.
+   * @param resultCode The result code of the step. See {@link ResultCodes}.
+   * @param data An intent containing extra result data.
+   * @return A new intent that can be used with {@link
+   *     android.app.Activity#startActivityForResult(Intent, int)} to start the next step of the
+   *     setup flow.
+   */
+  public static Intent getNextIntent(Intent originalIntent, int resultCode, Intent data) {
+    Intent intent = new Intent(ACTION_NEXT);
+    copyWizardManagerExtras(originalIntent, intent);
+    intent.putExtra(EXTRA_RESULT_CODE, resultCode);
+    if (data != null && data.getExtras() != null) {
+      intent.putExtras(data.getExtras());
+    }
+    intent.putExtra(EXTRA_THEME, originalIntent.getStringExtra(EXTRA_THEME));
+
+    return intent;
+  }
+
+  /**
+   * Copy the internal extras used by setup wizard from one intent to another. For low-level use
+   * only, such as when using {@link Intent#FLAG_ACTIVITY_FORWARD_RESULT} to relay to another
+   * intent.
+   *
+   * @param srcIntent Intent to get the wizard manager extras from.
+   * @param dstIntent Intent to copy the wizard manager extras to.
+   */
+  public static void copyWizardManagerExtras(Intent srcIntent, Intent dstIntent) {
+    dstIntent.putExtra(EXTRA_WIZARD_BUNDLE, srcIntent.getBundleExtra(EXTRA_WIZARD_BUNDLE));
+    for (String key :
+        Arrays.asList(
+            EXTRA_IS_FIRST_RUN,
+            EXTRA_IS_DEFERRED_SETUP,
+            EXTRA_IS_PRE_DEFERRED_SETUP,
+            EXTRA_IS_SETUP_FLOW)) {
+      dstIntent.putExtra(key, srcIntent.getBooleanExtra(key, false));
     }
 
-    /**
-     * Get an intent that will invoke the next step of setup wizard.
-     *
-     * @param originalIntent The original intent that was used to start the step, usually via
-     *                       {@link android.app.Activity#getIntent()}.
-     * @param resultCode The result code of the step. See {@link ResultCodes}.
-     * @param data An intent containing extra result data.
-     * @return A new intent that can be used with
-     *         {@link android.app.Activity#startActivityForResult(Intent, int)} to start the next
-     *         step of the setup flow.
-     */
-    public static Intent getNextIntent(Intent originalIntent, int resultCode, Intent data) {
-        Intent intent = new Intent(ACTION_NEXT);
-        copyWizardManagerExtras(originalIntent, intent);
-        intent.putExtra(EXTRA_RESULT_CODE, resultCode);
-        if (data != null && data.getExtras() != null) {
-            intent.putExtras(data.getExtras());
-        }
-        intent.putExtra(EXTRA_THEME, originalIntent.getStringExtra(EXTRA_THEME));
-
-        return intent;
+    for (String key : Arrays.asList(EXTRA_THEME, EXTRA_SCRIPT_URI, EXTRA_ACTION_ID)) {
+      dstIntent.putExtra(key, srcIntent.getStringExtra(key));
     }
+  }
 
-    /**
-     * Copy the internal extras used by setup wizard from one intent to another. For low-level use
-     * only, such as when using {@link Intent#FLAG_ACTIVITY_FORWARD_RESULT} to relay to another
-     * intent.
-     *
-     * @param srcIntent Intent to get the wizard manager extras from.
-     * @param dstIntent Intent to copy the wizard manager extras to.
-     */
-    public static void copyWizardManagerExtras(Intent srcIntent, Intent dstIntent) {
-        dstIntent.putExtra(EXTRA_WIZARD_BUNDLE, srcIntent.getBundleExtra(EXTRA_WIZARD_BUNDLE));
-        for (String key : Arrays.asList(
-                EXTRA_IS_FIRST_RUN, EXTRA_IS_DEFERRED_SETUP, EXTRA_IS_PRE_DEFERRED_SETUP)) {
-            dstIntent.putExtra(key, srcIntent.getBooleanExtra(key, false));
-        }
+  /**
+   * Check whether an intent is intended to be used within the setup wizard flow.
+   *
+   * @param intent The intent to be checked, usually from {@link android.app.Activity#getIntent()}.
+   * @return true if the intent passed in was intended to be used with setup wizard.
+   */
+  public static boolean isSetupWizardIntent(Intent intent) {
+    return intent.getBooleanExtra(EXTRA_IS_FIRST_RUN, false);
+  }
 
-        for (String key : Arrays.asList(EXTRA_THEME, EXTRA_SCRIPT_URI, EXTRA_ACTION_ID)) {
-            dstIntent.putExtra(key, srcIntent.getStringExtra(key));
-        }
+  /**
+   * Checks whether the current user has completed Setup Wizard. This is true if the current user
+   * has gone through Setup Wizard. The current user may or may not be the device owner and the
+   * device owner may have already completed setup wizard.
+   *
+   * @param context The context to retrieve the settings.
+   * @return true if the current user has completed Setup Wizard.
+   * @see #isDeviceProvisioned(android.content.Context)
+   */
+  public static boolean isUserSetupComplete(Context context) {
+    if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
+      return Settings.Secure.getInt(
+              context.getContentResolver(), SETTINGS_SECURE_USER_SETUP_COMPLETE, 0)
+          == 1;
+    } else {
+      // For versions below JB MR1, there are no user profiles. Just return the global device
+      // provisioned state.
+      return Settings.Secure.getInt(
+              context.getContentResolver(), SETTINGS_GLOBAL_DEVICE_PROVISIONED, 0)
+          == 1;
     }
+  }
 
-    /**
-     * Check whether an intent is intended to be used within the setup wizard flow.
-     *
-     * @param intent The intent to be checked, usually from
-     *               {@link android.app.Activity#getIntent()}.
-     * @return true if the intent passed in was intended to be used with setup wizard.
-     */
-    public static boolean isSetupWizardIntent(Intent intent) {
-        return intent.getBooleanExtra(EXTRA_IS_FIRST_RUN, false);
+  /**
+   * Checks whether the device is provisioned. This means that the device has gone through Setup
+   * Wizard at least once. Note that the user can still be in Setup Wizard even if this is true, for
+   * a secondary user profile triggered through Settings > Add account.
+   *
+   * @param context The context to retrieve the settings.
+   * @return true if the device is provisioned.
+   * @see #isUserSetupComplete(android.content.Context)
+   */
+  public static boolean isDeviceProvisioned(Context context) {
+    if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
+      return Settings.Global.getInt(
+              context.getContentResolver(), SETTINGS_GLOBAL_DEVICE_PROVISIONED, 0)
+          == 1;
+    } else {
+      return Settings.Secure.getInt(
+              context.getContentResolver(), SETTINGS_GLOBAL_DEVICE_PROVISIONED, 0)
+          == 1;
     }
+  }
 
-    /**
-     * Checks whether the current user has completed Setup Wizard. This is true if the current user
-     * has gone through Setup Wizard. The current user may or may not be the device owner and the
-     * device owner may have already completed setup wizard.
-     *
-     * @param context The context to retrieve the settings.
-     * @return true if the current user has completed Setup Wizard.
-     * @see #isDeviceProvisioned(android.content.Context)
-     */
-    public static boolean isUserSetupComplete(Context context) {
-        if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
-            return Settings.Secure.getInt(context.getContentResolver(),
-                    SETTINGS_SECURE_USER_SETUP_COMPLETE, 0) == 1;
-        } else {
-            // For versions below JB MR1, there are no user profiles. Just return the global device
-            // provisioned state.
-            return Settings.Secure.getInt(context.getContentResolver(),
-                    SETTINGS_GLOBAL_DEVICE_PROVISIONED, 0) == 1;
-        }
-    }
+  /**
+   * 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 whether the device is provisioned. This means that the device has gone through Setup
-     * Wizard at least once. Note that the user can still be in Setup Wizard even if this is true,
-     * for a secondary user profile triggered through Settings > Add account.
-     *
-     * @param context The context to retrieve the settings.
-     * @return true if the device is provisioned.
-     * @see #isUserSetupComplete(android.content.Context)
-     */
-    public static boolean isDeviceProvisioned(Context context) {
-        if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
-            return Settings.Global.getInt(context.getContentResolver(),
-                    SETTINGS_GLOBAL_DEVICE_PROVISIONED, 0) == 1;
-        } else {
-            return Settings.Secure.getInt(context.getContentResolver(),
-                    SETTINGS_GLOBAL_DEVICE_PROVISIONED, 0) == 1;
-        }
-    }
+  /**
+   * Checks whether an intent is running in "pre-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 "pre-deferred" setup wizard.
+   */
+  public static boolean isPreDeferredSetupWizard(Intent originalIntent) {
+    return originalIntent != null
+        && originalIntent.getBooleanExtra(EXTRA_IS_PRE_DEFERRED_SETUP, false);
+  }
 
-    /**
-     * 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. Note that day-night themes are not taken into account by this method.
+   *
+   * @param intent The intent used to start the activity, which the theme extra will be read from.
+   * @param def The default value if the theme is not specified.
+   * @return True if the activity started by the given intent should use light theme.
+   */
+  public static boolean isLightTheme(Intent intent, boolean def) {
+    final String theme = intent.getStringExtra(EXTRA_THEME);
+    return isLightTheme(theme, def);
+  }
 
-    /**
-     * Checks whether an intent is running in "pre-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 "pre-deferred" setup wizard.
-     */
-    public static boolean isPreDeferredSetupWizard(Intent originalIntent) {
-        return originalIntent != null
-                && originalIntent.getBooleanExtra(EXTRA_IS_PRE_DEFERRED_SETUP, false);
+  /**
+   * Checks whether {@code theme} represents a light or dark theme. If the theme specified is
+   * unknown, the value def will be returned. Note that day-night themes are not taken into account
+   * by this method.
+   *
+   * @param theme The theme as specified from an intent sent from setup wizard.
+   * @param def The default value if the theme is not known.
+   * @return True if {@code theme} represents a light theme.
+   */
+  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_V2_LIGHT.equals(theme)
+        || THEME_GLIF_V3_LIGHT.equals(theme)) {
+      return true;
+    } else if (THEME_HOLO.equals(theme)
+        || THEME_MATERIAL.equals(theme)
+        || THEME_GLIF.equals(theme)
+        || THEME_GLIF_V2.equals(theme)
+        || THEME_GLIF_V3.equals(theme)) {
+      return false;
+    } else {
+      return def;
     }
+  }
 
-    /**
-     * 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.
-     *
-     * @param intent The intent used to start the activity, which the theme extra will be read from.
-     * @param def The default value if the theme is not specified.
-     * @return True if the activity started by the given intent should use light theme.
-     */
-    public static boolean isLightTheme(Intent intent, boolean def) {
-        final String theme = intent.getStringExtra(EXTRA_THEME);
-        return isLightTheme(theme, def);
-    }
+  /**
+   * Gets the theme style resource defined by this library for the theme specified in the given
+   * intent. For example, for THEME_GLIF_LIGHT, the theme @style/SuwThemeGlif.Light is returned.
+   *
+   * @param intent The intent passed by setup wizard, or one with the theme propagated along using
+   *     {@link #copyWizardManagerExtras(Intent, Intent)}.
+   * @return The style corresponding to the theme in the given intent, or {@code defaultTheme} if
+   *     the given theme is not recognized.
+   * @see #getThemeRes(String, int)
+   * @deprecated it is recommended to use {@link ThemeResolver} which allows setting the default
+   *     theme in one place and applying it to multiple screens.
+   */
+  @Deprecated
+  public static @StyleRes int getThemeRes(Intent intent, @StyleRes int defaultTheme) {
+    return new ThemeResolver.Builder(ThemeResolver.getDefault())
+        .setDefaultTheme(defaultTheme)
+        .setUseDayNight(false)
+        .build()
+        .resolve(intent);
+  }
 
-    /**
-     * Checks whether {@code theme} represents a light or dark theme. If the theme specified is
-     * unknown, the value def will be returned.
-     *
-     * @param theme The theme as specified from an intent sent from setup wizard.
-     * @param def The default value if the theme is not known.
-     * @return True if {@code theme} represents a light theme.
-     */
-    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_V2_LIGHT.equals(theme)
-                || THEME_GLIF_V3_LIGHT.equals(theme)) {
-            return true;
-        } else if (THEME_HOLO.equals(theme) || THEME_MATERIAL.equals(theme)
-                || THEME_GLIF.equals(theme) || THEME_GLIF_V2.equals(theme)
-                || THEME_GLIF_V3.equals(theme)) {
-            return false;
-        } else {
-            return def;
-        }
-    }
+  /**
+   * Gets the theme style resource defined by this library for the theme specified in the given
+   * intent. For example, for THEME_GLIF_LIGHT, the theme @style/SuwThemeGlif.Light is returned.
+   *
+   * @param intent The intent passed by setup wizard, or one with the theme propagated along using
+   *     {@link #copyWizardManagerExtras(Intent, Intent)}.
+   * @return The style corresponding to the theme in the given intent, or {@code defaultTheme} if
+   *     the given theme is not recognized. Return the {@code defaultTheme} if the specified theme
+   *     is older than the oldest supported one.
+   * @see #getThemeRes(String, int)
+   * @deprecated it is recommended to use {@link ThemeResolver} which allows setting the default
+   *     theme and oldest supported theme in one place and applying it to multiple screens.
+   */
+  @Deprecated
+  public static @StyleRes int getThemeRes(
+      Intent intent, @StyleRes int defaultTheme, @Nullable String oldestSupportedTheme) {
+    return new ThemeResolver.Builder(ThemeResolver.getDefault())
+        .setDefaultTheme(defaultTheme)
+        .setUseDayNight(false)
+        .setOldestSupportedTheme(oldestSupportedTheme)
+        .build()
+        .resolve(intent);
+  }
 
-    /**
-     * Gets the theme style resource defined by this library for the theme specified in the given
-     * intent. For example, for THEME_GLIF_LIGHT, the theme @style/SuwThemeGlif.Light is returned.
-     *
-     * @param intent The intent passed by setup wizard, or one with the theme propagated along using
-     *               {@link #copyWizardManagerExtras(Intent, Intent)}.
-     * @return The style corresponding to the theme in the given intent, or {@code defaultTheme} if
-     *         the given theme is not recognized.
-     *
-     * @see #getThemeRes(String, int)
-     */
-    public static @StyleRes int getThemeRes(Intent intent, @StyleRes int defaultTheme) {
-        final String theme = intent.getStringExtra(EXTRA_THEME);
-        return getThemeRes(theme, defaultTheme);
-    }
+  /**
+   * Gets the theme style resource defined by this library for the given theme name. For example,
+   * for THEME_GLIF_LIGHT, the theme @style/SuwThemeGlif.Light is returned.
+   *
+   * <p>If you require extra theme attributes but want to ensure forward compatibility with new
+   * themes added here, consider overriding {@link android.app.Activity#onApplyThemeResource} in
+   * your activity and call {@link Theme#applyStyle(int, boolean)} using your theme overlay.
+   *
+   * <pre>{@code
+   * protected void onApplyThemeResource(Theme theme, int resid, boolean first) {
+   *     super.onApplyThemeResource(theme, resid, first);
+   *     theme.applyStyle(R.style.MyThemeOverlay, true);
+   * }
+   * }</pre>
+   *
+   * @param theme The string representation of the theme.
+   * @return The style corresponding to the given {@code theme}, or {@code defaultTheme} if the
+   *     given theme is not recognized.
+   * @deprecated it is recommended to use {@link ThemeResolver} which allows setting the default
+   *     theme in one place and applying it to multiple screens.
+   */
+  @Deprecated
+  public static @StyleRes int getThemeRes(
+      String theme, @StyleRes int defaultTheme, @Nullable String oldestSupportedTheme) {
+    return new ThemeResolver.Builder(ThemeResolver.getDefault())
+        .setDefaultTheme(defaultTheme)
+        .setUseDayNight(false)
+        .setOldestSupportedTheme(oldestSupportedTheme)
+        .build()
+        .resolve(theme);
+  }
 
-    /**
-     * Gets the theme style resource defined by this library for the given theme name. For example,
-     * for THEME_GLIF_LIGHT, the theme @style/SuwThemeGlif.Light is returned.
-     *
-     * <p>If you require extra theme attributes but want to ensure forward compatibility with new
-     * themes added here, consider overriding {@link android.app.Activity#onApplyThemeResource} in
-     * your activity and call {@link Theme#applyStyle(int, boolean)} using your theme overlay.
-     *
-     * <pre>{@code
-     * protected void onApplyThemeResource(Theme theme, int resid, boolean first) {
-     *     super.onApplyThemeResource(theme, resid, first);
-     *     theme.applyStyle(R.style.MyThemeOverlay, true);
-     * }
-     * }</pre>
-     *
-     * @param theme The string representation of the theme.
-     * @return The style corresponding to the given {@code theme}, or {@code defaultTheme} if the
-     *         given theme is not recognized.
-     */
-    public static @StyleRes int getThemeRes(String theme, @StyleRes int defaultTheme) {
-        if (theme != null) {
-            switch (theme) {
-                case THEME_GLIF_V3_LIGHT:
-                    return R.style.SuwThemeGlifV3_Light;
-                case THEME_GLIF_V3:
-                    return R.style.SuwThemeGlifV3;
-                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:
-                    return R.style.SuwThemeGlif;
-                case THEME_MATERIAL_LIGHT:
-                    return R.style.SuwThemeMaterial_Light;
-                case THEME_MATERIAL:
-                    return R.style.SuwThemeMaterial;
-                default:
-                    // fall through
-            }
-        }
-        return defaultTheme;
-    }
+  /**
+   * Gets the theme style resource defined by this library for the given theme name. For example,
+   * for THEME_GLIF_LIGHT, the theme @style/SuwThemeGlif.Light is returned.
+   *
+   * <p>If you require extra theme attributes but want to ensure forward compatibility with new
+   * themes added here, consider overriding {@link android.app.Activity#onApplyThemeResource} in
+   * your activity and call {@link Theme#applyStyle(int, boolean)} using your theme overlay.
+   *
+   * <pre>{@code
+   * protected void onApplyThemeResource(Theme theme, int resid, boolean first) {
+   *     super.onApplyThemeResource(theme, resid, first);
+   *     theme.applyStyle(R.style.MyThemeOverlay, true);
+   * }
+   * }</pre>
+   *
+   * @param theme The string representation of the theme.
+   * @return The style corresponding to the given {@code theme}, or {@code defaultTheme} if the
+   *     given theme is not recognized.
+   * @deprecated it is recommended to use {@link ThemeResolver} which allows setting the default
+   *     theme in one place and applying it to multiple screens.
+   */
+  @Deprecated
+  public static @StyleRes int getThemeRes(@Nullable String theme, @StyleRes int defaultTheme) {
+    return new ThemeResolver.Builder(ThemeResolver.getDefault())
+        .setDefaultTheme(defaultTheme)
+        .setUseDayNight(false)
+        .build()
+        .resolve(theme);
+  }
+
+  /**
+   * Reads the theme from the intent, and applies the theme to the activity as resolved by {@link
+   * ThemeResolver#getDefault()}.
+   *
+   * <p>If you require extra theme attributes, consider overriding {@link
+   * android.app.Activity#onApplyThemeResource} in your activity and call {@link
+   * Theme#applyStyle(int, boolean)} using your theme overlay.
+   *
+   * <pre>{@code
+   * protected void onApplyThemeResource(Theme theme, int resid, boolean first) {
+   *     super.onApplyThemeResource(theme, resid, first);
+   *     theme.applyStyle(R.style.MyThemeOverlay, true);
+   * }
+   * }</pre>
+   *
+   * @param activity the activity to get the intent from and apply the resulting theme to.
+   */
+  public static void applyTheme(Activity activity) {
+    ThemeResolver.getDefault().applyTheme(activity);
+  }
 }
diff --git a/library/main/src/com/android/setupwizardlib/view/BottomScrollView.java b/library/main/src/com/android/setupwizardlib/view/BottomScrollView.java
index eeb40a9..962538f 100644
--- a/library/main/src/com/android/setupwizardlib/view/BottomScrollView.java
+++ b/library/main/src/com/android/setupwizardlib/view/BottomScrollView.java
@@ -17,12 +17,11 @@
 package com.android.setupwizardlib.view;
 
 import android.content.Context;
+import androidx.annotation.VisibleForTesting;
 import android.util.AttributeSet;
 import android.view.View;
 import android.widget.ScrollView;
 
-import androidx.annotation.VisibleForTesting;
-
 /**
  * An extension of ScrollView that will invoke a listener callback when the ScrollView needs
  * scrolling, and when the ScrollView is being scrolled to the bottom. This is often used in Setup
@@ -30,75 +29,81 @@
  */
 public class BottomScrollView extends ScrollView {
 
-    public interface BottomScrollListener {
-        void onScrolledToBottom();
-        void onRequiresScroll();
-    }
+  public interface BottomScrollListener {
+    void onScrolledToBottom();
 
-    private BottomScrollListener mListener;
-    private int mScrollThreshold;
-    private boolean mRequiringScroll = false;
+    void onRequiresScroll();
+  }
 
-    private final Runnable mCheckScrollRunnable = new Runnable() {
+  private BottomScrollListener listener;
+  private int scrollThreshold;
+  private boolean requiringScroll = false;
+
+  private final Runnable checkScrollRunnable =
+      new Runnable() {
         @Override
         public void run() {
-            checkScroll();
+          checkScroll();
         }
-    };
+      };
 
-    public BottomScrollView(Context context) {
-        super(context);
+  public BottomScrollView(Context context) {
+    super(context);
+  }
+
+  public BottomScrollView(Context context, AttributeSet attrs) {
+    super(context, attrs);
+  }
+
+  public BottomScrollView(Context context, AttributeSet attrs, int defStyle) {
+    super(context, attrs, defStyle);
+  }
+
+  public void setBottomScrollListener(BottomScrollListener l) {
+    listener = l;
+  }
+
+  @VisibleForTesting
+  public BottomScrollListener getBottomScrollListener() {
+    return listener;
+  }
+
+  @VisibleForTesting
+  public int getScrollThreshold() {
+    return scrollThreshold;
+  }
+
+  @Override
+  protected void onLayout(boolean changed, int l, int t, int r, int b) {
+    super.onLayout(changed, l, t, r, b);
+    final View child = getChildAt(0);
+    if (child != null) {
+      scrollThreshold = Math.max(0, child.getMeasuredHeight() - b + t - getPaddingBottom());
     }
-
-    public BottomScrollView(Context context, AttributeSet attrs) {
-        super(context, attrs);
+    if (b - t > 0) {
+      // Post check scroll in the next run loop, so that the callback methods will be invoked
+      // after the layout pass. This way a new layout pass will be scheduled if view
+      // properties are changed in the callbacks.
+      post(checkScrollRunnable);
     }
+  }
 
-    public BottomScrollView(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
+  @Override
+  protected void onScrollChanged(int l, int t, int oldl, int oldt) {
+    super.onScrollChanged(l, t, oldl, oldt);
+    if (oldt != t) {
+      checkScroll();
     }
+  }
 
-    public void setBottomScrollListener(BottomScrollListener l) {
-        mListener = l;
+  private void checkScroll() {
+    if (listener != null) {
+      if (getScrollY() >= scrollThreshold) {
+        listener.onScrolledToBottom();
+      } else if (!requiringScroll) {
+        requiringScroll = true;
+        listener.onRequiresScroll();
+      }
     }
-
-    @VisibleForTesting
-    public int getScrollThreshold() {
-        return mScrollThreshold;
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        super.onLayout(changed, l, t, r, b);
-        final View child = getChildAt(0);
-        if (child != null) {
-            mScrollThreshold = Math.max(0, child.getMeasuredHeight() - b + t - getPaddingBottom());
-        }
-        if (b - t > 0) {
-            // Post check scroll in the next run loop, so that the callback methods will be invoked
-            // after the layout pass. This way a new layout pass will be scheduled if view
-            // properties are changed in the callbacks.
-            post(mCheckScrollRunnable);
-        }
-    }
-
-    @Override
-    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
-        super.onScrollChanged(l, t, oldl, oldt);
-        if (oldt != t) {
-            checkScroll();
-        }
-    }
-
-    private void checkScroll() {
-        if (mListener != null) {
-            if (getScrollY() >= mScrollThreshold) {
-                mListener.onScrolledToBottom();
-            } else if (!mRequiringScroll) {
-                mRequiringScroll = true;
-                mListener.onRequiresScroll();
-            }
-        }
-    }
-
+  }
 }
diff --git a/library/main/src/com/android/setupwizardlib/view/ButtonBarLayout.java b/library/main/src/com/android/setupwizardlib/view/ButtonBarLayout.java
index f7f5f99..4be4f56 100644
--- a/library/main/src/com/android/setupwizardlib/view/ButtonBarLayout.java
+++ b/library/main/src/com/android/setupwizardlib/view/ButtonBarLayout.java
@@ -20,105 +20,99 @@
 import android.util.AttributeSet;
 import android.view.View;
 import android.widget.LinearLayout;
-
 import com.android.setupwizardlib.R;
 
 /**
  * An extension of LinearLayout that automatically switches to vertical orientation when it can't
  * fit its child views horizontally.
  *
- * Modified from {@code com.android.internal.widget.ButtonBarLayout}
+ * <p>Modified from {@code com.android.internal.widget.ButtonBarLayout}
  */
 public class ButtonBarLayout extends LinearLayout {
 
-    private boolean mStacked = false;
-    private int mOriginalPaddingLeft;
-    private int mOriginalPaddingRight;
+  private boolean stacked = false;
+  private int originalPaddingLeft;
+  private int originalPaddingRight;
 
-    public ButtonBarLayout(Context context) {
-        super(context);
+  public ButtonBarLayout(Context context) {
+    super(context);
+  }
+
+  public ButtonBarLayout(Context context, AttributeSet attrs) {
+    super(context, attrs);
+  }
+
+  @Override
+  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+    final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
+
+    setStacked(false);
+
+    boolean needsRemeasure = false;
+
+    int initialWidthMeasureSpec = widthMeasureSpec;
+    if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY) {
+      // Measure with WRAP_CONTENT, so that we can compare the measured size with the
+      // available size to see if we need to stack.
+      initialWidthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+
+      // We'll need to remeasure again to fill excess space.
+      needsRemeasure = true;
     }
 
-    public ButtonBarLayout(Context context, AttributeSet attrs) {
-        super(context, attrs);
+    super.onMeasure(initialWidthMeasureSpec, heightMeasureSpec);
+
+    if (getMeasuredWidth() > widthSize) {
+      setStacked(true);
+
+      // Measure again in the new orientation.
+      needsRemeasure = true;
     }
 
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
+    if (needsRemeasure) {
+      super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+    }
+  }
 
-        setStacked(false);
-
-        boolean needsRemeasure = false;
-
-        int initialWidthMeasureSpec = widthMeasureSpec;
-        if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY) {
-            // Measure with WRAP_CONTENT, so that we can compare the measured size with the
-            // available size to see if we need to stack.
-            initialWidthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
-
-            // We'll need to remeasure again to fill excess space.
-            needsRemeasure = true;
+  private void setStacked(boolean stacked) {
+    if (this.stacked == stacked) {
+      return;
+    }
+    this.stacked = stacked;
+    int childCount = getChildCount();
+    for (int i = 0; i < childCount; i++) {
+      View child = getChildAt(i);
+      LayoutParams childParams = (LayoutParams) child.getLayoutParams();
+      if (stacked) {
+        child.setTag(R.id.suw_original_weight, childParams.weight);
+        childParams.weight = 0;
+      } else {
+        Float weight = (Float) child.getTag(R.id.suw_original_weight);
+        if (weight != null) {
+          childParams.weight = weight;
         }
-
-        super.onMeasure(initialWidthMeasureSpec, heightMeasureSpec);
-
-        if (getMeasuredWidth() > widthSize) {
-            setStacked(true);
-
-            // Measure again in the new orientation.
-            needsRemeasure = true;
-        }
-
-        if (needsRemeasure) {
-            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-        }
+      }
+      child.setLayoutParams(childParams);
     }
 
-    private void setStacked(boolean stacked) {
-        if (mStacked == stacked) {
-            return;
-        }
-        mStacked = stacked;
-        int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            View child = getChildAt(i);
-            LayoutParams childParams = (LayoutParams) child.getLayoutParams();
-            if (stacked) {
-                child.setTag(R.id.suw_original_weight, childParams.weight);
-                childParams.weight = 0;
-            } else {
-                Float weight = (Float) child.getTag(R.id.suw_original_weight);
-                if (weight != null) {
-                    childParams.weight = weight;
-                }
-            }
-            child.setLayoutParams(childParams);
-        }
+    setOrientation(stacked ? LinearLayout.VERTICAL : LinearLayout.HORIZONTAL);
 
-        setOrientation(stacked ? LinearLayout.VERTICAL : LinearLayout.HORIZONTAL);
-
-        // Reverse the child order, so that the primary button is towards the top when vertical
-        for (int i = childCount - 1; i >= 0; i--) {
-            bringChildToFront(getChildAt(i));
-        }
-
-        if (stacked) {
-            // HACK: In the default button bar style, the left and right paddings are not
-            // balanced to compensate for different alignment for borderless (left) button and
-            // the raised (right) button. When it's stacked, we want the buttons to be centered,
-            // so we balance out the paddings here.
-            mOriginalPaddingLeft = getPaddingLeft();
-            mOriginalPaddingRight = getPaddingRight();
-            int paddingHorizontal = Math.max(mOriginalPaddingLeft, mOriginalPaddingRight);
-            setPadding(
-                    paddingHorizontal, getPaddingTop(), paddingHorizontal, getPaddingBottom());
-        } else {
-            setPadding(
-                    mOriginalPaddingLeft,
-                    getPaddingTop(),
-                    mOriginalPaddingRight,
-                    getPaddingBottom());
-        }
+    // Reverse the child order, so that the primary button is towards the top when vertical
+    for (int i = childCount - 1; i >= 0; i--) {
+      bringChildToFront(getChildAt(i));
     }
+
+    if (stacked) {
+      // HACK: In the default button bar style, the left and right paddings are not
+      // balanced to compensate for different alignment for borderless (left) button and
+      // the raised (right) button. When it's stacked, we want the buttons to be centered,
+      // so we balance out the paddings here.
+      originalPaddingLeft = getPaddingLeft();
+      originalPaddingRight = getPaddingRight();
+      int paddingHorizontal = Math.max(originalPaddingLeft, originalPaddingRight);
+      setPadding(paddingHorizontal, getPaddingTop(), paddingHorizontal, getPaddingBottom());
+    } else {
+      setPadding(originalPaddingLeft, getPaddingTop(), originalPaddingRight, getPaddingBottom());
+    }
+  }
 }
diff --git a/library/main/src/com/android/setupwizardlib/view/CheckableLinearLayout.java b/library/main/src/com/android/setupwizardlib/view/CheckableLinearLayout.java
index 9605f99..fd2319c 100644
--- a/library/main/src/com/android/setupwizardlib/view/CheckableLinearLayout.java
+++ b/library/main/src/com/android/setupwizardlib/view/CheckableLinearLayout.java
@@ -19,74 +19,67 @@
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.os.Build.VERSION_CODES;
+import androidx.annotation.Nullable;
 import android.util.AttributeSet;
 import android.widget.Checkable;
 import android.widget.LinearLayout;
 
-import androidx.annotation.Nullable;
-
 /**
- * A LinearLayout which is checkable. This will set the checked state when
- * {@link #onCreateDrawableState(int)} is called, and can be used with
- * {@code android:duplicateParentState} to propagate the drawable state to child views.
+ * A LinearLayout which is checkable. This will set the checked state when {@link
+ * #onCreateDrawableState(int)} is called, and can be used with {@code android:duplicateParentState}
+ * to propagate the drawable state to child views.
  */
 public class CheckableLinearLayout extends LinearLayout implements Checkable {
 
-    private boolean mChecked = false;
+  private boolean checked = false;
 
-    public CheckableLinearLayout(Context context) {
-        super(context);
-    }
+  public CheckableLinearLayout(Context context) {
+    super(context);
+  }
 
-    public CheckableLinearLayout(Context context, @Nullable AttributeSet attrs) {
-        super(context, attrs);
-    }
+  public CheckableLinearLayout(Context context, @Nullable AttributeSet attrs) {
+    super(context, attrs);
+  }
 
-    @TargetApi(VERSION_CODES.HONEYCOMB)
-    public CheckableLinearLayout(
-            Context context,
-            @Nullable AttributeSet attrs,
-            int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-    }
+  @TargetApi(VERSION_CODES.HONEYCOMB)
+  public CheckableLinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+    super(context, attrs, defStyleAttr);
+  }
 
-    @TargetApi(VERSION_CODES.LOLLIPOP)
-    public CheckableLinearLayout(
-            Context context,
-            AttributeSet attrs,
-            int defStyleAttr,
-            int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-    }
+  @TargetApi(VERSION_CODES.LOLLIPOP)
+  public CheckableLinearLayout(
+      Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+    super(context, attrs, defStyleAttr, defStyleRes);
+  }
 
-    {
-        setFocusable(true);
-    }
+  {
+    setFocusable(true);
+  }
 
-    @Override
-    protected int[] onCreateDrawableState(int extraSpace) {
-        if (mChecked) {
-            final int[] superStates = super.onCreateDrawableState(extraSpace + 1);
-            final int[] checked = new int[] { android.R.attr.state_checked };
-            return mergeDrawableStates(superStates, checked);
-        } else {
-            return super.onCreateDrawableState(extraSpace);
-        }
+  @Override
+  protected int[] onCreateDrawableState(int extraSpace) {
+    if (this.checked) {
+      final int[] superStates = super.onCreateDrawableState(extraSpace + 1);
+      final int[] checked = new int[] {android.R.attr.state_checked};
+      return mergeDrawableStates(superStates, checked);
+    } else {
+      return super.onCreateDrawableState(extraSpace);
     }
+  }
 
-    @Override
-    public void setChecked(boolean checked) {
-        mChecked = checked;
-        refreshDrawableState();
-    }
+  @Override
+  public void setChecked(boolean checked) {
+    this.checked = checked;
+    refreshDrawableState();
+  }
 
-    @Override
-    public boolean isChecked() {
-        return mChecked;
-    }
+  @Override
+  public boolean isChecked() {
+    return checked;
+  }
 
-    @Override
-    public void toggle() {
-        setChecked(!isChecked());
-    }
+  @Override
+  public void toggle() {
+    setChecked(!isChecked());
+  }
 }
diff --git a/library/main/src/com/android/setupwizardlib/view/FillContentLayout.java b/library/main/src/com/android/setupwizardlib/view/FillContentLayout.java
index 2c28090..b72d4d2 100644
--- a/library/main/src/com/android/setupwizardlib/view/FillContentLayout.java
+++ b/library/main/src/com/android/setupwizardlib/view/FillContentLayout.java
@@ -21,13 +21,12 @@
 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.
+ * 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
@@ -42,84 +41,82 @@
  */
 public class FillContentLayout extends FrameLayout {
 
-    private int mMaxWidth;
-    private int mMaxHeight;
+  private int maxWidth;
+  private int maxHeight;
 
-    public FillContentLayout(Context context) {
-        this(context, null);
+  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);
+
+    maxHeight = a.getDimensionPixelSize(R.styleable.SuwFillContentLayout_android_maxHeight, -1);
+    maxWidth = 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());
     }
+  }
 
-    public FillContentLayout(Context context, AttributeSet attrs) {
-        this(context, attrs, R.attr.suwFillContentLayoutStyle);
+  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(maxWidth, parentWidth),
+            getPaddingLeft() + getPaddingRight() + lp.leftMargin + lp.rightMargin,
+            lp.width);
+    int childHeightMeasureSpec =
+        getMaxSizeMeasureSpec(
+            Math.min(maxHeight, 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);
     }
-
-    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;
-    }
+    return 0;
+  }
 }
diff --git a/library/main/src/com/android/setupwizardlib/view/Illustration.java b/library/main/src/com/android/setupwizardlib/view/Illustration.java
index c6968f8..e9cd4b5 100644
--- a/library/main/src/com/android/setupwizardlib/view/Illustration.java
+++ b/library/main/src/com/android/setupwizardlib/view/Illustration.java
@@ -30,7 +30,6 @@
 import android.view.Gravity;
 import android.view.ViewOutlineProvider;
 import android.widget.FrameLayout;
-
 import com.android.setupwizardlib.R;
 
 /**
@@ -39,184 +38,190 @@
  * drawable to fit the width of the view and fills the rest with the background.
  *
  * <p>If an aspect ratio is set, then the aspect ratio of the source drawable is maintained.
- * Otherwise the the aspect ratio will be ignored, only increasing the width of the illustration.
+ * Otherwise the aspect ratio will be ignored, only increasing the width of the illustration.
  */
 public class Illustration extends FrameLayout {
 
-    // Size of the baseline grid in pixels
-    private float mBaselineGridSize;
-    private Drawable mBackground;
-    private Drawable mIllustration;
-    private final Rect mViewBounds = new Rect();
-    private final Rect mIllustrationBounds = new Rect();
-    private float mScale = 1.0f;
-    private float mAspectRatio = 0.0f;
+  // Size of the baseline grid in pixels
+  private float baselineGridSize;
+  private Drawable background;
+  private Drawable illustration;
+  private final Rect viewBounds = new Rect();
+  private final Rect illustrationBounds = new Rect();
+  private float scale = 1.0f;
+  private float aspectRatio = 0.0f;
 
-    public Illustration(Context context) {
-        super(context);
-        init(null, 0);
+  public Illustration(Context context) {
+    super(context);
+    init(null, 0);
+  }
+
+  public Illustration(Context context, AttributeSet attrs) {
+    super(context, attrs);
+    init(attrs, 0);
+  }
+
+  @TargetApi(VERSION_CODES.HONEYCOMB)
+  public Illustration(Context context, AttributeSet attrs, int defStyleAttr) {
+    super(context, attrs, defStyleAttr);
+    init(attrs, defStyleAttr);
+  }
+
+  // All the constructors delegate to this init method. The 3-argument constructor is not
+  // available in FrameLayout before v11, so call super with the exact same arguments.
+  private void init(AttributeSet attrs, int defStyleAttr) {
+    if (attrs != null) {
+      TypedArray a =
+          getContext().obtainStyledAttributes(attrs, R.styleable.SuwIllustration, defStyleAttr, 0);
+      aspectRatio = a.getFloat(R.styleable.SuwIllustration_suwAspectRatio, 0.0f);
+      a.recycle();
     }
+    // Number of pixels of the 8dp baseline grid as defined in material design specs
+    baselineGridSize = getResources().getDisplayMetrics().density * 8;
+    setWillNotDraw(false);
+  }
 
-    public Illustration(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        init(attrs, 0);
+  /**
+   * The background will be drawn to fill up the rest of the view. It will also be scaled by the
+   * same amount as the foreground so their textures look the same.
+   */
+  // Override the deprecated setBackgroundDrawable method to support API < 16. View.setBackground
+  // forwards to setBackgroundDrawable in the framework implementation.
+  @SuppressWarnings("deprecation")
+  @Override
+  public void setBackgroundDrawable(Drawable background) {
+    if (background == this.background) {
+      return;
     }
+    this.background = background;
+    invalidate();
+    requestLayout();
+  }
 
-    @TargetApi(VERSION_CODES.HONEYCOMB)
-    public Illustration(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-        init(attrs, defStyleAttr);
+  /**
+   * Sets the drawable used as the illustration. The drawable is expected to have intrinsic width
+   * and height defined and will be scaled to fit the width of the view.
+   */
+  public void setIllustration(Drawable illustration) {
+    if (illustration == this.illustration) {
+      return;
     }
+    this.illustration = illustration;
+    invalidate();
+    requestLayout();
+  }
 
-    // All the constructors delegate to this init method. The 3-argument constructor is not
-    // available in FrameLayout before v11, so call super with the exact same arguments.
-    private void init(AttributeSet attrs, int defStyleAttr) {
-        if (attrs != null) {
-            TypedArray a = getContext().obtainStyledAttributes(attrs,
-                    R.styleable.SuwIllustration, defStyleAttr, 0);
-            mAspectRatio = a.getFloat(R.styleable.SuwIllustration_suwAspectRatio, 0.0f);
-            a.recycle();
-        }
-        // Number of pixels of the 8dp baseline grid as defined in material design specs
-        mBaselineGridSize = getResources().getDisplayMetrics().density * 8;
-        setWillNotDraw(false);
+  /**
+   * Set the aspect ratio reserved for the illustration. This overrides the top padding of the view
+   * according to the width of this view and the aspect ratio. Children views will start being laid
+   * out below this aspect ratio.
+   *
+   * @param aspectRatio A float value specifying the aspect ratio (= width / height). 0 to not
+   *     override the top padding.
+   */
+  public void setAspectRatio(float aspectRatio) {
+    this.aspectRatio = aspectRatio;
+    invalidate();
+    requestLayout();
+  }
+
+  @Override
+  @Deprecated
+  public void setForeground(Drawable d) {
+    setIllustration(d);
+  }
+
+  @Override
+  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+    if (aspectRatio != 0.0f) {
+      int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
+      int illustrationHeight = (int) (parentWidth / aspectRatio);
+      illustrationHeight = (int) (illustrationHeight - (illustrationHeight % baselineGridSize));
+      setPadding(0, illustrationHeight, 0, 0);
     }
-
-    /**
-     * The background will be drawn to fill up the rest of the view. It will also be scaled by the
-     * same amount as the foreground so their textures look the same.
-     */
-    // Override the deprecated setBackgroundDrawable method to support API < 16. View.setBackground
-    // forwards to setBackgroundDrawable in the framework implementation.
-    @SuppressWarnings("deprecation")
-    @Override
-    public void setBackgroundDrawable(Drawable background) {
-        if (background == mBackground) {
-            return;
-        }
-        mBackground = background;
-        invalidate();
-        requestLayout();
+    if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+      //noinspection AndroidLintInlinedApi
+      setOutlineProvider(ViewOutlineProvider.BOUNDS);
     }
+    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+  }
 
-    /**
-     * Sets the drawable used as the illustration. The drawable is expected to have intrinsic
-     * width and height defined and will be scaled to fit the width of the view.
-     */
-    public void setIllustration(Drawable illustration) {
-        if (illustration == mIllustration) {
-            return;
-        }
-        mIllustration = illustration;
-        invalidate();
-        requestLayout();
+  @Override
+  protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+    final int layoutWidth = right - left;
+    final int layoutHeight = bottom - top;
+    if (illustration != null) {
+      int intrinsicWidth = illustration.getIntrinsicWidth();
+      int intrinsicHeight = illustration.getIntrinsicHeight();
+
+      viewBounds.set(0, 0, layoutWidth, layoutHeight);
+      if (aspectRatio != 0f) {
+        scale = layoutWidth / (float) intrinsicWidth;
+        intrinsicWidth = layoutWidth;
+        intrinsicHeight = (int) (intrinsicHeight * scale);
+      }
+      Gravity.apply(
+          Gravity.FILL_HORIZONTAL | Gravity.TOP,
+          intrinsicWidth,
+          intrinsicHeight,
+          viewBounds,
+          illustrationBounds);
+      illustration.setBounds(illustrationBounds);
     }
-
-    /**
-     * Set the aspect ratio reserved for the illustration. This overrides the top padding of the
-     * view according to the width of this view and the aspect ratio. Children views will start
-     * being laid out below this aspect ratio.
-     *
-     * @param aspectRatio A float value specifying the aspect ratio (= width / height). 0 to not
-     *                    override the top padding.
-     */
-    public void setAspectRatio(float aspectRatio) {
-        mAspectRatio = aspectRatio;
-        invalidate();
-        requestLayout();
+    if (background != null) {
+      // Scale the background bounds by the same scale to compensate for the scale done to the
+      // canvas in onDraw.
+      background.setBounds(
+          0,
+          0,
+          (int) Math.ceil(layoutWidth / scale),
+          (int) Math.ceil((layoutHeight - illustrationBounds.height()) / scale));
     }
+    super.onLayout(changed, left, top, right, bottom);
+  }
 
-    @Override
-    @Deprecated
-    public void setForeground(Drawable d) {
-        setIllustration(d);
+  @Override
+  public void onDraw(Canvas canvas) {
+    if (background != null) {
+      // Draw the background filling parts not covered by the illustration
+      canvas.save();
+      canvas.translate(0, illustrationBounds.height());
+      // Scale the background so its size matches the foreground
+      canvas.scale(scale, scale, 0, 0);
+      if (VERSION.SDK_INT > VERSION_CODES.JELLY_BEAN_MR1
+          && shouldMirrorDrawable(background, getLayoutDirection())) {
+        // Flip the illustration for RTL layouts
+        canvas.scale(-1, 1);
+        canvas.translate(-background.getBounds().width(), 0);
+      }
+      background.draw(canvas);
+      canvas.restore();
     }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        if (mAspectRatio != 0.0f) {
-            int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
-            int illustrationHeight = (int) (parentWidth / mAspectRatio);
-            illustrationHeight =
-                    (int) (illustrationHeight - (illustrationHeight % mBaselineGridSize));
-            setPadding(0, illustrationHeight, 0, 0);
-        }
-        if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
-            //noinspection AndroidLintInlinedApi
-            setOutlineProvider(ViewOutlineProvider.BOUNDS);
-        }
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+    if (illustration != null) {
+      canvas.save();
+      if (VERSION.SDK_INT > VERSION_CODES.JELLY_BEAN_MR1
+          && shouldMirrorDrawable(illustration, getLayoutDirection())) {
+        // Flip the illustration for RTL layouts
+        canvas.scale(-1, 1);
+        canvas.translate(-illustrationBounds.width(), 0);
+      }
+      // Draw the illustration
+      illustration.draw(canvas);
+      canvas.restore();
     }
+    super.onDraw(canvas);
+  }
 
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        final int layoutWidth = right - left;
-        final int layoutHeight = bottom - top;
-        if (mIllustration != null) {
-            int intrinsicWidth = mIllustration.getIntrinsicWidth();
-            int intrinsicHeight = mIllustration.getIntrinsicHeight();
-
-            mViewBounds.set(0, 0, layoutWidth, layoutHeight);
-            if (mAspectRatio != 0f) {
-                mScale = layoutWidth / (float) intrinsicWidth;
-                intrinsicWidth = layoutWidth;
-                intrinsicHeight = (int) (intrinsicHeight * mScale);
-            }
-            Gravity.apply(Gravity.FILL_HORIZONTAL | Gravity.TOP, intrinsicWidth,
-                    intrinsicHeight, mViewBounds, mIllustrationBounds);
-            mIllustration.setBounds(mIllustrationBounds);
-        }
-        if (mBackground != null) {
-            // Scale the background bounds by the same scale to compensate for the scale done to the
-            // canvas in onDraw.
-            mBackground.setBounds(0, 0, (int) Math.ceil(layoutWidth / mScale),
-                    (int) Math.ceil((layoutHeight - mIllustrationBounds.height()) / mScale));
-        }
-        super.onLayout(changed, left, top, right, bottom);
+  private boolean shouldMirrorDrawable(Drawable drawable, int layoutDirection) {
+    if (layoutDirection == LayoutDirection.RTL) {
+      if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
+        return drawable.isAutoMirrored();
+      } else if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
+        final int flags = getContext().getApplicationInfo().flags;
+        //noinspection AndroidLintInlinedApi
+        return (flags & ApplicationInfo.FLAG_SUPPORTS_RTL) != 0;
+      }
     }
-
-    @Override
-    public void onDraw(Canvas canvas) {
-        if (mBackground != null) {
-            // Draw the background filling parts not covered by the illustration
-            canvas.save();
-            canvas.translate(0, mIllustrationBounds.height());
-            // Scale the background so its size matches the foreground
-            canvas.scale(mScale, mScale, 0, 0);
-            if (VERSION.SDK_INT > VERSION_CODES.JELLY_BEAN_MR1 &&
-                    shouldMirrorDrawable(mBackground, getLayoutDirection())) {
-                // Flip the illustration for RTL layouts
-                canvas.scale(-1, 1);
-                canvas.translate(-mBackground.getBounds().width(), 0);
-            }
-            mBackground.draw(canvas);
-            canvas.restore();
-        }
-        if (mIllustration != null) {
-            canvas.save();
-            if (VERSION.SDK_INT > VERSION_CODES.JELLY_BEAN_MR1 &&
-                    shouldMirrorDrawable(mIllustration, getLayoutDirection())) {
-                // Flip the illustration for RTL layouts
-                canvas.scale(-1, 1);
-                canvas.translate(-mIllustrationBounds.width(), 0);
-            }
-            // Draw the illustration
-            mIllustration.draw(canvas);
-            canvas.restore();
-        }
-        super.onDraw(canvas);
-    }
-
-    private boolean shouldMirrorDrawable(Drawable drawable, int layoutDirection) {
-        if (layoutDirection == LayoutDirection.RTL) {
-            if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
-                return drawable.isAutoMirrored();
-            } else if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
-                final int flags = getContext().getApplicationInfo().flags;
-                //noinspection AndroidLintInlinedApi
-                return (flags & ApplicationInfo.FLAG_SUPPORTS_RTL) != 0;
-            }
-        }
-        return false;
-    }
+    return false;
+  }
 }
diff --git a/library/main/src/com/android/setupwizardlib/view/IllustrationVideoView.java b/library/main/src/com/android/setupwizardlib/view/IllustrationVideoView.java
index 53149ea..375ee18 100644
--- a/library/main/src/com/android/setupwizardlib/view/IllustrationVideoView.java
+++ b/library/main/src/com/android/setupwizardlib/view/IllustrationVideoView.java
@@ -22,18 +22,23 @@
 import android.graphics.SurfaceTexture;
 import android.graphics.drawable.Animatable;
 import android.media.MediaPlayer;
+import android.media.MediaPlayer.OnErrorListener;
+import android.media.MediaPlayer.OnInfoListener;
+import android.media.MediaPlayer.OnPreparedListener;
+import android.media.MediaPlayer.OnSeekCompleteListener;
+import android.net.Uri;
 import android.os.Build.VERSION_CODES;
+import androidx.annotation.Nullable;
+import androidx.annotation.RawRes;
+import androidx.annotation.VisibleForTesting;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.Surface;
 import android.view.TextureView;
+import android.view.TextureView.SurfaceTextureListener;
 import android.view.View;
-
-import androidx.annotation.Nullable;
-import androidx.annotation.RawRes;
-import androidx.annotation.VisibleForTesting;
-
 import com.android.setupwizardlib.R;
+import java.io.IOException;
 
 /**
  * A view for displaying videos in a continuous loop (without audio). This is typically used for
@@ -44,255 +49,295 @@
  * 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]
+ * 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 {
+public class IllustrationVideoView extends TextureView
+    implements Animatable,
+        SurfaceTextureListener,
+        OnPreparedListener,
+        OnSeekCompleteListener,
+        OnInfoListener,
+        OnErrorListener {
 
-    private static final String TAG = "IllustrationVideoView";
+  private static final String TAG = "IllustrationVideoView";
 
-    protected float mAspectRatio = 1.0f; // initial guess until we know
+  protected float mAspectRatio = 1.0f; // initial guess until we know
 
-    @Nullable // Can be null when media player fails to initialize
-    protected MediaPlayer mMediaPlayer;
+  @Nullable // Can be null when media player fails to initialize
+  protected MediaPlayer mMediaPlayer;
 
-    private @RawRes int mVideoResId = 0;
+  private @RawRes int videoResId = 0;
 
-    @VisibleForTesting Surface mSurface;
+  private String videoResPackageName;
 
-    protected int mWindowVisibility;
+  @VisibleForTesting Surface surface;
 
-    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();
+  private boolean prepared;
 
-        // 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);
+  public IllustrationVideoView(Context context, AttributeSet attrs) {
+    super(context, attrs);
+    final TypedArray a =
+        context.obtainStyledAttributes(attrs, R.styleable.SuwIllustrationVideoView);
+    final int videoResId = a.getResourceId(R.styleable.SuwIllustrationVideoView_suwVideo, 0);
+    a.recycle();
+    setVideoResource(videoResId);
 
-        setSurfaceTextureListener(this);
+    // 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);
     }
 
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        int width = MeasureSpec.getSize(widthMeasureSpec);
-        int height = MeasureSpec.getSize(heightMeasureSpec);
+    super.onMeasure(
+        MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
+        MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
+  }
 
-        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);
-        }
+  /**
+   * Set the video and video package name to be played by this view.
+   *
+   * @param videoResId Resource ID of the video, typically an MP4 under res/raw.
+   * @param videoResPackageName The package name of videoResId.
+   */
+  public void setVideoResource(@RawRes int videoResId, String videoResPackageName) {
+    if (videoResId != this.videoResId
+        || (videoResPackageName != null && !videoResPackageName.equals(this.videoResPackageName))) {
+      this.videoResId = videoResId;
+      this.videoResPackageName = videoResPackageName;
+      createMediaPlayer();
+    }
+  }
 
-        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) {
+    setVideoResource(resId, getContext().getPackageName());
+  }
+
+  @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.
+   */
+  protected void createMediaPlayer() {
+    if (mMediaPlayer != null) {
+      mMediaPlayer.release();
+    }
+    if (surface == null || videoResId == 0) {
+      return;
     }
 
-    /**
-     * 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();
-        }
+    mMediaPlayer = new MediaPlayer();
+
+    mMediaPlayer.setSurface(surface);
+    mMediaPlayer.setOnPreparedListener(this);
+    mMediaPlayer.setOnSeekCompleteListener(this);
+    mMediaPlayer.setOnInfoListener(this);
+    mMediaPlayer.setOnErrorListener(this);
+
+    setVideoResourceInternal(videoResId, videoResPackageName);
+  }
+
+  private void setVideoResourceInternal(@RawRes int videoRes, String videoResPackageName) {
+    Uri uri = Uri.parse("android.resource://" + videoResPackageName + "/" + videoRes);
+    try {
+      mMediaPlayer.setDataSource(getContext(), uri, null);
+      mMediaPlayer.prepareAsync();
+    } catch (IOException e) {
+      Log.wtf(TAG, "Unable to set data source", e);
     }
+  }
 
-    @Override
-    public void onWindowFocusChanged(boolean hasWindowFocus) {
-        super.onWindowFocusChanged(hasWindowFocus);
-        if (hasWindowFocus) {
-            start();
-        } else {
-            stop();
-        }
+  protected void createSurface() {
+    if (surface != null) {
+      surface.release();
+      surface = null;
     }
-
-    /**
-     * 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.
-     */
-    protected void createMediaPlayer() {
-        if (mMediaPlayer != null) {
-            mMediaPlayer.release();
-        }
-        if (mSurface == null || mVideoResId == 0) {
-            return;
-        }
-
-        mMediaPlayer = MediaPlayer.create(getContext(), mVideoResId);
-
-        if (mMediaPlayer != null) {
-            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();
-            }
-        } else {
-            Log.wtf(TAG, "Unable to initialize media player for video view");
-        }
-        if (mWindowVisibility == View.VISIBLE) {
-            start();
-        }
+    // Reattach only if it has been previously released
+    SurfaceTexture surfaceTexture = getSurfaceTexture();
+    if (surfaceTexture != null) {
+      setVisibility(View.INVISIBLE);
+      surface = new Surface(surfaceTexture);
     }
+  }
 
-    protected void createSurface() {
-        if (mSurface != null) {
-            mSurface.release();
-            mSurface = null;
-        }
-        // Reattach only if it has been previously released
-        SurfaceTexture surfaceTexture = getSurfaceTexture();
-        if (surfaceTexture != null) {
-            setVisibility(View.INVISIBLE);
-            mSurface = new Surface(surfaceTexture);
-        }
+  @Override
+  protected void onWindowVisibilityChanged(int visibility) {
+    super.onWindowVisibilityChanged(visibility);
+    if (visibility == View.VISIBLE) {
+      reattach();
+    } else {
+      release();
     }
+  }
 
-    @Override
-    protected void onWindowVisibilityChanged(int visibility) {
-        super.onWindowVisibilityChanged(visibility);
-        mWindowVisibility = visibility;
-        if (visibility == View.VISIBLE) {
-            reattach();
-        } else {
-            release();
-        }
+  /**
+   * 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.release();
+      mMediaPlayer = null;
+      prepared = false;
     }
-
-    /**
-     * Whether the media player should play the video in a continuous loop. The default value is
-     * true.
-     */
-    protected boolean shouldLoop() {
-        return true;
+    if (surface != null) {
+      surface.release();
+      surface = null;
     }
+  }
 
-    /**
-     * 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;
-        }
+  private void reattach() {
+    if (surface == null) {
+      initVideo();
     }
+  }
 
-    private void reattach() {
-        if (mSurface == null) {
-            initVideo();
-        }
+  private void initVideo() {
+    if (getWindowVisibility() != View.VISIBLE) {
+      return;
     }
-
-    private void initVideo() {
-        if (mWindowVisibility != View.VISIBLE) {
-            return;
-        }
-        createSurface();
-        if (mSurface != null) {
-            createMediaPlayer();
-        } else {
-            Log.w("IllustrationVideoView", "Surface creation failed");
-        }
+    createSurface();
+    if (surface != null) {
+      createMediaPlayer();
+    } else {
+      Log.w(TAG, "Surface creation failed");
     }
+  }
 
-    protected void onRenderingStart() {
+  protected void onRenderingStart() {}
+
+  /* SurfaceTextureListener methods */
+
+  @Override
+  public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {
+    // Keep the view hidden until video starts
+    setVisibility(View.INVISIBLE);
+    initVideo();
+  }
+
+  @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 (prepared && mMediaPlayer != null && !mMediaPlayer.isPlaying()) {
+      mMediaPlayer.start();
     }
+  }
 
-    /* SurfaceTextureListener methods */
-
-    @Override
-    public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {
-        // Keep the view hidden until video starts
-        setVisibility(View.INVISIBLE);
-        initVideo();
+  @Override
+  public void stop() {
+    if (prepared && mMediaPlayer != null) {
+      mMediaPlayer.pause();
     }
+  }
 
-    @Override
-    public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int width, int height) {
+  @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);
+      onRenderingStart();
     }
+    return false;
+  }
 
-    @Override
-    public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
-        release();
-        return true;
+  @Override
+  public void onPrepared(MediaPlayer mp) {
+    prepared = true;
+    mp.setLooping(shouldLoop());
+
+    float aspectRatio = 0.0f;
+    if (mp.getVideoWidth() > 0 && mp.getVideoHeight() > 0) {
+      aspectRatio = (float) mp.getVideoHeight() / mp.getVideoWidth();
+    } else {
+      Log.w(TAG, "Unexpected video size=" + mp.getVideoWidth() + "x" + mp.getVideoHeight());
     }
-
-    @Override
-    public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
+    if (Float.compare(mAspectRatio, aspectRatio) != 0) {
+      mAspectRatio = aspectRatio;
+      requestLayout();
     }
-
-    /* Animatable methods */
-
-    @Override
-    public void start() {
-        if (mMediaPlayer != null && !mMediaPlayer.isPlaying()) {
-            mMediaPlayer.start();
-        }
+    if (getWindowVisibility() == View.VISIBLE) {
+      start();
     }
+  }
 
-    @Override
-    public void stop() {
-        if (mMediaPlayer != null) {
-            mMediaPlayer.pause();
-        }
+  @Override
+  public void onSeekComplete(MediaPlayer mp) {
+    if (isPrepared()) {
+      mp.start();
+    } else {
+      Log.wtf(TAG, "Seek complete but media player not prepared");
     }
+  }
 
-    @Override
-    public boolean isRunning() {
-        return mMediaPlayer != null && mMediaPlayer.isPlaying();
-    }
+  public int getCurrentPosition() {
+    return mMediaPlayer == null ? 0 : mMediaPlayer.getCurrentPosition();
+  }
 
-    /* MediaPlayer callbacks */
+  protected boolean isPrepared() {
+    return prepared;
+  }
 
-    @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);
-            onRenderingStart();
-        }
-        return false;
-    }
-
-    @Override
-    public void onPrepared(MediaPlayer mp) {
-        mp.setLooping(shouldLoop());
-    }
-
-    @Override
-    public void onSeekComplete(MediaPlayer mp) {
-        mp.start();
-    }
-
-    public int getCurrentPosition() {
-        return mMediaPlayer == null ? 0 : mMediaPlayer.getCurrentPosition();
-    }
+  @Override
+  public boolean onError(MediaPlayer mediaPlayer, int what, int extra) {
+    Log.w(TAG, "MediaPlayer error. what=" + what + " extra=" + extra);
+    return false;
+  }
 }
diff --git a/library/main/src/com/android/setupwizardlib/view/IntrinsicSizeFrameLayout.java b/library/main/src/com/android/setupwizardlib/view/IntrinsicSizeFrameLayout.java
index 02fdcc7..c6a38f6 100644
--- a/library/main/src/com/android/setupwizardlib/view/IntrinsicSizeFrameLayout.java
+++ b/library/main/src/com/android/setupwizardlib/view/IntrinsicSizeFrameLayout.java
@@ -22,71 +22,71 @@
 import android.os.Build.VERSION_CODES;
 import android.util.AttributeSet;
 import android.widget.FrameLayout;
-
 import com.android.setupwizardlib.R;
 
 /**
  * A FrameLayout subclass that has an "intrinsic size", which is the size it wants to be if that is
- * within the constraints given by the parent. The intrinsic size can be set with the
- * {@code android:width} and {@code android:height} attributes in XML.
+ * within the constraints given by the parent. The intrinsic size can be set with the {@code
+ * android:width} and {@code android:height} attributes in XML.
  *
- * Note that for the intrinsic size to be meaningful, {@code android:layout_width} and/or
- * {@code android:layout_height} will need to be {@code wrap_content}.
+ * <p>Note that for the intrinsic size to be meaningful, {@code android:layout_width} and/or {@code
+ * android:layout_height} will need to be {@code wrap_content}.
  */
 public class IntrinsicSizeFrameLayout extends FrameLayout {
 
-    private int mIntrinsicHeight = 0;
-    private int mIntrinsicWidth = 0;
+  private int intrinsicHeight = 0;
+  private int intrinsicWidth = 0;
 
-    public IntrinsicSizeFrameLayout(Context context) {
-        super(context);
-        init(context, null, 0);
-    }
+  public IntrinsicSizeFrameLayout(Context context) {
+    super(context);
+    init(context, null, 0);
+  }
 
-    public IntrinsicSizeFrameLayout(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        init(context, attrs, 0);
-    }
+  public IntrinsicSizeFrameLayout(Context context, AttributeSet attrs) {
+    super(context, attrs);
+    init(context, attrs, 0);
+  }
 
-    @TargetApi(VERSION_CODES.HONEYCOMB)
-    public IntrinsicSizeFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-        init(context, attrs, defStyleAttr);
-    }
+  @TargetApi(VERSION_CODES.HONEYCOMB)
+  public IntrinsicSizeFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+    super(context, attrs, defStyleAttr);
+    init(context, attrs, defStyleAttr);
+  }
 
-    private void init(Context context, AttributeSet attrs, int defStyleAttr) {
-        final TypedArray a = context.obtainStyledAttributes(attrs,
-                R.styleable.SuwIntrinsicSizeFrameLayout, defStyleAttr, 0);
-        mIntrinsicHeight =
-                a.getDimensionPixelSize(R.styleable.SuwIntrinsicSizeFrameLayout_android_height, 0);
-        mIntrinsicWidth =
-                a.getDimensionPixelSize(R.styleable.SuwIntrinsicSizeFrameLayout_android_width, 0);
-        a.recycle();
-    }
+  private void init(Context context, AttributeSet attrs, int defStyleAttr) {
+    final TypedArray a =
+        context.obtainStyledAttributes(
+            attrs, R.styleable.SuwIntrinsicSizeFrameLayout, defStyleAttr, 0);
+    intrinsicHeight =
+        a.getDimensionPixelSize(R.styleable.SuwIntrinsicSizeFrameLayout_android_height, 0);
+    intrinsicWidth =
+        a.getDimensionPixelSize(R.styleable.SuwIntrinsicSizeFrameLayout_android_width, 0);
+    a.recycle();
+  }
 
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        super.onMeasure(getIntrinsicMeasureSpec(widthMeasureSpec, mIntrinsicWidth),
-                getIntrinsicMeasureSpec(heightMeasureSpec, mIntrinsicHeight));
-    }
+  @Override
+  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+    super.onMeasure(
+        getIntrinsicMeasureSpec(widthMeasureSpec, intrinsicWidth),
+        getIntrinsicMeasureSpec(heightMeasureSpec, intrinsicHeight));
+  }
 
-    private int getIntrinsicMeasureSpec(int measureSpec, int intrinsicSize) {
-        if (intrinsicSize <= 0) {
-            // Intrinsic size is not set, just return the original spec
-            return measureSpec;
-        }
-        final int mode = MeasureSpec.getMode(measureSpec);
-        final int size = MeasureSpec.getSize(measureSpec);
-        if (mode == MeasureSpec.UNSPECIFIED) {
-            // Parent did not give any constraint, so we'll be the intrinsic size
-            return MeasureSpec.makeMeasureSpec(mIntrinsicHeight, MeasureSpec.EXACTLY);
-        } else if (mode == MeasureSpec.AT_MOST) {
-            // If intrinsic size is within parents constraint, take the intrinsic size.
-            // Otherwise take the parents size because that's closest to the intrinsic size.
-            return MeasureSpec.makeMeasureSpec(Math.min(size, mIntrinsicHeight),
-                    MeasureSpec.EXACTLY);
-        }
-        // Parent specified EXACTLY, or in all other cases, just return the original spec
-        return measureSpec;
+  private int getIntrinsicMeasureSpec(int measureSpec, int intrinsicSize) {
+    if (intrinsicSize <= 0) {
+      // Intrinsic size is not set, just return the original spec
+      return measureSpec;
     }
+    final int mode = MeasureSpec.getMode(measureSpec);
+    final int size = MeasureSpec.getSize(measureSpec);
+    if (mode == MeasureSpec.UNSPECIFIED) {
+      // Parent did not give any constraint, so we'll be the intrinsic size
+      return MeasureSpec.makeMeasureSpec(intrinsicHeight, MeasureSpec.EXACTLY);
+    } else if (mode == MeasureSpec.AT_MOST) {
+      // If intrinsic size is within parents constraint, take the intrinsic size.
+      // Otherwise take the parents size because that's closest to the intrinsic size.
+      return MeasureSpec.makeMeasureSpec(Math.min(size, intrinsicHeight), MeasureSpec.EXACTLY);
+    }
+    // Parent specified EXACTLY, or in all other cases, just return the original spec
+    return measureSpec;
+  }
 }
diff --git a/library/main/src/com/android/setupwizardlib/view/NavigationBar.java b/library/main/src/com/android/setupwizardlib/view/NavigationBar.java
index 9971bac..1044e56 100644
--- a/library/main/src/com/android/setupwizardlib/view/NavigationBar.java
+++ b/library/main/src/com/android/setupwizardlib/view/NavigationBar.java
@@ -21,14 +21,12 @@
 import android.content.res.TypedArray;
 import android.graphics.Color;
 import android.os.Build.VERSION_CODES;
+import androidx.annotation.StyleableRes;
 import android.util.AttributeSet;
 import android.view.ContextThemeWrapper;
 import android.view.View;
 import android.widget.Button;
 import android.widget.LinearLayout;
-
-import androidx.annotation.StyleableRes;
-
 import com.android.setupwizardlib.R;
 
 /**
@@ -40,104 +38,105 @@
  */
 public class NavigationBar extends LinearLayout implements View.OnClickListener {
 
-    /**
-     * An interface to listen to events of the navigation bar, namely when the user clicks on the
-     * back or next button.
-     */
-    public interface NavigationBarListener {
-        void onNavigateBack();
-        void onNavigateNext();
-    }
+  /**
+   * An interface to listen to events of the navigation bar, namely when the user clicks on the back
+   * or next button.
+   */
+  public interface NavigationBarListener {
+    void onNavigateBack();
 
-    private static int getNavbarTheme(Context context) {
-        // Normally we can automatically guess the theme by comparing the foreground color against
-        // the background color. But we also allow specifying explicitly using suwNavBarTheme.
-        TypedArray attributes = context.obtainStyledAttributes(
-                new int[] {
-                        R.attr.suwNavBarTheme,
-                        android.R.attr.colorForeground,
-                        android.R.attr.colorBackground });
-        @StyleableRes int suwNavBarTheme = 0;
-        @StyleableRes int colorForeground = 1;
-        @StyleableRes int colorBackground = 2;
-        int theme = attributes.getResourceId(suwNavBarTheme, 0);
-        if (theme == 0) {
-            // Compare the value of the foreground against the background color to see if current
-            // theme is light-on-dark or dark-on-light.
-            float[] foregroundHsv = new float[3];
-            float[] backgroundHsv = new float[3];
-            Color.colorToHSV(attributes.getColor(colorForeground, 0), foregroundHsv);
-            Color.colorToHSV(attributes.getColor(colorBackground, 0), backgroundHsv);
-            boolean isDarkBg = foregroundHsv[2] > backgroundHsv[2];
-            theme = isDarkBg ? R.style.SuwNavBarThemeDark : R.style.SuwNavBarThemeLight;
-        }
-        attributes.recycle();
-        return theme;
-    }
+    void onNavigateNext();
+  }
 
-    private static Context getThemedContext(Context context) {
-        final int theme = getNavbarTheme(context);
-        return new ContextThemeWrapper(context, theme);
+  private static int getNavbarTheme(Context context) {
+    // Normally we can automatically guess the theme by comparing the foreground color against
+    // the background color. But we also allow specifying explicitly using suwNavBarTheme.
+    TypedArray attributes =
+        context.obtainStyledAttributes(
+            new int[] {
+              R.attr.suwNavBarTheme, android.R.attr.colorForeground, android.R.attr.colorBackground
+            });
+    @StyleableRes int suwNavBarTheme = 0;
+    @StyleableRes int colorForeground = 1;
+    @StyleableRes int colorBackground = 2;
+    int theme = attributes.getResourceId(suwNavBarTheme, 0);
+    if (theme == 0) {
+      // Compare the value of the foreground against the background color to see if current
+      // theme is light-on-dark or dark-on-light.
+      float[] foregroundHsv = new float[3];
+      float[] backgroundHsv = new float[3];
+      Color.colorToHSV(attributes.getColor(colorForeground, 0), foregroundHsv);
+      Color.colorToHSV(attributes.getColor(colorBackground, 0), backgroundHsv);
+      boolean isDarkBg = foregroundHsv[2] > backgroundHsv[2];
+      theme = isDarkBg ? R.style.SuwNavBarThemeDark : R.style.SuwNavBarThemeLight;
     }
+    attributes.recycle();
+    return theme;
+  }
 
-    private Button mNextButton;
-    private Button mBackButton;
-    private Button mMoreButton;
-    private NavigationBarListener mListener;
+  private static Context getThemedContext(Context context) {
+    final int theme = getNavbarTheme(context);
+    return new ContextThemeWrapper(context, theme);
+  }
 
-    public NavigationBar(Context context) {
-        super(getThemedContext(context));
-        init();
+  private Button nextButton;
+  private Button backButton;
+  private Button moreButton;
+  private NavigationBarListener listener;
+
+  public NavigationBar(Context context) {
+    super(getThemedContext(context));
+    init();
+  }
+
+  public NavigationBar(Context context, AttributeSet attrs) {
+    super(getThemedContext(context), attrs);
+    init();
+  }
+
+  @TargetApi(VERSION_CODES.HONEYCOMB)
+  public NavigationBar(Context context, AttributeSet attrs, int defStyleAttr) {
+    super(getThemedContext(context), attrs, defStyleAttr);
+    init();
+  }
+
+  // All the constructors delegate to this init method. The 3-argument constructor is not
+  // available in LinearLayout before v11, so call super with the exact same arguments.
+  private void init() {
+    View.inflate(getContext(), R.layout.suw_navbar_view, this);
+    nextButton = (Button) findViewById(R.id.suw_navbar_next);
+    backButton = (Button) findViewById(R.id.suw_navbar_back);
+    moreButton = (Button) findViewById(R.id.suw_navbar_more);
+  }
+
+  public Button getBackButton() {
+    return backButton;
+  }
+
+  public Button getNextButton() {
+    return nextButton;
+  }
+
+  public Button getMoreButton() {
+    return moreButton;
+  }
+
+  public void setNavigationBarListener(NavigationBarListener listener) {
+    this.listener = listener;
+    if (this.listener != null) {
+      getBackButton().setOnClickListener(this);
+      getNextButton().setOnClickListener(this);
     }
+  }
 
-    public NavigationBar(Context context, AttributeSet attrs) {
-        super(getThemedContext(context), attrs);
-        init();
+  @Override
+  public void onClick(View view) {
+    if (listener != null) {
+      if (view == getBackButton()) {
+        listener.onNavigateBack();
+      } else if (view == getNextButton()) {
+        listener.onNavigateNext();
+      }
     }
-
-    @TargetApi(VERSION_CODES.HONEYCOMB)
-    public NavigationBar(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(getThemedContext(context), attrs, defStyleAttr);
-        init();
-    }
-
-    // All the constructors delegate to this init method. The 3-argument constructor is not
-    // available in LinearLayout before v11, so call super with the exact same arguments.
-    private void init() {
-        View.inflate(getContext(), R.layout.suw_navbar_view, this);
-        mNextButton = (Button) findViewById(R.id.suw_navbar_next);
-        mBackButton = (Button) findViewById(R.id.suw_navbar_back);
-        mMoreButton = (Button) findViewById(R.id.suw_navbar_more);
-    }
-
-    public Button getBackButton() {
-        return mBackButton;
-    }
-
-    public Button getNextButton() {
-        return mNextButton;
-    }
-
-    public Button getMoreButton() {
-        return mMoreButton;
-    }
-
-    public void setNavigationBarListener(NavigationBarListener listener) {
-        mListener = listener;
-        if (mListener != null) {
-            getBackButton().setOnClickListener(this);
-            getNextButton().setOnClickListener(this);
-        }
-    }
-
-    @Override
-    public void onClick(View view) {
-        if (mListener != null) {
-            if (view == getBackButton()) {
-                mListener.onNavigateBack();
-            } else if (view == getNextButton()) {
-                mListener.onNavigateNext();
-            }
-        }
-    }
+  }
 }
diff --git a/library/main/src/com/android/setupwizardlib/view/StatusBarBackgroundLayout.java b/library/main/src/com/android/setupwizardlib/view/StatusBarBackgroundLayout.java
index fe0bc8f..dd8c27f 100644
--- a/library/main/src/com/android/setupwizardlib/view/StatusBarBackgroundLayout.java
+++ b/library/main/src/com/android/setupwizardlib/view/StatusBarBackgroundLayout.java
@@ -26,7 +26,6 @@
 import android.util.AttributeSet;
 import android.view.WindowInsets;
 import android.widget.FrameLayout;
-
 import com.android.setupwizardlib.R;
 
 /**
@@ -40,74 +39,75 @@
  */
 public class StatusBarBackgroundLayout extends FrameLayout {
 
-    private Drawable mStatusBarBackground;
-    private Object mLastInsets;  // Use generic Object type for compatibility
+  private Drawable statusBarBackground;
+  private Object lastInsets; // Use generic Object type for compatibility
 
-    public StatusBarBackgroundLayout(Context context) {
-        super(context);
-        init(context, null, 0);
+  public StatusBarBackgroundLayout(Context context) {
+    super(context);
+    init(context, null, 0);
+  }
+
+  public StatusBarBackgroundLayout(Context context, AttributeSet attrs) {
+    super(context, attrs);
+    init(context, attrs, 0);
+  }
+
+  @TargetApi(VERSION_CODES.HONEYCOMB)
+  public StatusBarBackgroundLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+    super(context, attrs, defStyleAttr);
+    init(context, attrs, defStyleAttr);
+  }
+
+  private void init(Context context, AttributeSet attrs, int defStyleAttr) {
+    final TypedArray a =
+        context.obtainStyledAttributes(
+            attrs, R.styleable.SuwStatusBarBackgroundLayout, defStyleAttr, 0);
+    final Drawable statusBarBackground =
+        a.getDrawable(R.styleable.SuwStatusBarBackgroundLayout_suwStatusBarBackground);
+    setStatusBarBackground(statusBarBackground);
+    a.recycle();
+  }
+
+  @Override
+  protected void onAttachedToWindow() {
+    super.onAttachedToWindow();
+    if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+      if (lastInsets == null) {
+        requestApplyInsets();
+      }
     }
+  }
 
-    public StatusBarBackgroundLayout(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        init(context, attrs, 0);
-    }
-
-    @TargetApi(VERSION_CODES.HONEYCOMB)
-    public StatusBarBackgroundLayout(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-        init(context, attrs, defStyleAttr);
-    }
-
-    private void init(Context context, AttributeSet attrs, int defStyleAttr) {
-        final TypedArray a = context.obtainStyledAttributes(attrs,
-                R.styleable.SuwStatusBarBackgroundLayout, defStyleAttr, 0);
-        final Drawable statusBarBackground =
-                a.getDrawable(R.styleable.SuwStatusBarBackgroundLayout_suwStatusBarBackground);
-        setStatusBarBackground(statusBarBackground);
-        a.recycle();
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
-            if (mLastInsets == null) {
-                requestApplyInsets();
-            }
+  @Override
+  protected void onDraw(Canvas canvas) {
+    super.onDraw(canvas);
+    if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+      if (lastInsets != null) {
+        final int insetTop = ((WindowInsets) lastInsets).getSystemWindowInsetTop();
+        if (insetTop > 0) {
+          statusBarBackground.setBounds(0, 0, getWidth(), insetTop);
+          statusBarBackground.draw(canvas);
         }
+      }
     }
+  }
 
-    @Override
-    protected void onDraw(Canvas canvas) {
-        super.onDraw(canvas);
-        if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
-            if (mLastInsets != null) {
-                final int insetTop = ((WindowInsets) mLastInsets).getSystemWindowInsetTop();
-                if (insetTop > 0) {
-                    mStatusBarBackground.setBounds(0, 0, getWidth(), insetTop);
-                    mStatusBarBackground.draw(canvas);
-                }
-            }
-        }
+  public void setStatusBarBackground(Drawable background) {
+    statusBarBackground = background;
+    if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+      setWillNotDraw(background == null);
+      setFitsSystemWindows(background != null);
+      invalidate();
     }
+  }
 
-    public void setStatusBarBackground(Drawable background) {
-        mStatusBarBackground = background;
-        if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
-            setWillNotDraw(background == null);
-            setFitsSystemWindows(background != null);
-            invalidate();
-        }
-    }
+  public Drawable getStatusBarBackground() {
+    return statusBarBackground;
+  }
 
-    public Drawable getStatusBarBackground() {
-        return mStatusBarBackground;
-    }
-
-    @Override
-    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
-        mLastInsets = insets;
-        return super.onApplyWindowInsets(insets);
-    }
+  @Override
+  public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+    lastInsets = insets;
+    return super.onApplyWindowInsets(insets);
+  }
 }
diff --git a/library/main/src/com/android/setupwizardlib/view/StickyHeaderListView.java b/library/main/src/com/android/setupwizardlib/view/StickyHeaderListView.java
index 58274f6..c226f29 100644
--- a/library/main/src/com/android/setupwizardlib/view/StickyHeaderListView.java
+++ b/library/main/src/com/android/setupwizardlib/view/StickyHeaderListView.java
@@ -29,7 +29,6 @@
 import android.view.WindowInsets;
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.ListView;
-
 import com.android.setupwizardlib.R;
 
 /**
@@ -39,125 +38,129 @@
  * when the sticky element hits the top of the view.
  *
  * <p>There are a few things to note:
+ *
  * <ol>
  *   <li>The two supported scenarios are StickyHeaderListView -> Header (stickyContainer) -> sticky,
- *   and StickyHeaderListView -> Header (sticky). The arrow (->) represents parent/child
- *   relationship and must be immediate child.
+ *       and StickyHeaderListView -> Header (sticky). The arrow (->) represents parent/child
+ *       relationship and must be immediate child.
  *   <li>The view does not work well with padding. b/16190933
  *   <li>If fitsSystemWindows is true, then this will offset the sticking position by the height of
- *   the system decorations at the top of the screen.
+ *       the system decorations at the top of the screen.
  * </ol>
  *
  * @see StickyHeaderScrollView
  */
 public class StickyHeaderListView extends ListView {
 
-    private View mSticky;
-    private View mStickyContainer;
-    private int mStatusBarInset = 0;
-    private RectF mStickyRect = new RectF();
+  private View sticky;
+  private View stickyContainer;
+  private int statusBarInset = 0;
+  private final RectF stickyRect = new RectF();
 
-    public StickyHeaderListView(Context context) {
-        super(context);
-        init(null, android.R.attr.listViewStyle);
+  public StickyHeaderListView(Context context) {
+    super(context);
+    init(null, android.R.attr.listViewStyle);
+  }
+
+  public StickyHeaderListView(Context context, AttributeSet attrs) {
+    super(context, attrs);
+    init(attrs, android.R.attr.listViewStyle);
+  }
+
+  public StickyHeaderListView(Context context, AttributeSet attrs, int defStyleAttr) {
+    super(context, attrs, defStyleAttr);
+    init(attrs, defStyleAttr);
+  }
+
+  private void init(AttributeSet attrs, int defStyleAttr) {
+    final TypedArray a =
+        getContext()
+            .obtainStyledAttributes(attrs, R.styleable.SuwStickyHeaderListView, defStyleAttr, 0);
+    int headerResId = a.getResourceId(R.styleable.SuwStickyHeaderListView_suwHeader, 0);
+    if (headerResId != 0) {
+      LayoutInflater inflater = LayoutInflater.from(getContext());
+      View header = inflater.inflate(headerResId, this, false);
+      addHeaderView(header, null, false);
     }
+    a.recycle();
+  }
 
-    public StickyHeaderListView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        init(attrs, android.R.attr.listViewStyle);
+  @Override
+  protected void onLayout(boolean changed, int l, int t, int r, int b) {
+    super.onLayout(changed, l, t, r, b);
+    if (sticky == null) {
+      updateStickyView();
     }
+  }
 
-    public StickyHeaderListView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-        init(attrs, defStyleAttr);
+  public void updateStickyView() {
+    sticky = findViewWithTag("sticky");
+    stickyContainer = findViewWithTag("stickyContainer");
+  }
+
+  @Override
+  public boolean dispatchTouchEvent(MotionEvent ev) {
+    if (stickyRect.contains(ev.getX(), ev.getY())) {
+      ev.offsetLocation(-stickyRect.left, -stickyRect.top);
+      return stickyContainer.dispatchTouchEvent(ev);
+    } else {
+      return super.dispatchTouchEvent(ev);
     }
+  }
 
-    private void init(AttributeSet attrs, int defStyleAttr) {
-        final TypedArray a = getContext().obtainStyledAttributes(attrs,
-                R.styleable.SuwStickyHeaderListView, defStyleAttr, 0);
-        int headerResId = a.getResourceId(R.styleable.SuwStickyHeaderListView_suwHeader, 0);
-        if (headerResId != 0) {
-            LayoutInflater inflater = LayoutInflater.from(getContext());
-            View header = inflater.inflate(headerResId, this, false);
-            addHeaderView(header, null, false);
-        }
-        a.recycle();
+  @Override
+  public void draw(Canvas canvas) {
+    super.draw(canvas);
+    if (sticky != null) {
+      final int saveCount = canvas.save();
+      // The view to draw when sticking to the top
+      final View drawTarget = stickyContainer != null ? stickyContainer : sticky;
+      // The offset to draw the view at when sticky
+      final int drawOffset = stickyContainer != null ? sticky.getTop() : 0;
+      // Position of the draw target, relative to the outside of the scrollView
+      final int drawTop = drawTarget.getTop();
+      if (drawTop + drawOffset < statusBarInset || !drawTarget.isShown()) {
+        // ListView does not translate the canvas, so we can simply draw at the top
+        stickyRect.set(
+            0,
+            -drawOffset + statusBarInset,
+            drawTarget.getWidth(),
+            drawTarget.getHeight() - drawOffset + statusBarInset);
+        canvas.translate(0, stickyRect.top);
+        canvas.clipRect(0, 0, drawTarget.getWidth(), drawTarget.getHeight());
+        drawTarget.draw(canvas);
+      } else {
+        stickyRect.setEmpty();
+      }
+      canvas.restoreToCount(saveCount);
     }
+  }
 
-    @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        super.onLayout(changed, l, t, r, b);
-        if (mSticky == null) {
-            updateStickyView();
-        }
+  @Override
+  @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+  public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+    if (getFitsSystemWindows()) {
+      statusBarInset = insets.getSystemWindowInsetTop();
+      insets.replaceSystemWindowInsets(
+          insets.getSystemWindowInsetLeft(),
+          0, /* top */
+          insets.getSystemWindowInsetRight(),
+          insets.getSystemWindowInsetBottom());
     }
+    return insets;
+  }
 
-    public void updateStickyView() {
-        mSticky = findViewWithTag("sticky");
-        mStickyContainer = findViewWithTag("stickyContainer");
+  @Override
+  public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+    super.onInitializeAccessibilityEvent(event);
+
+    // Decoration-only headers should not count as an item for accessibility, adjust the
+    // accessibility event to account for that.
+    final int numberOfHeaders = sticky != null ? 1 : 0;
+    event.setItemCount(event.getItemCount() - numberOfHeaders);
+    event.setFromIndex(Math.max(event.getFromIndex() - numberOfHeaders, 0));
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+      event.setToIndex(Math.max(event.getToIndex() - numberOfHeaders, 0));
     }
-
-    @Override
-    public boolean dispatchTouchEvent(MotionEvent ev) {
-        if (mStickyRect.contains(ev.getX(), ev.getY())) {
-            ev.offsetLocation(-mStickyRect.left, -mStickyRect.top);
-            return mStickyContainer.dispatchTouchEvent(ev);
-        } else {
-            return super.dispatchTouchEvent(ev);
-        }
-    }
-
-    @Override
-    public void draw(Canvas canvas) {
-        super.draw(canvas);
-        if (mSticky != null) {
-            final int saveCount = canvas.save();
-            // The view to draw when sticking to the top
-            final View drawTarget = mStickyContainer != null ? mStickyContainer : mSticky;
-            // The offset to draw the view at when sticky
-            final int drawOffset = mStickyContainer != null ? mSticky.getTop() : 0;
-            // Position of the draw target, relative to the outside of the scrollView
-            final int drawTop = drawTarget.getTop();
-            if (drawTop + drawOffset < mStatusBarInset || !drawTarget.isShown()) {
-                // ListView does not translate the canvas, so we can simply draw at the top
-                mStickyRect.set(0, -drawOffset + mStatusBarInset, drawTarget.getWidth(),
-                        drawTarget.getHeight() - drawOffset + mStatusBarInset);
-                canvas.translate(0, mStickyRect.top);
-                canvas.clipRect(0, 0, drawTarget.getWidth(), drawTarget.getHeight());
-                drawTarget.draw(canvas);
-            } else {
-                mStickyRect.setEmpty();
-            }
-            canvas.restoreToCount(saveCount);
-        }
-    }
-
-    @Override
-    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
-    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
-        if (getFitsSystemWindows()) {
-            mStatusBarInset = insets.getSystemWindowInsetTop();
-            insets.replaceSystemWindowInsets(
-                    insets.getSystemWindowInsetLeft(),
-                    0, /* top */
-                    insets.getSystemWindowInsetRight(),
-                    insets.getSystemWindowInsetBottom()
-            );
-        }
-        return insets;
-    }
-
-    @Override
-    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEvent(event);
-
-        // Decoration-only headers should not count as an item for accessibility, adjust the
-        // accessibility event to account for that.
-        final int numberOfHeaders = mSticky != null ? 1 : 0;
-        event.setItemCount(event.getItemCount() - numberOfHeaders);
-        event.setFromIndex(Math.max(event.getFromIndex() - numberOfHeaders, 0));
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
-            event.setToIndex(Math.max(event.getToIndex() - numberOfHeaders, 0));
-        }
-    }
+  }
 }
diff --git a/library/main/src/com/android/setupwizardlib/view/StickyHeaderScrollView.java b/library/main/src/com/android/setupwizardlib/view/StickyHeaderScrollView.java
index ca47446..9fd7b0c 100644
--- a/library/main/src/com/android/setupwizardlib/view/StickyHeaderScrollView.java
+++ b/library/main/src/com/android/setupwizardlib/view/StickyHeaderScrollView.java
@@ -30,12 +30,13 @@
  * drawn when the sticky element hits the top of the view.
  *
  * <p>There are a few things to note:
+ *
  * <ol>
  *   <li>The two supported scenarios are StickyHeaderScrollView -> subview (stickyContainer) ->
- *   sticky, and StickyHeaderScrollView -> container -> subview (sticky).
- *   The arrow (->) represents parent/child relationship and must be immediate child.
+ *       sticky, and StickyHeaderScrollView -> container -> subview (sticky). The arrow (->)
+ *       represents parent/child relationship and must be immediate child.
  *   <li>If fitsSystemWindows is true, then this will offset the sticking position by the height of
- *   the system decorations at the top of the screen.
+ *       the system decorations at the top of the screen.
  *   <li>For versions before Honeycomb, this will behave like a regular ScrollView.
  * </ol>
  *
@@ -43,75 +44,75 @@
  */
 public class StickyHeaderScrollView extends BottomScrollView {
 
-    private View mSticky;
-    private View mStickyContainer;
-    private int mStatusBarInset = 0;
+  private View sticky;
+  private View stickyContainer;
+  private int statusBarInset = 0;
 
-    public StickyHeaderScrollView(Context context) {
-        super(context);
+  public StickyHeaderScrollView(Context context) {
+    super(context);
+  }
+
+  public StickyHeaderScrollView(Context context, AttributeSet attrs) {
+    super(context, attrs);
+  }
+
+  public StickyHeaderScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
+    super(context, attrs, defStyleAttr);
+  }
+
+  @Override
+  protected void onLayout(boolean changed, int l, int t, int r, int b) {
+    super.onLayout(changed, l, t, r, b);
+    if (sticky == null) {
+      updateStickyView();
     }
+    updateStickyHeaderPosition();
+  }
 
-    public StickyHeaderScrollView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
+  public void updateStickyView() {
+    sticky = findViewWithTag("sticky");
+    stickyContainer = findViewWithTag("stickyContainer");
+  }
 
-    public StickyHeaderScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-    }
-
-    @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        super.onLayout(changed, l, t, r, b);
-        if (mSticky == null) {
-            updateStickyView();
+  private void updateStickyHeaderPosition() {
+    // Note: for pre-Honeycomb the header will not be moved, so this ScrollView essentially
+    // behaves like a normal BottomScrollView.
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+      if (sticky != null) {
+        // The view to draw when sticking to the top
+        final View drawTarget = stickyContainer != null ? stickyContainer : sticky;
+        // The offset to draw the view at when sticky
+        final int drawOffset = stickyContainer != null ? sticky.getTop() : 0;
+        // Position of the draw target, relative to the outside of the scrollView
+        final int drawTop = drawTarget.getTop() - getScrollY();
+        if (drawTop + drawOffset < statusBarInset || !drawTarget.isShown()) {
+          // ScrollView translates the whole canvas so we have to compensate for that
+          drawTarget.setTranslationY(getScrollY() - drawOffset);
+        } else {
+          drawTarget.setTranslationY(0);
         }
-        updateStickyHeaderPosition();
+      }
     }
+  }
 
-    public void updateStickyView() {
-        mSticky = findViewWithTag("sticky");
-        mStickyContainer = findViewWithTag("stickyContainer");
-    }
+  @Override
+  protected void onScrollChanged(int l, int t, int oldl, int oldt) {
+    super.onScrollChanged(l, t, oldl, oldt);
+    updateStickyHeaderPosition();
+  }
 
-    private void updateStickyHeaderPosition() {
-        // Note: for pre-Honeycomb the header will not be moved, so this ScrollView essentially
-        // behaves like a normal BottomScrollView.
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
-            if (mSticky != null) {
-                // The view to draw when sticking to the top
-                final View drawTarget = mStickyContainer != null ? mStickyContainer : mSticky;
-                // The offset to draw the view at when sticky
-                final int drawOffset = mStickyContainer != null ? mSticky.getTop() : 0;
-                // Position of the draw target, relative to the outside of the scrollView
-                final int drawTop = drawTarget.getTop() - getScrollY();
-                if (drawTop + drawOffset < mStatusBarInset || !drawTarget.isShown()) {
-                    // ScrollView translates the whole canvas so we have to compensate for that
-                    drawTarget.setTranslationY(getScrollY() - drawOffset);
-                } else {
-                    drawTarget.setTranslationY(0);
-                }
-            }
-        }
+  @Override
+  @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+  public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+    if (getFitsSystemWindows()) {
+      statusBarInset = insets.getSystemWindowInsetTop();
+      insets =
+          insets.replaceSystemWindowInsets(
+              insets.getSystemWindowInsetLeft(),
+              0, /* top */
+              insets.getSystemWindowInsetRight(),
+              insets.getSystemWindowInsetBottom());
     }
-
-    @Override
-    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
-        super.onScrollChanged(l, t, oldl, oldt);
-        updateStickyHeaderPosition();
-    }
-
-    @Override
-    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
-    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
-        if (getFitsSystemWindows()) {
-            mStatusBarInset = insets.getSystemWindowInsetTop();
-            insets = insets.replaceSystemWindowInsets(
-                    insets.getSystemWindowInsetLeft(),
-                    0, /* top */
-                    insets.getSystemWindowInsetRight(),
-                    insets.getSystemWindowInsetBottom()
-            );
-        }
-        return insets;
-    }
+    return insets;
+  }
 }
diff --git a/library/main/src/com/android/setupwizardlib/view/TouchableMovementMethod.java b/library/main/src/com/android/setupwizardlib/view/TouchableMovementMethod.java
index 10e91f4..526883c 100644
--- a/library/main/src/com/android/setupwizardlib/view/TouchableMovementMethod.java
+++ b/library/main/src/com/android/setupwizardlib/view/TouchableMovementMethod.java
@@ -24,60 +24,56 @@
 import android.widget.TextView;
 
 /**
- * A movement method that tracks the last result of whether touch events are handled. This is
- * used to patch the return value of {@link TextView#onTouchEvent} so that it consumes the touch
- * events only when the movement method says the event is consumed.
+ * A movement method that tracks the last result of whether touch events are handled. This is used
+ * to patch the return value of {@link TextView#onTouchEvent} so that it consumes the touch events
+ * only when the movement method says the event is consumed.
  */
 public interface TouchableMovementMethod {
 
-    /**
-     * @return The last touch event received in {@link MovementMethod#onTouchEvent}
-     */
-    MotionEvent getLastTouchEvent();
+  /** @return The last touch event received in {@link MovementMethod#onTouchEvent} */
+  MotionEvent getLastTouchEvent();
 
-    /**
-     * @return The return value of the last {@link MovementMethod#onTouchEvent}, or whether the
-     * last touch event should be considered handled by the text view
-     */
-    boolean isLastTouchEventHandled();
+  /**
+   * @return The return value of the last {@link MovementMethod#onTouchEvent}, or whether the last
+   *     touch event should be considered handled by the text view
+   */
+  boolean isLastTouchEventHandled();
 
-    /**
-     * An extension of LinkMovementMethod that tracks whether the event is handled when it is
-     * touched.
-     */
-    class TouchableLinkMovementMethod extends LinkMovementMethod
-            implements TouchableMovementMethod {
+  /**
+   * An extension of LinkMovementMethod that tracks whether the event is handled when it is touched.
+   */
+  class TouchableLinkMovementMethod extends LinkMovementMethod implements TouchableMovementMethod {
 
-        public static TouchableLinkMovementMethod getInstance() {
-            return new TouchableLinkMovementMethod();
-        }
-
-        boolean mLastEventResult = false;
-        MotionEvent mLastEvent;
-
-        @Override
-        public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
-            mLastEvent = event;
-            boolean result = super.onTouchEvent(widget, buffer, event);
-            if (event.getAction() == MotionEvent.ACTION_DOWN) {
-                // Unfortunately, LinkMovementMethod extends ScrollMovementMethod, and it always
-                // consume the down event. So here we use the selection instead as a hint of whether
-                // the down event landed on a link.
-                mLastEventResult = Selection.getSelectionStart(buffer) != -1;
-            } else {
-                mLastEventResult = result;
-            }
-            return result;
-        }
-
-        @Override
-        public MotionEvent getLastTouchEvent() {
-            return mLastEvent;
-        }
-
-        @Override
-        public boolean isLastTouchEventHandled() {
-            return mLastEventResult;
-        }
+    public static TouchableLinkMovementMethod getInstance() {
+      return new TouchableLinkMovementMethod();
     }
+
+    boolean lastEventResult = false;
+    MotionEvent lastEvent;
+
+    @Override
+    public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
+      lastEvent = event;
+      boolean result = super.onTouchEvent(widget, buffer, event);
+      if (event.getAction() == MotionEvent.ACTION_DOWN) {
+        // Unfortunately, LinkMovementMethod extends ScrollMovementMethod, and it always
+        // consume the down event. So here we use the selection instead as a hint of whether
+        // the down event landed on a link.
+        lastEventResult = Selection.getSelectionStart(buffer) != -1;
+      } else {
+        lastEventResult = result;
+      }
+      return result;
+    }
+
+    @Override
+    public MotionEvent getLastTouchEvent() {
+      return lastEvent;
+    }
+
+    @Override
+    public boolean isLastTouchEventHandled() {
+      return lastEventResult;
+    }
+  }
 }
diff --git a/library/platform/AndroidManifest.xml b/library/platform/AndroidManifest.xml
new file mode 100644
index 0000000..9aa24dc
--- /dev/null
+++ b/library/platform/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2015 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.setupwizardlib">
+
+  <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28" />
+
+</manifest>
diff --git a/library/platform/res/values-v27/styles.xml b/library/platform/res/values-v27/styles.xml
index 6e36919..70101c9 100644
--- a/library/platform/res/values-v27/styles.xml
+++ b/library/platform/res/values-v27/styles.xml
@@ -44,6 +44,7 @@
 
         <item name="suwButtonAllCaps">true</item>
         <item name="suwButtonFontFamily">sans-serif</item>
+        <item name="suwButtonHighlightAlpha">0.24</item>
         <item name="suwCardBackground">@drawable/suw_card_bg</item>
         <item name="suwFillContentLayoutStyle">@style/SuwFillContentLayout</item>
         <item name="suwDividerInsetEnd">0dp</item>
@@ -75,6 +76,7 @@
 
         <item name="suwButtonAllCaps">true</item>
         <item name="suwButtonFontFamily">sans-serif</item>
+        <item name="suwButtonHighlightAlpha">0.24</item>
         <item name="suwCardBackground">@drawable/suw_card_bg</item>
         <item name="suwFillContentLayoutStyle">@style/SuwFillContentLayout</item>
         <item name="suwDividerInsetEnd">0dp</item>
@@ -109,7 +111,8 @@
 
         <item name="suwButtonAllCaps">true</item>
         <item name="suwButtonCornerRadius">@dimen/suw_glif_button_corner_radius</item>
-        <item name="suwButtonFontFamily">sans-serif</item>
+        <item name="suwButtonFontFamily">sans-serif-medium</item>
+        <item name="suwButtonHighlightAlpha">0.24</item>
         <item name="suwColorPrimary">?android:attr/colorPrimary</item>
         <item name="suwFillContentLayoutStyle">@style/SuwFillContentLayout</item>
         <item name="suwDividerInsetEnd">0dp</item>
@@ -145,7 +148,8 @@
 
         <item name="suwButtonAllCaps">true</item>
         <item name="suwButtonCornerRadius">@dimen/suw_glif_button_corner_radius</item>
-        <item name="suwButtonFontFamily">sans-serif</item>
+        <item name="suwButtonFontFamily">sans-serif-medium</item>
+        <item name="suwButtonHighlightAlpha">0.12</item>
         <item name="suwColorPrimary">?android:attr/colorPrimary</item>
         <item name="suwFillContentLayoutStyle">@style/SuwFillContentLayout</item>
         <item name="suwDividerInsetEnd">0dp</item>
@@ -218,4 +222,11 @@
         <item name="android:colorControlHighlight">@color/suw_flat_button_highlight</item>
     </style>
 
+    <!-- Ignore UnusedResources: used by clients -->
+    <style name="SuwGlifButton.Tertiary"
+        parent="SuwGlifButton.BaseTertiary"
+        tools:ignore="UnusedResources">
+        <item name="android:fontFamily">sans-serif-medium</item>
+    </style>
+
 </resources>
diff --git a/library/platform/src/com/android/setupwizardlib/view/NavigationBarButton.java b/library/platform/src/com/android/setupwizardlib/view/NavigationBarButton.java
index 45d3737..5d14aa7 100644
--- a/library/platform/src/com/android/setupwizardlib/view/NavigationBarButton.java
+++ b/library/platform/src/com/android/setupwizardlib/view/NavigationBarButton.java
@@ -22,11 +22,11 @@
 
 public class NavigationBarButton extends Button {
 
-    public NavigationBarButton(Context context) {
-        super(context);
-    }
+  public NavigationBarButton(Context context) {
+    super(context);
+  }
 
-    public NavigationBarButton(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
+  public NavigationBarButton(Context context, AttributeSet attrs) {
+    super(context, attrs);
+  }
 }
diff --git a/library/platform/src/com/android/setupwizardlib/view/RichTextView.java b/library/platform/src/com/android/setupwizardlib/view/RichTextView.java
index fa68a68..7eb29c6 100644
--- a/library/platform/src/com/android/setupwizardlib/view/RichTextView.java
+++ b/library/platform/src/com/android/setupwizardlib/view/RichTextView.java
@@ -27,15 +27,14 @@
 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.view.TouchableMovementMethod.TouchableLinkMovementMethod;
 
 /**
- * An extension of TextView that automatically replaces the annotation tags as specified in
- * {@link SpanHelper#replaceSpan(android.text.Spannable, Object, Object)}
+ * 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
@@ -44,133 +43,133 @@
  */
 public class RichTextView extends TextView implements OnLinkClickListener {
 
-    /* static section */
+  /* static section */
 
-    private static final String TAG = "RichTextView";
+  private static final String TAG = "RichTextView";
 
-    private static final String ANNOTATION_LINK = "link";
-    private static final String ANNOTATION_TEXT_APPEARANCE = "textAppearance";
+  private static final String ANNOTATION_LINK = "link";
+  private static final String ANNOTATION_TEXT_APPEARANCE = "textAppearance";
 
-    /**
-     * Replace &lt;annotation&gt; tags in strings to become their respective types. Currently 2
-     * types are supported:
-     * <ol>
-     *     <li>&lt;annotation link="foobar"&gt; will create a
-     *     {@link com.android.setupwizardlib.span.LinkSpan} that broadcasts with the key
-     *     "foobar"</li>
-     *     <li>&lt;annotation textAppearance="TextAppearance.FooBar"&gt; will create a
-     *     {@link android.text.style.TextAppearanceSpan} with @style/TextAppearance.FooBar</li>
-     * </ol>
-     */
-    public static CharSequence getRichText(Context context, CharSequence text) {
-        if (text instanceof Spanned) {
-            final SpannableString spannable = new SpannableString(text);
-            final Annotation[] spans = spannable.getSpans(0, spannable.length(), Annotation.class);
-            for (Annotation span : spans) {
-                final String key = span.getKey();
-                if (ANNOTATION_TEXT_APPEARANCE.equals(key)) {
-                    String textAppearance = span.getValue();
-                    final int style = context.getResources()
-                            .getIdentifier(textAppearance, "style", context.getPackageName());
-                    if (style == 0) {
-                        Log.w(TAG, "Cannot find resource: " + style);
-                    }
-                    final TextAppearanceSpan textAppearanceSpan =
-                            new TextAppearanceSpan(context, style);
-                    SpanHelper.replaceSpan(spannable, span, textAppearanceSpan);
-                } else if (ANNOTATION_LINK.equals(key)) {
-                    LinkSpan link = new LinkSpan(span.getValue());
-                    SpanHelper.replaceSpan(spannable, span, link);
-                }
-            }
-            return spannable;
+  /**
+   * Replace &lt;annotation&gt; tags in strings to become their respective types. Currently 2 types
+   * are supported:
+   *
+   * <ol>
+   *   <li>&lt;annotation link="foobar"&gt; will create a {@link
+   *       com.android.setupwizardlib.span.LinkSpan} that broadcasts with the key "foobar"
+   *   <li>&lt;annotation textAppearance="TextAppearance.FooBar"&gt; will create a {@link
+   *       android.text.style.TextAppearanceSpan} with @style/TextAppearance.FooBar
+   * </ol>
+   */
+  public static CharSequence getRichText(Context context, CharSequence text) {
+    if (text instanceof Spanned) {
+      final SpannableString spannable = new SpannableString(text);
+      final Annotation[] spans = spannable.getSpans(0, spannable.length(), Annotation.class);
+      for (Annotation span : spans) {
+        final String key = span.getKey();
+        if (ANNOTATION_TEXT_APPEARANCE.equals(key)) {
+          String textAppearance = span.getValue();
+          final int style =
+              context
+                  .getResources()
+                  .getIdentifier(textAppearance, "style", context.getPackageName());
+          if (style == 0) {
+            Log.w(TAG, "Cannot find resource: " + style);
+          }
+          final TextAppearanceSpan textAppearanceSpan = new TextAppearanceSpan(context, style);
+          SpanHelper.replaceSpan(spannable, span, textAppearanceSpan);
+        } else if (ANNOTATION_LINK.equals(key)) {
+          LinkSpan link = new LinkSpan(span.getValue());
+          SpanHelper.replaceSpan(spannable, span, link);
         }
-        return text;
+      }
+      return spannable;
     }
+    return text;
+  }
 
-    /* non-static section */
+  /* non-static section */
 
-    private OnLinkClickListener mOnLinkClickListener;
+  private OnLinkClickListener mOnLinkClickListener;
 
-    public RichTextView(Context context) {
-        super(context);
+  public RichTextView(Context context) {
+    super(context);
+  }
+
+  public RichTextView(Context context, AttributeSet attrs) {
+    super(context, attrs);
+  }
+
+  @Override
+  public void setText(CharSequence text, BufferType type) {
+    text = getRichText(getContext(), text);
+    // Set text first before doing anything else because setMovementMethod internally calls
+    // setText. This in turn ends up calling this method with mText as the first parameter
+    super.setText(text, type);
+    boolean hasLinks = hasLinks(text);
+
+    if (hasLinks) {
+      // When a TextView has a movement method, it will set the view to clickable. This makes
+      // View.onTouchEvent always return true and consumes the touch event, essentially
+      // nullifying any return values of MovementMethod.onTouchEvent.
+      // To still allow propagating touch events to the parent when this view doesn't have
+      // links, we only set the movement method here if the text contains links.
+      setMovementMethod(TouchableLinkMovementMethod.getInstance());
+    } else {
+      setMovementMethod(null);
     }
+    // ExploreByTouchHelper automatically enables focus for RichTextView
+    // even though it may not have any links. Causes problems during talkback
+    // as individual TextViews consume touch events and thereby reducing the focus window
+    // shown by Talkback. Disable focus if there are no links
+    setFocusable(hasLinks);
+    // Do not "reveal" (i.e. scroll to) this view when this view is focused. Since this view is
+    // focusable in touch mode, we may be focused when the screen is first shown, and starting
+    // a screen halfway scrolled down is confusing to the user.
+    setRevealOnFocusHint(false);
+    setFocusableInTouchMode(hasLinks);
+  }
 
-    public RichTextView(Context context, AttributeSet attrs) {
-        super(context, attrs);
+  private boolean hasLinks(CharSequence text) {
+    if (text instanceof Spanned) {
+      final ClickableSpan[] spans =
+          ((Spanned) text).getSpans(0, text.length(), ClickableSpan.class);
+      return spans.length > 0;
     }
+    return false;
+  }
 
-    @Override
-    public void setText(CharSequence text, BufferType type) {
-        text = getRichText(getContext(), text);
-        // Set text first before doing anything else because setMovementMethod internally calls
-        // setText. This in turn ends up calling this method with mText as the first parameter
-        super.setText(text, type);
-        boolean hasLinks = hasLinks(text);
-
-        if (hasLinks) {
-            // When a TextView has a movement method, it will set the view to clickable. This makes
-            // View.onTouchEvent always return true and consumes the touch event, essentially
-            // nullifying any return values of MovementMethod.onTouchEvent.
-            // To still allow propagating touch events to the parent when this view doesn't have
-            // links, we only set the movement method here if the text contains links.
-            setMovementMethod(TouchableLinkMovementMethod.getInstance());
-        } else {
-            setMovementMethod(null);
-        }
-        // ExploreByTouchHelper automatically enables focus for RichTextView
-        // even though it may not have any links. Causes problems during talkback
-        // as individual TextViews consume touch events and thereby reducing the focus window
-        // shown by Talkback. Disable focus if there are no links
-        setFocusable(hasLinks);
-        // Do not "reveal" (i.e. scroll to) this view when this view is focused. Since this view is
-        // focusable in touch mode, we may be focused when the screen is first shown, and starting
-        // a screen halfway scrolled down is confusing to the user.
-        setRevealOnFocusHint(false);
-        setFocusableInTouchMode(hasLinks);
+  @Override
+  @SuppressWarnings("ClickableViewAccessibility") // super.onTouchEvent is called
+  public boolean onTouchEvent(MotionEvent event) {
+    // Since View#onTouchEvent always return true if the view is clickable (which is the case
+    // when a TextView has a movement method), override the implementation to allow the movement
+    // method, if it implements TouchableMovementMethod, to say that the touch is not handled,
+    // allowing the event to bubble up to the parent view.
+    boolean superResult = super.onTouchEvent(event);
+    MovementMethod movementMethod = getMovementMethod();
+    if (movementMethod instanceof TouchableMovementMethod) {
+      TouchableMovementMethod touchableMovementMethod = (TouchableMovementMethod) movementMethod;
+      if (touchableMovementMethod.getLastTouchEvent() == event) {
+        return touchableMovementMethod.isLastTouchEventHandled();
+      }
     }
+    return superResult;
+  }
 
-    private boolean hasLinks(CharSequence text) {
-        if (text instanceof Spanned) {
-            final ClickableSpan[] spans =
-                    ((Spanned) text).getSpans(0, text.length(), ClickableSpan.class);
-            return spans.length > 0;
-        }
-        return false;
-    }
+  public void setOnLinkClickListener(OnLinkClickListener listener) {
+    mOnLinkClickListener = listener;
+  }
 
-    @Override
-    @SuppressWarnings("ClickableViewAccessibility")  // super.onTouchEvent is called
-    public boolean onTouchEvent(MotionEvent event) {
-        // Since View#onTouchEvent always return true if the view is clickable (which is the case
-        // when a TextView has a movement method), override the implementation to allow the movement
-        // method, if it implements TouchableMovementMethod, to say that the touch is not handled,
-        // allowing the event to bubble up to the parent view.
-        boolean superResult = super.onTouchEvent(event);
-        MovementMethod movementMethod = getMovementMethod();
-        if (movementMethod instanceof TouchableMovementMethod) {
-            TouchableMovementMethod touchableMovementMethod =
-                    (TouchableMovementMethod) movementMethod;
-            if (touchableMovementMethod.getLastTouchEvent() == event) {
-                return touchableMovementMethod.isLastTouchEventHandled();
-            }
-        }
-        return superResult;
-    }
+  public OnLinkClickListener getOnLinkClickListener() {
+    return mOnLinkClickListener;
+  }
 
-    public void setOnLinkClickListener(OnLinkClickListener listener) {
-        mOnLinkClickListener = listener;
+  @Override
+  public boolean onLinkClick(LinkSpan span) {
+    if (mOnLinkClickListener != null) {
+      return mOnLinkClickListener.onLinkClick(span);
     }
-
-    public OnLinkClickListener getOnLinkClickListener() {
-        return mOnLinkClickListener;
-    }
-
-    @Override
-    public boolean onLinkClick(LinkSpan span) {
-        if (mOnLinkClickListener != null) {
-            return mOnLinkClickListener.onLinkClick(span);
-        }
-        return false;
-    }
+    return false;
+  }
 }
diff --git a/library/platform/test/src/com/android/setupwizardlib/test/util/DrawingTestActivity.java b/library/platform/test/src/com/android/setupwizardlib/test/util/DrawingTestActivity.java
index 3d11e12..212b52c 100644
--- a/library/platform/test/src/com/android/setupwizardlib/test/util/DrawingTestActivity.java
+++ b/library/platform/test/src/com/android/setupwizardlib/test/util/DrawingTestActivity.java
@@ -25,5 +25,4 @@
  *
  * @see DrawingTestHelper
  */
-public class DrawingTestActivity extends Activity {
-}
+public class DrawingTestActivity extends Activity {}
diff --git a/library/recyclerview/src/com/android/setupwizardlib/DividerItemDecoration.java b/library/recyclerview/src/com/android/setupwizardlib/DividerItemDecoration.java
index 2db17f8..128ed6b 100644
--- a/library/recyclerview/src/com/android/setupwizardlib/DividerItemDecoration.java
+++ b/library/recyclerview/src/com/android/setupwizardlib/DividerItemDecoration.java
@@ -21,223 +21,207 @@
 import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
-import android.view.View;
-
 import androidx.annotation.IntDef;
 import androidx.core.view.ViewCompat;
 import androidx.recyclerview.widget.RecyclerView;
-
+import android.view.View;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
 /**
  * An {@link androidx.recyclerview.widget.RecyclerView.ItemDecoration} for RecyclerView to draw
- * dividers between items. This ItemDecoration will draw the drawable specified by
- * {@link #setDivider(android.graphics.drawable.Drawable)} as the divider in between each item by
- * default, and the behavior of whether the divider is shown can be customized by subclassing
- * {@link com.android.setupwizardlib.DividerItemDecoration.DividedViewHolder}.
+ * dividers between items. This ItemDecoration will draw the drawable specified by {@link
+ * #setDivider(android.graphics.drawable.Drawable)} as the divider in between each item by 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.
  */
 public class DividerItemDecoration extends RecyclerView.ItemDecoration {
 
-    /* static section */
+  /* 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 {
 
     /**
-     * An interface to be implemented by a {@link RecyclerView.ViewHolder} which controls whether
-     * dividers should be shown above and below that item.
+     * Returns whether divider is allowed above this item. A divider will be shown only if both
+     * items immediately above and below it allows this divider.
      */
-    public interface DividedViewHolder {
-
-        /**
-         * Returns whether divider is allowed above this item. A divider will be shown only if both
-         * items immediately above and below it allows this divider.
-         */
-        boolean isDividerAllowedAbove();
-
-        /**
-         * Returns whether divider is allowed below this item. A divider will be shown only if both
-         * items immediately above and below it allows this divider.
-         */
-        boolean isDividerAllowedBelow();
-    }
-
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({
-            DIVIDER_CONDITION_EITHER,
-            DIVIDER_CONDITION_BOTH})
-    public @interface DividerCondition {}
-
-    public static final int DIVIDER_CONDITION_EITHER = 0;
-    public static final int DIVIDER_CONDITION_BOTH = 1;
+    boolean isDividerAllowedAbove();
 
     /**
-     * @deprecated Use {@link #DividerItemDecoration(android.content.Context)}
+     * Returns whether divider is allowed below this item. A divider will be shown only if both
+     * items immediately above and below it allows this divider.
      */
-    @Deprecated
-    public static DividerItemDecoration getDefault(Context context) {
-        return new DividerItemDecoration(context);
-    }
+    boolean isDividerAllowedBelow();
+  }
 
-    /* non-static section */
+  @Retention(RetentionPolicy.SOURCE)
+  @IntDef({DIVIDER_CONDITION_EITHER, DIVIDER_CONDITION_BOTH})
+  public @interface DividerCondition {}
 
-    private Drawable mDivider;
-    private int mDividerHeight;
-    private int mDividerIntrinsicHeight;
+  public static final int DIVIDER_CONDITION_EITHER = 0;
+  public static final int DIVIDER_CONDITION_BOTH = 1;
+
+  /** @deprecated Use {@link #DividerItemDecoration(android.content.Context)} */
+  @Deprecated
+  public static DividerItemDecoration getDefault(Context context) {
+    return new DividerItemDecoration(context);
+  }
+
+  /* non-static section */
+
+  private Drawable divider;
+  private int dividerHeight;
+  private int dividerIntrinsicHeight;
+  @DividerCondition private int dividerCondition;
+
+  public DividerItemDecoration() {}
+
+  public DividerItemDecoration(Context context) {
+    final TypedArray a = context.obtainStyledAttributes(R.styleable.SuwDividerItemDecoration);
+    final Drawable divider =
+        a.getDrawable(R.styleable.SuwDividerItemDecoration_android_listDivider);
+    final int dividerHeight =
+        a.getDimensionPixelSize(R.styleable.SuwDividerItemDecoration_android_dividerHeight, 0);
     @DividerCondition
-    private int mDividerCondition;
+    final int dividerCondition =
+        a.getInt(
+            R.styleable.SuwDividerItemDecoration_suwDividerCondition, DIVIDER_CONDITION_EITHER);
+    a.recycle();
 
-    public DividerItemDecoration() {
+    setDivider(divider);
+    setDividerHeight(dividerHeight);
+    setDividerCondition(dividerCondition);
+  }
+
+  @Override
+  public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
+    if (divider == null) {
+      return;
     }
-
-    public DividerItemDecoration(Context context) {
-        final TypedArray a = context.obtainStyledAttributes(R.styleable.SuwDividerItemDecoration);
-        final Drawable divider = a.getDrawable(
-                R.styleable.SuwDividerItemDecoration_android_listDivider);
-        final int dividerHeight = a.getDimensionPixelSize(
-                R.styleable.SuwDividerItemDecoration_android_dividerHeight, 0);
-        @DividerCondition final int dividerCondition = a.getInt(
-                R.styleable.SuwDividerItemDecoration_suwDividerCondition,
-                DIVIDER_CONDITION_EITHER);
-        a.recycle();
-
-        setDivider(divider);
-        setDividerHeight(dividerHeight);
-        setDividerCondition(dividerCondition);
+    final int childCount = parent.getChildCount();
+    final int width = parent.getWidth();
+    final int dividerHeight = this.dividerHeight != 0 ? this.dividerHeight : dividerIntrinsicHeight;
+    for (int childViewIndex = 0; childViewIndex < childCount; childViewIndex++) {
+      final View view = parent.getChildAt(childViewIndex);
+      if (shouldDrawDividerBelow(view, parent)) {
+        final int top = (int) ViewCompat.getY(view) + view.getHeight();
+        divider.setBounds(0, top, width, top + dividerHeight);
+        divider.draw(c);
+      }
     }
+  }
 
-    @Override
-    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
-        if (mDivider == null) {
-            return;
-        }
-        final int childCount = parent.getChildCount();
-        final int width = parent.getWidth();
-        final int dividerHeight = mDividerHeight != 0 ? mDividerHeight : mDividerIntrinsicHeight;
-        for (int childViewIndex = 0; childViewIndex < childCount; childViewIndex++) {
-            final View view = parent.getChildAt(childViewIndex);
-            if (shouldDrawDividerBelow(view, parent)) {
-                final int top = (int) ViewCompat.getY(view) + view.getHeight();
-                mDivider.setBounds(0, top, width, top + dividerHeight);
-                mDivider.draw(c);
-            }
-        }
+  @Override
+  public void getItemOffsets(
+      Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
+    if (shouldDrawDividerBelow(view, parent)) {
+      outRect.bottom = dividerHeight != 0 ? dividerHeight : dividerIntrinsicHeight;
     }
+  }
 
-    @Override
-    public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
-            RecyclerView.State state) {
-        if (shouldDrawDividerBelow(view, parent)) {
-            outRect.bottom = mDividerHeight != 0 ? mDividerHeight : mDividerIntrinsicHeight;
-        }
-    }
-
-    private boolean shouldDrawDividerBelow(View view, RecyclerView parent) {
-        final RecyclerView.ViewHolder holder = parent.getChildViewHolder(view);
-        final int index = holder.getLayoutPosition();
-        final int lastItemIndex = parent.getAdapter().getItemCount() - 1;
-        if (isDividerAllowedBelow(holder)) {
-            if (mDividerCondition == DIVIDER_CONDITION_EITHER) {
-                // Draw the divider without consulting the next item if we only
-                // need permission for either above or below.
-                return true;
-            }
-        } else if (mDividerCondition == DIVIDER_CONDITION_BOTH || index == lastItemIndex) {
-            // Don't draw if the current view holder doesn't allow drawing below
-            // and the current theme requires permission for both the item below and above.
-            // Also, if this is the last item, there is no item below to ask permission
-            // for whether to draw a divider above, so don't draw it.
-            return false;
-        }
-        // Require permission from index below to draw the divider.
-        if (index < lastItemIndex) {
-            final RecyclerView.ViewHolder nextHolder =
-                    parent.findViewHolderForLayoutPosition(index + 1);
-            if (!isDividerAllowedAbove(nextHolder)) {
-                // Don't draw if the next view holder doesn't allow drawing above
-                return false;
-            }
-        }
+  private boolean shouldDrawDividerBelow(View view, RecyclerView parent) {
+    final RecyclerView.ViewHolder holder = parent.getChildViewHolder(view);
+    final int index = holder.getLayoutPosition();
+    final int lastItemIndex = parent.getAdapter().getItemCount() - 1;
+    if (isDividerAllowedBelow(holder)) {
+      if (dividerCondition == DIVIDER_CONDITION_EITHER) {
+        // Draw the divider without consulting the next item if we only
+        // need permission for either above or below.
         return true;
+      }
+    } else if (dividerCondition == DIVIDER_CONDITION_BOTH || index == lastItemIndex) {
+      // Don't draw if the current view holder doesn't allow drawing below
+      // and the current theme requires permission for both the item below and above.
+      // Also, if this is the last item, there is no item below to ask permission
+      // for whether to draw a divider above, so don't draw it.
+      return false;
     }
+    // Require permission from index below to draw the divider.
+    if (index < lastItemIndex) {
+      final RecyclerView.ViewHolder nextHolder = parent.findViewHolderForLayoutPosition(index + 1);
+      if (!isDividerAllowedAbove(nextHolder)) {
+        // Don't draw if the next view holder doesn't allow drawing above
+        return false;
+      }
+    }
+    return true;
+  }
 
-    /**
-     * Whether a divider is allowed above the view holder. The allowed values will be combined
-     * according to {@link #getDividerCondition()}. The default implementation delegates to
-     * {@link com.android.setupwizardlib.DividerItemDecoration.DividedViewHolder}, or simply allows
-     * the divider if the view holder doesn't implement {@code DividedViewHolder}. Subclasses can
-     * override this to give more information to decide whether a divider should be drawn.
-     *
-     * @return True if divider is allowed above this view holder.
-     */
-    protected boolean isDividerAllowedAbove(RecyclerView.ViewHolder viewHolder) {
-        return !(viewHolder instanceof DividedViewHolder)
-                || ((DividedViewHolder) viewHolder).isDividerAllowedAbove();
-    }
+  /**
+   * Whether a divider is allowed above the view holder. The allowed values will be combined
+   * according to {@link #getDividerCondition()}. The default implementation delegates to {@link
+   * com.android.setupwizardlib.DividerItemDecoration.DividedViewHolder}, or simply allows the
+   * divider if the view holder doesn't implement {@code DividedViewHolder}. Subclasses can override
+   * this to give more information to decide whether a divider should be drawn.
+   *
+   * @return True if divider is allowed above this view holder.
+   */
+  protected boolean isDividerAllowedAbove(RecyclerView.ViewHolder viewHolder) {
+    return !(viewHolder instanceof DividedViewHolder)
+        || ((DividedViewHolder) viewHolder).isDividerAllowedAbove();
+  }
 
-    /**
-     * Whether a divider is allowed below the view holder. The allowed values will be combined
-     * according to {@link #getDividerCondition()}. The default implementation delegates to
-     * {@link com.android.setupwizardlib.DividerItemDecoration.DividedViewHolder}, or simply allows
-     * the divider if the view holder doesn't implement {@code DividedViewHolder}. Subclasses can
-     * override this to give more information to decide whether a divider should be drawn.
-     *
-     * @return True if divider is allowed below this view holder.
-     */
-    protected boolean isDividerAllowedBelow(RecyclerView.ViewHolder viewHolder) {
-        return !(viewHolder instanceof DividedViewHolder)
-                || ((DividedViewHolder) viewHolder).isDividerAllowedBelow();
-    }
+  /**
+   * Whether a divider is allowed below the view holder. The allowed values will be combined
+   * according to {@link #getDividerCondition()}. The default implementation delegates to {@link
+   * com.android.setupwizardlib.DividerItemDecoration.DividedViewHolder}, or simply allows the
+   * divider if the view holder doesn't implement {@code DividedViewHolder}. Subclasses can override
+   * this to give more information to decide whether a divider should be drawn.
+   *
+   * @return True if divider is allowed below this view holder.
+   */
+  protected boolean isDividerAllowedBelow(RecyclerView.ViewHolder viewHolder) {
+    return !(viewHolder instanceof DividedViewHolder)
+        || ((DividedViewHolder) viewHolder).isDividerAllowedBelow();
+  }
 
-    /**
-     * Sets the drawable to be used as the divider.
-     */
-    public void setDivider(Drawable divider) {
-        if (divider != null) {
-            mDividerIntrinsicHeight = divider.getIntrinsicHeight();
-        } else {
-            mDividerIntrinsicHeight = 0;
-        }
-        mDivider = divider;
+  /** Sets the drawable to be used as the divider. */
+  public void setDivider(Drawable divider) {
+    if (divider != null) {
+      dividerIntrinsicHeight = divider.getIntrinsicHeight();
+    } else {
+      dividerIntrinsicHeight = 0;
     }
+    this.divider = divider;
+  }
 
-    /**
-     * Gets the drawable currently used as the divider.
-     */
-    public Drawable getDivider() {
-        return mDivider;
-    }
+  /** Gets the drawable currently used as the divider. */
+  public Drawable getDivider() {
+    return divider;
+  }
 
-    /**
-     * Sets the divider height, in pixels.
-     */
-    public void setDividerHeight(int dividerHeight) {
-        mDividerHeight = dividerHeight;
-    }
+  /** Sets the divider height, in pixels. */
+  public void setDividerHeight(int dividerHeight) {
+    this.dividerHeight = dividerHeight;
+  }
 
-    /**
-     * Gets the divider height, in pixels.
-     */
-    public int getDividerHeight() {
-        return mDividerHeight;
-    }
+  /** Gets the divider height, in pixels. */
+  public int getDividerHeight() {
+    return dividerHeight;
+  }
 
-    /**
-     * Sets whether the divider needs permission from both the item view holder below
-     * and above from where the divider would draw itself or just needs permission from
-     * one or the other before drawing itself.
-     */
-    public void setDividerCondition(@DividerCondition int dividerCondition) {
-        mDividerCondition = dividerCondition;
-    }
+  /**
+   * Sets whether the divider needs permission from both the item view holder below and above from
+   * where the divider would draw itself or just needs permission from one or the other before
+   * drawing itself.
+   */
+  public void setDividerCondition(@DividerCondition int dividerCondition) {
+    this.dividerCondition = dividerCondition;
+  }
 
-    /**
-     * Gets whether the divider needs permission from both the item view holder below
-     * and above from where the divider would draw itself or just needs permission from
-     * one or the other before drawing itself.
-     */
-    @DividerCondition
-    public int getDividerCondition() {
-        return mDividerCondition;
-    }
+  /**
+   * Gets whether the divider needs permission from both the item view holder below and above from
+   * where the divider would draw itself or just needs permission from one or the other before
+   * drawing itself.
+   */
+  @DividerCondition
+  public int getDividerCondition() {
+    return dividerCondition;
+  }
 }
diff --git a/library/recyclerview/src/com/android/setupwizardlib/GlifPreferenceLayout.java b/library/recyclerview/src/com/android/setupwizardlib/GlifPreferenceLayout.java
index af1a739..795fdad 100644
--- a/library/recyclerview/src/com/android/setupwizardlib/GlifPreferenceLayout.java
+++ b/library/recyclerview/src/com/android/setupwizardlib/GlifPreferenceLayout.java
@@ -18,21 +18,20 @@
 
 import android.content.Context;
 import android.os.Bundle;
+import androidx.recyclerview.widget.RecyclerView;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-
-import androidx.recyclerview.widget.RecyclerView;
-
 import com.android.setupwizardlib.template.RecyclerMixin;
 
 /**
  * A layout to be used with {@code PreferenceFragment} in v14 support library. This can be specified
- * as the {@code android:layout} in the {@code app:preferenceFragmentStyle} in
- * {@code app:preferenceTheme}.
+ * as the {@code android:layout} in the {@code app:preferenceFragmentStyle} in {@code
+ * app:preferenceTheme}.
  *
- * <p />Example:
+ * <p>Example:
+ *
  * <pre>{@code
  * &lt;style android:name="MyActivityTheme">
  *     &lt;item android:name="preferenceTheme">@style/MyPreferenceTheme&lt;/item>
@@ -47,10 +46,11 @@
  * &lt;/style>
  * }</pre>
  *
- * where {@code my_preference_layout} is a layout that contains
- * {@link com.android.setupwizardlib.GlifPreferenceLayout}.
+ * where {@code my_preference_layout} is a layout that contains {@link
+ * com.android.setupwizardlib.GlifPreferenceLayout}.
  *
- * <p />Example:
+ * <p>Example:
+ *
  * <pre>{@code
  * &lt;com.android.setupwizardlib.GlifPreferenceLayout
  *     xmlns:android="http://schemas.android.com/apk/res/android"
@@ -59,60 +59,57 @@
  *     android:layout_height="match_parent" />
  * }</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)}
+ * <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)}
  */
 public class GlifPreferenceLayout extends GlifRecyclerLayout {
 
-    public GlifPreferenceLayout(Context context) {
-        super(context);
-    }
+  public GlifPreferenceLayout(Context context) {
+    super(context);
+  }
 
-    public GlifPreferenceLayout(Context context, int template, int containerId) {
-        super(context, template, containerId);
-    }
+  public GlifPreferenceLayout(Context context, int template, int containerId) {
+    super(context, template, containerId);
+  }
 
-    public GlifPreferenceLayout(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
+  public GlifPreferenceLayout(Context context, AttributeSet attrs) {
+    super(context, attrs);
+  }
 
-    public GlifPreferenceLayout(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-    }
+  public GlifPreferenceLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+    super(context, attrs, defStyleAttr);
+  }
 
-    @Override
-    protected ViewGroup findContainer(int containerId) {
-        if (containerId == 0) {
-            containerId = R.id.suw_layout_content;
-        }
-        return super.findContainer(containerId);
+  @Override
+  protected ViewGroup findContainer(int containerId) {
+    if (containerId == 0) {
+      containerId = R.id.suw_layout_content;
     }
+    return super.findContainer(containerId);
+  }
 
-    /**
-     * This method must be called in {@code PreferenceFragment#onCreateRecyclerView}.
-     */
-    public RecyclerView onCreateRecyclerView(LayoutInflater inflater, ViewGroup parent,
-            Bundle savedInstanceState) {
-        return mRecyclerMixin.getRecyclerView();
-    }
+  /** This method must be called in {@code PreferenceFragment#onCreateRecyclerView}. */
+  public RecyclerView onCreateRecyclerView(
+      LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
+    return mRecyclerMixin.getRecyclerView();
+  }
 
-    @Override
-    protected View onInflateTemplate(LayoutInflater inflater, int template) {
-        if (template == 0) {
-            template = R.layout.suw_glif_preference_template;
-        }
-        return super.onInflateTemplate(inflater, template);
+  @Override
+  protected View onInflateTemplate(LayoutInflater inflater, int template) {
+    if (template == 0) {
+      template = R.layout.suw_glif_preference_template;
     }
+    return super.onInflateTemplate(inflater, template);
+  }
 
-    @Override
-    protected void onTemplateInflated() {
-        // Inflate the recycler view here, so attributes on the decoration views can be applied
-        // immediately.
-        final LayoutInflater inflater = LayoutInflater.from(getContext());
-        RecyclerView recyclerView = (RecyclerView) inflater.inflate(
-                R.layout.suw_glif_preference_recycler_view, this, false);
-        mRecyclerMixin = new RecyclerMixin(this, recyclerView);
-    }
+  @Override
+  protected void onTemplateInflated() {
+    // Inflate the recycler view here, so attributes on the decoration views can be applied
+    // immediately.
+    final LayoutInflater inflater = LayoutInflater.from(getContext());
+    RecyclerView recyclerView =
+        (RecyclerView) inflater.inflate(R.layout.suw_glif_preference_recycler_view, this, false);
+    mRecyclerMixin = new RecyclerMixin(this, recyclerView);
+  }
 }
diff --git a/library/recyclerview/src/com/android/setupwizardlib/GlifRecyclerLayout.java b/library/recyclerview/src/com/android/setupwizardlib/GlifRecyclerLayout.java
index 7e0b1b7..2595b79 100644
--- a/library/recyclerview/src/com/android/setupwizardlib/GlifRecyclerLayout.java
+++ b/library/recyclerview/src/com/android/setupwizardlib/GlifRecyclerLayout.java
@@ -20,15 +20,13 @@
 import android.content.Context;
 import android.graphics.drawable.Drawable;
 import android.os.Build.VERSION_CODES;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.RecyclerView.Adapter;
+import androidx.recyclerview.widget.RecyclerView.ViewHolder;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-
-import androidx.recyclerview.widget.RecyclerView;
-import androidx.recyclerview.widget.RecyclerView.Adapter;
-import androidx.recyclerview.widget.RecyclerView.ViewHolder;
-
 import com.android.setupwizardlib.template.RecyclerMixin;
 import com.android.setupwizardlib.template.RecyclerViewScrollHandlingDelegate;
 import com.android.setupwizardlib.template.RequireScrollMixin;
@@ -39,157 +37,137 @@
  */
 public class GlifRecyclerLayout extends GlifLayout {
 
-    protected RecyclerMixin mRecyclerMixin;
+  protected RecyclerMixin mRecyclerMixin;
 
-    public GlifRecyclerLayout(Context context) {
-        this(context, 0, 0);
+  public GlifRecyclerLayout(Context context) {
+    this(context, 0, 0);
+  }
+
+  public GlifRecyclerLayout(Context context, int template) {
+    this(context, template, 0);
+  }
+
+  public GlifRecyclerLayout(Context context, int template, int containerId) {
+    super(context, template, containerId);
+    init(null, 0);
+  }
+
+  public GlifRecyclerLayout(Context context, AttributeSet attrs) {
+    super(context, attrs);
+    init(attrs, 0);
+  }
+
+  @TargetApi(VERSION_CODES.HONEYCOMB)
+  public GlifRecyclerLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+    super(context, attrs, defStyleAttr);
+    init(attrs, defStyleAttr);
+  }
+
+  private void init(AttributeSet attrs, int defStyleAttr) {
+    mRecyclerMixin.parseAttributes(attrs, defStyleAttr);
+    registerMixin(RecyclerMixin.class, mRecyclerMixin);
+
+    final RequireScrollMixin requireScrollMixin = getMixin(RequireScrollMixin.class);
+    requireScrollMixin.setScrollHandlingDelegate(
+        new RecyclerViewScrollHandlingDelegate(requireScrollMixin, getRecyclerView()));
+  }
+
+  @Override
+  protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+    super.onLayout(changed, left, top, right, bottom);
+    mRecyclerMixin.onLayout();
+  }
+
+  @Override
+  protected View onInflateTemplate(LayoutInflater inflater, int template) {
+    if (template == 0) {
+      template = R.layout.suw_glif_recycler_template;
     }
+    return super.onInflateTemplate(inflater, template);
+  }
 
-    public GlifRecyclerLayout(Context context, int template) {
-        this(context, template, 0);
+  @Override
+  protected void onTemplateInflated() {
+    final View recyclerView = findViewById(R.id.suw_recycler_view);
+    if (recyclerView instanceof RecyclerView) {
+      mRecyclerMixin = new RecyclerMixin(this, (RecyclerView) recyclerView);
+    } else {
+      throw new IllegalStateException(
+          "GlifRecyclerLayout should use a template with recycler view");
     }
+  }
 
-    public GlifRecyclerLayout(Context context, int template, int containerId) {
-        super(context, template, containerId);
-        init(context, null, 0);
+  @Override
+  protected ViewGroup findContainer(int containerId) {
+    if (containerId == 0) {
+      containerId = R.id.suw_recycler_view;
     }
+    return super.findContainer(containerId);
+  }
 
-    public GlifRecyclerLayout(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        init(context, attrs, 0);
+  @Override
+  // Returning generic type is the common pattern used for findViewBy* methods
+  @SuppressWarnings("TypeParameterUnusedInFormals")
+  public <T extends View> T findManagedViewById(int id) {
+    final View header = mRecyclerMixin.getHeader();
+    if (header != null) {
+      final T view = header.findViewById(id);
+      if (view != null) {
+        return view;
+      }
     }
+    return super.findViewById(id);
+  }
 
-    @TargetApi(VERSION_CODES.HONEYCOMB)
-    public GlifRecyclerLayout(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-        init(context, attrs, defStyleAttr);
-    }
+  /** @see RecyclerMixin#setDividerItemDecoration(DividerItemDecoration) */
+  public void setDividerItemDecoration(DividerItemDecoration decoration) {
+    mRecyclerMixin.setDividerItemDecoration(decoration);
+  }
 
-    private void init(Context context, AttributeSet attrs, int defStyleAttr) {
-        mRecyclerMixin.parseAttributes(attrs, defStyleAttr);
-        registerMixin(RecyclerMixin.class, mRecyclerMixin);
+  /** @see RecyclerMixin#getRecyclerView() */
+  public RecyclerView getRecyclerView() {
+    return mRecyclerMixin.getRecyclerView();
+  }
 
-        final RequireScrollMixin requireScrollMixin = getMixin(RequireScrollMixin.class);
-        requireScrollMixin.setScrollHandlingDelegate(
-                new RecyclerViewScrollHandlingDelegate(requireScrollMixin, getRecyclerView()));
-    }
+  /** @see RecyclerMixin#setAdapter(Adapter) */
+  public void setAdapter(Adapter<? extends ViewHolder> adapter) {
+    mRecyclerMixin.setAdapter(adapter);
+  }
 
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        super.onLayout(changed, left, top, right, bottom);
-        mRecyclerMixin.onLayout();
-    }
+  /** @see RecyclerMixin#getAdapter() */
+  public Adapter<? extends ViewHolder> getAdapter() {
+    return mRecyclerMixin.getAdapter();
+  }
 
-    @Override
-    protected View onInflateTemplate(LayoutInflater inflater, int template) {
-        if (template == 0) {
-            template = R.layout.suw_glif_recycler_template;
-        }
-        return super.onInflateTemplate(inflater, template);
-    }
+  /** @deprecated Use {@link #setDividerInsets(int, int)} instead. */
+  @Deprecated
+  public void setDividerInset(int inset) {
+    mRecyclerMixin.setDividerInset(inset);
+  }
 
-    @Override
-    protected void onTemplateInflated() {
-        final View recyclerView = findViewById(R.id.suw_recycler_view);
-        if (recyclerView instanceof RecyclerView) {
-            mRecyclerMixin = new RecyclerMixin(this, (RecyclerView) recyclerView);
-        } else {
-            throw new IllegalStateException(
-                    "GlifRecyclerLayout should use a template with recycler view");
-        }
-    }
+  /** @see RecyclerMixin#setDividerInset(int) */
+  public void setDividerInsets(int start, int end) {
+    mRecyclerMixin.setDividerInsets(start, end);
+  }
 
-    @Override
-    protected ViewGroup findContainer(int containerId) {
-        if (containerId == 0) {
-            containerId = R.id.suw_recycler_view;
-        }
-        return super.findContainer(containerId);
-    }
+  /** @deprecated Use {@link #getDividerInsetStart()} instead. */
+  @Deprecated
+  public int getDividerInset() {
+    return mRecyclerMixin.getDividerInset();
+  }
 
-    @Override
-    // Returning generic type is the common pattern used for findViewBy* methods
-    @SuppressWarnings("TypeParameterUnusedInFormals")
-    public <T extends View> T findManagedViewById(int id) {
-        final View header = mRecyclerMixin.getHeader();
-        if (header != null) {
-            final T view = header.findViewById(id);
-            if (view != null) {
-                return view;
-            }
-        }
-        return super.findViewById(id);
-    }
+  /** @see RecyclerMixin#getDividerInsetStart() */
+  public int getDividerInsetStart() {
+    return mRecyclerMixin.getDividerInsetStart();
+  }
 
-    /**
-     * @see RecyclerMixin#setDividerItemDecoration(DividerItemDecoration)
-     */
-    public void setDividerItemDecoration(DividerItemDecoration decoration) {
-        mRecyclerMixin.setDividerItemDecoration(decoration);
-    }
+  /** @see RecyclerMixin#getDividerInsetEnd() */
+  public int getDividerInsetEnd() {
+    return mRecyclerMixin.getDividerInsetEnd();
+  }
 
-    /**
-     * @see RecyclerMixin#getRecyclerView()
-     */
-    public RecyclerView getRecyclerView() {
-        return mRecyclerMixin.getRecyclerView();
-    }
-
-    /**
-     * @see RecyclerMixin#setAdapter(Adapter)
-     */
-    public void setAdapter(Adapter<? extends ViewHolder> adapter) {
-        mRecyclerMixin.setAdapter(adapter);
-    }
-
-    /**
-     * @see RecyclerMixin#getAdapter()
-     */
-    public Adapter<? extends ViewHolder> getAdapter() {
-        return mRecyclerMixin.getAdapter();
-    }
-
-    /**
-     * @deprecated Use {@link #setDividerInsets(int, int)} instead.
-     */
-    @Deprecated
-    public void setDividerInset(int inset) {
-        mRecyclerMixin.setDividerInset(inset);
-    }
-
-    /**
-     * @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() {
-        return mRecyclerMixin.getDivider();
-    }
+  /** @see RecyclerMixin#getDivider() */
+  public Drawable getDivider() {
+    return mRecyclerMixin.getDivider();
+  }
 }
diff --git a/library/recyclerview/src/com/android/setupwizardlib/SetupWizardPreferenceLayout.java b/library/recyclerview/src/com/android/setupwizardlib/SetupWizardPreferenceLayout.java
index 670c309..e9aa329 100644
--- a/library/recyclerview/src/com/android/setupwizardlib/SetupWizardPreferenceLayout.java
+++ b/library/recyclerview/src/com/android/setupwizardlib/SetupWizardPreferenceLayout.java
@@ -18,21 +18,20 @@
 
 import android.content.Context;
 import android.os.Bundle;
+import androidx.recyclerview.widget.RecyclerView;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-
-import androidx.recyclerview.widget.RecyclerView;
-
 import com.android.setupwizardlib.template.RecyclerMixin;
 
 /**
  * A layout to be used with {@code PreferenceFragment} in v14 support library. This can be specified
- * as the {@code android:layout} in the {@code app:preferenceFragmentStyle} in
- * {@code app:preferenceTheme}.
+ * as the {@code android:layout} in the {@code app:preferenceFragmentStyle} in {@code
+ * app:preferenceTheme}.
  *
- * <p />Example:
+ * <p>Example:
+ *
  * <pre>{@code
  * &lt;style android:name="MyActivityTheme">
  *     &lt;item android:name="preferenceTheme">@style/MyPreferenceTheme&lt;/item>
@@ -47,10 +46,11 @@
  * &lt;/style>
  * }</pre>
  *
- * where {@code my_preference_layout} is a layout that contains
- * {@link com.android.setupwizardlib.SetupWizardPreferenceLayout}.
+ * where {@code my_preference_layout} is a layout that contains {@link
+ * com.android.setupwizardlib.SetupWizardPreferenceLayout}.
  *
- * <p />Example:
+ * <p>Example:
+ *
  * <pre>{@code
  * &lt;com.android.setupwizardlib.SetupWizardPreferenceLayout
  *     xmlns:android="http://schemas.android.com/apk/res/android"
@@ -59,58 +59,56 @@
  *     android:layout_height="match_parent" />
  * }</pre>
  *
- * <p />Fragments using this layout <em>must</em> delegate {@code onCreateRecyclerView} to the
+ * <p>Fragments using this layout <em>must</em> delegate {@code onCreateRecyclerView} to the
  * implementation in this class: {@link #onCreateRecyclerView}
  */
 public class SetupWizardPreferenceLayout extends SetupWizardRecyclerLayout {
 
-    public SetupWizardPreferenceLayout(Context context) {
-        super(context);
-    }
+  public SetupWizardPreferenceLayout(Context context) {
+    super(context);
+  }
 
-    public SetupWizardPreferenceLayout(Context context, int template, int containerId) {
-        super(context, template, containerId);
-    }
+  public SetupWizardPreferenceLayout(Context context, int template, int containerId) {
+    super(context, template, containerId);
+  }
 
-    public SetupWizardPreferenceLayout(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
+  public SetupWizardPreferenceLayout(Context context, AttributeSet attrs) {
+    super(context, attrs);
+  }
 
-    public SetupWizardPreferenceLayout(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-    }
+  public SetupWizardPreferenceLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+    super(context, attrs, defStyleAttr);
+  }
 
-    @Override
-    protected ViewGroup findContainer(int containerId) {
-        if (containerId == 0) {
-            containerId = R.id.suw_layout_content;
-        }
-        return super.findContainer(containerId);
+  @Override
+  protected ViewGroup findContainer(int containerId) {
+    if (containerId == 0) {
+      containerId = R.id.suw_layout_content;
     }
+    return super.findContainer(containerId);
+  }
 
-    /**
-     * This method must be called in {@code PreferenceFragment#onCreateRecyclerView}.
-     */
-    public RecyclerView onCreateRecyclerView(LayoutInflater inflater, ViewGroup parent,
-            Bundle savedInstanceState) {
-        return mRecyclerMixin.getRecyclerView();
-    }
+  /** This method must be called in {@code PreferenceFragment#onCreateRecyclerView}. */
+  public RecyclerView onCreateRecyclerView(
+      LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
+    return mRecyclerMixin.getRecyclerView();
+  }
 
-    @Override
-    protected View onInflateTemplate(LayoutInflater inflater, int template) {
-        if (template == 0) {
-            template = R.layout.suw_preference_template;
-        }
-        return super.onInflateTemplate(inflater, template);
+  @Override
+  protected View onInflateTemplate(LayoutInflater inflater, int template) {
+    if (template == 0) {
+      template = R.layout.suw_preference_template;
     }
+    return super.onInflateTemplate(inflater, template);
+  }
 
-    @Override
-    protected void onTemplateInflated() {
-        // Inflate the recycler view here, so attributes on the decoration views can be applied
-        // immediately.
-        final LayoutInflater inflater = LayoutInflater.from(getContext());
-        RecyclerView recyclerView = (RecyclerView) inflater.inflate(
-                R.layout.suw_preference_recycler_view, this, false);
-        mRecyclerMixin = new RecyclerMixin(this, recyclerView);
-    }
+  @Override
+  protected void onTemplateInflated() {
+    // Inflate the recycler view here, so attributes on the decoration views can be applied
+    // immediately.
+    final LayoutInflater inflater = LayoutInflater.from(getContext());
+    RecyclerView recyclerView =
+        (RecyclerView) inflater.inflate(R.layout.suw_preference_recycler_view, this, false);
+    mRecyclerMixin = new RecyclerMixin(this, recyclerView);
+  }
 }
diff --git a/library/recyclerview/src/com/android/setupwizardlib/SetupWizardRecyclerLayout.java b/library/recyclerview/src/com/android/setupwizardlib/SetupWizardRecyclerLayout.java
index 5d3f1a5..ba0b598 100644
--- a/library/recyclerview/src/com/android/setupwizardlib/SetupWizardRecyclerLayout.java
+++ b/library/recyclerview/src/com/android/setupwizardlib/SetupWizardRecyclerLayout.java
@@ -18,178 +18,156 @@
 
 import android.content.Context;
 import android.graphics.drawable.Drawable;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.RecyclerView.Adapter;
+import androidx.recyclerview.widget.RecyclerView.ViewHolder;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-
-import androidx.recyclerview.widget.RecyclerView;
-import androidx.recyclerview.widget.RecyclerView.Adapter;
-import androidx.recyclerview.widget.RecyclerView.ViewHolder;
-
 import com.android.setupwizardlib.template.RecyclerMixin;
 import com.android.setupwizardlib.template.RecyclerViewScrollHandlingDelegate;
 import com.android.setupwizardlib.template.RequireScrollMixin;
 
 /**
- * A setup wizard layout for use with {@link androidx.recyclerview.widget.RecyclerView}.
- * {@code android:entries} can also be used to specify an
- * {@link com.android.setupwizardlib.items.ItemHierarchy} to be used with this layout in XML.
+ * A setup wizard layout for use with {@link androidx.recyclerview.widget.RecyclerView}. {@code
+ * android:entries} can also be used to specify an {@link
+ * com.android.setupwizardlib.items.ItemHierarchy} to be used with this layout in XML.
  *
  * @see SetupWizardListLayout
  */
 public class SetupWizardRecyclerLayout extends SetupWizardLayout {
 
-    private static final String TAG = "RecyclerLayout";
+  protected RecyclerMixin mRecyclerMixin;
 
-    protected RecyclerMixin mRecyclerMixin;
+  public SetupWizardRecyclerLayout(Context context) {
+    this(context, 0, 0);
+  }
 
-    public SetupWizardRecyclerLayout(Context context) {
-        this(context, 0, 0);
+  public SetupWizardRecyclerLayout(Context context, int template, int containerId) {
+    super(context, template, containerId);
+    init(null, 0);
+  }
+
+  public SetupWizardRecyclerLayout(Context context, AttributeSet attrs) {
+    super(context, attrs);
+    init(attrs, 0);
+  }
+
+  public SetupWizardRecyclerLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+    super(context, attrs, defStyleAttr);
+    init(attrs, defStyleAttr);
+  }
+
+  private void init(AttributeSet attrs, int defStyleAttr) {
+    mRecyclerMixin.parseAttributes(attrs, defStyleAttr);
+    registerMixin(RecyclerMixin.class, mRecyclerMixin);
+
+    final RequireScrollMixin requireScrollMixin = getMixin(RequireScrollMixin.class);
+    requireScrollMixin.setScrollHandlingDelegate(
+        new RecyclerViewScrollHandlingDelegate(requireScrollMixin, getRecyclerView()));
+  }
+
+  @Override
+  protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+    super.onLayout(changed, left, top, right, bottom);
+    mRecyclerMixin.onLayout();
+  }
+
+  /** @see RecyclerMixin#getAdapter() */
+  public Adapter<? extends ViewHolder> getAdapter() {
+    return mRecyclerMixin.getAdapter();
+  }
+
+  /** @see RecyclerMixin#setAdapter(Adapter) */
+  public void setAdapter(Adapter<? extends ViewHolder> adapter) {
+    mRecyclerMixin.setAdapter(adapter);
+  }
+
+  /** @see RecyclerMixin#getRecyclerView() */
+  public RecyclerView getRecyclerView() {
+    return mRecyclerMixin.getRecyclerView();
+  }
+
+  @Override
+  protected ViewGroup findContainer(int containerId) {
+    if (containerId == 0) {
+      containerId = R.id.suw_recycler_view;
     }
+    return super.findContainer(containerId);
+  }
 
-    public SetupWizardRecyclerLayout(Context context, int template, int containerId) {
-        super(context, template, containerId);
-        init(context, null, 0);
+  @Override
+  protected View onInflateTemplate(LayoutInflater inflater, int template) {
+    if (template == 0) {
+      template = R.layout.suw_recycler_template;
     }
+    return super.onInflateTemplate(inflater, template);
+  }
 
-    public SetupWizardRecyclerLayout(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        init(context, attrs, 0);
+  @Override
+  protected void onTemplateInflated() {
+    final View recyclerView = findViewById(R.id.suw_recycler_view);
+    if (recyclerView instanceof RecyclerView) {
+      mRecyclerMixin = new RecyclerMixin(this, (RecyclerView) recyclerView);
+    } else {
+      throw new IllegalStateException(
+          "SetupWizardRecyclerLayout should use a template with recycler view");
     }
+  }
 
-    public SetupWizardRecyclerLayout(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-        init(context, attrs, defStyleAttr);
+  @Override
+  // Returning generic type is the common pattern used for findViewBy* methods
+  @SuppressWarnings("TypeParameterUnusedInFormals")
+  public <T extends View> T findManagedViewById(int id) {
+    final View header = mRecyclerMixin.getHeader();
+    if (header != null) {
+      final T view = header.findViewById(id);
+      if (view != null) {
+        return view;
+      }
     }
+    return super.findViewById(id);
+  }
 
-    private void init(Context context, AttributeSet attrs, int defStyleAttr) {
-        mRecyclerMixin.parseAttributes(attrs, defStyleAttr);
-        registerMixin(RecyclerMixin.class, mRecyclerMixin);
+  /** @deprecated Use {@link #setDividerInsets(int, int)} instead. */
+  @Deprecated
+  public void setDividerInset(int inset) {
+    mRecyclerMixin.setDividerInset(inset);
+  }
 
+  /**
+   * 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);
+  }
 
-        final RequireScrollMixin requireScrollMixin = getMixin(RequireScrollMixin.class);
-        requireScrollMixin.setScrollHandlingDelegate(
-                new RecyclerViewScrollHandlingDelegate(requireScrollMixin, getRecyclerView()));
-    }
+  /** @deprecated Use {@link #getDividerInsetStart()} instead. */
+  @Deprecated
+  public int getDividerInset() {
+    return mRecyclerMixin.getDividerInset();
+  }
 
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        super.onLayout(changed, left, top, right, bottom);
-        mRecyclerMixin.onLayout();
-    }
+  /** @see RecyclerMixin#getDividerInsetStart() */
+  public int getDividerInsetStart() {
+    return mRecyclerMixin.getDividerInsetStart();
+  }
 
-    /**
-     * @see RecyclerMixin#getAdapter()
-     */
-    public Adapter<? extends ViewHolder> getAdapter() {
-        return mRecyclerMixin.getAdapter();
-    }
+  /** @see RecyclerMixin#getDividerInsetEnd() */
+  public int getDividerInsetEnd() {
+    return mRecyclerMixin.getDividerInsetEnd();
+  }
 
-    /**
-     * @see RecyclerMixin#setAdapter(Adapter)
-     */
-    public void setAdapter(Adapter<? extends ViewHolder> adapter) {
-        mRecyclerMixin.setAdapter(adapter);
-    }
-
-    /**
-     * @see RecyclerMixin#getRecyclerView()
-     */
-    public RecyclerView getRecyclerView() {
-        return mRecyclerMixin.getRecyclerView();
-    }
-
-    @Override
-    protected ViewGroup findContainer(int containerId) {
-        if (containerId == 0) {
-            containerId = R.id.suw_recycler_view;
-        }
-        return super.findContainer(containerId);
-    }
-
-    @Override
-    protected View onInflateTemplate(LayoutInflater inflater, int template) {
-        if (template == 0) {
-            template = R.layout.suw_recycler_template;
-        }
-        return super.onInflateTemplate(inflater, template);
-    }
-
-    @Override
-    protected void onTemplateInflated() {
-        final View recyclerView = findViewById(R.id.suw_recycler_view);
-        if (recyclerView instanceof RecyclerView) {
-            mRecyclerMixin = new RecyclerMixin(this, (RecyclerView) recyclerView);
-        } else {
-            throw new IllegalStateException(
-                    "SetupWizardRecyclerLayout should use a template with recycler view");
-        }
-    }
-
-    @Override
-    // Returning generic type is the common pattern used for findViewBy* methods
-    @SuppressWarnings("TypeParameterUnusedInFormals")
-    public <T extends View> T findManagedViewById(int id) {
-        final View header = mRecyclerMixin.getHeader();
-        if (header != null) {
-            final T view = header.findViewById(id);
-            if (view != null) {
-                return view;
-            }
-        }
-        return super.findViewById(id);
-    }
-
-    /**
-     * @deprecated Use {@link #setDividerInsets(int, int)} instead.
-     */
-    @Deprecated
-    public void setDividerInset(int inset) {
-        mRecyclerMixin.setDividerInset(inset);
-    }
-
-    /**
-     * 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() {
-        return mRecyclerMixin.getDivider();
-    }
+  /** @see RecyclerMixin#getDivider() */
+  public Drawable getDivider() {
+    return mRecyclerMixin.getDivider();
+  }
 }
diff --git a/library/recyclerview/src/com/android/setupwizardlib/items/ItemViewHolder.java b/library/recyclerview/src/com/android/setupwizardlib/items/ItemViewHolder.java
index aeaba68..419e2aa 100644
--- a/library/recyclerview/src/com/android/setupwizardlib/items/ItemViewHolder.java
+++ b/library/recyclerview/src/com/android/setupwizardlib/items/ItemViewHolder.java
@@ -16,44 +16,42 @@
 
 package com.android.setupwizardlib.items;
 
-import android.view.View;
-
 import androidx.recyclerview.widget.RecyclerView;
-
+import android.view.View;
 import com.android.setupwizardlib.DividerItemDecoration;
 
 class ItemViewHolder extends RecyclerView.ViewHolder
-            implements DividerItemDecoration.DividedViewHolder {
+    implements DividerItemDecoration.DividedViewHolder {
 
-    private boolean mIsEnabled;
-    private IItem mItem;
+  private boolean isEnabled;
+  private IItem item;
 
-    ItemViewHolder(View itemView) {
-        super(itemView);
-    }
+  ItemViewHolder(View itemView) {
+    super(itemView);
+  }
 
-    @Override
-    public boolean isDividerAllowedAbove() {
-        return mIsEnabled;
-    }
+  @Override
+  public boolean isDividerAllowedAbove() {
+    return isEnabled;
+  }
 
-    @Override
-    public boolean isDividerAllowedBelow() {
-        return mIsEnabled;
-    }
+  @Override
+  public boolean isDividerAllowedBelow() {
+    return isEnabled;
+  }
 
-    public void setEnabled(boolean isEnabled) {
-        mIsEnabled = isEnabled;
-        itemView.setClickable(isEnabled);
-        itemView.setEnabled(isEnabled);
-        itemView.setFocusable(isEnabled);
-    }
+  public void setEnabled(boolean isEnabled) {
+    this.isEnabled = isEnabled;
+    itemView.setClickable(isEnabled);
+    itemView.setEnabled(isEnabled);
+    itemView.setFocusable(isEnabled);
+  }
 
-    public void setItem(IItem item) {
-        mItem = item;
-    }
+  public void setItem(IItem item) {
+    this.item = item;
+  }
 
-    public IItem getItem() {
-        return mItem;
-    }
+  public IItem getItem() {
+    return item;
+  }
 }
diff --git a/library/recyclerview/src/com/android/setupwizardlib/items/RecyclerItemAdapter.java b/library/recyclerview/src/com/android/setupwizardlib/items/RecyclerItemAdapter.java
index 56c60e7..ee753b8 100644
--- a/library/recyclerview/src/com/android/setupwizardlib/items/RecyclerItemAdapter.java
+++ b/library/recyclerview/src/com/android/setupwizardlib/items/RecyclerItemAdapter.java
@@ -20,14 +20,12 @@
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.LayerDrawable;
+import androidx.annotation.VisibleForTesting;
+import androidx.recyclerview.widget.RecyclerView;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-
-import androidx.annotation.VisibleForTesting;
-import androidx.recyclerview.widget.RecyclerView;
-
 import com.android.setupwizardlib.R;
 
 /**
@@ -36,217 +34,214 @@
  * XML.
  */
 public class RecyclerItemAdapter extends RecyclerView.Adapter<ItemViewHolder>
-        implements ItemHierarchy.Observer {
+    implements ItemHierarchy.Observer {
 
-    private static final String TAG = "RecyclerItemAdapter";
+  private static final String TAG = "RecyclerItemAdapter";
+
+  /**
+   * A view tag set by {@link View#setTag(Object)}. If set on the root view of a layout, it will not
+   * create the default background for the list item. This means the item will not have ripple touch
+   * feedback by default.
+   */
+  public static final String TAG_NO_BACKGROUND = "noBackground";
+
+  /** Listener for item selection in this adapter. */
+  public interface OnItemSelectedListener {
 
     /**
-     * A view tag set by {@link View#setTag(Object)}. If set on the root view of a layout, it will
-     * not create the default background for the list item. This means the item will not have ripple
-     * touch feedback by default.
-     */
-    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);
-    }
-
-    private final ItemHierarchy mItemHierarchy;
-    private OnItemSelectedListener mListener;
-
-    public RecyclerItemAdapter(ItemHierarchy hierarchy) {
-        mItemHierarchy = hierarchy;
-        mItemHierarchy.registerObserver(this);
-    }
-
-    /**
-     * Gets the item at the given position.
+     * Called when an item in this adapter is clicked.
      *
-     * @see ItemHierarchy#getItemAt(int)
+     * @param item The Item corresponding to the position being clicked.
      */
-    public IItem getItem(int position) {
-        return mItemHierarchy.getItemAt(position);
+    void onItemSelected(IItem item);
+  }
+
+  private final ItemHierarchy itemHierarchy;
+  private OnItemSelectedListener listener;
+
+  public RecyclerItemAdapter(ItemHierarchy hierarchy) {
+    itemHierarchy = hierarchy;
+    itemHierarchy.registerObserver(this);
+  }
+
+  /**
+   * Gets the item at the given position.
+   *
+   * @see ItemHierarchy#getItemAt(int)
+   */
+  public IItem getItem(int position) {
+    return itemHierarchy.getItemAt(position);
+  }
+
+  @Override
+  public long getItemId(int position) {
+    IItem mItem = getItem(position);
+    if (mItem instanceof AbstractItem) {
+      final int id = ((AbstractItem) mItem).getId();
+      return id > 0 ? id : RecyclerView.NO_ID;
+    } else {
+      return RecyclerView.NO_ID;
+    }
+  }
+
+  @Override
+  public int getItemCount() {
+    return itemHierarchy.getCount();
+  }
+
+  @Override
+  public ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+    final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
+    final View view = inflater.inflate(viewType, parent, false);
+    final ItemViewHolder viewHolder = new ItemViewHolder(view);
+
+    final Object viewTag = view.getTag();
+    if (!TAG_NO_BACKGROUND.equals(viewTag)) {
+      final TypedArray typedArray =
+          parent.getContext().obtainStyledAttributes(R.styleable.SuwRecyclerItemAdapter);
+      Drawable selectableItemBackground =
+          typedArray.getDrawable(
+              R.styleable.SuwRecyclerItemAdapter_android_selectableItemBackground);
+      if (selectableItemBackground == null) {
+        selectableItemBackground =
+            typedArray.getDrawable(R.styleable.SuwRecyclerItemAdapter_selectableItemBackground);
+      }
+
+      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."
+                + " selectableItemBackground="
+                + selectableItemBackground
+                + " background="
+                + background);
+      } else {
+        final Drawable[] layers = {background, selectableItemBackground};
+        view.setBackgroundDrawable(new PatchedLayerDrawable(layers));
+      }
+
+      typedArray.recycle();
     }
 
-    @Override
-    public long getItemId(int position) {
-        IItem mItem = getItem(position);
-        if (mItem instanceof AbstractItem) {
-            final int id = ((AbstractItem) mItem).getId();
-            return id > 0 ? id : RecyclerView.NO_ID;
-        } else {
-            return RecyclerView.NO_ID;
-        }
-    }
-
-    @Override
-    public int getItemCount() {
-        return mItemHierarchy.getCount();
-    }
-
-    @Override
-    public ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
-        final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
-        final View view = inflater.inflate(viewType, parent, false);
-        final ItemViewHolder viewHolder = new ItemViewHolder(view);
-
-        final Object viewTag = view.getTag();
-        if (!TAG_NO_BACKGROUND.equals(viewTag)) {
-            final TypedArray typedArray = parent.getContext()
-                    .obtainStyledAttributes(R.styleable.SuwRecyclerItemAdapter);
-            Drawable selectableItemBackground = typedArray.getDrawable(
-                    R.styleable.SuwRecyclerItemAdapter_android_selectableItemBackground);
-            if (selectableItemBackground == null) {
-                selectableItemBackground = typedArray.getDrawable(
-                        R.styleable.SuwRecyclerItemAdapter_selectableItemBackground);
+    view.setOnClickListener(
+        new View.OnClickListener() {
+          @Override
+          public void onClick(View view) {
+            final IItem item = viewHolder.getItem();
+            if (listener != null && item != null && item.isEnabled()) {
+              listener.onItemSelected(item);
             }
-
-            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."
-                        + " selectableItemBackground=" + selectableItemBackground
-                        + " background=" + background);
-            } else {
-                final Drawable[] layers = {background, selectableItemBackground};
-                view.setBackgroundDrawable(new PatchedLayerDrawable(layers));
-            }
-
-            typedArray.recycle();
-        }
-
-        view.setOnClickListener(new View.OnClickListener() {
-            @Override
-            public void onClick(View view) {
-                final IItem item = viewHolder.getItem();
-                if (mListener != null && item != null && item.isEnabled()) {
-                    mListener.onItemSelected(item);
-                }
-            }
+          }
         });
 
-        return viewHolder;
+    return viewHolder;
+  }
+
+  @Override
+  public void onBindViewHolder(ItemViewHolder holder, int position) {
+    final IItem item = getItem(position);
+    holder.setEnabled(item.isEnabled());
+    holder.setItem(item);
+    item.onBindView(holder.itemView);
+  }
+
+  @Override
+  public int getItemViewType(int position) {
+    // Use layout resource as item view type. RecyclerView item type does not have to be
+    // contiguous.
+    IItem item = getItem(position);
+    return item.getLayoutResource();
+  }
+
+  @Override
+  public void onChanged(ItemHierarchy hierarchy) {
+    notifyDataSetChanged();
+  }
+
+  @Override
+  public void onItemRangeChanged(ItemHierarchy itemHierarchy, int positionStart, int itemCount) {
+    notifyItemRangeChanged(positionStart, itemCount);
+  }
+
+  @Override
+  public void onItemRangeInserted(ItemHierarchy itemHierarchy, int positionStart, int itemCount) {
+    notifyItemRangeInserted(positionStart, itemCount);
+  }
+
+  @Override
+  public void onItemRangeMoved(
+      ItemHierarchy itemHierarchy, int fromPosition, int toPosition, int itemCount) {
+    // There is no notifyItemRangeMoved
+    // https://code.google.com/p/android/issues/detail?id=125984
+    if (itemCount == 1) {
+      notifyItemMoved(fromPosition, toPosition);
+    } else {
+      // If more than one, degenerate into the catch-all data set changed callback, since I'm
+      // not sure how recycler view handles multiple calls to notifyItemMoved (if the result
+      // is committed after every notification then naively calling
+      // notifyItemMoved(from + i, to + i) is wrong).
+      // Logging this in case this is a more common occurrence than expected.
+      Log.i(TAG, "onItemRangeMoved with more than one item");
+      notifyDataSetChanged();
+    }
+  }
+
+  @Override
+  public void onItemRangeRemoved(ItemHierarchy itemHierarchy, int positionStart, int itemCount) {
+    notifyItemRangeRemoved(positionStart, itemCount);
+  }
+
+  /**
+   * Find an item hierarchy within the root hierarchy.
+   *
+   * @see ItemHierarchy#findItemById(int)
+   */
+  public ItemHierarchy findItemById(int id) {
+    return itemHierarchy.findItemById(id);
+  }
+
+  /** Gets the root item hierarchy in this adapter. */
+  public ItemHierarchy getRootItemHierarchy() {
+    return itemHierarchy;
+  }
+
+  /**
+   * Sets the listener to listen for when user clicks on a item.
+   *
+   * @see OnItemSelectedListener
+   */
+  public void setOnItemSelectedListener(OnItemSelectedListener listener) {
+    this.listener = listener;
+  }
+
+  /**
+   * Before Lollipop, LayerDrawable always return true in getPadding, even if the children layers do
+   * not have any padding. Patch the implementation so that getPadding returns false if the padding
+   * is empty.
+   *
+   * <p>When getPadding is true, the padding of the view will be replaced by the padding of the
+   * drawable when {@link View#setBackgroundDrawable(Drawable)} is called. This patched class makes
+   * sure layer drawables without padding does not clear out original padding on the view.
+   */
+  @VisibleForTesting
+  static class PatchedLayerDrawable extends LayerDrawable {
+
+    /** {@inheritDoc} */
+    PatchedLayerDrawable(Drawable[] layers) {
+      super(layers);
     }
 
     @Override
-    public void onBindViewHolder(ItemViewHolder holder, int position) {
-        final IItem item = getItem(position);
-        holder.setEnabled(item.isEnabled());
-        holder.setItem(item);
-        item.onBindView(holder.itemView);
+    public boolean getPadding(Rect padding) {
+      final boolean superHasPadding = super.getPadding(padding);
+      return superHasPadding
+          && !(padding.left == 0 && padding.top == 0 && padding.right == 0 && padding.bottom == 0);
     }
-
-    @Override
-    public int getItemViewType(int position) {
-        // Use layout resource as item view type. RecyclerView item type does not have to be
-        // contiguous.
-        IItem item = getItem(position);
-        return item.getLayoutResource();
-    }
-
-    @Override
-    public void onChanged(ItemHierarchy hierarchy) {
-        notifyDataSetChanged();
-    }
-
-    @Override
-    public void onItemRangeChanged(ItemHierarchy itemHierarchy, int positionStart, int itemCount) {
-        notifyItemRangeChanged(positionStart, itemCount);
-    }
-
-    @Override
-    public void onItemRangeInserted(ItemHierarchy itemHierarchy, int positionStart, int itemCount) {
-        notifyItemRangeInserted(positionStart, itemCount);
-    }
-
-    @Override
-    public void onItemRangeMoved(ItemHierarchy itemHierarchy, int fromPosition, int toPosition,
-            int itemCount) {
-        // There is no notifyItemRangeMoved
-        // https://code.google.com/p/android/issues/detail?id=125984
-        if (itemCount == 1) {
-            notifyItemMoved(fromPosition, toPosition);
-        } else {
-            // If more than one, degenerate into the catch-all data set changed callback, since I'm
-            // not sure how recycler view handles multiple calls to notifyItemMoved (if the result
-            // is committed after every notification then naively calling
-            // notifyItemMoved(from + i, to + i) is wrong).
-            // Logging this in case this is a more common occurrence than expected.
-            Log.i(TAG, "onItemRangeMoved with more than one item");
-            notifyDataSetChanged();
-        }
-    }
-
-    @Override
-    public void onItemRangeRemoved(ItemHierarchy itemHierarchy, int positionStart, int itemCount) {
-        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;
-    }
-
-    /**
-     * Before Lollipop, LayerDrawable always return true in getPadding, even if the children layers
-     * do not have any padding. Patch the implementation so that getPadding returns false if the
-     * padding is empty.
-     *
-     * When getPadding is true, the padding of the view will be replaced by the padding of the
-     * drawable when {@link View#setBackgroundDrawable(Drawable)} is called. This patched class
-     * makes sure layer drawables without padding does not clear out original padding on the view.
-     */
-    @VisibleForTesting
-    static class PatchedLayerDrawable extends LayerDrawable {
-
-        /**
-         * {@inheritDoc}
-         */
-        PatchedLayerDrawable(Drawable[] layers) {
-            super(layers);
-        }
-
-        @Override
-        public boolean getPadding(Rect padding) {
-            final boolean superHasPadding = super.getPadding(padding);
-            return superHasPadding
-                    && !(padding.left == 0
-                            && padding.top == 0
-                            && padding.right == 0
-                            && padding.bottom == 0);
-        }
-    }
+  }
 }
diff --git a/library/recyclerview/src/com/android/setupwizardlib/template/RecyclerMixin.java b/library/recyclerview/src/com/android/setupwizardlib/template/RecyclerMixin.java
index 32e7bd8..a6c6526 100644
--- a/library/recyclerview/src/com/android/setupwizardlib/template/RecyclerMixin.java
+++ b/library/recyclerview/src/com/android/setupwizardlib/template/RecyclerMixin.java
@@ -21,16 +21,14 @@
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.os.Build.VERSION_CODES;
-import android.util.AttributeSet;
-import android.view.View;
-
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.recyclerview.widget.LinearLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
 import androidx.recyclerview.widget.RecyclerView.Adapter;
 import androidx.recyclerview.widget.RecyclerView.ViewHolder;
-
+import android.util.AttributeSet;
+import android.view.View;
 import com.android.setupwizardlib.DividerItemDecoration;
 import com.android.setupwizardlib.R;
 import com.android.setupwizardlib.TemplateLayout;
@@ -51,223 +49,208 @@
  */
 public class RecyclerMixin implements Mixin {
 
-    private TemplateLayout mTemplateLayout;
+  private final TemplateLayout templateLayout;
 
-    @NonNull
-    private final RecyclerView mRecyclerView;
+  @NonNull private final RecyclerView recyclerView;
 
-    @Nullable
-    private View mHeader;
+  @Nullable private View header;
 
-    @NonNull
-    private DividerItemDecoration mDividerDecoration;
+  @NonNull private DividerItemDecoration dividerDecoration;
 
-    private Drawable mDefaultDivider;
-    private Drawable mDivider;
+  private Drawable defaultDivider;
+  private Drawable divider;
 
-    private int mDividerInsetStart;
-    private int mDividerInsetEnd;
+  private int dividerInsetStart;
+  private int dividerInsetEnd;
 
-    /**
-     * Creates the RecyclerMixin. Unlike typical mixins which are created in the constructor, this
-     * mixin should be called in {@link TemplateLayout#onTemplateInflated()}, which is called by
-     * the super constructor, because the recycler view and the header needs to be made available
-     * before other mixins from the super class.
-     *
-     * @param layout The layout this mixin belongs to.
-     */
-    public RecyclerMixin(@NonNull TemplateLayout layout, @NonNull RecyclerView recyclerView) {
-        mTemplateLayout = layout;
+  /**
+   * Creates the RecyclerMixin. Unlike typical mixins which are created in the constructor, this
+   * mixin should be called in {@link TemplateLayout#onTemplateInflated()}, which is called by the
+   * super constructor, because the recycler view and the header needs to be made available before
+   * other mixins from the super class.
+   *
+   * @param layout The layout this mixin belongs to.
+   */
+  public RecyclerMixin(@NonNull TemplateLayout layout, @NonNull RecyclerView recyclerView) {
+    templateLayout = layout;
 
-        mDividerDecoration = new DividerItemDecoration(mTemplateLayout.getContext());
+    dividerDecoration = new DividerItemDecoration(templateLayout.getContext());
 
-        // The recycler view needs to be available
-        mRecyclerView = recyclerView;
-        mRecyclerView.setLayoutManager(new LinearLayoutManager(mTemplateLayout.getContext()));
+    // The recycler view needs to be available
+    this.recyclerView = recyclerView;
+    this.recyclerView.setLayoutManager(new LinearLayoutManager(templateLayout.getContext()));
 
-        if (recyclerView instanceof HeaderRecyclerView) {
-            mHeader = ((HeaderRecyclerView) recyclerView).getHeader();
-        }
-
-        mRecyclerView.addItemDecoration(mDividerDecoration);
+    if (recyclerView instanceof HeaderRecyclerView) {
+      header = ((HeaderRecyclerView) recyclerView).getHeader();
     }
 
-    /**
-     * Parse XML attributes and configures this mixin and the recycler view accordingly. This should
-     * be called from the constructor of the layout.
-     *
-     * @param attrs The {@link AttributeSet} as passed into the constructor. Can be null if the
-     *              layout was not created from XML.
-     * @param defStyleAttr The default style attribute as passed into the layout constructor. Can be
-     *                     0 if it is not needed.
-     */
-    public void parseAttributes(@Nullable AttributeSet attrs, int defStyleAttr) {
-        final Context context = mTemplateLayout.getContext();
-        final TypedArray a = context.obtainStyledAttributes(
-                attrs, R.styleable.SuwRecyclerMixin, defStyleAttr, 0);
+    this.recyclerView.addItemDecoration(dividerDecoration);
+  }
 
-        final int entries = a.getResourceId(R.styleable.SuwRecyclerMixin_android_entries, 0);
-        if (entries != 0) {
-            final ItemHierarchy inflated = new ItemInflater(context).inflate(entries);
-            final RecyclerItemAdapter adapter = new RecyclerItemAdapter(inflated);
-            adapter.setHasStableIds(a.getBoolean(
-                    R.styleable.SuwRecyclerMixin_suwHasStableIds, false));
-            setAdapter(adapter);
-        }
-        int 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);
-        }
+  /**
+   * Parse XML attributes and configures this mixin and the recycler view accordingly. This should
+   * be called from the constructor of the layout.
+   *
+   * @param attrs The {@link AttributeSet} as passed into the constructor. Can be null if the layout
+   *     was not created from XML.
+   * @param defStyleAttr The default style attribute as passed into the layout constructor. Can be 0
+   *     if it is not needed.
+   */
+  public void parseAttributes(@Nullable AttributeSet attrs, int defStyleAttr) {
+    final Context context = templateLayout.getContext();
+    final TypedArray a =
+        context.obtainStyledAttributes(attrs, R.styleable.SuwRecyclerMixin, defStyleAttr, 0);
 
-        a.recycle();
+    final int entries = a.getResourceId(R.styleable.SuwRecyclerMixin_android_entries, 0);
+    if (entries != 0) {
+      final ItemHierarchy inflated = new ItemInflater(context).inflate(entries);
+      final RecyclerItemAdapter adapter = new RecyclerItemAdapter(inflated);
+      adapter.setHasStableIds(a.getBoolean(R.styleable.SuwRecyclerMixin_suwHasStableIds, false));
+      setAdapter(adapter);
+    }
+    int 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);
     }
 
-    /**
-     * @return The recycler view contained in the layout, as marked by
-     *         {@code @id/suw_recycler_view}. This will return {@code null} if the recycler view
-     *         doesn't exist in the layout.
-     */
-    @SuppressWarnings("NullableProblems") // If clients guarantee that the template has a recycler
-                                          // view, and call this after the template is inflated,
-                                          // this will not return null.
-    public RecyclerView getRecyclerView() {
-        return mRecyclerView;
-    }
+    a.recycle();
+  }
 
-    /**
-     * Gets the header view of the recycler layout. This is useful for other mixins if they need to
-     * access views within the header, usually via {@link TemplateLayout#findManagedViewById(int)}.
-     */
-    @SuppressWarnings("NullableProblems") // If clients guarantee that the template has a header,
-                                          // this call will not return null.
-    public View getHeader() {
-        return mHeader;
-    }
+  /**
+   * @return The recycler view contained in the layout, as marked by {@code @id/suw_recycler_view}.
+   *     This will return {@code null} if the recycler view doesn't exist in the layout.
+   */
+  @SuppressWarnings("NullableProblems") // If clients guarantee that the template has a recycler
+  // view, and call this after the template is inflated,
+  // this will not return null.
+  public RecyclerView getRecyclerView() {
+    return recyclerView;
+  }
 
-    /**
-     * Recycler mixin needs to update the dividers if the layout direction has changed. This method
-     * should be called when {@link View#onLayout(boolean, int, int, int, int)} of the template
-     * is called.
-     */
-    public void onLayout() {
-        if (mDivider == null) {
-            // Update divider in case layout direction has just been resolved
-            updateDivider();
-        }
-    }
+  /**
+   * Gets the header view of the recycler layout. This is useful for other mixins if they need to
+   * access views within the header, usually via {@link TemplateLayout#findManagedViewById(int)}.
+   */
+  @SuppressWarnings("NullableProblems") // If clients guarantee that the template has a header,
+  // this call will not return null.
+  public View getHeader() {
+    return header;
+  }
 
-    /**
-     * Gets the adapter of the recycler view in this layout. If the adapter includes a header,
-     * this method will unwrap it and return the underlying adapter.
-     *
-     * @return The adapter, or {@code null} if the recycler view has no adapter.
-     */
-    public Adapter<? extends ViewHolder> getAdapter() {
-        @SuppressWarnings("unchecked") // RecyclerView.getAdapter returns raw type :(
-        final RecyclerView.Adapter<? extends ViewHolder> adapter = mRecyclerView.getAdapter();
-        if (adapter instanceof HeaderAdapter) {
-            return ((HeaderAdapter<? extends ViewHolder>) adapter).getWrappedAdapter();
-        }
-        return adapter;
+  /**
+   * Recycler mixin needs to update the dividers if the layout direction has changed. This method
+   * should be called when {@link View#onLayout(boolean, int, int, int, int)} of the template is
+   * called.
+   */
+  public void onLayout() {
+    if (divider == null) {
+      // Update divider in case layout direction has just been resolved
+      updateDivider();
     }
+  }
 
-    /**
-     * Sets the adapter on the recycler view in this layout.
-     */
-    public void setAdapter(Adapter<? extends ViewHolder> adapter) {
-        mRecyclerView.setAdapter(adapter);
+  /**
+   * Gets the adapter of the recycler view in this layout. If the adapter includes a header, this
+   * method will unwrap it and return the underlying adapter.
+   *
+   * @return The adapter, or {@code null} if the recycler view has no adapter.
+   */
+  public Adapter<? extends ViewHolder> getAdapter() {
+    @SuppressWarnings("unchecked") // RecyclerView.getAdapter returns raw type :(
+    final RecyclerView.Adapter<? extends ViewHolder> adapter = recyclerView.getAdapter();
+    if (adapter instanceof HeaderAdapter) {
+      return ((HeaderAdapter<? extends ViewHolder>) adapter).getWrappedAdapter();
     }
+    return adapter;
+  }
 
-    /**
-     * @deprecated Use {@link #setDividerInsets(int, int)} instead.
-     */
-    @Deprecated
-    public void setDividerInset(int inset) {
-        setDividerInsets(inset, 0);
-    }
+  /** Sets the adapter on the recycler view in this layout. */
+  public void setAdapter(Adapter<? extends ViewHolder> adapter) {
+    recyclerView.setAdapter(adapter);
+  }
 
-    /**
-     * 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.
-     */
-    public void setDividerInsets(int start, int end) {
-        mDividerInsetStart = start;
-        mDividerInsetEnd = end;
-        updateDivider();
-    }
+  /** @deprecated Use {@link #setDividerInsets(int, int)} instead. */
+  @Deprecated
+  public void setDividerInset(int inset) {
+    setDividerInsets(inset, 0);
+  }
 
-    /**
-     * @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 getDividerInsetStart();
-    }
+  /**
+   * 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.
+   */
+  public void setDividerInsets(int start, int end) {
+    dividerInsetStart = start;
+    dividerInsetEnd = end;
+    updateDivider();
+  }
 
-    /**
-     * @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 start side of the divider.
+   * @deprecated This is the same as {@link #getDividerInsetStart()}. Use that instead.
+   */
+  @Deprecated
+  public int getDividerInset() {
+    return getDividerInsetStart();
+  }
 
-    /**
-     * @return The number of pixels inset on the end side of the divider.
-     */
-    public int getDividerInsetEnd() {
-        return mDividerInsetEnd;
-    }
+  /** @return The number of pixels inset on the start side of the divider. */
+  public int getDividerInsetStart() {
+    return dividerInsetStart;
+  }
 
-    private void updateDivider() {
-        boolean shouldUpdate = true;
-        if (Build.VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
-            shouldUpdate = mTemplateLayout.isLayoutDirectionResolved();
-        }
-        if (shouldUpdate) {
-            if (mDefaultDivider == null) {
-                mDefaultDivider = mDividerDecoration.getDivider();
-            }
-            mDivider = DrawableLayoutDirectionHelper.createRelativeInsetDrawable(
-                    mDefaultDivider,
-                    mDividerInsetStart /* start */,
-                    0 /* top */,
-                    mDividerInsetEnd /* end */,
-                    0 /* bottom */,
-                    mTemplateLayout);
-            mDividerDecoration.setDivider(mDivider);
-        }
-    }
+  /** @return The number of pixels inset on the end side of the divider. */
+  public int getDividerInsetEnd() {
+    return dividerInsetEnd;
+  }
 
-    /**
-     * @return The drawable used as the divider.
-     */
-    public Drawable getDivider() {
-        return mDivider;
+  private void updateDivider() {
+    boolean shouldUpdate = true;
+    if (Build.VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
+      shouldUpdate = templateLayout.isLayoutDirectionResolved();
     }
+    if (shouldUpdate) {
+      if (defaultDivider == null) {
+        defaultDivider = dividerDecoration.getDivider();
+      }
+      divider =
+          DrawableLayoutDirectionHelper.createRelativeInsetDrawable(
+              defaultDivider,
+              dividerInsetStart /* start */,
+              0 /* top */,
+              dividerInsetEnd /* end */,
+              0 /* bottom */,
+              templateLayout);
+      dividerDecoration.setDivider(divider);
+    }
+  }
 
-    /**
-     * Sets the divider item decoration directly. This is a low level method which should be used
-     * only if custom divider behavior is needed, for example if the divider should be shown /
-     * hidden in some specific cases for view holders that cannot implement
-     * {@link com.android.setupwizardlib.DividerItemDecoration.DividedViewHolder}.
-     */
-    public void setDividerItemDecoration(@NonNull DividerItemDecoration decoration) {
-        mRecyclerView.removeItemDecoration(mDividerDecoration);
-        mDividerDecoration = decoration;
-        mRecyclerView.addItemDecoration(mDividerDecoration);
-        updateDivider();
-    }
+  /** @return The drawable used as the divider. */
+  public Drawable getDivider() {
+    return divider;
+  }
+
+  /**
+   * Sets the divider item decoration directly. This is a low level method which should be used only
+   * if custom divider behavior is needed, for example if the divider should be shown / hidden in
+   * some specific cases for view holders that cannot implement {@link
+   * com.android.setupwizardlib.DividerItemDecoration.DividedViewHolder}.
+   */
+  public void setDividerItemDecoration(@NonNull DividerItemDecoration decoration) {
+    recyclerView.removeItemDecoration(dividerDecoration);
+    dividerDecoration = decoration;
+    recyclerView.addItemDecoration(dividerDecoration);
+    updateDivider();
+  }
 }
diff --git a/library/recyclerview/src/com/android/setupwizardlib/template/RecyclerViewScrollHandlingDelegate.java b/library/recyclerview/src/com/android/setupwizardlib/template/RecyclerViewScrollHandlingDelegate.java
index bfe8df2..8838c44 100644
--- a/library/recyclerview/src/com/android/setupwizardlib/template/RecyclerViewScrollHandlingDelegate.java
+++ b/library/recyclerview/src/com/android/setupwizardlib/template/RecyclerViewScrollHandlingDelegate.java
@@ -16,12 +16,10 @@
 
 package com.android.setupwizardlib.template;
 
-import android.util.Log;
-
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.recyclerview.widget.RecyclerView;
-
+import android.util.Log;
 import com.android.setupwizardlib.template.RequireScrollMixin.ScrollHandlingDelegate;
 
 /**
@@ -30,55 +28,53 @@
  */
 public class RecyclerViewScrollHandlingDelegate implements ScrollHandlingDelegate {
 
-    private static final String TAG = "RVRequireScrollMixin";
+  private static final String TAG = "RVRequireScrollMixin";
 
-    @Nullable
-    private final RecyclerView mRecyclerView;
+  @Nullable private final RecyclerView recyclerView;
 
-    @NonNull
-    private final RequireScrollMixin mRequireScrollMixin;
+  @NonNull private final RequireScrollMixin requireScrollMixin;
 
-    public RecyclerViewScrollHandlingDelegate(
-            @NonNull RequireScrollMixin requireScrollMixin,
-            @Nullable RecyclerView recyclerView) {
-        mRequireScrollMixin = requireScrollMixin;
-        mRecyclerView = recyclerView;
+  public RecyclerViewScrollHandlingDelegate(
+      @NonNull RequireScrollMixin requireScrollMixin, @Nullable RecyclerView recyclerView) {
+    this.requireScrollMixin = requireScrollMixin;
+    this.recyclerView = recyclerView;
+  }
+
+  private boolean canScrollDown() {
+    if (recyclerView != null) {
+      // Compatibility implementation of View#canScrollVertically
+      final int offset = recyclerView.computeVerticalScrollOffset();
+      final int range =
+          recyclerView.computeVerticalScrollRange() - recyclerView.computeVerticalScrollExtent();
+      return range != 0 && offset < range - 1;
     }
+    return false;
+  }
 
-    private boolean canScrollDown() {
-        if (mRecyclerView != null) {
-            // Compatibility implementation of View#canScrollVertically
-            final int offset = mRecyclerView.computeVerticalScrollOffset();
-            final int range = mRecyclerView.computeVerticalScrollRange()
-                    - mRecyclerView.computeVerticalScrollExtent();
-            return range != 0 && offset < range - 1;
-        }
-        return false;
-    }
-
-    @Override
-    public void startListening() {
-        if (mRecyclerView != null) {
-            mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
-                @Override
-                public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
-                    mRequireScrollMixin.notifyScrollabilityChange(canScrollDown());
-                }
-            });
-
-            if (canScrollDown()) {
-                mRequireScrollMixin.notifyScrollabilityChange(true);
+  @Override
+  public void startListening() {
+    if (this.recyclerView != null) {
+      this.recyclerView.addOnScrollListener(
+          new RecyclerView.OnScrollListener() {
+            @Override
+            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
+              requireScrollMixin.notifyScrollabilityChange(canScrollDown());
             }
-        } else {
-            Log.w(TAG, "Cannot require scroll. Recycler view is null.");
-        }
-    }
+          });
 
-    @Override
-    public void pageScrollDown() {
-        if (mRecyclerView != null) {
-            final int height = mRecyclerView.getHeight();
-            mRecyclerView.smoothScrollBy(0, height);
-        }
+      if (canScrollDown()) {
+        requireScrollMixin.notifyScrollabilityChange(true);
+      }
+    } else {
+      Log.w(TAG, "Cannot require scroll. Recycler view is null.");
     }
+  }
+
+  @Override
+  public void pageScrollDown() {
+    if (recyclerView != null) {
+      final int height = recyclerView.getHeight();
+      recyclerView.smoothScrollBy(0, height);
+    }
+  }
 }
diff --git a/library/recyclerview/src/com/android/setupwizardlib/view/HeaderRecyclerView.java b/library/recyclerview/src/com/android/setupwizardlib/view/HeaderRecyclerView.java
index 0304b65..3808e11 100644
--- a/library/recyclerview/src/com/android/setupwizardlib/view/HeaderRecyclerView.java
+++ b/library/recyclerview/src/com/android/setupwizardlib/view/HeaderRecyclerView.java
@@ -19,259 +19,257 @@
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.os.Build;
+import androidx.recyclerview.widget.RecyclerView;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.FrameLayout;
-
-import androidx.recyclerview.widget.RecyclerView;
-
 import com.android.setupwizardlib.DividerItemDecoration;
 import com.android.setupwizardlib.R;
 
 /**
  * A RecyclerView that can display a header item at the start of the list. The header can be set by
- * {@code app:suwHeader} in XML. Note that the header will not be inflated until a layout manager
- * is set.
+ * {@code app:suwHeader} in XML. Note that the header will not be inflated until a layout manager is
+ * set.
  */
 public class HeaderRecyclerView extends RecyclerView {
 
-    private static class HeaderViewHolder extends ViewHolder
-            implements DividerItemDecoration.DividedViewHolder {
+  private static class HeaderViewHolder extends ViewHolder
+      implements DividerItemDecoration.DividedViewHolder {
 
-        HeaderViewHolder(View itemView) {
-            super(itemView);
-        }
-
-        @Override
-        public boolean isDividerAllowedAbove() {
-            return false;
-        }
-
-        @Override
-        public boolean isDividerAllowedBelow() {
-            return false;
-        }
+    HeaderViewHolder(View itemView) {
+      super(itemView);
     }
 
-    /**
-     * An adapter that can optionally add one header item to the RecyclerView.
-     *
-     * @param <CVH> Type of the content view holder. i.e. view holder type of the wrapped adapter.
-     */
-    public static class HeaderAdapter<CVH extends ViewHolder>
-            extends RecyclerView.Adapter<ViewHolder> {
+    @Override
+    public boolean isDividerAllowedAbove() {
+      return false;
+    }
 
-        private static final int HEADER_VIEW_TYPE = Integer.MAX_VALUE;
+    @Override
+    public boolean isDividerAllowedBelow() {
+      return false;
+    }
+  }
 
-        private RecyclerView.Adapter<CVH> mAdapter;
-        private View mHeader;
+  /**
+   * An adapter that can optionally add one header item to the RecyclerView.
+   *
+   * @param <CVH> Type of the content view holder. i.e. view holder type of the wrapped adapter.
+   */
+  public static class HeaderAdapter<CVH extends ViewHolder>
+      extends RecyclerView.Adapter<ViewHolder> {
 
-        private final AdapterDataObserver mObserver = new AdapterDataObserver() {
+    private static final int HEADER_VIEW_TYPE = Integer.MAX_VALUE;
 
-            @Override
-            public void onChanged() {
-                notifyDataSetChanged();
+    private final RecyclerView.Adapter<CVH> adapter;
+    private View header;
+
+    private final AdapterDataObserver observer =
+        new AdapterDataObserver() {
+
+          @Override
+          public void onChanged() {
+            notifyDataSetChanged();
+          }
+
+          @Override
+          public void onItemRangeChanged(int positionStart, int itemCount) {
+            if (header != null) {
+              positionStart++;
             }
+            notifyItemRangeChanged(positionStart, itemCount);
+          }
 
-            @Override
-            public void onItemRangeChanged(int positionStart, int itemCount) {
-                if (mHeader != null) {
-                    positionStart++;
-                }
-                notifyItemRangeChanged(positionStart, itemCount);
+          @Override
+          public void onItemRangeInserted(int positionStart, int itemCount) {
+            if (header != null) {
+              positionStart++;
             }
+            notifyItemRangeInserted(positionStart, itemCount);
+          }
 
-            @Override
-            public void onItemRangeInserted(int positionStart, int itemCount) {
-                if (mHeader != null) {
-                    positionStart++;
-                }
-                notifyItemRangeInserted(positionStart, itemCount);
+          @Override
+          public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
+            if (header != null) {
+              fromPosition++;
+              toPosition++;
             }
+            // Why is there no notifyItemRangeMoved?
+            for (int i = 0; i < itemCount; i++) {
+              notifyItemMoved(fromPosition + i, toPosition + i);
+            }
+          }
 
-            @Override
-            public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
-                if (mHeader != null) {
-                    fromPosition++;
-                    toPosition++;
-                }
-                // Why is there no notifyItemRangeMoved?
-                for (int i = 0; i < itemCount; i++) {
-                    notifyItemMoved(fromPosition + i, toPosition + i);
-                }
+          @Override
+          public void onItemRangeRemoved(int positionStart, int itemCount) {
+            if (header != null) {
+              positionStart++;
             }
-
-            @Override
-            public void onItemRangeRemoved(int positionStart, int itemCount) {
-                if (mHeader != null) {
-                    positionStart++;
-                }
-                notifyItemRangeRemoved(positionStart, itemCount);
-            }
+            notifyItemRangeRemoved(positionStart, itemCount);
+          }
         };
 
-        public HeaderAdapter(RecyclerView.Adapter<CVH> adapter) {
-            mAdapter = adapter;
-            mAdapter.registerAdapterDataObserver(mObserver);
-            setHasStableIds(mAdapter.hasStableIds());
-        }
-
-        @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 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.setLayoutParams(params);
-                return new HeaderViewHolder(frameLayout);
-            } else {
-                return mAdapter.onCreateViewHolder(parent, viewType);
-            }
-        }
-
-        @Override
-        @SuppressWarnings("unchecked") // Non-header position always return type CVH
-        public void onBindViewHolder(ViewHolder holder, int position) {
-            if (mHeader != null) {
-                position--;
-            }
-
-            if (holder instanceof HeaderViewHolder) {
-                if (mHeader == null) {
-                    throw new IllegalStateException("HeaderViewHolder cannot find mHeader");
-                }
-                if (mHeader.getParent() != null) {
-                    ((ViewGroup) mHeader.getParent()).removeView(mHeader);
-                }
-                FrameLayout mHeaderParent = (FrameLayout) holder.itemView;
-                mHeaderParent.addView(mHeader);
-            } else {
-                mAdapter.onBindViewHolder((CVH) holder, position);
-            }
-        }
-
-        @Override
-        public int getItemViewType(int position) {
-            if (mHeader != null) {
-                position--;
-            }
-            if (position < 0) {
-                return HEADER_VIEW_TYPE;
-            }
-            return mAdapter.getItemViewType(position);
-        }
-
-        @Override
-        public int getItemCount() {
-            int count = mAdapter.getItemCount();
-            if (mHeader != null) {
-                count++;
-            }
-            return count;
-        }
-
-        @Override
-        public long getItemId(int position) {
-            if (mHeader != null) {
-                position--;
-            }
-            if (position < 0) {
-                return Long.MAX_VALUE;
-            }
-            return mAdapter.getItemId(position);
-        }
-
-        public void setHeader(View header) {
-            mHeader = header;
-        }
-
-        public RecyclerView.Adapter<CVH> getWrappedAdapter() {
-            return mAdapter;
-        }
-    }
-
-    private View mHeader;
-    private int mHeaderRes;
-
-    public HeaderRecyclerView(Context context) {
-        super(context);
-        init(null, 0);
-    }
-
-    public HeaderRecyclerView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        init(attrs, 0);
-    }
-
-    public HeaderRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-        init(attrs, defStyleAttr);
-    }
-
-    private void init(AttributeSet attrs, int defStyleAttr) {
-        final TypedArray a = getContext().obtainStyledAttributes(attrs,
-                R.styleable.SuwHeaderRecyclerView, defStyleAttr, 0);
-        mHeaderRes = a.getResourceId(R.styleable.SuwHeaderRecyclerView_suwHeader, 0);
-        a.recycle();
+    public HeaderAdapter(RecyclerView.Adapter<CVH> adapter) {
+      this.adapter = adapter;
+      this.adapter.registerAdapterDataObserver(observer);
+      setHasStableIds(this.adapter.hasStableIds());
     }
 
     @Override
-    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
-        super.onInitializeAccessibilityEvent(event);
+    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+      // Returning the same view (header) 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.setLayoutParams(params);
+        return new HeaderViewHolder(frameLayout);
+      } else {
+        return adapter.onCreateViewHolder(parent, viewType);
+      }
+    }
 
-        // Decoration-only headers should not count as an item for accessibility, adjust the
-        // accessibility event to account for that.
-        final int numberOfHeaders = mHeader != null ? 1 : 0;
-        event.setItemCount(event.getItemCount() - numberOfHeaders);
-        event.setFromIndex(Math.max(event.getFromIndex() - numberOfHeaders, 0));
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
-            event.setToIndex(Math.max(event.getToIndex() - numberOfHeaders, 0));
+    @Override
+    @SuppressWarnings("unchecked") // Non-header position always return type CVH
+    public void onBindViewHolder(ViewHolder holder, int position) {
+      if (header != null) {
+        position--;
+      }
+
+      if (holder instanceof HeaderViewHolder) {
+        if (header == null) {
+          throw new IllegalStateException("HeaderViewHolder cannot find mHeader");
         }
+        if (header.getParent() != null) {
+          ((ViewGroup) header.getParent()).removeView(header);
+        }
+        FrameLayout mHeaderParent = (FrameLayout) holder.itemView;
+        mHeaderParent.addView(header);
+      } else {
+        adapter.onBindViewHolder((CVH) holder, position);
+      }
     }
 
-    /**
-     * Gets the header view of this RecyclerView, or {@code null} if there are no headers.
-     */
-    public View getHeader() {
-        return mHeader;
+    @Override
+    public int getItemViewType(int position) {
+      if (header != null) {
+        position--;
+      }
+      if (position < 0) {
+        return HEADER_VIEW_TYPE;
+      }
+      return adapter.getItemViewType(position);
     }
 
-    /**
-     * Set the view to use as the header of this recycler view.
-     * Note: This must be called before setAdapter.
-     */
+    @Override
+    public int getItemCount() {
+      int count = adapter.getItemCount();
+      if (header != null) {
+        count++;
+      }
+      return count;
+    }
+
+    @Override
+    public long getItemId(int position) {
+      if (header != null) {
+        position--;
+      }
+      if (position < 0) {
+        return Long.MAX_VALUE;
+      }
+      return adapter.getItemId(position);
+    }
+
     public void setHeader(View header) {
-        mHeader = header;
+      this.header = header;
     }
 
-    @Override
-    public void setLayoutManager(LayoutManager layout) {
-        super.setLayoutManager(layout);
-        if (layout != null && mHeader == null && mHeaderRes != 0) {
-            // Inflating a child view requires the layout manager to be set. Check here to see if
-            // any header item is specified in XML and inflate them.
-            final LayoutInflater inflater = LayoutInflater.from(getContext());
-            mHeader = inflater.inflate(mHeaderRes, this, false);
-        }
+    public RecyclerView.Adapter<CVH> getWrappedAdapter() {
+      return adapter;
     }
+  }
 
-    @Override
-    @SuppressWarnings("rawtypes,unchecked") // RecyclerView.setAdapter uses raw type :(
-    public void setAdapter(Adapter adapter) {
-        if (mHeader != null && adapter != null) {
-            final HeaderAdapter headerAdapter = new HeaderAdapter(adapter);
-            headerAdapter.setHeader(mHeader);
-            adapter = headerAdapter;
-        }
-        super.setAdapter(adapter);
+  private View header;
+  private int headerRes;
+
+  public HeaderRecyclerView(Context context) {
+    super(context);
+    init(null, 0);
+  }
+
+  public HeaderRecyclerView(Context context, AttributeSet attrs) {
+    super(context, attrs);
+    init(attrs, 0);
+  }
+
+  public HeaderRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
+    super(context, attrs, defStyleAttr);
+    init(attrs, defStyleAttr);
+  }
+
+  private void init(AttributeSet attrs, int defStyleAttr) {
+    final TypedArray a =
+        getContext()
+            .obtainStyledAttributes(attrs, R.styleable.SuwHeaderRecyclerView, defStyleAttr, 0);
+    headerRes = a.getResourceId(R.styleable.SuwHeaderRecyclerView_suwHeader, 0);
+    a.recycle();
+  }
+
+  @Override
+  public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+    super.onInitializeAccessibilityEvent(event);
+
+    // Decoration-only headers should not count as an item for accessibility, adjust the
+    // accessibility event to account for that.
+    final int numberOfHeaders = header != null ? 1 : 0;
+    event.setItemCount(event.getItemCount() - numberOfHeaders);
+    event.setFromIndex(Math.max(event.getFromIndex() - numberOfHeaders, 0));
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
+      event.setToIndex(Math.max(event.getToIndex() - numberOfHeaders, 0));
     }
+  }
+
+  /** Gets the header view of this RecyclerView, or {@code null} if there are no headers. */
+  public View getHeader() {
+    return header;
+  }
+
+  /**
+   * Set the view to use as the header of this recycler view. Note: This must be called before
+   * setAdapter.
+   */
+  public void setHeader(View header) {
+    this.header = header;
+  }
+
+  @Override
+  public void setLayoutManager(LayoutManager layout) {
+    super.setLayoutManager(layout);
+    if (layout != null && header == null && headerRes != 0) {
+      // Inflating a child view requires the layout manager to be set. Check here to see if
+      // any header item is specified in XML and inflate them.
+      final LayoutInflater inflater = LayoutInflater.from(getContext());
+      header = inflater.inflate(headerRes, this, false);
+    }
+  }
+
+  @Override
+  @SuppressWarnings("rawtypes,unchecked") // RecyclerView.setAdapter uses raw type :(
+  public void setAdapter(Adapter adapter) {
+    if (header != null && adapter != null) {
+      final HeaderAdapter headerAdapter = new HeaderAdapter(adapter);
+      headerAdapter.setHeader(header);
+      adapter = headerAdapter;
+    }
+    super.setAdapter(adapter);
+  }
 }
diff --git a/library/recyclerview/src/com/android/setupwizardlib/view/StickyHeaderRecyclerView.java b/library/recyclerview/src/com/android/setupwizardlib/view/StickyHeaderRecyclerView.java
index d51ea56..a5fa69c 100644
--- a/library/recyclerview/src/com/android/setupwizardlib/view/StickyHeaderRecyclerView.java
+++ b/library/recyclerview/src/com/android/setupwizardlib/view/StickyHeaderRecyclerView.java
@@ -32,112 +32,114 @@
  * to be drawn when the sticky element hits the top of the view.
  *
  * <p>There are a few things to note:
+ *
  * <ol>
  *   <li>The view does not work well with padding. b/16190933
  *   <li>If fitsSystemWindows is true, then this will offset the sticking position by the height of
- *   the system decorations at the top of the screen.
+ *       the system decorations at the top of the screen.
  * </ol>
  */
 public class StickyHeaderRecyclerView extends HeaderRecyclerView {
 
-    private View mSticky;
-    private int mStatusBarInset = 0;
-    private RectF mStickyRect = new RectF();
+  private View sticky;
+  private int statusBarInset = 0;
+  private final RectF stickyRect = new RectF();
 
-    public StickyHeaderRecyclerView(Context context) {
-        super(context);
-    }
+  public StickyHeaderRecyclerView(Context context) {
+    super(context);
+  }
 
-    public StickyHeaderRecyclerView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
+  public StickyHeaderRecyclerView(Context context, AttributeSet attrs) {
+    super(context, attrs);
+  }
 
-    public StickyHeaderRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-    }
+  public StickyHeaderRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
+    super(context, attrs, defStyleAttr);
+  }
 
-    @Override
-    protected void onLayout(boolean changed, int l, int t, int r, int b) {
-        super.onLayout(changed, l, t, r, b);
-        if (mSticky == null) {
-            updateStickyView();
-        }
-        if (mSticky != null) {
-            final View headerView = getHeader();
-            if (headerView != null && headerView.getHeight() == 0) {
-                headerView.layout(0, -headerView.getMeasuredHeight(),
-                        headerView.getMeasuredWidth(), 0);
-            }
-        }
+  @Override
+  protected void onLayout(boolean changed, int l, int t, int r, int b) {
+    super.onLayout(changed, l, t, r, b);
+    if (sticky == null) {
+      updateStickyView();
     }
+    if (sticky != null) {
+      final View headerView = getHeader();
+      if (headerView != null && headerView.getHeight() == 0) {
+        headerView.layout(0, -headerView.getMeasuredHeight(), headerView.getMeasuredWidth(), 0);
+      }
+    }
+  }
 
-    @Override
-    protected void onMeasure(int widthSpec, int heightSpec) {
-        super.onMeasure(widthSpec, heightSpec);
-        if (mSticky != null) {
-            measureChild(getHeader(), widthSpec, heightSpec);
-        }
+  @Override
+  protected void onMeasure(int widthSpec, int heightSpec) {
+    super.onMeasure(widthSpec, heightSpec);
+    if (sticky != null) {
+      measureChild(getHeader(), widthSpec, heightSpec);
     }
+  }
 
-    /**
-     * 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) {
-            mSticky = header.findViewWithTag("sticky");
-        }
+  /**
+   * 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) {
+      sticky = header.findViewWithTag("sticky");
     }
+  }
 
-    @Override
-    public void draw(Canvas canvas) {
-        super.draw(canvas);
-        if (mSticky != null) {
-            final View headerView = getHeader();
-            final int saveCount = canvas.save();
-            // The view to draw when sticking to the top
-            final View drawTarget = headerView != null ? headerView : mSticky;
-            // The offset to draw the view at when sticky
-            final int drawOffset = headerView != null ? mSticky.getTop() : 0;
-            // Position of the draw target, relative to the outside of the scrollView
-            final int drawTop = drawTarget.getTop();
-            if (drawTop + drawOffset < mStatusBarInset || !drawTarget.isShown()) {
-                // RecyclerView does not translate the canvas, so we can simply draw at the top
-                mStickyRect.set(0, -drawOffset + mStatusBarInset, drawTarget.getWidth(),
-                        drawTarget.getHeight() - drawOffset + mStatusBarInset);
-                canvas.translate(0, mStickyRect.top);
-                canvas.clipRect(0, 0, drawTarget.getWidth(), drawTarget.getHeight());
-                drawTarget.draw(canvas);
-            } else {
-                mStickyRect.setEmpty();
-            }
-            canvas.restoreToCount(saveCount);
-        }
+  @Override
+  public void draw(Canvas canvas) {
+    super.draw(canvas);
+    if (sticky != null) {
+      final View headerView = getHeader();
+      final int saveCount = canvas.save();
+      // The view to draw when sticking to the top
+      final View drawTarget = headerView != null ? headerView : sticky;
+      // The offset to draw the view at when sticky
+      final int drawOffset = headerView != null ? sticky.getTop() : 0;
+      // Position of the draw target, relative to the outside of the scrollView
+      final int drawTop = drawTarget.getTop();
+      if (drawTop + drawOffset < statusBarInset || !drawTarget.isShown()) {
+        // RecyclerView does not translate the canvas, so we can simply draw at the top
+        stickyRect.set(
+            0,
+            -drawOffset + statusBarInset,
+            drawTarget.getWidth(),
+            drawTarget.getHeight() - drawOffset + statusBarInset);
+        canvas.translate(0, stickyRect.top);
+        canvas.clipRect(0, 0, drawTarget.getWidth(), drawTarget.getHeight());
+        drawTarget.draw(canvas);
+      } else {
+        stickyRect.setEmpty();
+      }
+      canvas.restoreToCount(saveCount);
     }
+  }
 
-    @Override
-    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
-    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
-        if (getFitsSystemWindows()) {
-            mStatusBarInset = insets.getSystemWindowInsetTop();
-            insets.replaceSystemWindowInsets(
-                    insets.getSystemWindowInsetLeft(),
-                    0, /* top */
-                    insets.getSystemWindowInsetRight(),
-                    insets.getSystemWindowInsetBottom()
-            );
-        }
-        return insets;
+  @Override
+  @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+  public WindowInsets onApplyWindowInsets(WindowInsets insets) {
+    if (getFitsSystemWindows()) {
+      statusBarInset = insets.getSystemWindowInsetTop();
+      insets.replaceSystemWindowInsets(
+          insets.getSystemWindowInsetLeft(),
+          0, /* top */
+          insets.getSystemWindowInsetRight(),
+          insets.getSystemWindowInsetBottom());
     }
+    return insets;
+  }
 
-    @Override
-    public boolean dispatchTouchEvent(MotionEvent ev) {
-        if (mStickyRect.contains(ev.getX(), ev.getY())) {
-            ev.offsetLocation(-mStickyRect.left, -mStickyRect.top);
-            return getHeader().dispatchTouchEvent(ev);
-        } else {
-            return super.dispatchTouchEvent(ev);
-        }
+  @Override
+  public boolean dispatchTouchEvent(MotionEvent ev) {
+    if (stickyRect.contains(ev.getX(), ev.getY())) {
+      ev.offsetLocation(-stickyRect.left, -stickyRect.top);
+      return getHeader().dispatchTouchEvent(ev);
+    } else {
+      return super.dispatchTouchEvent(ev);
     }
+  }
 }
diff --git a/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/items/RecyclerItemAdapterTest.java b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/items/RecyclerItemAdapterTest.java
index 6f42e84..bed736e 100644
--- a/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/items/RecyclerItemAdapterTest.java
+++ b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/items/RecyclerItemAdapterTest.java
@@ -32,16 +32,13 @@
 import android.graphics.drawable.GradientDrawable;
 import android.graphics.drawable.ShapeDrawable;
 import android.graphics.drawable.shapes.RectShape;
+import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver;
+import android.widget.FrameLayout;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.widget.FrameLayout;
-
-import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver;
-
 import com.android.setupwizardlib.items.RecyclerItemAdapter.PatchedLayerDrawable;
 import com.android.setupwizardlib.test.R;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -50,111 +47,110 @@
 @SmallTest
 public class RecyclerItemAdapterTest {
 
-    private Item[] mItems = new Item[5];
-    private ItemGroup mItemGroup = new ItemGroup();
+  private Item[] mItems = new Item[5];
+  private ItemGroup mItemGroup = new ItemGroup();
 
-    @Before
-    public void setUp() throws Exception {
-        for (int i = 0; i < 5; i++) {
-            Item item = new Item();
-            item.setTitle("TestTitle" + i);
-            item.setId(i);
-            // Layout resource: 0 -> 1, 1 -> 11, 2 -> 21, 3 -> 1, 4 -> 11.
-            // (Resource IDs cannot be 0)
-            item.setLayoutResource((i % 3) * 10 + 1);
-            mItems[i] = item;
-            mItemGroup.addChild(item);
-        }
+  @Before
+  public void setUp() throws Exception {
+    for (int i = 0; i < 5; i++) {
+      Item item = new Item();
+      item.setTitle("TestTitle" + i);
+      item.setId(i);
+      // Layout resource: 0 -> 1, 1 -> 11, 2 -> 21, 3 -> 1, 4 -> 11.
+      // (Resource IDs cannot be 0)
+      item.setLayoutResource((i % 3) * 10 + 1);
+      mItems[i] = item;
+      mItemGroup.addChild(item);
     }
+  }
 
-    @Test
-    public void testAdapter() {
-        RecyclerItemAdapter adapter = new RecyclerItemAdapter(mItemGroup);
-        assertEquals("Adapter should have 5 items", 5, adapter.getItemCount());
-        assertEquals("Adapter should return the first item", mItems[0], adapter.getItem(0));
-        assertEquals("ID should be same as position", 2, adapter.getItemId(2));
+  @Test
+  public void testAdapter() {
+    RecyclerItemAdapter adapter = new RecyclerItemAdapter(mItemGroup);
+    assertEquals("Adapter should have 5 items", 5, adapter.getItemCount());
+    assertEquals("Adapter should return the first item", mItems[0], adapter.getItem(0));
+    assertEquals("ID should be same as position", 2, adapter.getItemId(2));
 
-        // ViewType is same as layout resource for RecyclerItemAdapter
-        assertEquals("Second item should have view type 21", 21, adapter.getItemViewType(2));
-    }
+    // ViewType is same as layout resource for RecyclerItemAdapter
+    assertEquals("Second item should have view type 21", 21, adapter.getItemViewType(2));
+  }
 
-    @Test
-    public void testGetRootItemHierarchy() {
-        RecyclerItemAdapter adapter = new RecyclerItemAdapter(mItemGroup);
-        ItemHierarchy root = adapter.getRootItemHierarchy();
-        assertSame("Root item hierarchy should be mItemGroup", mItemGroup, root);
-    }
+  @Test
+  public void testGetRootItemHierarchy() {
+    RecyclerItemAdapter adapter = new RecyclerItemAdapter(mItemGroup);
+    ItemHierarchy root = adapter.getRootItemHierarchy();
+    assertSame("Root item hierarchy should be mItemGroup", mItemGroup, root);
+  }
 
-    @Test
-    public void testPatchedLayerDrawableNoPadding() {
-        ShapeDrawable child = new ShapeDrawable(new RectShape());
-        child.setPadding(0, 0, 0, 0);
-        PatchedLayerDrawable drawable = new PatchedLayerDrawable(new Drawable[] { child });
+  @Test
+  public void testPatchedLayerDrawableNoPadding() {
+    ShapeDrawable child = new ShapeDrawable(new RectShape());
+    child.setPadding(0, 0, 0, 0);
+    PatchedLayerDrawable drawable = new PatchedLayerDrawable(new Drawable[] {child});
 
-        Rect padding = new Rect();
-        assertFalse("Patched layer drawable should not have padding", drawable.getPadding(padding));
-        assertEquals(new Rect(0, 0, 0, 0), padding);
-    }
+    Rect padding = new Rect();
+    assertFalse("Patched layer drawable should not have padding", drawable.getPadding(padding));
+    assertEquals(new Rect(0, 0, 0, 0), padding);
+  }
 
-    @Test
-    public void testPatchedLayerDrawableWithPadding() {
-        ShapeDrawable child = new ShapeDrawable(new RectShape());
-        child.setPadding(10, 10, 10, 10);
-        PatchedLayerDrawable drawable = new PatchedLayerDrawable(new Drawable[] { child });
+  @Test
+  public void testPatchedLayerDrawableWithPadding() {
+    ShapeDrawable child = new ShapeDrawable(new RectShape());
+    child.setPadding(10, 10, 10, 10);
+    PatchedLayerDrawable drawable = new PatchedLayerDrawable(new Drawable[] {child});
 
-        Rect padding = new Rect();
-        assertTrue("Patched layer drawable should have padding", drawable.getPadding(padding));
-        assertEquals(new Rect(10, 10, 10, 10), padding);
-    }
+    Rect padding = new Rect();
+    assertTrue("Patched layer drawable should have padding", drawable.getPadding(padding));
+    assertEquals(new Rect(10, 10, 10, 10), padding);
+  }
 
-    @Test
-    public void testAdapterNotifications() {
-        RecyclerItemAdapter adapter = new RecyclerItemAdapter(mItemGroup);
-        final AdapterDataObserver observer = mock(AdapterDataObserver.class);
-        adapter.registerAdapterDataObserver(observer);
+  @Test
+  public void testAdapterNotifications() {
+    RecyclerItemAdapter adapter = new RecyclerItemAdapter(mItemGroup);
+    final AdapterDataObserver observer = mock(AdapterDataObserver.class);
+    adapter.registerAdapterDataObserver(observer);
 
-        mItems[0].setTitle("Child 1");
-        verify(observer).onItemRangeChanged(eq(0), eq(1), anyObject());
+    mItems[0].setTitle("Child 1");
+    verify(observer).onItemRangeChanged(eq(0), eq(1), anyObject());
 
-        mItemGroup.removeChild(mItems[1]);
-        verify(observer).onItemRangeRemoved(eq(1), eq(1));
+    mItemGroup.removeChild(mItems[1]);
+    verify(observer).onItemRangeRemoved(eq(1), eq(1));
 
-        mItemGroup.addChild(mItems[1]);
-        verify(observer).onItemRangeInserted(eq(4), eq(1));
-    }
+    mItemGroup.addChild(mItems[1]);
+    verify(observer).onItemRangeInserted(eq(4), eq(1));
+  }
 
-    @Test
-    public void testCreateViewHolder() {
-        RecyclerItemAdapter adapter = new RecyclerItemAdapter(mItemGroup);
-        FrameLayout parent = new FrameLayout(InstrumentationRegistry.getContext());
+  @Test
+  public void testCreateViewHolder() {
+    RecyclerItemAdapter adapter = new RecyclerItemAdapter(mItemGroup);
+    FrameLayout parent = new FrameLayout(InstrumentationRegistry.getContext());
 
-        final ItemViewHolder viewHolder =
-                adapter.onCreateViewHolder(parent, R.layout.test_list_item);
-        assertNotNull("Background should be set", viewHolder.itemView.getBackground());
-        assertEquals("foobar", viewHolder.itemView.getTag());
-    }
+    final ItemViewHolder viewHolder = adapter.onCreateViewHolder(parent, R.layout.test_list_item);
+    assertNotNull("Background should be set", viewHolder.itemView.getBackground());
+    assertEquals("foobar", viewHolder.itemView.getTag());
+  }
 
-    @Test
-    public void testCreateViewHolderNoBackground() {
-        RecyclerItemAdapter adapter = new RecyclerItemAdapter(mItemGroup);
-        FrameLayout parent = new FrameLayout(InstrumentationRegistry.getContext());
+  @Test
+  public void testCreateViewHolderNoBackground() {
+    RecyclerItemAdapter adapter = new RecyclerItemAdapter(mItemGroup);
+    FrameLayout parent = new FrameLayout(InstrumentationRegistry.getContext());
 
-        final ItemViewHolder viewHolder =
-                adapter.onCreateViewHolder(parent, R.layout.test_list_item_no_background);
-        assertNull("Background should be null", viewHolder.itemView.getBackground());
-    }
+    final ItemViewHolder viewHolder =
+        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());
+  @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);
+    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);
-    }
+    PatchedLayerDrawable layerDrawable = (PatchedLayerDrawable) background;
+    assertTrue(layerDrawable.getDrawable(0) instanceof GradientDrawable);
+  }
 }
diff --git a/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/template/RecyclerMixinTest.java b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/template/RecyclerMixinTest.java
index ece4bf9..f295b91 100644
--- a/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/template/RecyclerMixinTest.java
+++ b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/template/RecyclerMixinTest.java
@@ -30,17 +30,14 @@
 import android.graphics.drawable.InsetDrawable;
 import android.os.Build.VERSION;
 import android.os.Build.VERSION_CODES;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.RecyclerView.Adapter;
+import android.view.View;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.view.View;
-
-import androidx.recyclerview.widget.RecyclerView;
-import androidx.recyclerview.widget.RecyclerView.Adapter;
-
 import com.android.setupwizardlib.TemplateLayout;
 import com.android.setupwizardlib.test.R;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -51,120 +48,119 @@
 @SmallTest
 public class RecyclerMixinTest {
 
-    private Context mContext;
-    private TemplateLayout mTemplateLayout;
+  private Context mContext;
+  private TemplateLayout mTemplateLayout;
 
-    private RecyclerView mRecyclerView;
+  private RecyclerView mRecyclerView;
 
-    @Mock
-    private Adapter mAdapter;
+  @Mock private Adapter mAdapter;
 
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
+  @Before
+  public void setUp() {
+    MockitoAnnotations.initMocks(this);
 
-        mContext = InstrumentationRegistry.getTargetContext();
-        mTemplateLayout = spy(new TemplateLayout(mContext, R.layout.test_template,
-                R.id.suw_layout_content));
+    mContext = InstrumentationRegistry.getTargetContext();
+    mTemplateLayout =
+        spy(new TemplateLayout(mContext, R.layout.test_template, R.id.suw_layout_content));
 
-        mRecyclerView = mock(RecyclerView.class, delegatesTo(new RecyclerView(mContext)));
+    mRecyclerView = mock(RecyclerView.class, delegatesTo(new RecyclerView(mContext)));
 
-        doReturn(true).when(mTemplateLayout).isLayoutDirectionResolved();
+    doReturn(true).when(mTemplateLayout).isLayoutDirectionResolved();
+  }
+
+  @Test
+  public void testGetRecyclerView() {
+    RecyclerMixin mixin = new RecyclerMixin(mTemplateLayout, mRecyclerView);
+    assertSame(mRecyclerView, mixin.getRecyclerView());
+  }
+
+  @Test
+  public void testGetAdapter() {
+    mRecyclerView.setAdapter(mAdapter);
+
+    RecyclerMixin mixin = new RecyclerMixin(mTemplateLayout, mRecyclerView);
+    assertSame(mAdapter, mixin.getAdapter());
+  }
+
+  @Test
+  public void testSetAdapter() {
+    assertNull(mRecyclerView.getAdapter());
+
+    RecyclerMixin mixin = new RecyclerMixin(mTemplateLayout, mRecyclerView);
+    mixin.setAdapter(mAdapter);
+
+    assertSame(mAdapter, mRecyclerView.getAdapter());
+  }
+
+  @Test
+  public void testDividerLegacyInset() {
+    RecyclerMixin mixin = new RecyclerMixin(mTemplateLayout, mRecyclerView);
+    mixin.setDividerInset(123);
+
+    assertEquals(123, mixin.getDividerInset());
+
+    final Drawable divider = mixin.getDivider();
+    InsetDrawable insetDrawable = (InsetDrawable) divider;
+    Rect rect = new Rect();
+    insetDrawable.getPadding(rect);
+
+    assertEquals(new Rect(123, 0, 0, 0), rect);
+  }
+
+  @Test
+  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();
+
+      RecyclerMixin mixin = new RecyclerMixin(mTemplateLayout, mRecyclerView);
+      mixin.setDividerInset(123);
+
+      assertEquals(123, mixin.getDividerInset());
+
+      final Drawable divider = mixin.getDivider();
+      InsetDrawable insetDrawable = (InsetDrawable) divider;
+      Rect rect = new Rect();
+      insetDrawable.getPadding(rect);
+
+      assertEquals(new Rect(0, 0, 123, 0), rect);
     }
+    // else the test passes
+  }
 
-    @Test
-    public void testGetRecyclerView() {
-        RecyclerMixin mixin = new RecyclerMixin(mTemplateLayout, mRecyclerView);
-        assertSame(mRecyclerView, mixin.getRecyclerView());
+  @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);
     }
-
-    @Test
-    public void testGetAdapter() {
-        mRecyclerView.setAdapter(mAdapter);
-
-        RecyclerMixin mixin = new RecyclerMixin(mTemplateLayout, mRecyclerView);
-        assertSame(mAdapter, mixin.getAdapter());
-    }
-
-    @Test
-    public void testSetAdapter() {
-        assertNull(mRecyclerView.getAdapter());
-
-        RecyclerMixin mixin = new RecyclerMixin(mTemplateLayout, mRecyclerView);
-        mixin.setAdapter(mAdapter);
-
-        assertSame(mAdapter, mRecyclerView.getAdapter());
-    }
-
-    @Test
-    public void testDividerLegacyInset() {
-        RecyclerMixin mixin = new RecyclerMixin(mTemplateLayout, mRecyclerView);
-        mixin.setDividerInset(123);
-
-        assertEquals(123, mixin.getDividerInset());
-
-        final Drawable divider = mixin.getDivider();
-        InsetDrawable insetDrawable = (InsetDrawable) divider;
-        Rect rect = new Rect();
-        insetDrawable.getPadding(rect);
-
-        assertEquals(new Rect(123, 0, 0, 0), rect);
-    }
-
-    @Test
-    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();
-
-            RecyclerMixin mixin = new RecyclerMixin(mTemplateLayout, mRecyclerView);
-            mixin.setDividerInset(123);
-
-            assertEquals(123, mixin.getDividerInset());
-
-            final Drawable divider = mixin.getDivider();
-            InsetDrawable insetDrawable = (InsetDrawable) divider;
-            Rect rect = new Rect();
-            insetDrawable.getPadding(rect);
-
-            assertEquals(new Rect(0, 0, 123, 0), rect);
-        }
-        // 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
-    }
+    // else the test passes
+  }
 }
diff --git a/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/DividerItemDecorationTest.java b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/DividerItemDecorationTest.java
index 9cf33b9..a3a0cfe 100644
--- a/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/DividerItemDecorationTest.java
+++ b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/DividerItemDecorationTest.java
@@ -28,17 +28,14 @@
 import android.graphics.PorterDuffXfermode;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import android.view.View;
+import android.view.ViewGroup;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.view.View;
-import android.view.ViewGroup;
-
-import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
-
 import com.android.setupwizardlib.DividerItemDecoration;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -46,176 +43,179 @@
 @SmallTest
 public class DividerItemDecorationTest {
 
-    @Test
-    public void testDivider() {
-        final DividerItemDecoration decoration = new DividerItemDecoration();
-        Drawable divider = new ColorDrawable();
-        decoration.setDivider(divider);
-        assertSame("Divider should be same as set", divider, decoration.getDivider());
-    }
+  @Test
+  public void testDivider() {
+    final DividerItemDecoration decoration = new DividerItemDecoration();
+    Drawable divider = new ColorDrawable();
+    decoration.setDivider(divider);
+    assertSame("Divider should be same as set", divider, decoration.getDivider());
+  }
 
-    @Test
-    public void testDividerHeight() {
-        final DividerItemDecoration decoration = new DividerItemDecoration();
-        decoration.setDividerHeight(123);
-        assertEquals("Divider height should be 123", 123, decoration.getDividerHeight());
-    }
+  @Test
+  public void testDividerHeight() {
+    final DividerItemDecoration decoration = new DividerItemDecoration();
+    decoration.setDividerHeight(123);
+    assertEquals("Divider height should be 123", 123, decoration.getDividerHeight());
+  }
 
-    @Test
-    public void testShouldDrawDividerBelowWithEitherCondition() {
-        // Set up the item decoration, with 1px red divider line
-        final DividerItemDecoration decoration = new DividerItemDecoration();
-        Drawable divider = new ColorDrawable(Color.RED);
-        decoration.setDivider(divider);
-        decoration.setDividerHeight(1);
+  @Test
+  public void testShouldDrawDividerBelowWithEitherCondition() {
+    // Set up the item decoration, with 1px red divider line
+    final DividerItemDecoration decoration = new DividerItemDecoration();
+    Drawable divider = new ColorDrawable(Color.RED);
+    decoration.setDivider(divider);
+    decoration.setDividerHeight(1);
 
-        Bitmap bitmap = drawDecoration(decoration, true, true);
+    Bitmap bitmap = drawDecoration(decoration, true, true);
 
-        // Draw the expected result on a bitmap
-        Bitmap expectedBitmap = Bitmap.createBitmap(20, 20, Bitmap.Config.ARGB_4444);
-        Canvas expectedCanvas = new Canvas(expectedBitmap);
-        Paint paint = new Paint();
-        paint.setColor(Color.RED);
-        expectedCanvas.drawRect(0, 5, 20, 6, paint);
-        expectedCanvas.drawRect(0, 10, 20, 11, paint);
-        expectedCanvas.drawRect(0, 15, 20, 16, paint);
-        // Compare the two bitmaps
-        assertBitmapEquals(expectedBitmap, bitmap);
+    // Draw the expected result on a bitmap
+    Bitmap expectedBitmap = Bitmap.createBitmap(20, 20, Bitmap.Config.ARGB_4444);
+    Canvas expectedCanvas = new Canvas(expectedBitmap);
+    Paint paint = new Paint();
+    paint.setColor(Color.RED);
+    expectedCanvas.drawRect(0, 5, 20, 6, paint);
+    expectedCanvas.drawRect(0, 10, 20, 11, paint);
+    expectedCanvas.drawRect(0, 15, 20, 16, paint);
+    // Compare the two bitmaps
+    assertBitmapEquals(expectedBitmap, bitmap);
 
-        bitmap.recycle();
-        bitmap = drawDecoration(decoration, false, true);
-        // should still be the same.
-        assertBitmapEquals(expectedBitmap, bitmap);
+    bitmap.recycle();
+    bitmap = drawDecoration(decoration, false, true);
+    // should still be the same.
+    assertBitmapEquals(expectedBitmap, bitmap);
 
-        bitmap.recycle();
-        bitmap = drawDecoration(decoration, true, false);
-        // last item should not have a divider below it now
-        paint.setColor(Color.TRANSPARENT);
-        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
-        expectedCanvas.drawRect(0, 15, 20, 16, paint);
-        assertBitmapEquals(expectedBitmap, bitmap);
+    bitmap.recycle();
+    bitmap = drawDecoration(decoration, true, false);
+    // last item should not have a divider below it now
+    paint.setColor(Color.TRANSPARENT);
+    paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
+    expectedCanvas.drawRect(0, 15, 20, 16, paint);
+    assertBitmapEquals(expectedBitmap, bitmap);
 
-        bitmap.recycle();
-        bitmap = drawDecoration(decoration, false, false);
-        // everything should be transparent now
-        expectedCanvas.drawRect(0, 5, 20, 6, paint);
-        expectedCanvas.drawRect(0, 10, 20, 11, paint);
-        assertBitmapEquals(expectedBitmap, bitmap);
+    bitmap.recycle();
+    bitmap = drawDecoration(decoration, false, false);
+    // everything should be transparent now
+    expectedCanvas.drawRect(0, 5, 20, 6, paint);
+    expectedCanvas.drawRect(0, 10, 20, 11, paint);
+    assertBitmapEquals(expectedBitmap, bitmap);
+  }
 
-    }
+  @Test
+  public void testShouldDrawDividerBelowWithBothCondition() {
+    // Set up the item decoration, with 1px green divider line
+    final DividerItemDecoration decoration = new DividerItemDecoration();
+    Drawable divider = new ColorDrawable(Color.GREEN);
+    decoration.setDivider(divider);
+    decoration.setDividerHeight(1);
+    decoration.setDividerCondition(DividerItemDecoration.DIVIDER_CONDITION_BOTH);
 
-    @Test
-    public void testShouldDrawDividerBelowWithBothCondition() {
-        // Set up the item decoration, with 1px green divider line
-        final DividerItemDecoration decoration = new DividerItemDecoration();
-        Drawable divider = new ColorDrawable(Color.GREEN);
-        decoration.setDivider(divider);
-        decoration.setDividerHeight(1);
-        decoration.setDividerCondition(DividerItemDecoration.DIVIDER_CONDITION_BOTH);
+    Bitmap bitmap = drawDecoration(decoration, true, true);
+    Paint paint = new Paint();
+    paint.setColor(Color.GREEN);
+    paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));
+    Bitmap expectedBitmap = Bitmap.createBitmap(20, 20, Bitmap.Config.ARGB_4444);
+    Canvas expectedCanvas = new Canvas(expectedBitmap);
+    expectedCanvas.drawRect(0, 5, 20, 6, paint);
+    expectedCanvas.drawRect(0, 10, 20, 11, paint);
+    expectedCanvas.drawRect(0, 15, 20, 16, paint);
+    // Should have all the dividers
+    assertBitmapEquals(expectedBitmap, bitmap);
 
-        Bitmap bitmap = drawDecoration(decoration, true, true);
-        Paint paint = new Paint();
-        paint.setColor(Color.GREEN);
-        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));
-        Bitmap expectedBitmap = Bitmap.createBitmap(20, 20, Bitmap.Config.ARGB_4444);
-        Canvas expectedCanvas = new Canvas(expectedBitmap);
-        expectedCanvas.drawRect(0, 5, 20, 6, paint);
-        expectedCanvas.drawRect(0, 10, 20, 11, paint);
-        expectedCanvas.drawRect(0, 15, 20, 16, paint);
-        // Should have all the dividers
-        assertBitmapEquals(expectedBitmap, bitmap);
+    bitmap.recycle();
+    bitmap = drawDecoration(decoration, false, true);
+    paint.setColor(Color.TRANSPARENT);
+    paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
+    expectedCanvas.drawRect(0, 5, 20, 6, paint);
+    expectedCanvas.drawRect(0, 10, 20, 11, paint);
+    assertBitmapEquals(expectedBitmap, bitmap);
 
-        bitmap.recycle();
-        bitmap = drawDecoration(decoration, false, true);
-        paint.setColor(Color.TRANSPARENT);
-        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
-        expectedCanvas.drawRect(0, 5, 20, 6, paint);
-        expectedCanvas.drawRect(0, 10, 20, 11, paint);
-        assertBitmapEquals(expectedBitmap, bitmap);
+    bitmap.recycle();
+    bitmap = drawDecoration(decoration, true, false);
+    // nothing should be drawn now.
+    expectedCanvas.drawRect(0, 15, 20, 16, paint);
+    assertBitmapEquals(expectedBitmap, bitmap);
 
-        bitmap.recycle();
-        bitmap = drawDecoration(decoration, true, false);
-        // nothing should be drawn now.
-        expectedCanvas.drawRect(0, 15, 20, 16, paint);
-        assertBitmapEquals(expectedBitmap, bitmap);
+    bitmap.recycle();
+    bitmap = drawDecoration(decoration, false, false);
+    assertBitmapEquals(expectedBitmap, bitmap);
+  }
 
-        bitmap.recycle();
-        bitmap = drawDecoration(decoration, false, false);
-        assertBitmapEquals(expectedBitmap, bitmap);
-    }
+  private Bitmap drawDecoration(
+      DividerItemDecoration decoration,
+      final boolean allowDividerAbove,
+      final boolean allowDividerBelow) {
+    // Set up the canvas to be drawn
+    Bitmap bitmap = Bitmap.createBitmap(20, 20, Bitmap.Config.ARGB_4444);
+    Canvas canvas = new Canvas(bitmap);
 
-    private Bitmap drawDecoration(DividerItemDecoration decoration, final boolean allowDividerAbove,
-            final boolean allowDividerBelow) {
-        // Set up the canvas to be drawn
-        Bitmap bitmap = Bitmap.createBitmap(20, 20, Bitmap.Config.ARGB_4444);
-        Canvas canvas = new Canvas(bitmap);
+    final Context context = InstrumentationRegistry.getContext();
+    // Set up recycler view with vertical linear layout manager
+    RecyclerView testRecyclerView = new RecyclerView(context);
+    testRecyclerView.setLayoutManager(new LinearLayoutManager(context));
 
-        final Context context = InstrumentationRegistry.getContext();
-        // Set up recycler view with vertical linear layout manager
-        RecyclerView testRecyclerView = new RecyclerView(context);
-        testRecyclerView.setLayoutManager(new LinearLayoutManager(context));
+    // Set up adapter with 3 items, each 5px tall
+    testRecyclerView.setAdapter(
+        new RecyclerView.Adapter() {
+          @Override
+          public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
+            final View itemView = new View(context);
+            itemView.setMinimumWidth(20);
+            itemView.setMinimumHeight(5);
+            return ViewHolder.createInstance(itemView, allowDividerAbove, allowDividerBelow);
+          }
 
-        // Set up adapter with 3 items, each 5px tall
-        testRecyclerView.setAdapter(new RecyclerView.Adapter() {
-            @Override
-            public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
-                final View itemView = new View(context);
-                itemView.setMinimumWidth(20);
-                itemView.setMinimumHeight(5);
-                return ViewHolder.createInstance(itemView, allowDividerAbove, allowDividerBelow);
-            }
+          @Override
+          public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int i) {}
 
-            @Override
-            public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int i) {
-            }
-
-            @Override
-            public int getItemCount() {
-                return 3;
-            }
+          @Override
+          public int getItemCount() {
+            return 3;
+          }
         });
 
-        testRecyclerView.layout(0, 0, 20, 20);
-        decoration.onDraw(canvas, testRecyclerView, null);
-        return bitmap;
+    testRecyclerView.layout(0, 0, 20, 20);
+    decoration.onDraw(canvas, testRecyclerView, null);
+    return bitmap;
+  }
+
+  private void assertBitmapEquals(Bitmap expected, Bitmap actual) {
+    assertEquals("Width should be the same", expected.getWidth(), actual.getWidth());
+    assertEquals("Height should be the same", expected.getHeight(), actual.getHeight());
+    for (int x = 0; x < expected.getWidth(); x++) {
+      for (int y = 0; y < expected.getHeight(); y++) {
+        assertEquals(
+            "Pixel at (" + x + ", " + y + ") should be the same",
+            expected.getPixel(x, y),
+            actual.getPixel(x, y));
+      }
+    }
+  }
+
+  private static class ViewHolder extends RecyclerView.ViewHolder
+      implements DividerItemDecoration.DividedViewHolder {
+
+    private boolean mAllowDividerAbove;
+    private boolean mAllowDividerBelow;
+
+    public static ViewHolder createInstance(
+        View itemView, boolean allowDividerAbove, boolean allowDividerBelow) {
+      return new ViewHolder(itemView, allowDividerAbove, allowDividerBelow);
     }
 
-    private void assertBitmapEquals(Bitmap expected, Bitmap actual) {
-        assertEquals("Width should be the same", expected.getWidth(), actual.getWidth());
-        assertEquals("Height should be the same", expected.getHeight(), actual.getHeight());
-        for (int x = 0; x < expected.getWidth(); x++) {
-            for (int y = 0; y < expected.getHeight(); y++) {
-                assertEquals("Pixel at (" + x + ", " + y + ") should be the same",
-                        expected.getPixel(x, y), actual.getPixel(x, y));
-            }
-        }
+    private ViewHolder(View itemView, boolean allowDividerAbove, boolean allowDividerBelow) {
+      super(itemView);
+      mAllowDividerAbove = allowDividerAbove;
+      mAllowDividerBelow = allowDividerBelow;
     }
 
-    private static class ViewHolder extends RecyclerView.ViewHolder
-            implements DividerItemDecoration.DividedViewHolder {
-
-        private boolean mAllowDividerAbove;
-        private boolean mAllowDividerBelow;
-
-        public static ViewHolder createInstance(View itemView, boolean allowDividerAbove,
-                boolean allowDividerBelow) {
-            return new ViewHolder(itemView, allowDividerAbove, allowDividerBelow);
-        }
-
-        private ViewHolder(View itemView, boolean allowDividerAbove, boolean allowDividerBelow) {
-            super(itemView);
-            mAllowDividerAbove = allowDividerAbove;
-            mAllowDividerBelow = allowDividerBelow;
-        }
-
-        @Override
-        public boolean isDividerAllowedAbove() {
-            return mAllowDividerAbove;
-        }
-
-        @Override
-        public boolean isDividerAllowedBelow() {
-            return mAllowDividerBelow;
-        }
+    @Override
+    public boolean isDividerAllowedAbove() {
+      return mAllowDividerAbove;
     }
+
+    @Override
+    public boolean isDividerAllowedBelow() {
+      return mAllowDividerBelow;
+    }
+  }
 }
diff --git a/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/GlifPreferenceLayoutTest.java b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/GlifPreferenceLayoutTest.java
index 4d2876d..d55ba23 100644
--- a/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/GlifPreferenceLayoutTest.java
+++ b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/GlifPreferenceLayoutTest.java
@@ -24,18 +24,15 @@
 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;
+import androidx.recyclerview.widget.RecyclerView;
 import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-
-import androidx.recyclerview.widget.RecyclerView;
-
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
 import com.android.setupwizardlib.GlifPreferenceLayout;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -44,62 +41,63 @@
 @SmallTest
 public class GlifPreferenceLayoutTest {
 
-    private Context mContext;
+  private Context mContext;
 
-    @Before
-    public void setUp() throws Exception {
-        mContext = new ContextThemeWrapper(InstrumentationRegistry.getContext(),
-                R.style.SuwThemeGlif_Light);
+  @Before
+  public void setUp() throws Exception {
+    mContext =
+        new ContextThemeWrapper(InstrumentationRegistry.getContext(), R.style.SuwThemeGlif_Light);
+  }
+
+  @Test
+  public void testDefaultTemplate() {
+    GlifPreferenceLayout layout = new GlifPreferenceLayout(mContext);
+    assertPreferenceTemplateInflated(layout);
+  }
+
+  @Test
+  public void testGetRecyclerView() {
+    GlifPreferenceLayout layout = new GlifPreferenceLayout(mContext);
+    assertPreferenceTemplateInflated(layout);
+    assertNotNull("getRecyclerView should not be null", layout.getRecyclerView());
+  }
+
+  @Test
+  public void testOnCreateRecyclerView() {
+    GlifPreferenceLayout layout = new GlifPreferenceLayout(mContext);
+    assertPreferenceTemplateInflated(layout);
+    final RecyclerView recyclerView =
+        layout.onCreateRecyclerView(
+            LayoutInflater.from(mContext), layout, null /* savedInstanceState */);
+    assertNotNull("RecyclerView created should not be null", recyclerView);
+  }
+
+  @Test
+  public void testDividerInset() {
+    GlifPreferenceLayout layout = new GlifPreferenceLayout(mContext);
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+      layout.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
     }
+    assertPreferenceTemplateInflated(layout);
 
-    @Test
-    public void testDefaultTemplate() {
-        GlifPreferenceLayout layout = new GlifPreferenceLayout(mContext);
-        assertPreferenceTemplateInflated(layout);
-    }
+    layout.addView(
+        layout.onCreateRecyclerView(
+            LayoutInflater.from(mContext), layout, null /* savedInstanceState */));
 
-    @Test
-    public void testGetRecyclerView() {
-        GlifPreferenceLayout layout = new GlifPreferenceLayout(mContext);
-        assertPreferenceTemplateInflated(layout);
-        assertNotNull("getRecyclerView should not be null", layout.getRecyclerView());
-    }
+    layout.setDividerInset(10);
+    assertEquals("Divider inset should be 10", 10, layout.getDividerInset());
 
-    @Test
-    public void testOnCreateRecyclerView() {
-        GlifPreferenceLayout layout = new GlifPreferenceLayout(mContext);
-        assertPreferenceTemplateInflated(layout);
-        final RecyclerView recyclerView = layout.onCreateRecyclerView(LayoutInflater.from(mContext),
-                layout, null /* savedInstanceState */);
-        assertNotNull("RecyclerView created should not be null", recyclerView);
-    }
+    final Drawable divider = layout.getDivider();
+    assertTrue("Divider should be instance of InsetDrawable", divider instanceof InsetDrawable);
+  }
 
-    @Test
-    public void testDividerInset() {
-        GlifPreferenceLayout layout = new GlifPreferenceLayout(mContext);
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
-            layout.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
-        }
-        assertPreferenceTemplateInflated(layout);
+  private void assertPreferenceTemplateInflated(GlifPreferenceLayout layout) {
+    View contentContainer = layout.findViewById(R.id.suw_layout_content);
+    assertTrue(
+        "@id/suw_layout_content should be a ViewGroup", contentContainer instanceof ViewGroup);
 
-        layout.addView(layout.onCreateRecyclerView(LayoutInflater.from(mContext), layout,
-                null /* savedInstanceState */));
-
-        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);
-    }
-
-    private void assertPreferenceTemplateInflated(GlifPreferenceLayout layout) {
-        View contentContainer = layout.findViewById(R.id.suw_layout_content);
-        assertTrue("@id/suw_layout_content should be a ViewGroup",
-                contentContainer instanceof ViewGroup);
-
-        assertNotNull("Header text view should not be null",
-                layout.findManagedViewById(R.id.suw_layout_title));
-        assertNotNull("Icon view should not be null",
-                layout.findManagedViewById(R.id.suw_layout_icon));
-    }
+    assertNotNull(
+        "Header text view should not be null", layout.findManagedViewById(R.id.suw_layout_title));
+    assertNotNull("Icon view should not be null", layout.findManagedViewById(R.id.suw_layout_icon));
+  }
 }
diff --git a/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/GlifRecyclerLayoutTest.java b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/GlifRecyclerLayoutTest.java
index a68faf0..5db7db5 100644
--- a/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/GlifRecyclerLayoutTest.java
+++ b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/GlifRecyclerLayoutTest.java
@@ -26,20 +26,17 @@
 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;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.RecyclerView.Adapter;
 import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View.MeasureSpec;
 import android.view.ViewGroup;
-
-import androidx.recyclerview.widget.RecyclerView;
-import androidx.recyclerview.widget.RecyclerView.Adapter;
-
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
 import com.android.setupwizardlib.GlifRecyclerLayout;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -48,130 +45,127 @@
 @SmallTest
 public class GlifRecyclerLayoutTest {
 
-    private Context mContext;
+  private Context mContext;
 
-    @Before
-    public void setUp() throws Exception {
-        mContext = new ContextThemeWrapper(InstrumentationRegistry.getContext(),
-                R.style.SuwThemeGlif_Light);
+  @Before
+  public void setUp() throws Exception {
+    mContext =
+        new ContextThemeWrapper(InstrumentationRegistry.getContext(), R.style.SuwThemeGlif_Light);
+  }
+
+  @Test
+  public void testDefaultTemplate() {
+    GlifRecyclerLayout layout = new GlifRecyclerLayout(mContext);
+    assertRecyclerTemplateInflated(layout);
+  }
+
+  @Test
+  public void testInflateFromXml() {
+    LayoutInflater inflater = LayoutInflater.from(mContext);
+    GlifRecyclerLayout layout =
+        (GlifRecyclerLayout) inflater.inflate(R.layout.test_glif_recycler_layout, null);
+    assertRecyclerTemplateInflated(layout);
+  }
+
+  @Test
+  public void testGetRecyclerView() {
+    GlifRecyclerLayout layout = new GlifRecyclerLayout(mContext);
+    assertRecyclerTemplateInflated(layout);
+    assertNotNull("getRecyclerView should not be null", layout.getRecyclerView());
+  }
+
+  @Test
+  public void testAdapter() {
+    GlifRecyclerLayout layout = new GlifRecyclerLayout(mContext);
+    assertRecyclerTemplateInflated(layout);
+
+    final RecyclerView.Adapter adapter = createTestAdapter(1);
+    layout.setAdapter(adapter);
+
+    final RecyclerView.Adapter gotAdapter = layout.getAdapter();
+    // Note: The wrapped adapter should be returned, not the HeaderAdapter.
+    assertSame("Adapter got from GlifRecyclerLayout should be same as set", adapter, gotAdapter);
+  }
+
+  @Test
+  public void testLayout() {
+    GlifRecyclerLayout layout = new GlifRecyclerLayout(mContext);
+    assertRecyclerTemplateInflated(layout);
+
+    layout.setAdapter(createTestAdapter(3));
+
+    layout.measure(
+        MeasureSpec.makeMeasureSpec(500, MeasureSpec.EXACTLY),
+        MeasureSpec.makeMeasureSpec(500, MeasureSpec.EXACTLY));
+    layout.layout(0, 0, 500, 500);
+    // Test that the layout code doesn't crash.
+  }
+
+  @Test
+  public void testDividerInsetLegacy() {
+    GlifRecyclerLayout layout = new GlifRecyclerLayout(mContext);
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+      layout.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
     }
+    assertRecyclerTemplateInflated(layout);
 
-    @Test
-    public void testDefaultTemplate() {
-        GlifRecyclerLayout layout = new GlifRecyclerLayout(mContext);
-        assertRecyclerTemplateInflated(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() {
+    GlifRecyclerLayout layout = new GlifRecyclerLayout(mContext);
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+      layout.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
     }
+    assertRecyclerTemplateInflated(layout);
 
-    @Test
-    public void testInflateFromXml() {
-        LayoutInflater inflater = LayoutInflater.from(mContext);
-        GlifRecyclerLayout layout = (GlifRecyclerLayout)
-                inflater.inflate(R.layout.test_glif_recycler_layout, null);
-        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);
+      fail("Creating GlifRecyclerLayout with no recycler view should throw exception");
+    } catch (Exception e) {
+      // pass
     }
+  }
 
-    @Test
-    public void testGetRecyclerView() {
-        GlifRecyclerLayout layout = new GlifRecyclerLayout(mContext);
-        assertRecyclerTemplateInflated(layout);
-        assertNotNull("getRecyclerView should not be null", layout.getRecyclerView());
-    }
+  private void assertRecyclerTemplateInflated(GlifRecyclerLayout layout) {
+    View recyclerView = layout.findViewById(R.id.suw_recycler_view);
+    assertTrue(
+        "@id/suw_recycler_view should be a RecyclerView", recyclerView instanceof RecyclerView);
 
-    @Test
-    public void testAdapter() {
-        GlifRecyclerLayout layout = new GlifRecyclerLayout(mContext);
-        assertRecyclerTemplateInflated(layout);
+    assertNotNull(
+        "Header text view should not be null", layout.findManagedViewById(R.id.suw_layout_title));
+    assertNotNull("Icon view should not be null", layout.findManagedViewById(R.id.suw_layout_icon));
+  }
 
-        final RecyclerView.Adapter adapter = createTestAdapter(1);
-        layout.setAdapter(adapter);
+  private Adapter createTestAdapter(final int itemCount) {
+    return new RecyclerView.Adapter() {
+      @Override
+      public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int position) {
+        return new RecyclerView.ViewHolder(new View(parent.getContext())) {};
+      }
 
-        final RecyclerView.Adapter gotAdapter = layout.getAdapter();
-        // Note: The wrapped adapter should be returned, not the HeaderAdapter.
-        assertSame("Adapter got from GlifRecyclerLayout should be same as set",
-                adapter, gotAdapter);
-    }
+      @Override
+      public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {}
 
-    @Test
-    public void testLayout() {
-        GlifRecyclerLayout layout = new GlifRecyclerLayout(mContext);
-        assertRecyclerTemplateInflated(layout);
-
-        layout.setAdapter(createTestAdapter(3));
-
-        layout.measure(
-                MeasureSpec.makeMeasureSpec(500, MeasureSpec.EXACTLY),
-                MeasureSpec.makeMeasureSpec(500, MeasureSpec.EXACTLY));
-        layout.layout(0, 0, 500, 500);
-        // Test that the layout code doesn't crash.
-    }
-
-    @Test
-    public void testDividerInsetLegacy() {
-        GlifRecyclerLayout layout = new GlifRecyclerLayout(mContext);
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
-            layout.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
-        }
-        assertRecyclerTemplateInflated(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() {
-        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);
-            fail("Creating GlifRecyclerLayout with no recycler view should throw exception");
-        } catch (Exception e) {
-            // pass
-        }
-    }
-
-    private void assertRecyclerTemplateInflated(GlifRecyclerLayout layout) {
-        View recyclerView = layout.findViewById(R.id.suw_recycler_view);
-        assertTrue("@id/suw_recycler_view should be a RecyclerView",
-                recyclerView instanceof RecyclerView);
-
-        assertNotNull("Header text view should not be null",
-                layout.findManagedViewById(R.id.suw_layout_title));
-        assertNotNull("Icon view should not be null",
-                layout.findManagedViewById(R.id.suw_layout_icon));
-    }
-
-    private Adapter createTestAdapter(final int itemCount) {
-        return new RecyclerView.Adapter() {
-            @Override
-            public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int position) {
-                return new RecyclerView.ViewHolder(new View(parent.getContext())) {};
-            }
-
-            @Override
-            public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
-            }
-
-            @Override
-            public int getItemCount() {
-                return itemCount;
-            }
-        };
-    }
+      @Override
+      public int getItemCount() {
+        return itemCount;
+      }
+    };
+  }
 }
diff --git a/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/HeaderRecyclerViewTest.java b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/HeaderRecyclerViewTest.java
index 9af68a7..3d0f9da 100644
--- a/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/HeaderRecyclerViewTest.java
+++ b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/HeaderRecyclerViewTest.java
@@ -19,160 +19,143 @@
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.verify;
 
+import androidx.recyclerview.widget.RecyclerView;
+import android.view.View;
+import android.view.ViewGroup;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.view.View;
-import android.view.ViewGroup;
-
-import androidx.recyclerview.widget.RecyclerView;
-
 import com.android.setupwizardlib.view.HeaderRecyclerView.HeaderAdapter;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-/**
- * Test for {@link com.android.setupwizardlib.view.HeaderRecyclerView}
- */
+/** Test for {@link com.android.setupwizardlib.view.HeaderRecyclerView} */
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class HeaderRecyclerViewTest {
 
-    private TestAdapter mWrappedAdapter;
-    private HeaderAdapter mHeaderAdapter;
+  private TestAdapter mWrappedAdapter;
+  private HeaderAdapter mHeaderAdapter;
 
-    @Mock
-    private RecyclerView.AdapterDataObserver mObserver;
+  @Mock private RecyclerView.AdapterDataObserver mObserver;
 
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
+  @Before
+  public void setUp() {
+    MockitoAnnotations.initMocks(this);
 
-        mWrappedAdapter = new TestAdapter();
+    mWrappedAdapter = new TestAdapter();
 
-        mHeaderAdapter = new HeaderAdapter(mWrappedAdapter);
-        mHeaderAdapter.registerAdapterDataObserver(mObserver);
+    mHeaderAdapter = new HeaderAdapter(mWrappedAdapter);
+    mHeaderAdapter.registerAdapterDataObserver(mObserver);
+  }
+
+  /** Test that notifyDataSetChanged gets propagated by HeaderRecyclerView's adapter. */
+  @Test
+  public void testNotifyChanged() {
+    mWrappedAdapter.notifyDataSetChanged();
+
+    verify(mObserver).onChanged();
+  }
+
+  /** Test that notifyItemChanged gets propagated by HeaderRecyclerView's adapter. */
+  @Test
+  public void testNotifyItemChangedNoHeader() {
+    mWrappedAdapter.notifyItemChanged(12);
+
+    verify(mObserver).onItemRangeChanged(eq(12), eq(1), eq(null));
+  }
+
+  /**
+   * Test that notifyItemChanged gets propagated by HeaderRecyclerView's adapter and adds 1 to the
+   * position for the extra header items.
+   */
+  @Test
+  public void testNotifyItemChangedWithHeader() {
+    mHeaderAdapter.setHeader(new View(InstrumentationRegistry.getTargetContext()));
+    mWrappedAdapter.notifyItemChanged(12);
+
+    verify(mObserver).onItemRangeChanged(eq(13), eq(1), eq(null));
+  }
+
+  /** Test that notifyItemInserted gets propagated by HeaderRecyclerView's adapter. */
+  @Test
+  public void testNotifyItemInsertedNoHeader() {
+    mWrappedAdapter.notifyItemInserted(12);
+
+    verify(mObserver).onItemRangeInserted(eq(12), eq(1));
+  }
+
+  /**
+   * Test that notifyItemInserted gets propagated by HeaderRecyclerView's adapter and adds 1 to the
+   * position for the extra header item.
+   */
+  @Test
+  public void testNotifyItemInsertedWithHeader() {
+    mHeaderAdapter.setHeader(new View(InstrumentationRegistry.getTargetContext()));
+    mWrappedAdapter.notifyItemInserted(12);
+
+    verify(mObserver).onItemRangeInserted(eq(13), eq(1));
+  }
+
+  /** Test that notifyItemRemoved gets propagated by HeaderRecyclerView's adapter. */
+  @Test
+  public void testNotifyItemRemovedNoHeader() {
+    mWrappedAdapter.notifyItemRemoved(12);
+
+    verify(mObserver).onItemRangeRemoved(eq(12), eq(1));
+  }
+
+  /**
+   * Test that notifyItemRemoved gets propagated by HeaderRecyclerView's adapter and adds 1 to the
+   * position for the extra header item.
+   */
+  @Test
+  public void testNotifyItemRemovedWithHeader() {
+    mHeaderAdapter.setHeader(new View(InstrumentationRegistry.getTargetContext()));
+    mWrappedAdapter.notifyItemRemoved(12);
+
+    verify(mObserver).onItemRangeRemoved(eq(13), eq(1));
+  }
+
+  /** Test that notifyItemMoved gets propagated by HeaderRecyclerView's adapter. */
+  @Test
+  public void testNotifyItemMovedNoHeader() {
+    mWrappedAdapter.notifyItemMoved(12, 18);
+
+    verify(mObserver).onItemRangeMoved(eq(12), eq(18), eq(1));
+  }
+
+  /**
+   * Test that notifyItemMoved gets propagated by HeaderRecyclerView's adapter and adds 1 to the
+   * position for the extra header item.
+   */
+  @Test
+  public void testNotifyItemMovedWithHeader() {
+    mHeaderAdapter.setHeader(new View(InstrumentationRegistry.getTargetContext()));
+    mWrappedAdapter.notifyItemMoved(12, 18);
+
+    verify(mObserver).onItemRangeMoved(eq(13), eq(19), eq(1));
+  }
+
+  /**
+   * Test adapter to be wrapped inside {@link HeaderAdapter} to to send item change notifications.
+   */
+  public static class TestAdapter extends RecyclerView.Adapter {
+
+    @Override
+    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
+      return null;
     }
 
-    /**
-     * Test that notifyDataSetChanged gets propagated by HeaderRecyclerView's adapter.
-     */
-    @Test
-    public void testNotifyChanged() {
-        mWrappedAdapter.notifyDataSetChanged();
+    @Override
+    public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int i) {}
 
-        verify(mObserver).onChanged();
+    @Override
+    public int getItemCount() {
+      return 0;
     }
-
-    /**
-     * Test that notifyItemChanged gets propagated by HeaderRecyclerView's adapter.
-     */
-    @Test
-    public void testNotifyItemChangedNoHeader() {
-        mWrappedAdapter.notifyItemChanged(12);
-
-        verify(mObserver).onItemRangeChanged(eq(12), eq(1), eq(null));
-    }
-
-    /**
-     * Test that notifyItemChanged gets propagated by HeaderRecyclerView's adapter and adds 1 to the
-     * position for the extra header items.
-     */
-    @Test
-    public void testNotifyItemChangedWithHeader() {
-        mHeaderAdapter.setHeader(new View(InstrumentationRegistry.getTargetContext()));
-        mWrappedAdapter.notifyItemChanged(12);
-
-        verify(mObserver).onItemRangeChanged(eq(13), eq(1), eq(null));
-    }
-
-    /**
-     * Test that notifyItemInserted gets propagated by HeaderRecyclerView's adapter.
-     */
-    @Test
-    public void testNotifyItemInsertedNoHeader() {
-        mWrappedAdapter.notifyItemInserted(12);
-
-        verify(mObserver).onItemRangeInserted(eq(12), eq(1));
-    }
-
-    /**
-     * Test that notifyItemInserted gets propagated by HeaderRecyclerView's adapter and adds 1 to
-     * the position for the extra header item.
-     */
-    @Test
-    public void testNotifyItemInsertedWithHeader() {
-        mHeaderAdapter.setHeader(new View(InstrumentationRegistry.getTargetContext()));
-        mWrappedAdapter.notifyItemInserted(12);
-
-        verify(mObserver).onItemRangeInserted(eq(13), eq(1));
-    }
-
-    /**
-     * Test that notifyItemRemoved gets propagated by HeaderRecyclerView's adapter.
-     */
-    @Test
-    public void testNotifyItemRemovedNoHeader() {
-        mWrappedAdapter.notifyItemRemoved(12);
-
-        verify(mObserver).onItemRangeRemoved(eq(12), eq(1));
-    }
-
-    /**
-     * Test that notifyItemRemoved gets propagated by HeaderRecyclerView's adapter and adds 1 to
-     * the position for the extra header item.
-     */
-    @Test
-    public void testNotifyItemRemovedWithHeader() {
-        mHeaderAdapter.setHeader(new View(InstrumentationRegistry.getTargetContext()));
-        mWrappedAdapter.notifyItemRemoved(12);
-
-        verify(mObserver).onItemRangeRemoved(eq(13), eq(1));
-    }
-
-    /**
-     * Test that notifyItemMoved gets propagated by HeaderRecyclerView's adapter.
-     */
-    @Test
-    public void testNotifyItemMovedNoHeader() {
-        mWrappedAdapter.notifyItemMoved(12, 18);
-
-        verify(mObserver).onItemRangeMoved(eq(12), eq(18), eq(1));
-    }
-
-    /**
-     * Test that notifyItemMoved gets propagated by HeaderRecyclerView's adapter and adds 1 to
-     * the position for the extra header item.
-     */
-    @Test
-    public void testNotifyItemMovedWithHeader() {
-        mHeaderAdapter.setHeader(new View(InstrumentationRegistry.getTargetContext()));
-        mWrappedAdapter.notifyItemMoved(12, 18);
-
-        verify(mObserver).onItemRangeMoved(eq(13), eq(19), eq(1));
-    }
-
-    /**
-     * Test adapter to be wrapped inside {@link HeaderAdapter} to to send item change notifications.
-     */
-    public static class TestAdapter extends RecyclerView.Adapter {
-
-        @Override
-        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
-            return null;
-        }
-
-        @Override
-        public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int i) {
-        }
-
-        @Override
-        public int getItemCount() {
-            return 0;
-        }
-    }
+  }
 }
diff --git a/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardPreferenceLayoutTest.java b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardPreferenceLayoutTest.java
index 316793f..39929dc 100644
--- a/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardPreferenceLayoutTest.java
+++ b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardPreferenceLayoutTest.java
@@ -24,18 +24,15 @@
 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;
+import androidx.recyclerview.widget.RecyclerView;
 import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-
-import androidx.recyclerview.widget.RecyclerView;
-
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
 import com.android.setupwizardlib.SetupWizardPreferenceLayout;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -44,62 +41,65 @@
 @SmallTest
 public class SetupWizardPreferenceLayoutTest {
 
-    private Context mContext;
+  private Context mContext;
 
-    @Before
-    public void setUp() throws Exception {
-        mContext = new ContextThemeWrapper(InstrumentationRegistry.getContext(),
-                R.style.SuwThemeMaterial_Light);
+  @Before
+  public void setUp() throws Exception {
+    mContext =
+        new ContextThemeWrapper(
+            InstrumentationRegistry.getContext(), R.style.SuwThemeMaterial_Light);
+  }
+
+  @Test
+  public void testDefaultTemplate() {
+    SetupWizardPreferenceLayout layout = new SetupWizardPreferenceLayout(mContext);
+    assertPreferenceTemplateInflated(layout);
+  }
+
+  @Test
+  public void testGetRecyclerView() {
+    SetupWizardPreferenceLayout layout = new SetupWizardPreferenceLayout(mContext);
+    assertPreferenceTemplateInflated(layout);
+    assertNotNull("getRecyclerView should not be null", layout.getRecyclerView());
+  }
+
+  @Test
+  public void testOnCreateRecyclerView() {
+    SetupWizardPreferenceLayout layout = new SetupWizardPreferenceLayout(mContext);
+    assertPreferenceTemplateInflated(layout);
+    final RecyclerView recyclerView =
+        layout.onCreateRecyclerView(
+            LayoutInflater.from(mContext), layout, null /* savedInstanceState */);
+    assertNotNull("RecyclerView created should not be null", recyclerView);
+  }
+
+  @Test
+  public void testDividerInset() {
+    SetupWizardPreferenceLayout layout = new SetupWizardPreferenceLayout(mContext);
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+      layout.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
     }
+    assertPreferenceTemplateInflated(layout);
 
-    @Test
-    public void testDefaultTemplate() {
-        SetupWizardPreferenceLayout layout = new SetupWizardPreferenceLayout(mContext);
-        assertPreferenceTemplateInflated(layout);
-    }
+    layout.addView(
+        layout.onCreateRecyclerView(
+            LayoutInflater.from(mContext), layout, null /* savedInstanceState */));
 
-    @Test
-    public void testGetRecyclerView() {
-        SetupWizardPreferenceLayout layout = new SetupWizardPreferenceLayout(mContext);
-        assertPreferenceTemplateInflated(layout);
-        assertNotNull("getRecyclerView should not be null", layout.getRecyclerView());
-    }
+    layout.setDividerInset(10);
+    assertEquals("Divider inset should be 10", 10, layout.getDividerInset());
 
-    @Test
-    public void testOnCreateRecyclerView() {
-        SetupWizardPreferenceLayout layout = new SetupWizardPreferenceLayout(mContext);
-        assertPreferenceTemplateInflated(layout);
-        final RecyclerView recyclerView = layout.onCreateRecyclerView(LayoutInflater.from(mContext),
-                layout, null /* savedInstanceState */);
-        assertNotNull("RecyclerView created should not be null", recyclerView);
-    }
+    final Drawable divider = layout.getDivider();
+    assertTrue("Divider should be instance of InsetDrawable", divider instanceof InsetDrawable);
+  }
 
-    @Test
-    public void testDividerInset() {
-        SetupWizardPreferenceLayout layout = new SetupWizardPreferenceLayout(mContext);
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
-            layout.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
-        }
-        assertPreferenceTemplateInflated(layout);
+  private void assertPreferenceTemplateInflated(SetupWizardPreferenceLayout layout) {
+    View contentContainer = layout.findViewById(R.id.suw_layout_content);
+    assertTrue(
+        "@id/suw_layout_content should be a ViewGroup", contentContainer instanceof ViewGroup);
 
-        layout.addView(layout.onCreateRecyclerView(LayoutInflater.from(mContext), layout,
-                null /* savedInstanceState */));
-
-        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);
-    }
-
-    private void assertPreferenceTemplateInflated(SetupWizardPreferenceLayout layout) {
-        View contentContainer = layout.findViewById(R.id.suw_layout_content);
-        assertTrue("@id/suw_layout_content should be a ViewGroup",
-                contentContainer instanceof ViewGroup);
-
-        assertNotNull("Header text view should not be null",
-                layout.findManagedViewById(R.id.suw_layout_title));
-        assertNotNull("Decoration view should not be null",
-                layout.findManagedViewById(R.id.suw_layout_decor));
-    }
+    assertNotNull(
+        "Header text view should not be null", layout.findManagedViewById(R.id.suw_layout_title));
+    assertNotNull(
+        "Decoration view should not be null", layout.findManagedViewById(R.id.suw_layout_decor));
+  }
 }
diff --git a/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardRecyclerLayoutTest.java b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardRecyclerLayoutTest.java
index bbe773b..46a665d 100644
--- a/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardRecyclerLayoutTest.java
+++ b/library/recyclerview/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardRecyclerLayoutTest.java
@@ -26,21 +26,18 @@
 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;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.RecyclerView.Adapter;
+import androidx.recyclerview.widget.RecyclerView.ViewHolder;
 import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View.MeasureSpec;
 import android.view.ViewGroup;
-
-import androidx.recyclerview.widget.RecyclerView;
-import androidx.recyclerview.widget.RecyclerView.Adapter;
-import androidx.recyclerview.widget.RecyclerView.ViewHolder;
-
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
 import com.android.setupwizardlib.SetupWizardRecyclerLayout;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -49,133 +46,129 @@
 @SmallTest
 public class SetupWizardRecyclerLayoutTest {
 
-    private Context mContext;
+  private Context mContext;
 
-    @Before
-    public void setUp() throws Exception {
-        mContext = new ContextThemeWrapper(InstrumentationRegistry.getContext(),
-                R.style.SuwThemeMaterial_Light);
+  @Before
+  public void setUp() throws Exception {
+    mContext =
+        new ContextThemeWrapper(
+            InstrumentationRegistry.getContext(), R.style.SuwThemeMaterial_Light);
+  }
+
+  @Test
+  public void testDefaultTemplate() {
+    SetupWizardRecyclerLayout layout = new SetupWizardRecyclerLayout(mContext);
+    assertRecyclerTemplateInflated(layout);
+  }
+
+  @Test
+  public void testInflateFromXml() {
+    LayoutInflater inflater = LayoutInflater.from(mContext);
+    SetupWizardRecyclerLayout layout =
+        (SetupWizardRecyclerLayout) inflater.inflate(R.layout.test_recycler_layout, null);
+    assertRecyclerTemplateInflated(layout);
+  }
+
+  @Test
+  public void testGetRecyclerView() {
+    SetupWizardRecyclerLayout layout = new SetupWizardRecyclerLayout(mContext);
+    assertRecyclerTemplateInflated(layout);
+    assertNotNull("getRecyclerView should not be null", layout.getRecyclerView());
+  }
+
+  @Test
+  public void testAdapter() {
+    SetupWizardRecyclerLayout layout = new SetupWizardRecyclerLayout(mContext);
+    assertRecyclerTemplateInflated(layout);
+
+    final Adapter adapter = createTestAdapter(1);
+    layout.setAdapter(adapter);
+
+    final Adapter gotAdapter = layout.getAdapter();
+    // Note: The wrapped adapter should be returned, not the HeaderAdapter.
+    assertSame("Adapter got from SetupWizardLayout should be same as set", adapter, gotAdapter);
+  }
+
+  @Test
+  public void testLayout() {
+    SetupWizardRecyclerLayout layout = new SetupWizardRecyclerLayout(mContext);
+    assertRecyclerTemplateInflated(layout);
+
+    layout.setAdapter(createTestAdapter(3));
+
+    layout.measure(
+        MeasureSpec.makeMeasureSpec(500, MeasureSpec.EXACTLY),
+        MeasureSpec.makeMeasureSpec(500, MeasureSpec.EXACTLY));
+    layout.layout(0, 0, 500, 500);
+    // Test that the layout code doesn't crash.
+  }
+
+  @Test
+  public void testDividerInsetLegacy() {
+    SetupWizardRecyclerLayout layout = new SetupWizardRecyclerLayout(mContext);
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+      layout.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
     }
+    assertRecyclerTemplateInflated(layout);
 
-    @Test
-    public void testDefaultTemplate() {
-        SetupWizardRecyclerLayout layout = new SetupWizardRecyclerLayout(mContext);
-        assertRecyclerTemplateInflated(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() {
+    SetupWizardRecyclerLayout layout = new SetupWizardRecyclerLayout(mContext);
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+      layout.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
     }
+    assertRecyclerTemplateInflated(layout);
 
-    @Test
-    public void testInflateFromXml() {
-        LayoutInflater inflater = LayoutInflater.from(mContext);
-        SetupWizardRecyclerLayout layout = (SetupWizardRecyclerLayout)
-                inflater.inflate(R.layout.test_recycler_layout, null);
-        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(mContext, R.layout.suw_glif_template, R.id.suw_recycler_view);
+      fail("Creating SetupWizardRecyclerLayout with no recycler view should throw exception");
+    } catch (Exception e) {
+      // pass
     }
+  }
 
-    @Test
-    public void testGetRecyclerView() {
-        SetupWizardRecyclerLayout layout = new SetupWizardRecyclerLayout(mContext);
-        assertRecyclerTemplateInflated(layout);
-        assertNotNull("getRecyclerView should not be null", layout.getRecyclerView());
-    }
+  private void assertRecyclerTemplateInflated(SetupWizardRecyclerLayout layout) {
+    View recyclerView = layout.findViewById(R.id.suw_recycler_view);
+    assertTrue(
+        "@id/suw_recycler_view should be a RecyclerView", recyclerView instanceof RecyclerView);
 
-    @Test
-    public void testAdapter() {
-        SetupWizardRecyclerLayout layout = new SetupWizardRecyclerLayout(mContext);
-        assertRecyclerTemplateInflated(layout);
+    assertNotNull(
+        "Header text view should not be null", layout.findManagedViewById(R.id.suw_layout_title));
+    assertNotNull(
+        "Decoration view should not be null", layout.findManagedViewById(R.id.suw_layout_decor));
+  }
 
-        final Adapter adapter = createTestAdapter(1);
-        layout.setAdapter(adapter);
+  private Adapter createTestAdapter(final int itemCount) {
+    return new Adapter() {
+      @Override
+      public ViewHolder onCreateViewHolder(ViewGroup parent, int position) {
+        return new ViewHolder(new View(parent.getContext())) {};
+      }
 
-        final Adapter gotAdapter = layout.getAdapter();
-        // Note: The wrapped adapter should be returned, not the HeaderAdapter.
-        assertSame("Adapter got from SetupWizardLayout should be same as set",
-                adapter, gotAdapter);
-    }
+      @Override
+      public void onBindViewHolder(ViewHolder viewHolder, int position) {}
 
-    @Test
-    public void testLayout() {
-        SetupWizardRecyclerLayout layout = new SetupWizardRecyclerLayout(mContext);
-        assertRecyclerTemplateInflated(layout);
-
-        layout.setAdapter(createTestAdapter(3));
-
-        layout.measure(
-                MeasureSpec.makeMeasureSpec(500, MeasureSpec.EXACTLY),
-                MeasureSpec.makeMeasureSpec(500, MeasureSpec.EXACTLY));
-        layout.layout(0, 0, 500, 500);
-        // Test that the layout code doesn't crash.
-    }
-
-    @Test
-    public void testDividerInsetLegacy() {
-        SetupWizardRecyclerLayout layout = new SetupWizardRecyclerLayout(mContext);
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
-            layout.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
-        }
-        assertRecyclerTemplateInflated(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() {
-        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(
-                    mContext,
-                    R.layout.suw_glif_template,
-                    R.id.suw_recycler_view);
-            fail("Creating SetupWizardRecyclerLayout with no recycler view should throw exception");
-        } catch (Exception e) {
-            // pass
-        }
-    }
-
-    private void assertRecyclerTemplateInflated(SetupWizardRecyclerLayout layout) {
-        View recyclerView = layout.findViewById(R.id.suw_recycler_view);
-        assertTrue("@id/suw_recycler_view should be a RecyclerView",
-                recyclerView instanceof RecyclerView);
-
-        assertNotNull("Header text view should not be null",
-                layout.findManagedViewById(R.id.suw_layout_title));
-        assertNotNull("Decoration view should not be null",
-                layout.findManagedViewById(R.id.suw_layout_decor));
-    }
-
-    private Adapter createTestAdapter(final int itemCount) {
-        return new Adapter() {
-            @Override
-            public ViewHolder onCreateViewHolder(ViewGroup parent, int position) {
-                return new ViewHolder(new View(parent.getContext())) {};
-            }
-
-            @Override
-            public void onBindViewHolder(ViewHolder viewHolder, int position) {
-            }
-
-            @Override
-            public int getItemCount() {
-                return itemCount;
-            }
-        };
-    }
+      @Override
+      public int getItemCount() {
+        return itemCount;
+      }
+    };
+  }
 }
diff --git a/library/recyclerview/test/robotest/src/com/android/setupwizardlib/template/RecyclerViewScrollHandlingDelegateTest.java b/library/recyclerview/test/robotest/src/com/android/setupwizardlib/template/RecyclerViewScrollHandlingDelegateTest.java
index 6fa4c54..bcb51a8 100644
--- a/library/recyclerview/test/robotest/src/com/android/setupwizardlib/template/RecyclerViewScrollHandlingDelegateTest.java
+++ b/library/recyclerview/test/robotest/src/com/android/setupwizardlib/template/RecyclerViewScrollHandlingDelegateTest.java
@@ -26,63 +26,60 @@
 
 import androidx.recyclerview.widget.RecyclerView;
 import androidx.recyclerview.widget.RecyclerView.OnScrollListener;
-
-import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.annotation.Config;
 
-@Config(sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
-@RunWith(SuwLibRobolectricTestRunner.class)
+@Config(sdk = {Config.OLDEST_SDK, Config.NEWEST_SDK})
+@RunWith(RobolectricTestRunner.class)
 public class RecyclerViewScrollHandlingDelegateTest {
 
-    @Mock
-    private RequireScrollMixin mRequireScrollMixin;
+  @Mock private RequireScrollMixin mRequireScrollMixin;
 
-    private RecyclerView mRecyclerView;
-    private RecyclerViewScrollHandlingDelegate mDelegate;
-    private ArgumentCaptor<OnScrollListener> mListenerCaptor;
+  private RecyclerView mRecyclerView;
+  private RecyclerViewScrollHandlingDelegate mDelegate;
+  private ArgumentCaptor<OnScrollListener> mListenerCaptor;
 
-    @Before
-    public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
+  @Before
+  public void setUp() throws Exception {
+    MockitoAnnotations.initMocks(this);
 
-        mRecyclerView = spy(new RecyclerView(application));
-        doReturn(20).when(mRecyclerView).computeVerticalScrollRange();
-        doReturn(0).when(mRecyclerView).computeVerticalScrollExtent();
-        doReturn(0).when(mRecyclerView).computeVerticalScrollOffset();
-        mListenerCaptor = ArgumentCaptor.forClass(OnScrollListener.class);
-        doNothing().when(mRecyclerView).addOnScrollListener(mListenerCaptor.capture());
+    mRecyclerView = spy(new RecyclerView(application));
+    doReturn(20).when(mRecyclerView).computeVerticalScrollRange();
+    doReturn(0).when(mRecyclerView).computeVerticalScrollExtent();
+    doReturn(0).when(mRecyclerView).computeVerticalScrollOffset();
+    mListenerCaptor = ArgumentCaptor.forClass(OnScrollListener.class);
+    doNothing().when(mRecyclerView).addOnScrollListener(mListenerCaptor.capture());
 
-        mDelegate = new RecyclerViewScrollHandlingDelegate(mRequireScrollMixin, mRecyclerView);
-        mRecyclerView.layout(0, 0, 50, 50);
-    }
+    mDelegate = new RecyclerViewScrollHandlingDelegate(mRequireScrollMixin, mRecyclerView);
+    mRecyclerView.layout(0, 0, 50, 50);
+  }
 
-    @Test
-    public void testRequireScroll() {
-        mDelegate.startListening();
-        verify(mRequireScrollMixin).notifyScrollabilityChange(true);
-    }
+  @Test
+  public void testRequireScroll() {
+    mDelegate.startListening();
+    verify(mRequireScrollMixin).notifyScrollabilityChange(true);
+  }
 
-    @Test
-    public void testScrolledToBottom() {
-        mDelegate.startListening();
-        verify(mRequireScrollMixin).notifyScrollabilityChange(true);
+  @Test
+  public void testScrolledToBottom() {
+    mDelegate.startListening();
+    verify(mRequireScrollMixin).notifyScrollabilityChange(true);
 
-        doReturn(20).when(mRecyclerView).computeVerticalScrollOffset();
-        mListenerCaptor.getValue().onScrolled(mRecyclerView, 0, 20);
+    doReturn(20).when(mRecyclerView).computeVerticalScrollOffset();
+    mListenerCaptor.getValue().onScrolled(mRecyclerView, 0, 20);
 
-        verify(mRequireScrollMixin).notifyScrollabilityChange(false);
-    }
+    verify(mRequireScrollMixin).notifyScrollabilityChange(false);
+  }
 
-    @Test
-    public void testClickScrollButton() {
-        mDelegate.pageScrollDown();
-        verify(mRecyclerView).smoothScrollBy(anyInt(), eq(50));
-    }
+  @Test
+  public void testClickScrollButton() {
+    mDelegate.pageScrollDown();
+    verify(mRecyclerView).smoothScrollBy(anyInt(), eq(50));
+  }
 }
diff --git a/library/rules.gradle b/library/rules.gradle
index 9baa390..4e815ce 100644
--- a/library/rules.gradle
+++ b/library/rules.gradle
@@ -26,7 +26,7 @@
         // Provides backwards compatibility for Gingerbread or above, using support libraries.
         gingerbreadCompat {
             dimension 'compat'
-            minSdkVersion 9
+            minSdkVersion 14
         }
     }
 
diff --git a/library/self.gradle b/library/self.gradle
index a4bff4e..f649581 100644
--- a/library/self.gradle
+++ b/library/self.gradle
@@ -25,8 +25,8 @@
         res.srcDirs = ['test/instrumentation/res']
 
         dependencies {
-            androidTestImplementation 'com.android.support.test:rules:1.0.0'
-            androidTestImplementation 'com.android.support.test:runner:1.0.0'
+            androidTestImplementation 'com.android.support.test:rules:1.0.1'
+            androidTestImplementation 'com.android.support.test:runner:1.0.1'
             androidTestImplementation 'com.google.dexmaker:dexmaker:1.2'
             androidTestImplementation 'com.google.dexmaker:dexmaker-mockito:1.2'
             androidTestImplementation 'com.google.truth:truth:0.31'
@@ -51,8 +51,8 @@
         java.srcDirs = ['test/robotest/src']
 
         dependencies {
-            testImplementation 'org.robolectric:robolectric:3.6.1'
-            testImplementation 'org.robolectric:shadows-framework:3.6.1'
+            testImplementation 'org.robolectric:robolectric:4.0-alpha-3'
+            testImplementation 'org.robolectric:shadows-framework:4.0-alpha-3'
             testImplementation 'junit:junit:4.+'
             testImplementation 'com.google.truth:truth:0.31'
             testImplementation 'org.mockito:mockito-core:1.9.5'
diff --git a/library/test/instrumentation/src/com/android/setupwizardlib/TemplateLayoutTest.java b/library/test/instrumentation/src/com/android/setupwizardlib/TemplateLayoutTest.java
index ddce677..a5026ee 100644
--- a/library/test/instrumentation/src/com/android/setupwizardlib/TemplateLayoutTest.java
+++ b/library/test/instrumentation/src/com/android/setupwizardlib/TemplateLayoutTest.java
@@ -23,16 +23,14 @@
 import static org.junit.Assert.fail;
 
 import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.TextView;
-
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
 import com.android.setupwizardlib.template.HeaderMixin;
 import com.android.setupwizardlib.test.R;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -41,65 +39,64 @@
 @SmallTest
 public class TemplateLayoutTest {
 
-    private Context mContext;
+  private Context mContext;
 
-    @Before
-    public void setUp() throws Exception {
-        mContext = InstrumentationRegistry.getContext();
+  @Before
+  public void setUp() throws Exception {
+    mContext = InstrumentationRegistry.getContext();
+  }
+
+  @Test
+  public void testAddView() {
+    TemplateLayout layout =
+        new TemplateLayout(mContext, R.layout.test_template, R.id.suw_layout_content);
+    TextView tv = new TextView(mContext);
+    tv.setId(R.id.test_view_id);
+    layout.addView(tv);
+    View view = layout.findViewById(R.id.test_view_id);
+    assertSame("The view added should be the same text view", tv, view);
+  }
+
+  @Test
+  public void testInflateFromXml() {
+    LayoutInflater inflater = LayoutInflater.from(mContext);
+    TemplateLayout layout = (TemplateLayout) inflater.inflate(R.layout.test_template_layout, null);
+    View content = layout.findViewById(R.id.test_content);
+    assertTrue("@id/test_content should be a TextView", content instanceof TextView);
+  }
+
+  @Test
+  public void testTemplate() {
+    TemplateLayout layout =
+        new TemplateLayout(mContext, R.layout.test_template, R.id.suw_layout_content);
+    View templateView = layout.findViewById(R.id.test_template_view);
+    assertNotNull("@id/test_template_view should exist in template", templateView);
+
+    TextView tv = new TextView(mContext);
+    tv.setId(R.id.test_view_id);
+    layout.addView(tv);
+
+    templateView = layout.findViewById(R.id.test_template_view);
+    assertNotNull("@id/test_template_view should exist in template", templateView);
+    View contentView = layout.findViewById(R.id.test_view_id);
+    assertSame("The view added should be the same text view", tv, contentView);
+  }
+
+  @Test
+  public void testNoTemplate() {
+    try {
+      new TemplateLayout(mContext, 0, 0);
+      fail("Inflating TemplateLayout without template should throw exception");
+    } catch (IllegalArgumentException e) {
+      // Expected IllegalArgumentException
     }
+  }
 
-    @Test
-    public void testAddView() {
-        TemplateLayout layout = new TemplateLayout(mContext, R.layout.test_template,
-                R.id.suw_layout_content);
-        TextView tv = new TextView(mContext);
-        tv.setId(R.id.test_view_id);
-        layout.addView(tv);
-        View view = layout.findViewById(R.id.test_view_id);
-        assertSame("The view added should be the same text view", tv, view);
-    }
-
-    @Test
-    public void testInflateFromXml() {
-        LayoutInflater inflater = LayoutInflater.from(mContext);
-        TemplateLayout layout =
-                (TemplateLayout) inflater.inflate(R.layout.test_template_layout, null);
-        View content = layout.findViewById(R.id.test_content);
-        assertTrue("@id/test_content should be a TextView", content instanceof TextView);
-    }
-
-    @Test
-    public void testTemplate() {
-        TemplateLayout layout = new TemplateLayout(mContext, R.layout.test_template,
-                R.id.suw_layout_content);
-        View templateView = layout.findViewById(R.id.test_template_view);
-        assertNotNull("@id/test_template_view should exist in template", templateView);
-
-        TextView tv = new TextView(mContext);
-        tv.setId(R.id.test_view_id);
-        layout.addView(tv);
-
-        templateView = layout.findViewById(R.id.test_template_view);
-        assertNotNull("@id/test_template_view should exist in template", templateView);
-        View contentView = layout.findViewById(R.id.test_view_id);
-        assertSame("The view added should be the same text view", tv, contentView);
-    }
-
-    @Test
-    public void testNoTemplate() {
-        try {
-            new TemplateLayout(mContext, 0, 0);
-            fail("Inflating TemplateLayout without template should throw exception");
-        } catch (IllegalArgumentException e) {
-            // Expected IllegalArgumentException
-        }
-    }
-
-    @Test
-    public void testGetMixin() {
-        TemplateLayout layout = new TemplateLayout(mContext, R.layout.test_template,
-                R.id.suw_layout_content);
-        final HeaderMixin mixin = layout.getMixin(HeaderMixin.class);
-        assertNull("getMixin for a mixin that doesn't exist should return null", mixin);
-    }
+  @Test
+  public void testGetMixin() {
+    TemplateLayout layout =
+        new TemplateLayout(mContext, R.layout.test_template, R.id.suw_layout_content);
+    final HeaderMixin mixin = layout.getMixin(HeaderMixin.class);
+    assertNull("getMixin for a mixin that doesn't exist should return null", mixin);
+  }
 }
diff --git a/library/test/instrumentation/src/com/android/setupwizardlib/template/ButtonFooterMixinTest.java b/library/test/instrumentation/src/com/android/setupwizardlib/template/ButtonFooterMixinTest.java
index 08f5958..971211b 100644
--- a/library/test/instrumentation/src/com/android/setupwizardlib/template/ButtonFooterMixinTest.java
+++ b/library/test/instrumentation/src/com/android/setupwizardlib/template/ButtonFooterMixinTest.java
@@ -24,9 +24,7 @@
 import static org.mockito.Mockito.spy;
 
 import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.annotation.IdRes;
 import android.util.DisplayMetrics;
 import android.util.TypedValue;
 import android.view.View;
@@ -34,12 +32,11 @@
 import android.widget.Button;
 import android.widget.FrameLayout;
 import android.widget.LinearLayout;
-
-import androidx.annotation.IdRes;
-
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
 import com.android.setupwizardlib.TemplateLayout;
 import com.android.setupwizardlib.test.R;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -48,125 +45,128 @@
 @SmallTest
 public class ButtonFooterMixinTest {
 
-    private Context mContext;
-    private TemplateLayout mTemplateLayout;
+  private Context mContext;
+  private TemplateLayout mTemplateLayout;
 
-    // The parent view to contain the view stub and views it inflates.
-    private FrameLayout mStubParent;
-    private ViewStub mFooterStub;
+  // The parent view to contain the view stub and views it inflates.
+  private FrameLayout mStubParent;
+  private ViewStub mFooterStub;
 
-    @Before
-    public void setUp() {
-        mContext = InstrumentationRegistry.getTargetContext();
-        mTemplateLayout = spy(new TemplateLayout(mContext, R.layout.test_template,
-                R.id.suw_layout_content));
+  @Before
+  public void setUp() {
+    mContext = InstrumentationRegistry.getTargetContext();
+    mTemplateLayout =
+        spy(new TemplateLayout(mContext, R.layout.test_template, R.id.suw_layout_content));
 
-        mFooterStub = new ViewStub(mContext, R.layout.suw_glif_footer_button_bar);
-        mStubParent = new FrameLayout(mContext);
-        mStubParent.addView(mFooterStub);
-        doReturn(mFooterStub).when(mTemplateLayout).findManagedViewById(eq(R.id.suw_layout_footer));
-    }
+    mFooterStub = new ViewStub(mContext, R.layout.suw_glif_footer_button_bar);
+    mStubParent = new FrameLayout(mContext);
+    mStubParent.addView(mFooterStub);
+    doReturn(mFooterStub).when(mTemplateLayout).findManagedViewById(eq(R.id.suw_layout_footer));
+  }
 
-    @Test
-    public void testAddButton() {
-        ButtonFooterMixin mixin = new ButtonFooterMixin(mTemplateLayout);
-        final Button button = mixin.addButton("foobar", R.style.SuwGlifButton_Primary);
+  @Test
+  public void testAddButton() {
+    ButtonFooterMixin mixin = new ButtonFooterMixin(mTemplateLayout);
+    final Button button = mixin.addButton("foobar", R.style.SuwGlifButton_Primary);
 
-        assertNotNull(button);
-        @IdRes final int id = 12345;
-        button.setId(id);
-        assertNotNull(mStubParent.findViewById(id));
+    assertNotNull(button);
+    @IdRes final int id = 12345;
+    button.setId(id);
+    assertNotNull(mStubParent.findViewById(id));
 
-        assertEquals("foobar", button.getText());
+    assertEquals("foobar", button.getText());
 
-        // Make sure the style is applied by checking the paddings
-        assertEquals(dp2Px(16), button.getPaddingLeft());
-        assertEquals(dp2Px(16), button.getPaddingRight());
-    }
+    // Make sure the style is applied by checking the paddings
+    assertEquals(dp2Px(16), button.getPaddingLeft());
+    assertEquals(dp2Px(16), button.getPaddingRight());
+  }
 
-    @Test
-    public void testAddButtonTextRes() {
-        ButtonFooterMixin mixin = new ButtonFooterMixin(mTemplateLayout);
-        final Button button = mixin.addButton(R.string.suw_next_button_label,
-                R.style.SuwGlifButton_Primary);
+  @Test
+  public void testAddButtonTextRes() {
+    ButtonFooterMixin mixin = new ButtonFooterMixin(mTemplateLayout);
+    final Button button =
+        mixin.addButton(R.string.suw_next_button_label, R.style.SuwGlifButton_Primary);
 
-        assertNotNull(button);
-        button.setTag("button");
-        assertNotNull(mStubParent.findViewWithTag("button"));
+    assertNotNull(button);
+    button.setTag("button");
+    assertNotNull(mStubParent.findViewWithTag("button"));
 
-        assertEquals("Next", button.getText());
+    assertEquals("Next", button.getText());
 
-        // Make sure the style is applied by checking the paddings
-        assertEquals(dp2Px(16), button.getPaddingLeft());
-        assertEquals(dp2Px(16), button.getPaddingRight());
-    }
+    // Make sure the style is applied by checking the paddings
+    assertEquals(dp2Px(16), button.getPaddingLeft());
+    assertEquals(dp2Px(16), button.getPaddingRight());
+  }
 
-    @Test
-    public void testAddSpace() {
-        ButtonFooterMixin mixin = new ButtonFooterMixin(mTemplateLayout);
-        mixin.addButton("foo", R.style.SuwGlifButton_Secondary);
-        final View space = mixin.addSpace();
-        mixin.addButton("bar", R.style.SuwGlifButton_Primary);
+  @Test
+  public void testAddSpace() {
+    ButtonFooterMixin mixin = new ButtonFooterMixin(mTemplateLayout);
+    mixin.addButton("foo", R.style.SuwGlifButton_Secondary);
+    final View space = mixin.addSpace();
+    mixin.addButton("bar", R.style.SuwGlifButton_Primary);
 
-        space.setTag("space");
-        assertNotNull(mStubParent.findViewWithTag("space"));
-        assertEquals("Space should have weight of 1",
-                1f, ((LinearLayout.LayoutParams) space.getLayoutParams()).weight, 0.001);
-    }
+    space.setTag("space");
+    assertNotNull(mStubParent.findViewWithTag("space"));
+    assertEquals(
+        "Space should have weight of 1",
+        1f,
+        ((LinearLayout.LayoutParams) space.getLayoutParams()).weight,
+        0.001);
+  }
 
-    @Test
-    public void testRemoveButton() {
-        ButtonFooterMixin mixin = new ButtonFooterMixin(mTemplateLayout);
-        final Button fooButton = mixin.addButton("foo", R.style.SuwGlifButton_Secondary);
-        final Button barButton = mixin.addButton("bar", R.style.SuwGlifButton_Secondary);
+  @Test
+  public void testRemoveButton() {
+    ButtonFooterMixin mixin = new ButtonFooterMixin(mTemplateLayout);
+    final Button fooButton = mixin.addButton("foo", R.style.SuwGlifButton_Secondary);
+    final Button barButton = mixin.addButton("bar", R.style.SuwGlifButton_Secondary);
 
-        fooButton.setTag("foo");
-        barButton.setTag("bar");
-        assertNotNull("Foo button should exist", mStubParent.findViewWithTag("foo"));
-        assertNotNull("Bar button should exist", mStubParent.findViewWithTag("bar"));
+    fooButton.setTag("foo");
+    barButton.setTag("bar");
+    assertNotNull("Foo button should exist", mStubParent.findViewWithTag("foo"));
+    assertNotNull("Bar button should exist", mStubParent.findViewWithTag("bar"));
 
-        mixin.removeButton(fooButton);
+    mixin.removeButton(fooButton);
 
-        assertNull("Foo button should be removed", mStubParent.findViewWithTag("foo"));
-        assertNotNull("Bar button should not be removed", mStubParent.findViewWithTag("bar"));
-    }
+    assertNull("Foo button should be removed", mStubParent.findViewWithTag("foo"));
+    assertNotNull("Bar button should not be removed", mStubParent.findViewWithTag("bar"));
+  }
 
-    @Test
-    public void testRemoveSpace() {
-        ButtonFooterMixin mixin = new ButtonFooterMixin(mTemplateLayout);
-        final Button fooButton = mixin.addButton("foo", R.style.SuwGlifButton_Secondary);
-        final View space = mixin.addSpace();
+  @Test
+  public void testRemoveSpace() {
+    ButtonFooterMixin mixin = new ButtonFooterMixin(mTemplateLayout);
+    final Button fooButton = mixin.addButton("foo", R.style.SuwGlifButton_Secondary);
+    final View space = mixin.addSpace();
 
-        fooButton.setTag("foo");
-        space.setTag("space");
-        assertNotNull("Foo button should exist", mStubParent.findViewWithTag("foo"));
-        assertNotNull("space should exist", mStubParent.findViewWithTag("space"));
+    fooButton.setTag("foo");
+    space.setTag("space");
+    assertNotNull("Foo button should exist", mStubParent.findViewWithTag("foo"));
+    assertNotNull("space should exist", mStubParent.findViewWithTag("space"));
 
-        mixin.removeSpace(space);
+    mixin.removeSpace(space);
 
-        assertNotNull("Foo button should not be removed", mStubParent.findViewWithTag("foo"));
-        assertNull("Space should be removed", mStubParent.findViewWithTag("space"));
-    }
+    assertNotNull("Foo button should not be removed", mStubParent.findViewWithTag("foo"));
+    assertNull("Space should be removed", mStubParent.findViewWithTag("space"));
+  }
 
-    @Test
-    public void testRemoveAllViews() {
-        ButtonFooterMixin mixin = new ButtonFooterMixin(mTemplateLayout);
-        final Button fooButton = mixin.addButton("foo", R.style.SuwGlifButton_Secondary);
-        final View space = mixin.addSpace();
+  @Test
+  public void testRemoveAllViews() {
+    ButtonFooterMixin mixin = new ButtonFooterMixin(mTemplateLayout);
+    final Button fooButton = mixin.addButton("foo", R.style.SuwGlifButton_Secondary);
+    final View space = mixin.addSpace();
 
-        fooButton.setTag("foo");
-        space.setTag("space");
-        assertNotNull("Foo button should exist", mStubParent.findViewWithTag("foo"));
-        assertNotNull("space should exist", mStubParent.findViewWithTag("space"));
+    fooButton.setTag("foo");
+    space.setTag("space");
+    assertNotNull("Foo button should exist", mStubParent.findViewWithTag("foo"));
+    assertNotNull("space should exist", mStubParent.findViewWithTag("space"));
 
-        mixin.removeAllViews();
+    mixin.removeAllViews();
 
-        assertNull("Foo button should be removed", mStubParent.findViewWithTag("foo"));
-        assertNull("Space should be removed", mStubParent.findViewWithTag("space"));
-    }
+    assertNull("Foo button should be removed", mStubParent.findViewWithTag("foo"));
+    assertNull("Space should be removed", mStubParent.findViewWithTag("space"));
+  }
 
-    private int dp2Px(float dp) {
-        DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();
-        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, displayMetrics);
-    }
+  private int dp2Px(float dp) {
+    DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();
+    return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, displayMetrics);
+  }
 }
diff --git a/library/test/instrumentation/src/com/android/setupwizardlib/template/ColoredHeaderMixinTest.java b/library/test/instrumentation/src/com/android/setupwizardlib/template/ColoredHeaderMixinTest.java
index 1c86af1..3ea8f6e 100644
--- a/library/test/instrumentation/src/com/android/setupwizardlib/template/ColoredHeaderMixinTest.java
+++ b/library/test/instrumentation/src/com/android/setupwizardlib/template/ColoredHeaderMixinTest.java
@@ -25,67 +25,62 @@
 import android.content.res.ColorStateList;
 import android.content.res.XmlResourceParser;
 import android.graphics.Color;
+import android.util.Xml;
+import android.widget.TextView;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.util.Xml;
-import android.widget.TextView;
-
 import com.android.setupwizardlib.TemplateLayout;
 import com.android.setupwizardlib.test.R;
-
+import java.io.IOException;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.xmlpull.v1.XmlPullParserException;
 
-import java.io.IOException;
-
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class ColoredHeaderMixinTest {
 
-    private Context mContext;
-    private TemplateLayout mTemplateLayout;
-    private TextView mHeaderTextView;
+  private Context mContext;
+  private TemplateLayout mTemplateLayout;
+  private TextView mHeaderTextView;
 
-    @Before
-    public void setUp() {
-        mContext = InstrumentationRegistry.getTargetContext();
-        mTemplateLayout = spy(new TemplateLayout(mContext, R.layout.test_template,
-                R.id.suw_layout_content));
+  @Before
+  public void setUp() {
+    mContext = InstrumentationRegistry.getTargetContext();
+    mTemplateLayout =
+        spy(new TemplateLayout(mContext, R.layout.test_template, R.id.suw_layout_content));
 
-        mHeaderTextView = new TextView(mContext);
-        doReturn(mHeaderTextView).when(mTemplateLayout)
-                .findManagedViewById(eq(R.id.suw_layout_title));
+    mHeaderTextView = new TextView(mContext);
+    doReturn(mHeaderTextView).when(mTemplateLayout).findManagedViewById(eq(R.id.suw_layout_title));
+  }
+
+  @Test
+  public void testSetColor() {
+    ColoredHeaderMixin mixin = new ColoredHeaderMixin(mTemplateLayout, null, 0);
+    mixin.setColor(ColorStateList.valueOf(Color.MAGENTA));
+
+    assertEquals(ColorStateList.valueOf(Color.MAGENTA), mHeaderTextView.getTextColors());
+  }
+
+  @Test
+  public void testGetColor() {
+    ColoredHeaderMixin mixin = new ColoredHeaderMixin(mTemplateLayout, null, 0);
+    mHeaderTextView.setTextColor(ColorStateList.valueOf(Color.GREEN));
+
+    assertEquals(ColorStateList.valueOf(Color.GREEN), mixin.getColor());
+  }
+
+  @SuppressWarnings("ResourceType") // Needed to create attribute set from layout XML.
+  @Test
+  public void testSetColorFromXml() throws IOException, XmlPullParserException {
+    final XmlResourceParser parser = mContext.getResources().getXml(R.layout.test_mixin_attributes);
+    while (!TemplateLayout.class.getName().equals(parser.getName())) {
+      parser.next();
     }
+    new ColoredHeaderMixin(mTemplateLayout, Xml.asAttributeSet(parser), 0);
 
-    @Test
-    public void testSetColor() {
-        ColoredHeaderMixin mixin = new ColoredHeaderMixin(mTemplateLayout, null, 0);
-        mixin.setColor(ColorStateList.valueOf(Color.MAGENTA));
-
-        assertEquals(ColorStateList.valueOf(Color.MAGENTA), mHeaderTextView.getTextColors());
-    }
-
-    @Test
-    public void testGetColor() {
-        ColoredHeaderMixin mixin = new ColoredHeaderMixin(mTemplateLayout, null, 0);
-        mHeaderTextView.setTextColor(ColorStateList.valueOf(Color.GREEN));
-
-        assertEquals(ColorStateList.valueOf(Color.GREEN), mixin.getColor());
-    }
-
-    @SuppressWarnings("ResourceType")  // Needed to create attribute set from layout XML.
-    @Test
-    public void testSetColorFromXml() throws IOException, XmlPullParserException {
-        final XmlResourceParser parser =
-                mContext.getResources().getXml(R.layout.test_mixin_attributes);
-        while (!TemplateLayout.class.getName().equals(parser.getName())) {
-            parser.next();
-        }
-        new ColoredHeaderMixin(mTemplateLayout, Xml.asAttributeSet(parser), 0);
-
-        assertEquals(ColorStateList.valueOf(Color.RED), mHeaderTextView.getTextColors());
-    }
+    assertEquals(ColorStateList.valueOf(Color.RED), mHeaderTextView.getTextColors());
+  }
 }
diff --git a/library/test/instrumentation/src/com/android/setupwizardlib/template/HeaderMixinTest.java b/library/test/instrumentation/src/com/android/setupwizardlib/template/HeaderMixinTest.java
index a1b4b59..211f95f 100644
--- a/library/test/instrumentation/src/com/android/setupwizardlib/template/HeaderMixinTest.java
+++ b/library/test/instrumentation/src/com/android/setupwizardlib/template/HeaderMixinTest.java
@@ -25,82 +25,77 @@
 import android.annotation.SuppressLint;
 import android.content.Context;
 import android.content.res.XmlResourceParser;
+import android.util.Xml;
+import android.widget.TextView;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.util.Xml;
-import android.widget.TextView;
-
 import com.android.setupwizardlib.TemplateLayout;
 import com.android.setupwizardlib.test.R;
-
+import java.io.IOException;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.xmlpull.v1.XmlPullParserException;
 
-import java.io.IOException;
-
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class HeaderMixinTest {
 
-    private Context mContext;
-    private TemplateLayout mTemplateLayout;
-    private TextView mHeaderTextView;
+  private Context mContext;
+  private TemplateLayout mTemplateLayout;
+  private TextView mHeaderTextView;
 
-    @Before
-    public void setUp() {
-        mContext = InstrumentationRegistry.getTargetContext();
-        mTemplateLayout = spy(new TemplateLayout(mContext, R.layout.test_template,
-                R.id.suw_layout_content));
+  @Before
+  public void setUp() {
+    mContext = InstrumentationRegistry.getTargetContext();
+    mTemplateLayout =
+        spy(new TemplateLayout(mContext, R.layout.test_template, R.id.suw_layout_content));
 
-        mHeaderTextView = new TextView(mContext);
-        doReturn(mHeaderTextView).when(mTemplateLayout)
-                .findManagedViewById(eq(R.id.suw_layout_title));
+    mHeaderTextView = new TextView(mContext);
+    doReturn(mHeaderTextView).when(mTemplateLayout).findManagedViewById(eq(R.id.suw_layout_title));
+  }
+
+  @Test
+  public void testGetTextView() {
+    HeaderMixin mixin = new HeaderMixin(mTemplateLayout, null, 0);
+    assertSame(mHeaderTextView, mixin.getTextView());
+  }
+
+  @Test
+  public void testSetTextId() {
+    HeaderMixin mixin = new HeaderMixin(mTemplateLayout, null, 0);
+    mixin.setText(R.string.suw_next_button_label);
+
+    assertEquals("Next", mHeaderTextView.getText());
+  }
+
+  @Test
+  public void testSetText() {
+    HeaderMixin mixin = new HeaderMixin(mTemplateLayout, null, 0);
+    mixin.setText("Foobar");
+
+    assertEquals("Foobar", mHeaderTextView.getText());
+  }
+
+  @SuppressLint("SetTextI18n") // It's OK, this is a test
+  @Test
+  public void testGetText() {
+    mHeaderTextView.setText("Lorem ipsum");
+
+    HeaderMixin mixin = new HeaderMixin(mTemplateLayout, null, 0);
+    assertEquals("Lorem ipsum", mixin.getText());
+  }
+
+  @SuppressWarnings("ResourceType") // Needed to create attribute set from layout XML.
+  @Test
+  public void testSetTextFromXml() throws IOException, XmlPullParserException {
+    final XmlResourceParser parser = mContext.getResources().getXml(R.layout.test_mixin_attributes);
+    while (!TemplateLayout.class.getName().equals(parser.getName())) {
+      parser.next();
     }
+    new HeaderMixin(mTemplateLayout, Xml.asAttributeSet(parser), 0);
 
-    @Test
-    public void testGetTextView() {
-        HeaderMixin mixin = new HeaderMixin(mTemplateLayout, null, 0);
-        assertSame(mHeaderTextView, mixin.getTextView());
-    }
-
-    @Test
-    public void testSetTextId() {
-        HeaderMixin mixin = new HeaderMixin(mTemplateLayout, null, 0);
-        mixin.setText(R.string.suw_next_button_label);
-
-        assertEquals("Next", mHeaderTextView.getText());
-    }
-
-    @Test
-    public void testSetText() {
-        HeaderMixin mixin = new HeaderMixin(mTemplateLayout, null, 0);
-        mixin.setText("Foobar");
-
-        assertEquals("Foobar", mHeaderTextView.getText());
-    }
-
-    @SuppressLint("SetTextI18n")  // It's OK, this is a test
-    @Test
-    public void testGetText() {
-        mHeaderTextView.setText("Lorem ipsum");
-
-        HeaderMixin mixin = new HeaderMixin(mTemplateLayout, null, 0);
-        assertEquals("Lorem ipsum", mixin.getText());
-    }
-
-    @SuppressWarnings("ResourceType")  // Needed to create attribute set from layout XML.
-    @Test
-    public void testSetTextFromXml() throws IOException, XmlPullParserException {
-        final XmlResourceParser parser =
-                mContext.getResources().getXml(R.layout.test_mixin_attributes);
-        while (!TemplateLayout.class.getName().equals(parser.getName())) {
-            parser.next();
-        }
-        new HeaderMixin(mTemplateLayout, Xml.asAttributeSet(parser), 0);
-
-        assertEquals("lorem ipsum", mHeaderTextView.getText());
-    }
+    assertEquals("lorem ipsum", mHeaderTextView.getText());
+  }
 }
diff --git a/library/test/instrumentation/src/com/android/setupwizardlib/template/IconMixinTest.java b/library/test/instrumentation/src/com/android/setupwizardlib/template/IconMixinTest.java
index 5a36f4a..001fe33 100644
--- a/library/test/instrumentation/src/com/android/setupwizardlib/template/IconMixinTest.java
+++ b/library/test/instrumentation/src/com/android/setupwizardlib/template/IconMixinTest.java
@@ -17,7 +17,6 @@
 package com.android.setupwizardlib.template;
 
 import static com.google.common.truth.Truth.assertThat;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertSame;
 import static org.mockito.Matchers.eq;
@@ -30,115 +29,111 @@
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.util.Xml;
 import android.view.View;
 import android.widget.ImageView;
-
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
 import com.android.setupwizardlib.TemplateLayout;
 import com.android.setupwizardlib.test.R;
-
+import java.io.IOException;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.xmlpull.v1.XmlPullParserException;
 
-import java.io.IOException;
-
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class IconMixinTest {
 
-    private Context mContext;
-    private TemplateLayout mTemplateLayout;
-    private ImageView mIconView;
+  private Context mContext;
+  private TemplateLayout mTemplateLayout;
+  private ImageView mIconView;
 
-    @Before
-    public void setUp() {
-        mContext = InstrumentationRegistry.getContext();
-        mTemplateLayout = spy(new TemplateLayout(mContext, R.layout.test_template,
-                R.id.suw_layout_content));
+  @Before
+  public void setUp() {
+    mContext = InstrumentationRegistry.getContext();
+    mTemplateLayout =
+        spy(new TemplateLayout(mContext, R.layout.test_template, R.id.suw_layout_content));
 
-        mIconView = new ImageView(mContext);
-        doReturn(mIconView).when(mTemplateLayout).findManagedViewById(eq(R.id.suw_layout_icon));
+    mIconView = new ImageView(mContext);
+    doReturn(mIconView).when(mTemplateLayout).findManagedViewById(eq(R.id.suw_layout_icon));
+  }
+
+  @Test
+  public void testGetIconView() {
+    IconMixin mixin = new IconMixin(mTemplateLayout, null, 0);
+    assertSame(mIconView, mixin.getView());
+  }
+
+  @Test
+  public void testSetIcon() {
+    final ColorDrawable drawable = new ColorDrawable(Color.CYAN);
+    IconMixin mixin = new IconMixin(mTemplateLayout, null, 0);
+    mixin.setIcon(drawable);
+
+    assertSame(drawable, mIconView.getDrawable());
+    assertEquals(View.VISIBLE, mIconView.getVisibility());
+  }
+
+  @Test
+  public void setIcon_resourceId_shouldSetIcon() {
+    int icon = android.R.drawable.ic_menu_add;
+    IconMixin mixin = new IconMixin(mTemplateLayout, null, 0);
+    mixin.setIcon(icon);
+
+    Drawable drawable = mIconView.getDrawable();
+    assertThat(drawable).isInstanceOf(BitmapDrawable.class);
+    assertEquals(View.VISIBLE, mIconView.getVisibility());
+  }
+
+  @Test
+  public void setIcon_shouldSetVisibilityToGone_whenIconIsNull() {
+    IconMixin mixin = new IconMixin(mTemplateLayout, null, 0);
+    mixin.setIcon(null);
+
+    assertEquals(View.GONE, mIconView.getVisibility());
+  }
+
+  @Test
+  public void testGetIcon() {
+    final ColorDrawable drawable = new ColorDrawable(Color.BLUE);
+    mIconView.setImageDrawable(drawable);
+
+    IconMixin mixin = new IconMixin(mTemplateLayout, null, 0);
+    assertSame(drawable, mixin.getIcon());
+  }
+
+  @SuppressWarnings("ResourceType") // Needed to create attribute set from layout XML.
+  @Test
+  public void testSetIconFromXml() throws IOException, XmlPullParserException {
+    final XmlResourceParser parser = mContext.getResources().getXml(R.layout.test_mixin_attributes);
+    while (!TemplateLayout.class.getName().equals(parser.getName())) {
+      parser.next();
     }
+    new IconMixin(mTemplateLayout, Xml.asAttributeSet(parser), 0);
 
-    @Test
-    public void testGetIconView() {
-        IconMixin mixin = new IconMixin(mTemplateLayout, null, 0);
-        assertSame(mIconView, mixin.getView());
-    }
+    // Check that the bitmaps themselves are equal because BitmapDrawable does not implement
+    // equals()
+    final BitmapDrawable expected =
+        (BitmapDrawable) mContext.getResources().getDrawable(android.R.drawable.ic_menu_add);
+    final BitmapDrawable actual = (BitmapDrawable) mIconView.getDrawable();
+    assertEquals(expected.getBitmap(), actual.getBitmap());
+    assertEquals(View.VISIBLE, mIconView.getVisibility());
+  }
 
-    @Test
-    public void testSetIcon() {
-        final ColorDrawable drawable = new ColorDrawable(Color.CYAN);
-        IconMixin mixin = new IconMixin(mTemplateLayout, null, 0);
-        mixin.setIcon(drawable);
+  @Test
+  public void setContentDescription_shouldSetContentDescriptionOnIconView() {
+    IconMixin mixin = new IconMixin(mTemplateLayout, null, 0);
+    mixin.setContentDescription("hello world");
+    assertThat(mIconView.getContentDescription()).isEqualTo("hello world");
+  }
 
-        assertSame(drawable, mIconView.getDrawable());
-        assertEquals(View.VISIBLE, mIconView.getVisibility());
-    }
-
-    @Test
-    public void setIcon_resourceId_shouldSetIcon() {
-        int icon = android.R.drawable.ic_menu_add;
-        IconMixin mixin = new IconMixin(mTemplateLayout, null, 0);
-        mixin.setIcon(icon);
-
-        Drawable drawable = mIconView.getDrawable();
-        assertThat(drawable).isInstanceOf(BitmapDrawable.class);
-        assertEquals(View.VISIBLE, mIconView.getVisibility());
-    }
-
-    @Test
-    public void setIcon_shouldSetVisibilityToGone_whenIconIsNull() {
-        IconMixin mixin = new IconMixin(mTemplateLayout, null, 0);
-        mixin.setIcon(null);
-
-        assertEquals(View.GONE, mIconView.getVisibility());
-    }
-
-    @Test
-    public void testGetIcon() {
-        final ColorDrawable drawable = new ColorDrawable(Color.BLUE);
-        mIconView.setImageDrawable(drawable);
-
-        IconMixin mixin = new IconMixin(mTemplateLayout, null, 0);
-        assertSame(drawable, mixin.getIcon());
-    }
-
-    @SuppressWarnings("ResourceType")  // Needed to create attribute set from layout XML.
-    @Test
-    public void testSetIconFromXml() throws IOException, XmlPullParserException {
-        final XmlResourceParser parser =
-                mContext.getResources().getXml(R.layout.test_mixin_attributes);
-        while (!TemplateLayout.class.getName().equals(parser.getName())) {
-            parser.next();
-        }
-        new IconMixin(mTemplateLayout, Xml.asAttributeSet(parser), 0);
-
-        // Check that the bitmaps themselves are equal because BitmapDrawable does not implement
-        // equals()
-        final BitmapDrawable expected = (BitmapDrawable) mContext.getResources()
-                .getDrawable(android.R.drawable.ic_menu_add);
-        final BitmapDrawable actual = (BitmapDrawable) mIconView.getDrawable();
-        assertEquals(expected.getBitmap(), actual.getBitmap());
-        assertEquals(View.VISIBLE, mIconView.getVisibility());
-    }
-
-    @Test
-    public void setContentDescription_shouldSetContentDescriptionOnIconView() {
-        IconMixin mixin = new IconMixin(mTemplateLayout, null, 0);
-        mixin.setContentDescription("hello world");
-        assertThat(mIconView.getContentDescription()).isEqualTo("hello world");
-    }
-
-    @Test
-    public void getContentDescription_shouldReturnContentDescriptionFromView() {
-        IconMixin mixin = new IconMixin(mTemplateLayout, null, 0);
-        mIconView.setContentDescription("aloha");
-        assertThat(mixin.getContentDescription()).isEqualTo("aloha");
-    }
+  @Test
+  public void getContentDescription_shouldReturnContentDescriptionFromView() {
+    IconMixin mixin = new IconMixin(mTemplateLayout, null, 0);
+    mIconView.setContentDescription("aloha");
+    assertThat(mixin.getContentDescription()).isEqualTo("aloha");
+  }
 }
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 30d68f1..e73e2bc 100644
--- a/library/test/instrumentation/src/com/android/setupwizardlib/template/ListMixinTest.java
+++ b/library/test/instrumentation/src/com/android/setupwizardlib/template/ListMixinTest.java
@@ -32,16 +32,14 @@
 import android.graphics.drawable.InsetDrawable;
 import android.os.Build.VERSION;
 import android.os.Build.VERSION_CODES;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.view.View;
 import android.widget.ListAdapter;
 import android.widget.ListView;
-
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
 import com.android.setupwizardlib.TemplateLayout;
 import com.android.setupwizardlib.test.R;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -52,140 +50,138 @@
 @SmallTest
 public class ListMixinTest {
 
-    private Context mContext;
-    private TemplateLayout mTemplateLayout;
+  private Context mContext;
+  private TemplateLayout mTemplateLayout;
 
-    private ListView mListView;
+  private ListView mListView;
 
-    @Mock
-    private ListAdapter mAdapter;
+  @Mock private ListAdapter mAdapter;
 
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
+  @Before
+  public void setUp() {
+    MockitoAnnotations.initMocks(this);
 
-        mContext = InstrumentationRegistry.getTargetContext();
-        mTemplateLayout = spy(new TemplateLayout(mContext, R.layout.test_template,
-                R.id.suw_layout_content));
+    mContext = InstrumentationRegistry.getTargetContext();
+    mTemplateLayout =
+        spy(new TemplateLayout(mContext, R.layout.test_template, R.id.suw_layout_content));
 
-        mListView = mock(ListView.class, delegatesTo(new ListView(mContext)));
-        doReturn(1).when(mAdapter).getViewTypeCount();
+    mListView = mock(ListView.class, delegatesTo(new ListView(mContext)));
+    doReturn(1).when(mAdapter).getViewTypeCount();
 
-        doReturn(mListView).when(mTemplateLayout)
-                .findManagedViewById(eq(android.R.id.list));
-        doReturn(true).when(mTemplateLayout).isLayoutDirectionResolved();
+    doReturn(mListView).when(mTemplateLayout).findManagedViewById(eq(android.R.id.list));
+    doReturn(true).when(mTemplateLayout).isLayoutDirectionResolved();
+  }
+
+  @Test
+  public void testGetListView() {
+    ListMixin mixin = new ListMixin(mTemplateLayout, null, 0);
+    assertSame(mListView, mixin.getListView());
+  }
+
+  @Test
+  public void testGetAdapter() {
+    mListView.setAdapter(mAdapter);
+
+    ListMixin mixin = new ListMixin(mTemplateLayout, null, 0);
+    assertSame(mAdapter, mixin.getAdapter());
+  }
+
+  @Test
+  public void testSetAdapter() {
+    assertNull(mListView.getAdapter());
+
+    ListMixin mixin = new ListMixin(mTemplateLayout, null, 0);
+    mixin.setAdapter(mAdapter);
+
+    assertSame(mAdapter, mListView.getAdapter());
+  }
+
+  @Test
+  public void testDividerInsetLegacy() {
+    ListMixin mixin = new ListMixin(mTemplateLayout, null, 0);
+    mixin.setDividerInset(123);
+
+    assertEquals(123, mixin.getDividerInset());
+
+    final Drawable divider = mListView.getDivider();
+    InsetDrawable insetDrawable = (InsetDrawable) divider;
+    Rect rect = new Rect();
+    insetDrawable.getPadding(rect);
+
+    assertEquals(new Rect(123, 0, 0, 0), rect);
+  }
+
+  @Test
+  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();
+
+      ListMixin mixin = new ListMixin(mTemplateLayout, null, 0);
+      mixin.setDividerInset(123);
+
+      assertEquals(123, mixin.getDividerInset());
+
+      final Drawable divider = mListView.getDivider();
+      InsetDrawable insetDrawable = (InsetDrawable) divider;
+      Rect rect = new Rect();
+      insetDrawable.getPadding(rect);
+
+      assertEquals(new Rect(0, 0, 123, 0), rect);
     }
+    // else the test passes
+  }
 
-    @Test
-    public void testGetListView() {
-        ListMixin mixin = new ListMixin(mTemplateLayout, null, 0);
-        assertSame(mListView, mixin.getListView());
+  @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 testGetAdapter() {
-        mListView.setAdapter(mAdapter);
+  @Test
+  public void testNoList() {
+    doReturn(null).when(mTemplateLayout).findManagedViewById(eq(android.R.id.list));
 
-        ListMixin mixin = new ListMixin(mTemplateLayout, null, 0);
-        assertSame(mAdapter, mixin.getAdapter());
-    }
+    ListMixin mixin = new ListMixin(mTemplateLayout, null, 0);
 
-    @Test
-    public void testSetAdapter() {
-        assertNull(mListView.getAdapter());
+    mixin.setAdapter(mAdapter);
+    mixin.setDividerInset(123);
 
-        ListMixin mixin = new ListMixin(mTemplateLayout, null, 0);
-        mixin.setAdapter(mAdapter);
+    assertNull(mixin.getListView());
+    assertNull(mixin.getAdapter());
+    mixin.getDividerInset(); // Test that it doesn't crash. The return value is not significant.
+    assertNull(mixin.getDivider());
 
-        assertSame(mAdapter, mListView.getAdapter());
-    }
-
-    @Test
-    public void testDividerInsetLegacy() {
-        ListMixin mixin = new ListMixin(mTemplateLayout, null, 0);
-        mixin.setDividerInset(123);
-
-        assertEquals(123, mixin.getDividerInset());
-
-        final Drawable divider = mListView.getDivider();
-        InsetDrawable insetDrawable = (InsetDrawable) divider;
-        Rect rect = new Rect();
-        insetDrawable.getPadding(rect);
-
-        assertEquals(new Rect(123, 0, 0, 0), rect);
-    }
-
-    @Test
-    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();
-
-            ListMixin mixin = new ListMixin(mTemplateLayout, null, 0);
-            mixin.setDividerInset(123);
-
-            assertEquals(123, mixin.getDividerInset());
-
-            final Drawable divider = mListView.getDivider();
-            InsetDrawable insetDrawable = (InsetDrawable) divider;
-            Rect rect = new Rect();
-            insetDrawable.getPadding(rect);
-
-            assertEquals(new Rect(0, 0, 123, 0), rect);
-        }
-        // 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();
-
-            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));
-
-        ListMixin mixin = new ListMixin(mTemplateLayout, null, 0);
-
-        mixin.setAdapter(mAdapter);
-        mixin.setDividerInset(123);
-
-        assertNull(mixin.getListView());
-        assertNull(mixin.getAdapter());
-        mixin.getDividerInset(); // Test that it doesn't crash. The return value is not significant.
-        assertNull(mixin.getDivider());
-
-        verifyNoMoreInteractions(mListView);
-    }
+    verifyNoMoreInteractions(mListView);
+  }
 }
diff --git a/library/test/instrumentation/src/com/android/setupwizardlib/template/NavigationBarMixinTest.java b/library/test/instrumentation/src/com/android/setupwizardlib/template/NavigationBarMixinTest.java
index aca6084..1e2aff3 100644
--- a/library/test/instrumentation/src/com/android/setupwizardlib/template/NavigationBarMixinTest.java
+++ b/library/test/instrumentation/src/com/android/setupwizardlib/template/NavigationBarMixinTest.java
@@ -29,12 +29,10 @@
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-
 import com.android.setupwizardlib.TemplateLayout;
 import com.android.setupwizardlib.test.R;
 import com.android.setupwizardlib.view.NavigationBar;
 import com.android.setupwizardlib.view.NavigationBar.NavigationBarListener;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -43,56 +41,57 @@
 @SmallTest
 public class NavigationBarMixinTest {
 
-    private Context mContext;
-    private TemplateLayout mTemplateLayout;
-    private NavigationBar mNavigationBar;
+  private Context mContext;
+  private TemplateLayout mTemplateLayout;
+  private NavigationBar mNavigationBar;
 
-    @Before
-    public void setUp() {
-        mContext = InstrumentationRegistry.getContext();
-        mTemplateLayout = spy(new TemplateLayout(mContext, R.layout.test_template,
-                R.id.suw_layout_content));
+  @Before
+  public void setUp() {
+    mContext = InstrumentationRegistry.getContext();
+    mTemplateLayout =
+        spy(new TemplateLayout(mContext, R.layout.test_template, R.id.suw_layout_content));
 
-        mNavigationBar = new NavigationBar(mContext);
-        doReturn(mNavigationBar).when(mTemplateLayout)
-                .findManagedViewById(eq(R.id.suw_layout_navigation_bar));
-    }
+    mNavigationBar = new NavigationBar(mContext);
+    doReturn(mNavigationBar)
+        .when(mTemplateLayout)
+        .findManagedViewById(eq(R.id.suw_layout_navigation_bar));
+  }
 
-    @Test
-    public void testGetNavigationBar() {
-        NavigationBarMixin mixin = new NavigationBarMixin(mTemplateLayout);
-        assertSame(mNavigationBar, mixin.getNavigationBar());
-    }
+  @Test
+  public void testGetNavigationBar() {
+    NavigationBarMixin mixin = new NavigationBarMixin(mTemplateLayout);
+    assertSame(mNavigationBar, mixin.getNavigationBar());
+  }
 
-    @Test
-    public void testSetNextButtonText() {
-        NavigationBarMixin mixin = new NavigationBarMixin(mTemplateLayout);
-        mixin.setNextButtonText(R.string.suw_more_button_label);
-        assertEquals("More", mNavigationBar.getNextButton().getText());
+  @Test
+  public void testSetNextButtonText() {
+    NavigationBarMixin mixin = new NavigationBarMixin(mTemplateLayout);
+    mixin.setNextButtonText(R.string.suw_more_button_label);
+    assertEquals("More", mNavigationBar.getNextButton().getText());
 
-        mixin.setNextButtonText("Foobar");
-        assertEquals("Foobar", mNavigationBar.getNextButton().getText());
-    }
+    mixin.setNextButtonText("Foobar");
+    assertEquals("Foobar", mNavigationBar.getNextButton().getText());
+  }
 
-    @SuppressLint("SetTextI18n")  // It's OK, this is just a test
-    @Test
-    public void testGetNextButtonText() {
-        mNavigationBar.getNextButton().setText("lorem ipsum");
+  @SuppressLint("SetTextI18n") // It's OK, this is just a test
+  @Test
+  public void testGetNextButtonText() {
+    mNavigationBar.getNextButton().setText("lorem ipsum");
 
-        NavigationBarMixin mixin = new NavigationBarMixin(mTemplateLayout);
-        assertSame("lorem ipsum", mixin.getNextButtonText());
-    }
+    NavigationBarMixin mixin = new NavigationBarMixin(mTemplateLayout);
+    assertSame("lorem ipsum", mixin.getNextButtonText());
+  }
 
-    @Test
-    public void testSetNavigationBarListener() {
-        final NavigationBarListener listener = mock(NavigationBarListener.class);
-        NavigationBarMixin mixin = new NavigationBarMixin(mTemplateLayout);
-        mixin.setNavigationBarListener(listener);
+  @Test
+  public void testSetNavigationBarListener() {
+    final NavigationBarListener listener = mock(NavigationBarListener.class);
+    NavigationBarMixin mixin = new NavigationBarMixin(mTemplateLayout);
+    mixin.setNavigationBarListener(listener);
 
-        mNavigationBar.getNextButton().performClick();
-        verify(listener).onNavigateNext();
+    mNavigationBar.getNextButton().performClick();
+    verify(listener).onNavigateNext();
 
-        mNavigationBar.getBackButton().performClick();
-        verify(listener).onNavigateBack();
-    }
+    mNavigationBar.getBackButton().performClick();
+    verify(listener).onNavigateBack();
+  }
 }
diff --git a/library/test/instrumentation/src/com/android/setupwizardlib/template/ProgressBarMixinTest.java b/library/test/instrumentation/src/com/android/setupwizardlib/template/ProgressBarMixinTest.java
index 5b2fb50..78ebe1e 100644
--- a/library/test/instrumentation/src/com/android/setupwizardlib/template/ProgressBarMixinTest.java
+++ b/library/test/instrumentation/src/com/android/setupwizardlib/template/ProgressBarMixinTest.java
@@ -29,16 +29,14 @@
 import android.graphics.Color;
 import android.os.Build.VERSION;
 import android.os.Build.VERSION_CODES;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.view.ContextThemeWrapper;
 import android.view.View;
 import android.widget.ProgressBar;
-
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
 import com.android.setupwizardlib.TemplateLayout;
 import com.android.setupwizardlib.test.R;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -47,111 +45,107 @@
 @SmallTest
 public class ProgressBarMixinTest {
 
-    private TemplateLayout mTemplateLayout;
+  private TemplateLayout mTemplateLayout;
 
-    @Before
-    public void setUp() {
-        Context context = new ContextThemeWrapper(InstrumentationRegistry.getContext(),
-                R.style.SuwThemeMaterial_Light);
-        mTemplateLayout = new TemplateLayout(
-                context,
-                R.layout.test_progress_bar_template, R.id.suw_layout_content);
+  @Before
+  public void setUp() {
+    Context context =
+        new ContextThemeWrapper(
+            InstrumentationRegistry.getContext(), R.style.SuwThemeMaterial_Light);
+    mTemplateLayout =
+        new TemplateLayout(context, R.layout.test_progress_bar_template, R.id.suw_layout_content);
+  }
+
+  @Test
+  public void testSetShown() {
+    ProgressBarMixin mixin = new ProgressBarMixin(mTemplateLayout);
+    mixin.setShown(true);
+
+    ProgressBar progressBar = (ProgressBar) mTemplateLayout.findViewById(R.id.suw_layout_progress);
+    assertNotNull("Progress bar should be available after setting to shown", progressBar);
+    assertEquals(View.VISIBLE, progressBar.getVisibility());
+  }
+
+  @Test
+  public void testNotShown() {
+    ProgressBarMixin mixin = new ProgressBarMixin(mTemplateLayout);
+    mixin.setShown(true);
+    mixin.setShown(false);
+
+    ProgressBar progressBar = (ProgressBar) mTemplateLayout.findViewById(R.id.suw_layout_progress);
+    assertNotEquals(View.VISIBLE, progressBar.getVisibility());
+  }
+
+  @Test
+  public void testIsShown() {
+    ProgressBarMixin mixin = new ProgressBarMixin(mTemplateLayout);
+
+    mixin.setShown(true);
+    assertTrue(mixin.isShown());
+
+    mixin.setShown(false);
+    assertFalse(mixin.isShown());
+  }
+
+  @Test
+  public void testPeekProgressBar() {
+    ProgressBarMixin mixin = new ProgressBarMixin(mTemplateLayout);
+    assertNull(
+        "PeekProgressBar should return null when stub not inflated yet", mixin.peekProgressBar());
+
+    mixin.setShown(true);
+    assertNotNull(
+        "PeekProgressBar should be available after setting to shown", mixin.peekProgressBar());
+  }
+
+  @Test
+  public void testSetColorBeforeSetShown() {
+    ProgressBarMixin mixin = new ProgressBarMixin(mTemplateLayout);
+    mixin.setColor(ColorStateList.valueOf(Color.MAGENTA));
+
+    mixin.setShown(true);
+
+    if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+      ProgressBar progressBar =
+          (ProgressBar) mTemplateLayout.findViewById(R.id.suw_layout_progress);
+      assertEquals(ColorStateList.valueOf(Color.MAGENTA), progressBar.getIndeterminateTintList());
+      assertEquals(
+          ColorStateList.valueOf(Color.MAGENTA), progressBar.getProgressBackgroundTintList());
     }
+    // this method is a no-op on versions < lollipop. Just check that it doesn't crash.
+  }
 
-    @Test
-    public void testSetShown() {
-        ProgressBarMixin mixin = new ProgressBarMixin(mTemplateLayout);
-        mixin.setShown(true);
+  @Test
+  public void testSetColorAfterSetShown() {
+    ProgressBarMixin mixin = new ProgressBarMixin(mTemplateLayout);
+    mixin.setShown(true);
 
-        ProgressBar progressBar = (ProgressBar) mTemplateLayout.findViewById(
-                R.id.suw_layout_progress);
-        assertNotNull("Progress bar should be available after setting to shown", progressBar);
-        assertEquals(View.VISIBLE, progressBar.getVisibility());
+    mixin.setColor(ColorStateList.valueOf(Color.YELLOW));
+
+    if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+      ProgressBar progressBar =
+          (ProgressBar) mTemplateLayout.findViewById(R.id.suw_layout_progress);
+      assertEquals(ColorStateList.valueOf(Color.YELLOW), progressBar.getIndeterminateTintList());
+      assertEquals(
+          ColorStateList.valueOf(Color.YELLOW), progressBar.getProgressBackgroundTintList());
     }
+    // this method is a no-op on versions < lollipop. Just check that it doesn't crash.
+  }
 
-    @Test
-    public void testNotShown() {
-        ProgressBarMixin mixin = new ProgressBarMixin(mTemplateLayout);
-        mixin.setShown(true);
-        mixin.setShown(false);
+  @Test
+  public void testDeterminateProgressBarNullTint() {
+    ProgressBarMixin mixin = new ProgressBarMixin(mTemplateLayout);
+    mixin.setShown(true);
+    mixin.peekProgressBar().setIndeterminate(false);
 
-        ProgressBar progressBar = (ProgressBar) mTemplateLayout.findViewById(
-                R.id.suw_layout_progress);
-        assertNotEquals(View.VISIBLE, progressBar.getVisibility());
+    mixin.setColor(null);
+
+    if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+      ProgressBar progressBar =
+          (ProgressBar) mTemplateLayout.findViewById(R.id.suw_layout_progress);
+      assertEquals(null, progressBar.getProgressBackgroundTintList());
+      progressBar.draw(new Canvas());
     }
-
-    @Test
-    public void testIsShown() {
-        ProgressBarMixin mixin = new ProgressBarMixin(mTemplateLayout);
-
-        mixin.setShown(true);
-        assertTrue(mixin.isShown());
-
-        mixin.setShown(false);
-        assertFalse(mixin.isShown());
-    }
-
-    @Test
-    public void testPeekProgressBar() {
-        ProgressBarMixin mixin = new ProgressBarMixin(mTemplateLayout);
-        assertNull("PeekProgressBar should return null when stub not inflated yet",
-                mixin.peekProgressBar());
-
-        mixin.setShown(true);
-        assertNotNull("PeekProgressBar should be available after setting to shown",
-                mixin.peekProgressBar());
-    }
-
-    @Test
-    public void testSetColorBeforeSetShown() {
-        ProgressBarMixin mixin = new ProgressBarMixin(mTemplateLayout);
-        mixin.setColor(ColorStateList.valueOf(Color.MAGENTA));
-
-        mixin.setShown(true);
-
-        if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
-            ProgressBar progressBar = (ProgressBar) mTemplateLayout.findViewById(
-                    R.id.suw_layout_progress);
-            assertEquals(ColorStateList.valueOf(Color.MAGENTA),
-                    progressBar.getIndeterminateTintList());
-            assertEquals(ColorStateList.valueOf(Color.MAGENTA),
-                    progressBar.getProgressBackgroundTintList());
-        }
-        // this method is a no-op on versions < lollipop. Just check that it doesn't crash.
-    }
-
-    @Test
-    public void testSetColorAfterSetShown() {
-        ProgressBarMixin mixin = new ProgressBarMixin(mTemplateLayout);
-        mixin.setShown(true);
-
-        mixin.setColor(ColorStateList.valueOf(Color.YELLOW));
-
-        if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
-            ProgressBar progressBar = (ProgressBar) mTemplateLayout.findViewById(
-                    R.id.suw_layout_progress);
-            assertEquals(ColorStateList.valueOf(Color.YELLOW),
-                    progressBar.getIndeterminateTintList());
-            assertEquals(ColorStateList.valueOf(Color.YELLOW),
-                    progressBar.getProgressBackgroundTintList());
-        }
-        // this method is a no-op on versions < lollipop. Just check that it doesn't crash.
-    }
-
-    @Test
-    public void testDeterminateProgressBarNullTint() {
-        ProgressBarMixin mixin = new ProgressBarMixin(mTemplateLayout);
-        mixin.setShown(true);
-        mixin.peekProgressBar().setIndeterminate(false);
-
-        mixin.setColor(null);
-
-        if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
-            ProgressBar progressBar = (ProgressBar) mTemplateLayout.findViewById(
-                    R.id.suw_layout_progress);
-            assertEquals(null, progressBar.getProgressBackgroundTintList());
-            progressBar.draw(new Canvas());
-        }
-        // setColor is a no-op on versions < lollipop. Just check that it doesn't crash.
-    }
+    // setColor is a no-op on versions < lollipop. Just check that it doesn't crash.
+  }
 }
diff --git a/library/test/instrumentation/src/com/android/setupwizardlib/template/TemplateLayoutMixinTest.java b/library/test/instrumentation/src/com/android/setupwizardlib/template/TemplateLayoutMixinTest.java
index 7cc934a..6adebc6 100644
--- a/library/test/instrumentation/src/com/android/setupwizardlib/template/TemplateLayoutMixinTest.java
+++ b/library/test/instrumentation/src/com/android/setupwizardlib/template/TemplateLayoutMixinTest.java
@@ -24,10 +24,8 @@
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-
 import com.android.setupwizardlib.TemplateLayout;
 import com.android.setupwizardlib.test.R;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -36,35 +34,38 @@
 @SmallTest
 public class TemplateLayoutMixinTest {
 
-    private TestTemplateLayout mLayout;
+  private TestTemplateLayout mLayout;
 
-    @Before
-    public void setUp() throws Exception {
-        mLayout = new TestTemplateLayout(InstrumentationRegistry.getContext());
+  @Before
+  public void setUp() throws Exception {
+    mLayout = new TestTemplateLayout(InstrumentationRegistry.getContext());
+  }
+
+  @Test
+  public void testGetMixin() {
+    final TestMixin mixin = mLayout.getMixin(TestMixin.class);
+    assertNotNull("TestMixin should not be null", mixin);
+    assertTrue(
+        "TestMixin should be an instance of TestMixinSubclass. "
+            + "Found "
+            + mixin.getClass()
+            + " instead.",
+        mixin instanceof TestMixinSubclass);
+
+    // Mixin must be retrieved using the interface it's registered with, not the concrete class,
+    // although they are often the same.
+    assertNull("TestMixinSubclass should be null", mLayout.getMixin(TestMixinSubclass.class));
+  }
+
+  private static class TestTemplateLayout extends TemplateLayout {
+
+    TestTemplateLayout(Context context) {
+      super(context, R.layout.test_template, R.id.suw_layout_content);
+      registerMixin(TestMixin.class, new TestMixinSubclass());
     }
+  }
 
-    @Test
-    public void testGetMixin() {
-        final TestMixin mixin = mLayout.getMixin(TestMixin.class);
-        assertNotNull("TestMixin should not be null", mixin);
-        assertTrue("TestMixin should be an instance of TestMixinSubclass. "
-                + "Found " + mixin.getClass() + " instead.",
-                mixin instanceof TestMixinSubclass);
+  private static class TestMixin implements Mixin {}
 
-        // Mixin must be retrieved using the interface it's registered with, not the concrete class,
-        // although they are often the same.
-        assertNull("TestMixinSubclass should be null", mLayout.getMixin(TestMixinSubclass.class));
-    }
-
-    private static class TestTemplateLayout extends TemplateLayout {
-
-        TestTemplateLayout(Context context) {
-            super(context, R.layout.test_template, R.id.suw_layout_content);
-            registerMixin(TestMixin.class, new TestMixinSubclass());
-        }
-    }
-
-    private static class TestMixin implements Mixin {}
-
-    private static class TestMixinSubclass extends TestMixin {}
+  private static class TestMixinSubclass extends TestMixin {}
 }
diff --git a/library/test/instrumentation/src/com/android/setupwizardlib/test/BottomScrollViewTest.java b/library/test/instrumentation/src/com/android/setupwizardlib/test/BottomScrollViewTest.java
index 1a8eb21..4f9487c 100644
--- a/library/test/instrumentation/src/com/android/setupwizardlib/test/BottomScrollViewTest.java
+++ b/library/test/instrumentation/src/com/android/setupwizardlib/test/BottomScrollViewTest.java
@@ -21,13 +21,11 @@
 import static org.junit.Assert.assertTrue;
 
 import android.content.Context;
+import android.view.View;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.view.View;
-
 import com.android.setupwizardlib.view.BottomScrollView;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -36,101 +34,101 @@
 @SmallTest
 public class BottomScrollViewTest {
 
-    private TestBottomScrollListener mListener;
+  private TestBottomScrollListener mListener;
 
-    @Before
-    public void setUp() throws Exception {
-        mListener = new TestBottomScrollListener();
+  @Before
+  public void setUp() throws Exception {
+    mListener = new TestBottomScrollListener();
+  }
+
+  @Test
+  public void testNoNeedScroll() {
+    createScrollView(20);
+    assertTrue("Scroll should not be required", mListener.mScrolledToBottom);
+  }
+
+  @Test
+  public void testNeedScroll() {
+    createScrollView(110);
+    assertFalse("Scroll should be required", mListener.mScrolledToBottom);
+  }
+
+  @Test
+  public void testScrollToBottom() {
+    final BottomScrollView bottomScrollView = createScrollView(110);
+
+    assertFalse("Scroll should be required", mListener.mScrolledToBottom);
+
+    bottomScrollView.scrollTo(0, 10);
+    assertTrue("Should already be scrolled to bottom", mListener.mScrolledToBottom);
+  }
+
+  @Test
+  public void testScrollThreshold() {
+    final BottomScrollView bottomScrollView = createScrollView(110);
+    assertEquals("Scroll threshold should be 10", 10, bottomScrollView.getScrollThreshold());
+  }
+
+  private BottomScrollView createScrollView(final int childHeight) {
+    final Context context = InstrumentationRegistry.getContext();
+    final BottomScrollView bottomScrollView = new TestBottomScrollView(context);
+    bottomScrollView.setBottomScrollListener(mListener);
+
+    final View child = new TestChildView(context, childHeight);
+
+    child.measure(0, 0); // TestChildView's measured dimensions doesn't depend on the arguments
+    bottomScrollView.addView(child);
+    bottomScrollView.layout(0, 0, 100, 100);
+
+    return bottomScrollView;
+  }
+
+  private static class TestChildView extends View {
+
+    private static final int WIDTH = 10;
+    private int mHeight;
+
+    TestChildView(Context context, int height) {
+      super(context);
+      mHeight = height;
     }
 
-    @Test
-    public void testNoNeedScroll() {
-        createScrollView(20);
-        assertTrue("Scroll should not be required", mListener.mScrolledToBottom);
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+      setMeasuredDimension(WIDTH, mHeight);
     }
 
-    @Test
-    public void testNeedScroll() {
-        createScrollView(110);
-        assertFalse("Scroll should be required", mListener.mScrolledToBottom);
+    public void setHeight(int height) {
+      mHeight = height;
+    }
+  }
+
+  private static class TestBottomScrollView extends BottomScrollView {
+
+    TestBottomScrollView(Context context) {
+      super(context);
     }
 
-    @Test
-    public void testScrollToBottom() {
-        final BottomScrollView bottomScrollView = createScrollView(110);
+    @Override
+    public boolean post(Runnable action) {
+      // Post all runnables synchronously so that tests can check the callbacks.
+      action.run();
+      return true;
+    }
+  }
 
-        assertFalse("Scroll should be required", mListener.mScrolledToBottom);
+  private static class TestBottomScrollListener implements BottomScrollView.BottomScrollListener {
 
-        bottomScrollView.scrollTo(0, 10);
-        assertTrue("Should already be scrolled to bottom", mListener.mScrolledToBottom);
+    boolean mScrolledToBottom = true;
+
+    @Override
+    public void onScrolledToBottom() {
+      mScrolledToBottom = true;
     }
 
-    @Test
-    public void testScrollThreshold() {
-        final BottomScrollView bottomScrollView = createScrollView(110);
-        assertEquals("Scroll threshold should be 10", 10, bottomScrollView.getScrollThreshold());
+    @Override
+    public void onRequiresScroll() {
+      mScrolledToBottom = false;
     }
-
-    private BottomScrollView createScrollView(final int childHeight) {
-        final Context context = InstrumentationRegistry.getContext();
-        final BottomScrollView bottomScrollView = new TestBottomScrollView(context);
-        bottomScrollView.setBottomScrollListener(mListener);
-
-        final View child = new TestChildView(context, childHeight);
-
-        child.measure(0, 0); // TestChildView's measured dimensions doesn't depend on the arguments
-        bottomScrollView.addView(child);
-        bottomScrollView.layout(0, 0, 100, 100);
-
-        return bottomScrollView;
-    }
-
-    private static class TestChildView extends View {
-
-        private static final int WIDTH = 10;
-        private int mHeight;
-
-        TestChildView(Context context, int height) {
-            super(context);
-            mHeight = height;
-        }
-
-        @Override
-        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-            setMeasuredDimension(WIDTH, mHeight);
-        }
-
-        public void setHeight(int height) {
-            mHeight = height;
-        }
-    }
-
-    private static class TestBottomScrollView extends BottomScrollView {
-
-        TestBottomScrollView(Context context) {
-            super(context);
-        }
-
-        @Override
-        public boolean post(Runnable action) {
-            // Post all runnables synchronously so that tests can check the callbacks.
-            action.run();
-            return true;
-        }
-    }
-
-    private static class TestBottomScrollListener implements BottomScrollView.BottomScrollListener {
-
-        boolean mScrolledToBottom = true;
-
-        @Override
-        public void onScrolledToBottom() {
-            mScrolledToBottom = true;
-        }
-
-        @Override
-        public void onRequiresScroll() {
-            mScrolledToBottom = false;
-        }
-    }
+  }
 }
diff --git a/library/test/instrumentation/src/com/android/setupwizardlib/test/ButtonBarItemTest.java b/library/test/instrumentation/src/com/android/setupwizardlib/test/ButtonBarItemTest.java
index 18c295e..aacffeb 100644
--- a/library/test/instrumentation/src/com/android/setupwizardlib/test/ButtonBarItemTest.java
+++ b/library/test/instrumentation/src/com/android/setupwizardlib/test/ButtonBarItemTest.java
@@ -20,18 +20,16 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.fail;
 
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.view.ViewGroup;
 import android.widget.Button;
 import android.widget.LinearLayout;
-
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
 import com.android.setupwizardlib.items.ButtonBarItem;
 import com.android.setupwizardlib.items.ButtonItem;
 import com.android.setupwizardlib.items.Item;
 import com.android.setupwizardlib.items.ItemHierarchy;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -40,87 +38,92 @@
 @SmallTest
 public class ButtonBarItemTest {
 
-    private ButtonItem mChild1;
-    private ButtonItem mChild2;
-    private ButtonItem mChild3;
+  private ButtonItem mChild1;
+  private ButtonItem mChild2;
+  private ButtonItem mChild3;
 
-    @Before
-    public void setUp() throws Exception {
-        mChild1 = new ButtonItem();
-        mChild2 = new ButtonItem();
-        mChild3 = new ButtonItem();
+  @Before
+  public void setUp() throws Exception {
+    mChild1 = new ButtonItem();
+    mChild2 = new ButtonItem();
+    mChild3 = new ButtonItem();
+  }
+
+  @Test
+  public void testFindItemById() {
+    ButtonBarItem item = new ButtonBarItem();
+    item.setId(888);
+
+    mChild1.setId(123);
+    mChild2.setId(456);
+    mChild3.setId(789);
+    item.addChild(mChild1);
+    item.addChild(mChild2);
+    item.addChild(mChild3);
+
+    assertEquals("Finding 123 should return child1", mChild1, item.findItemById(123));
+    assertEquals("Finding 456 should return child2", mChild2, item.findItemById(456));
+    assertEquals("Finding 789 should return child3", mChild3, item.findItemById(789));
+
+    assertEquals("Finding 888 should return ButtonBarItem itself", item, item.findItemById(888));
+
+    assertNull("Finding 999 should return null", item.findItemById(999));
+  }
+
+  @Test
+  public void testBindEmpty() {
+    ButtonBarItem item = new ButtonBarItem();
+    final ViewGroup layout = createLayout();
+    item.onBindView(layout);
+
+    assertEquals(
+        "Binding empty ButtonBar should not create any children", 0, layout.getChildCount());
+  }
+
+  @Test
+  public void testBind() {
+    ButtonBarItem item = new ButtonBarItem();
+
+    item.addChild(mChild1);
+    mChild1.setText("child1");
+    item.addChild(mChild2);
+    mChild2.setText("child2");
+    item.addChild(mChild3);
+    mChild3.setText("child3");
+
+    final ViewGroup layout = createLayout();
+    item.onBindView(layout);
+
+    assertEquals("Binding ButtonBar should create 3 children", 3, layout.getChildCount());
+    assertEquals(
+        "First button should have text \"child1\"",
+        "child1",
+        ((Button) layout.getChildAt(0)).getText());
+    assertEquals(
+        "Second button should have text \"child2\"",
+        "child2",
+        ((Button) layout.getChildAt(1)).getText());
+    assertEquals(
+        "Third button should have text \"child3\"",
+        "child3",
+        ((Button) layout.getChildAt(2)).getText());
+  }
+
+  @Test
+  public void testAddInvalidChild() {
+    ButtonBarItem item = new ButtonBarItem();
+
+    ItemHierarchy invalidChild = new Item();
+
+    try {
+      item.addChild(invalidChild);
+      fail("Adding non ButtonItem to ButtonBarItem should throw exception");
+    } catch (UnsupportedOperationException e) {
+      // pass
     }
+  }
 
-    @Test
-    public void testFindItemById() {
-        ButtonBarItem item = new ButtonBarItem();
-        item.setId(888);
-
-        mChild1.setId(123);
-        mChild2.setId(456);
-        mChild3.setId(789);
-        item.addChild(mChild1);
-        item.addChild(mChild2);
-        item.addChild(mChild3);
-
-        assertEquals("Finding 123 should return child1", mChild1, item.findItemById(123));
-        assertEquals("Finding 456 should return child2", mChild2, item.findItemById(456));
-        assertEquals("Finding 789 should return child3", mChild3, item.findItemById(789));
-
-        assertEquals("Finding 888 should return ButtonBarItem itself", item,
-                item.findItemById(888));
-
-        assertNull("Finding 999 should return null", item.findItemById(999));
-    }
-
-    @Test
-    public void testBindEmpty() {
-        ButtonBarItem item = new ButtonBarItem();
-        final ViewGroup layout = createLayout();
-        item.onBindView(layout);
-
-        assertEquals("Binding empty ButtonBar should not create any children", 0,
-                layout.getChildCount());
-    }
-
-    @Test
-    public void testBind() {
-        ButtonBarItem item = new ButtonBarItem();
-
-        item.addChild(mChild1);
-        mChild1.setText("child1");
-        item.addChild(mChild2);
-        mChild2.setText("child2");
-        item.addChild(mChild3);
-        mChild3.setText("child3");
-
-        final ViewGroup layout = createLayout();
-        item.onBindView(layout);
-
-        assertEquals("Binding ButtonBar should create 3 children", 3, layout.getChildCount());
-        assertEquals("First button should have text \"child1\"", "child1",
-                ((Button) layout.getChildAt(0)).getText());
-        assertEquals("Second button should have text \"child2\"", "child2",
-                ((Button) layout.getChildAt(1)).getText());
-        assertEquals("Third button should have text \"child3\"", "child3",
-                ((Button) layout.getChildAt(2)).getText());
-    }
-
-    @Test
-    public void testAddInvalidChild() {
-        ButtonBarItem item = new ButtonBarItem();
-
-        ItemHierarchy invalidChild = new Item();
-
-        try {
-            item.addChild(invalidChild);
-            fail("Adding non ButtonItem to ButtonBarItem should throw exception");
-        } catch (UnsupportedOperationException e) {
-            // pass
-        }
-    }
-
-    private ViewGroup createLayout() {
-        return new LinearLayout(InstrumentationRegistry.getContext());
-    }
+  private ViewGroup createLayout() {
+    return new LinearLayout(InstrumentationRegistry.getContext());
+  }
 }
diff --git a/library/test/instrumentation/src/com/android/setupwizardlib/test/DrawableLayoutDirectionHelperTest.java b/library/test/instrumentation/src/com/android/setupwizardlib/test/DrawableLayoutDirectionHelperTest.java
index 95245b0..1445660 100644
--- a/library/test/instrumentation/src/com/android/setupwizardlib/test/DrawableLayoutDirectionHelperTest.java
+++ b/library/test/instrumentation/src/com/android/setupwizardlib/test/DrawableLayoutDirectionHelperTest.java
@@ -28,117 +28,124 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.InsetDrawable;
 import android.os.Build;
+import android.view.View;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.view.View;
-
 import com.android.setupwizardlib.util.DrawableLayoutDirectionHelper;
-
+import java.util.Locale;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.Locale;
-
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class DrawableLayoutDirectionHelperTest {
 
-    @Test
-    public void testCreateRelativeInsetDrawableLtr() {
-        final Drawable drawable = new ColorDrawable(Color.RED);
-        @SuppressLint("InlinedApi") // Testing with inlined constant is OK here
-        final InsetDrawable insetDrawable =
-                DrawableLayoutDirectionHelper.createRelativeInsetDrawable(drawable,
-                        1 /* start */, 2 /* top */, 3 /* end */, 4 /* bottom */,
-                        View.LAYOUT_DIRECTION_LTR);
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
-            assertSame("Drawable from getDrawable() should be same as passed in", drawable,
-                    insetDrawable.getDrawable());
-        }
-        Rect outRect = new Rect();
-        insetDrawable.getPadding(outRect);
-        assertEquals("InsetDrawable padding should be same as inset", new Rect(1, 2, 3, 4),
-                outRect);
+  @Test
+  public void testCreateRelativeInsetDrawableLtr() {
+    final Drawable drawable = new ColorDrawable(Color.RED);
+    @SuppressLint("InlinedApi") // Testing with inlined constant is OK here
+    final InsetDrawable insetDrawable =
+        DrawableLayoutDirectionHelper.createRelativeInsetDrawable(
+            drawable,
+            1 /* start */,
+            2 /* top */,
+            3 /* end */,
+            4 /* bottom */,
+            View.LAYOUT_DIRECTION_LTR);
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+      assertSame(
+          "Drawable from getDrawable() should be same as passed in",
+          drawable,
+          insetDrawable.getDrawable());
+    }
+    Rect outRect = new Rect();
+    insetDrawable.getPadding(outRect);
+    assertEquals("InsetDrawable padding should be same as inset", new Rect(1, 2, 3, 4), outRect);
+  }
+
+  @Test
+  public void testCreateRelativeInsetDrawableRtl() {
+    final Drawable drawable = new ColorDrawable(Color.RED);
+    @SuppressLint("InlinedApi") // Testing with inlined constant is OK here
+    final InsetDrawable insetDrawable =
+        DrawableLayoutDirectionHelper.createRelativeInsetDrawable(
+            drawable,
+            1 /* start */,
+            2 /* top */,
+            3 /* end */,
+            4 /* bottom */,
+            View.LAYOUT_DIRECTION_RTL);
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+      assertSame(
+          "Drawable from getDrawable() should be same as passed in",
+          drawable,
+          insetDrawable.getDrawable());
+    }
+    Rect outRect = new Rect();
+    insetDrawable.getPadding(outRect);
+    assertEquals("InsetDrawable padding should be same as inset", new Rect(3, 2, 1, 4), outRect);
+  }
+
+  @Test
+  public void testCreateRelativeInsetDrawableViewRtl() {
+    final Drawable drawable = new ColorDrawable(Color.RED);
+    final View view = new ForceRtlView(InstrumentationRegistry.getContext());
+    final InsetDrawable insetDrawable =
+        DrawableLayoutDirectionHelper.createRelativeInsetDrawable(
+            drawable, 1 /* start */, 2 /* top */, 3 /* end */, 4 /* bottom */, view);
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+      assertSame(
+          "Drawable from getDrawable() should be same as passed in",
+          drawable,
+          insetDrawable.getDrawable());
+    }
+    Rect outRect = new Rect();
+    insetDrawable.getPadding(outRect);
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+      assertEquals("InsetDrawable padding should be same as inset", new Rect(3, 2, 1, 4), outRect);
+    } else {
+      assertEquals("InsetDrawable padding should be same as inset", new Rect(1, 2, 3, 4), outRect);
+    }
+  }
+
+  @Test
+  public void testCreateRelativeInsetDrawableContextRtl() {
+    Context context = InstrumentationRegistry.getContext();
+    final Drawable drawable = new ColorDrawable(Color.RED);
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+      final Configuration config = new Configuration();
+      config.setLayoutDirection(new Locale("fa", "IR"));
+      context = context.createConfigurationContext(config);
+    }
+    final InsetDrawable insetDrawable =
+        DrawableLayoutDirectionHelper.createRelativeInsetDrawable(
+            drawable, 1 /* start */, 2 /* top */, 3 /* end */, 4 /* bottom */, context);
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+      assertSame(
+          "Drawable from getDrawable() should be same as passed in",
+          drawable,
+          insetDrawable.getDrawable());
+    }
+    Rect outRect = new Rect();
+    insetDrawable.getPadding(outRect);
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+      assertEquals("InsetDrawable padding should be same as inset", new Rect(3, 2, 1, 4), outRect);
+    } else {
+      assertEquals("InsetDrawable padding should be same as inset", new Rect(1, 2, 3, 4), outRect);
+    }
+  }
+
+  private static class ForceRtlView extends View {
+
+    ForceRtlView(Context context) {
+      super(context);
     }
 
-    @Test
-    public void testCreateRelativeInsetDrawableRtl() {
-        final Drawable drawable = new ColorDrawable(Color.RED);
-        @SuppressLint("InlinedApi") // Testing with inlined constant is OK here
-        final InsetDrawable insetDrawable =
-                DrawableLayoutDirectionHelper.createRelativeInsetDrawable(drawable,
-                        1 /* start */, 2 /* top */, 3 /* end */, 4 /* bottom */,
-                        View.LAYOUT_DIRECTION_RTL);
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
-            assertSame("Drawable from getDrawable() should be same as passed in", drawable,
-                    insetDrawable.getDrawable());
-        }
-        Rect outRect = new Rect();
-        insetDrawable.getPadding(outRect);
-        assertEquals("InsetDrawable padding should be same as inset", new Rect(3, 2, 1, 4),
-                outRect);
+    @Override
+    @SuppressLint("InlinedApi") // Testing with inlined constant is OK here
+    public int getLayoutDirection() {
+      return View.LAYOUT_DIRECTION_RTL;
     }
-
-    @Test
-    public void testCreateRelativeInsetDrawableViewRtl() {
-        final Drawable drawable = new ColorDrawable(Color.RED);
-        final View view = new ForceRtlView(InstrumentationRegistry.getContext());
-        final InsetDrawable insetDrawable =
-                DrawableLayoutDirectionHelper.createRelativeInsetDrawable(drawable,
-                        1 /* start */, 2 /* top */, 3 /* end */, 4 /* bottom */, view);
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
-            assertSame("Drawable from getDrawable() should be same as passed in", drawable,
-                    insetDrawable.getDrawable());
-        }
-        Rect outRect = new Rect();
-        insetDrawable.getPadding(outRect);
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
-            assertEquals("InsetDrawable padding should be same as inset", new Rect(3, 2, 1, 4),
-                    outRect);
-        } else {
-            assertEquals("InsetDrawable padding should be same as inset", new Rect(1, 2, 3, 4),
-                    outRect);
-        }
-    }
-
-    @Test
-    public void testCreateRelativeInsetDrawableContextRtl() {
-        Context context =  InstrumentationRegistry.getContext();
-        final Drawable drawable = new ColorDrawable(Color.RED);
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
-            final Configuration config = new Configuration();
-            config.setLayoutDirection(new Locale("fa", "IR"));
-            context = context.createConfigurationContext(config);
-        }
-        final InsetDrawable insetDrawable =
-                DrawableLayoutDirectionHelper.createRelativeInsetDrawable(drawable,
-                        1 /* start */, 2 /* top */, 3 /* end */, 4 /* bottom */, context);
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
-            assertSame("Drawable from getDrawable() should be same as passed in", drawable,
-                    insetDrawable.getDrawable());
-        }
-        Rect outRect = new Rect();
-        insetDrawable.getPadding(outRect);
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
-            assertEquals("InsetDrawable padding should be same as inset", new Rect(3, 2, 1, 4),
-                    outRect);
-        } else {
-            assertEquals("InsetDrawable padding should be same as inset", new Rect(1, 2, 3, 4),
-                    outRect);
-        }
-    }
-
-    private static class ForceRtlView extends View {
-
-        ForceRtlView(Context context) {
-            super(context);
-        }
-
-        @Override
-        @SuppressLint("InlinedApi") // Testing with inlined constant is OK here
-        public int getLayoutDirection() {
-            return View.LAYOUT_DIRECTION_RTL;
-        }
-    }
+  }
 }
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 e12b31d..f8aae5a 100644
--- a/library/test/instrumentation/src/com/android/setupwizardlib/test/GlifLayoutTest.java
+++ b/library/test/instrumentation/src/com/android/setupwizardlib/test/GlifLayoutTest.java
@@ -26,9 +26,6 @@
 import android.graphics.Color;
 import android.os.Build.VERSION;
 import android.os.Build.VERSION_CODES;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.view.ContextThemeWrapper;
 import android.view.Gravity;
 import android.view.LayoutInflater;
@@ -36,9 +33,10 @@
 import android.widget.LinearLayout;
 import android.widget.ScrollView;
 import android.widget.TextView;
-
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
 import com.android.setupwizardlib.GlifLayout;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -47,94 +45,99 @@
 @SmallTest
 public class GlifLayoutTest {
 
-    private Context mContext;
+  private Context mContext;
 
-    @Before
-    public void setUp() throws Exception {
-        mContext = new ContextThemeWrapper(InstrumentationRegistry.getContext(),
-                R.style.SuwThemeGlif_Light);
+  @Before
+  public void setUp() throws Exception {
+    mContext =
+        new ContextThemeWrapper(InstrumentationRegistry.getContext(), R.style.SuwThemeGlif_Light);
+  }
+
+  @Test
+  public void testInflateFromXml() {
+    LayoutInflater inflater = LayoutInflater.from(mContext);
+    GlifLayout layout = (GlifLayout) inflater.inflate(R.layout.test_glif_layout, null);
+    assertDefaultTemplateInflated(layout);
+    View content = layout.findViewById(R.id.test_content);
+    assertTrue("@id/test_content should be a TextView", content instanceof TextView);
+  }
+
+  @Test
+  public void testPrimaryColorFromXml() {
+    LayoutInflater inflater = LayoutInflater.from(mContext);
+    GlifLayout layout =
+        (GlifLayout) inflater.inflate(R.layout.test_glif_layout_primary_color, null);
+    assertDefaultTemplateInflated(layout);
+
+    assertEquals(ColorStateList.valueOf(Color.RED), layout.getPrimaryColor());
+  }
+
+  @Test
+  public void testSetProgressBarShownInvalid() {
+    GlifLayout layout = new GlifLayout(mContext, R.layout.test_template);
+    layout.setProgressBarShown(true);
+    // This is a no-op because there is no progress bar stub
+  }
+
+  @Test
+  public void testGlifTheme() {
+    mContext =
+        new ContextThemeWrapper(InstrumentationRegistry.getContext(), R.style.SuwThemeGlif_Light);
+    final GlifLayout glifLayout = new GlifLayout(mContext);
+
+    if (VERSION.SDK_INT >= VERSION_CODES.M) {
+      // Scroll indicators are only available on versions >= M
+      assertEquals(View.SCROLL_INDICATOR_BOTTOM, glifLayout.getScrollView().getScrollIndicators());
+    }
+  }
+
+  @Test
+  public void testGlifV2Theme() {
+    mContext =
+        new ContextThemeWrapper(InstrumentationRegistry.getContext(), 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 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 v2 theme",
+          Gravity.CENTER_HORIZONTAL,
+          parent.getGravity() & Gravity.CENTER_HORIZONTAL);
     }
 
-    @Test
-    public void testInflateFromXml() {
-        LayoutInflater inflater = LayoutInflater.from(mContext);
-        GlifLayout layout = (GlifLayout) inflater.inflate(R.layout.test_glif_layout, null);
-        assertDefaultTemplateInflated(layout);
-        View content = layout.findViewById(R.id.test_content);
-        assertTrue("@id/test_content should be a TextView", content instanceof TextView);
+    assertEquals(
+        "Status bar color should be white in GLIF v2 theme",
+        "ffffffff",
+        Integer.toHexString(glifLayout.getBackgroundBaseColor().getDefaultColor()));
+    assertFalse(
+        "GLIF v2 theme shuold not have patterned background", glifLayout.isBackgroundPatterned());
+
+    if (VERSION.SDK_INT >= VERSION_CODES.M) {
+      // Scroll indicators are only available on versions >= M
+      assertEquals(
+          View.SCROLL_INDICATOR_TOP | View.SCROLL_INDICATOR_BOTTOM,
+          glifLayout.getScrollView().getScrollIndicators());
     }
+  }
 
-    @Test
-    public void testPrimaryColorFromXml() {
-        LayoutInflater inflater = LayoutInflater.from(mContext);
-        GlifLayout layout =
-                (GlifLayout) inflater.inflate(R.layout.test_glif_layout_primary_color, null);
-        assertDefaultTemplateInflated(layout);
+  private void assertDefaultTemplateInflated(GlifLayout layout) {
+    View title = layout.findViewById(R.id.suw_layout_title);
+    assertNotNull("@id/suw_layout_title should not be null", title);
 
-        assertEquals(ColorStateList.valueOf(Color.RED), layout.getPrimaryColor());
-    }
+    View icon = layout.findViewById(R.id.suw_layout_icon);
+    assertNotNull("@id/suw_layout_icon should not be null", icon);
 
-    @Test
-    public void testSetProgressBarShownInvalid() {
-        GlifLayout layout = new GlifLayout(mContext, R.layout.test_template);
-        layout.setProgressBarShown(true);
-        // This is a no-op because there is no progress bar stub
-    }
-
-    @Test
-    public void testGlifTheme() {
-        mContext = new ContextThemeWrapper(InstrumentationRegistry.getContext(),
-                R.style.SuwThemeGlif_Light);
-        final GlifLayout glifLayout = new GlifLayout(mContext);
-
-        if (VERSION.SDK_INT >= VERSION_CODES.M) {
-            // Scroll indicators are only available on versions >= M
-            assertEquals(View.SCROLL_INDICATOR_BOTTOM,
-                    glifLayout.getScrollView().getScrollIndicators());
-        }
-    }
-
-    @Test
-    public void testGlifV2Theme() {
-        mContext = new ContextThemeWrapper(InstrumentationRegistry.getContext(),
-                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 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 v2 theme",
-                    Gravity.CENTER_HORIZONTAL, parent.getGravity() & Gravity.CENTER_HORIZONTAL);
-        }
-
-        assertEquals("Status bar color should be white in GLIF v2 theme",
-                "ffffffff",
-                Integer.toHexString(glifLayout.getBackgroundBaseColor().getDefaultColor()));
-        assertFalse("GLIF v2 theme shuold not have patterned background",
-                glifLayout.isBackgroundPatterned());
-
-        if (VERSION.SDK_INT >= VERSION_CODES.M) {
-            // Scroll indicators are only available on versions >= M
-            assertEquals(View.SCROLL_INDICATOR_TOP | View.SCROLL_INDICATOR_BOTTOM,
-                    glifLayout.getScrollView().getScrollIndicators());
-        }
-    }
-
-    private void assertDefaultTemplateInflated(GlifLayout layout) {
-        View title = layout.findViewById(R.id.suw_layout_title);
-        assertNotNull("@id/suw_layout_title should not be null", title);
-
-        View icon = layout.findViewById(R.id.suw_layout_icon);
-        assertNotNull("@id/suw_layout_icon should not be null", icon);
-
-        View scrollView = layout.findViewById(R.id.suw_scroll_view);
-        assertTrue("@id/suw_scroll_view should be a ScrollView", scrollView instanceof ScrollView);
-    }
+    View scrollView = layout.findViewById(R.id.suw_scroll_view);
+    assertTrue("@id/suw_scroll_view should be a ScrollView", scrollView instanceof ScrollView);
+  }
 }
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 c2e932c..0665bfe 100644
--- a/library/test/instrumentation/src/com/android/setupwizardlib/test/GlifListLayoutTest.java
+++ b/library/test/instrumentation/src/com/android/setupwizardlib/test/GlifListLayoutTest.java
@@ -26,9 +26,6 @@
 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;
 import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -36,9 +33,10 @@
 import android.widget.ListAdapter;
 import android.widget.ListView;
 import android.widget.TextView;
-
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
 import com.android.setupwizardlib.GlifListLayout;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -47,102 +45,100 @@
 @SmallTest
 public class GlifListLayoutTest {
 
-    private Context mContext;
+  private Context mContext;
 
-    @Before
-    public void setUp() throws Exception {
-        mContext = new ContextThemeWrapper(InstrumentationRegistry.getContext(),
-                R.style.SuwThemeGlif_Light);
+  @Before
+  public void setUp() throws Exception {
+    mContext =
+        new ContextThemeWrapper(InstrumentationRegistry.getContext(), R.style.SuwThemeGlif_Light);
+  }
+
+  @Test
+  public void testDefaultTemplate() {
+    GlifListLayout layout = new GlifListLayout(mContext);
+    assertListTemplateInflated(layout);
+  }
+
+  @Test
+  public void testAddView() {
+    GlifListLayout layout = new GlifListLayout(mContext);
+    TextView tv = new TextView(mContext);
+    try {
+      layout.addView(tv);
+      fail("Adding view to ListLayout should throw");
+    } catch (UnsupportedOperationException e) {
+      // Expected exception
     }
+  }
 
-    @Test
-    public void testDefaultTemplate() {
-        GlifListLayout layout = new GlifListLayout(mContext);
-        assertListTemplateInflated(layout);
+  @Test
+  public void testInflateFromXml() {
+    LayoutInflater inflater = LayoutInflater.from(mContext);
+    GlifListLayout layout = (GlifListLayout) inflater.inflate(R.layout.test_glif_list_layout, null);
+    assertListTemplateInflated(layout);
+  }
+
+  @Test
+  public void testGetListView() {
+    GlifListLayout layout = new GlifListLayout(mContext);
+    assertListTemplateInflated(layout);
+    assertNotNull("getListView should not be null", layout.getListView());
+  }
+
+  @Test
+  public void testAdapter() {
+    GlifListLayout layout = new GlifListLayout(mContext);
+    assertListTemplateInflated(layout);
+
+    final ArrayAdapter<String> adapter =
+        new ArrayAdapter<>(mContext, android.R.layout.simple_list_item_1);
+    adapter.add("Abracadabra");
+    layout.setAdapter(adapter);
+
+    final ListAdapter gotAdapter = layout.getAdapter();
+    // Note: the wrapped adapter should be returned directly, not the HeaderViewListAdapter.
+    assertSame("Adapter got from GlifListLayout should be same as set", adapter, gotAdapter);
+  }
+
+  @Test
+  public void testDividerInsetLegacy() {
+    GlifListLayout layout = new GlifListLayout(mContext);
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+      layout.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
     }
+    assertListTemplateInflated(layout);
 
-    @Test
-    public void testAddView() {
-        GlifListLayout layout = new GlifListLayout(mContext);
-        TextView tv = new TextView(mContext);
-        try {
-            layout.addView(tv);
-            fail("Adding view to ListLayout should throw");
-        } catch (UnsupportedOperationException e) {
-            // Expected exception
-        }
+    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() {
+    GlifListLayout layout = new GlifListLayout(mContext);
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+      layout.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
     }
+    assertListTemplateInflated(layout);
 
-    @Test
-    public void testInflateFromXml() {
-        LayoutInflater inflater = LayoutInflater.from(mContext);
-        GlifListLayout layout = (GlifListLayout)
-                inflater.inflate(R.layout.test_glif_list_layout, null);
-        assertListTemplateInflated(layout);
-    }
+    layout.setDividerInsets(10, 15);
+    assertEquals("Divider inset should be 10", 10, layout.getDividerInsetStart());
+    assertEquals("Divider inset should be 15", 15, layout.getDividerInsetEnd());
 
-    @Test
-    public void testGetListView() {
-        GlifListLayout layout = new GlifListLayout(mContext);
-        assertListTemplateInflated(layout);
-        assertNotNull("getListView should not be null", layout.getListView());
-    }
+    final Drawable divider = layout.getDivider();
+    assertTrue("Divider should be instance of InsetDrawable", divider instanceof InsetDrawable);
+  }
 
-    @Test
-    public void testAdapter() {
-        GlifListLayout layout = new GlifListLayout(mContext);
-        assertListTemplateInflated(layout);
+  private void assertListTemplateInflated(GlifListLayout layout) {
+    View title = layout.findViewById(R.id.suw_layout_title);
+    assertNotNull("@id/suw_layout_title should not be null", title);
 
-        final ArrayAdapter<String> adapter =
-                new ArrayAdapter<>(mContext, android.R.layout.simple_list_item_1);
-        adapter.add("Abracadabra");
-        layout.setAdapter(adapter);
+    View icon = layout.findViewById(R.id.suw_layout_icon);
+    assertNotNull("@id/suw_layout_icon should not be null", icon);
 
-        final ListAdapter gotAdapter = layout.getAdapter();
-        // Note: the wrapped adapter should be returned directly, not the HeaderViewListAdapter.
-        assertSame("Adapter got from GlifListLayout should be same as set",
-                adapter, gotAdapter);
-    }
-
-    @Test
-    public void testDividerInsetLegacy() {
-        GlifListLayout layout = new GlifListLayout(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() {
-        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);
-
-        View icon = layout.findViewById(R.id.suw_layout_icon);
-        assertNotNull("@id/suw_layout_icon should not be null", icon);
-
-        View listView = layout.findViewById(android.R.id.list);
-        assertTrue("@android:id/list should be a ListView", listView instanceof ListView);
-    }
+    View listView = layout.findViewById(android.R.id.list);
+    assertTrue("@android:id/list should be a ListView", listView instanceof ListView);
+  }
 }
diff --git a/library/test/instrumentation/src/com/android/setupwizardlib/test/GlifPatternDrawableTest.java b/library/test/instrumentation/src/com/android/setupwizardlib/test/GlifPatternDrawableTest.java
index 37ac41a..1783d4e 100644
--- a/library/test/instrumentation/src/com/android/setupwizardlib/test/GlifPatternDrawableTest.java
+++ b/library/test/instrumentation/src/com/android/setupwizardlib/test/GlifPatternDrawableTest.java
@@ -25,14 +25,11 @@
 import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.os.Debug;
+import android.util.Log;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.util.Log;
-
 import com.android.setupwizardlib.GlifPatternDrawable;
-
 import junit.framework.AssertionFailedError;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -41,141 +38,144 @@
 @SmallTest
 public class GlifPatternDrawableTest {
 
-    private static final String TAG = "GlifPatternDrawableTest";
+  private static final String TAG = "GlifPatternDrawableTest";
 
-    @Before
-    public void setUp() throws Exception {
-        GlifPatternDrawable.invalidatePattern();
+  @Before
+  public void setUp() throws Exception {
+    GlifPatternDrawable.invalidatePattern();
+  }
+
+  @Test
+  public void testDraw() {
+    final Bitmap bitmap = Bitmap.createBitmap(1366, 768, Bitmap.Config.ARGB_8888);
+    final Canvas canvas = new Canvas(bitmap);
+
+    final GlifPatternDrawable drawable = new GlifPatternDrawable(Color.RED);
+    drawable.setBounds(0, 0, 1366, 768);
+    drawable.draw(canvas);
+
+    assertSameColor("Top left pixel should be #e61a1a", 0xffe61a1a, bitmap.getPixel(0, 0));
+    assertSameColor("Center pixel should be #d90d0d", 0xffd90d0d, bitmap.getPixel(683, 384));
+    assertSameColor("Bottom right pixel should be #d40808", 0xffd40808, bitmap.getPixel(1365, 767));
+  }
+
+  @Test
+  public void testDrawTwice() {
+    // Test that the second time the drawable is drawn is also correct, to make sure caching is
+    // done correctly.
+
+    final Bitmap bitmap = Bitmap.createBitmap(1366, 768, Bitmap.Config.ARGB_8888);
+    final Canvas canvas = new Canvas(bitmap);
+
+    final GlifPatternDrawable drawable = new GlifPatternDrawable(Color.RED);
+    drawable.setBounds(0, 0, 1366, 768);
+    drawable.draw(canvas);
+
+    Paint paint = new Paint();
+    paint.setColor(Color.WHITE);
+    canvas.drawRect(0, 0, 1366, 768, paint); // Erase the entire canvas
+
+    drawable.draw(canvas);
+
+    assertSameColor("Top left pixel should be #e61a1a", 0xffe61a1a, bitmap.getPixel(0, 0));
+    assertSameColor("Center pixel should be #d90d0d", 0xffd90d0d, bitmap.getPixel(683, 384));
+    assertSameColor("Bottom right pixel should be #d40808", 0xffd40808, bitmap.getPixel(1365, 767));
+  }
+
+  @Test
+  public void testScaleToCanvasSquare() {
+    final Canvas canvas = new Canvas();
+    Matrix expected = new Matrix(canvas.getMatrix());
+
+    Bitmap mockBitmapCache = Bitmap.createBitmap(1366, 768, Bitmap.Config.ALPHA_8);
+
+    final GlifPatternDrawable drawable = new GlifPatternDrawable(Color.RED);
+    drawable.setBounds(0, 0, 683, 384); // half each side of the view box
+    drawable.scaleCanvasToBounds(canvas, mockBitmapCache, drawable.getBounds());
+
+    expected.postScale(0.5f, 0.5f);
+
+    assertEquals("Matrices should match", expected, canvas.getMatrix());
+  }
+
+  @Test
+  public void testScaleToCanvasTall() {
+    final Canvas canvas = new Canvas();
+    final Matrix expected = new Matrix(canvas.getMatrix());
+
+    Bitmap mockBitmapCache = Bitmap.createBitmap(1366, 768, Bitmap.Config.ALPHA_8);
+
+    final GlifPatternDrawable drawable = new GlifPatternDrawable(Color.RED);
+    drawable.setBounds(0, 0, 683, 768); // half the width only
+    drawable.scaleCanvasToBounds(canvas, mockBitmapCache, drawable.getBounds());
+
+    expected.postScale(1f, 1f);
+    expected.postTranslate(-99.718f, 0f);
+
+    assertEquals("Matrices should match", expected, canvas.getMatrix());
+  }
+
+  @Test
+  public void testScaleToCanvasWide() {
+    final Canvas canvas = new Canvas();
+    final Matrix expected = new Matrix(canvas.getMatrix());
+
+    Bitmap mockBitmapCache = Bitmap.createBitmap(1366, 768, Bitmap.Config.ALPHA_8);
+
+    final GlifPatternDrawable drawable = new GlifPatternDrawable(Color.RED);
+    drawable.setBounds(0, 0, 1366, 384); // half the height only
+    drawable.scaleCanvasToBounds(canvas, mockBitmapCache, drawable.getBounds());
+
+    expected.postScale(1f, 1f);
+    expected.postTranslate(0f, -87.552f);
+
+    assertEquals("Matrices should match", expected, canvas.getMatrix());
+  }
+
+  @Test
+  public void testScaleToCanvasMaxSize() {
+    final Canvas canvas = new Canvas();
+    final Matrix expected = new Matrix(canvas.getMatrix());
+
+    Bitmap mockBitmapCache = Bitmap.createBitmap(2049, 1152, Bitmap.Config.ALPHA_8);
+
+    final GlifPatternDrawable drawable = new GlifPatternDrawable(Color.RED);
+    drawable.setBounds(0, 0, 1366, 768); // original viewbox size
+    drawable.scaleCanvasToBounds(canvas, mockBitmapCache, drawable.getBounds());
+
+    expected.postScale(1 / 1.5f, 1 / 1.5f);
+    expected.postTranslate(0f, 0f);
+
+    assertEquals("Matrices should match", expected, canvas.getMatrix());
+  }
+
+  @Test
+  public void testMemoryAllocation() {
+    Debug.MemoryInfo memoryInfo = new Debug.MemoryInfo();
+    Debug.getMemoryInfo(memoryInfo);
+    final long memoryBefore = memoryInfo.getTotalPss(); // Get memory usage in KB
+
+    final GlifPatternDrawable drawable = new GlifPatternDrawable(Color.RED);
+    drawable.setBounds(0, 0, 1366, 768);
+    drawable.createBitmapCache(2049, 1152);
+
+    Debug.getMemoryInfo(memoryInfo);
+    final long memoryAfter = memoryInfo.getTotalPss();
+    Log.i(TAG, "Memory allocated for bitmap cache: " + (memoryAfter - memoryBefore));
+    assertTrue("Memory allocation should not exceed 5MB", memoryAfter < memoryBefore + 5000);
+  }
+
+  private void assertSameColor(String message, int expected, int actual) {
+    try {
+      assertEquals(expected, actual);
+    } catch (AssertionFailedError e) {
+      throw new AssertionFailedError(
+          message
+              + " expected <#"
+              + Integer.toHexString(expected)
+              + "> but found <#"
+              + Integer.toHexString(actual)
+              + "> instead");
     }
-
-    @Test
-    public void testDraw() {
-        final Bitmap bitmap = Bitmap.createBitmap(1366, 768, Bitmap.Config.ARGB_8888);
-        final Canvas canvas = new Canvas(bitmap);
-
-        final GlifPatternDrawable drawable = new GlifPatternDrawable(Color.RED);
-        drawable.setBounds(0, 0, 1366, 768);
-        drawable.draw(canvas);
-
-        assertSameColor("Top left pixel should be #e61a1a", 0xffe61a1a, bitmap.getPixel(0, 0));
-        assertSameColor("Center pixel should be #d90d0d", 0xffd90d0d, bitmap.getPixel(683, 384));
-        assertSameColor("Bottom right pixel should be #d40808", 0xffd40808,
-                bitmap.getPixel(1365, 767));
-    }
-
-    @Test
-    public void testDrawTwice() {
-        // Test that the second time the drawable is drawn is also correct, to make sure caching is
-        // done correctly.
-
-        final Bitmap bitmap = Bitmap.createBitmap(1366, 768, Bitmap.Config.ARGB_8888);
-        final Canvas canvas = new Canvas(bitmap);
-
-        final GlifPatternDrawable drawable = new GlifPatternDrawable(Color.RED);
-        drawable.setBounds(0, 0, 1366, 768);
-        drawable.draw(canvas);
-
-        Paint paint = new Paint();
-        paint.setColor(Color.WHITE);
-        canvas.drawRect(0, 0, 1366, 768, paint);  // Erase the entire canvas
-
-        drawable.draw(canvas);
-
-        assertSameColor("Top left pixel should be #e61a1a", 0xffe61a1a, bitmap.getPixel(0, 0));
-        assertSameColor("Center pixel should be #d90d0d", 0xffd90d0d, bitmap.getPixel(683, 384));
-        assertSameColor("Bottom right pixel should be #d40808", 0xffd40808,
-                bitmap.getPixel(1365, 767));
-    }
-
-    @Test
-    public void testScaleToCanvasSquare() {
-        final Canvas canvas = new Canvas();
-        Matrix expected = new Matrix(canvas.getMatrix());
-
-        Bitmap mockBitmapCache = Bitmap.createBitmap(1366, 768, Bitmap.Config.ALPHA_8);
-
-        final GlifPatternDrawable drawable = new GlifPatternDrawable(Color.RED);
-        drawable.setBounds(0, 0, 683, 384);  // half each side of the view box
-        drawable.scaleCanvasToBounds(canvas, mockBitmapCache, drawable.getBounds());
-
-        expected.postScale(0.5f, 0.5f);
-
-        assertEquals("Matrices should match", expected, canvas.getMatrix());
-    }
-
-    @Test
-    public void testScaleToCanvasTall() {
-        final Canvas canvas = new Canvas();
-        final Matrix expected = new Matrix(canvas.getMatrix());
-
-        Bitmap mockBitmapCache = Bitmap.createBitmap(1366, 768, Bitmap.Config.ALPHA_8);
-
-        final GlifPatternDrawable drawable = new GlifPatternDrawable(Color.RED);
-        drawable.setBounds(0, 0, 683, 768);  // half the width only
-        drawable.scaleCanvasToBounds(canvas, mockBitmapCache, drawable.getBounds());
-
-        expected.postScale(1f, 1f);
-        expected.postTranslate(-99.718f, 0f);
-
-        assertEquals("Matrices should match", expected, canvas.getMatrix());
-    }
-
-    @Test
-    public void testScaleToCanvasWide() {
-        final Canvas canvas = new Canvas();
-        final Matrix expected = new Matrix(canvas.getMatrix());
-
-        Bitmap mockBitmapCache = Bitmap.createBitmap(1366, 768, Bitmap.Config.ALPHA_8);
-
-        final GlifPatternDrawable drawable = new GlifPatternDrawable(Color.RED);
-        drawable.setBounds(0, 0, 1366, 384);  // half the height only
-        drawable.scaleCanvasToBounds(canvas, mockBitmapCache, drawable.getBounds());
-
-        expected.postScale(1f, 1f);
-        expected.postTranslate(0f, -87.552f);
-
-        assertEquals("Matrices should match", expected, canvas.getMatrix());
-    }
-
-    @Test
-    public void testScaleToCanvasMaxSize() {
-        final Canvas canvas = new Canvas();
-        final Matrix expected = new Matrix(canvas.getMatrix());
-
-        Bitmap mockBitmapCache = Bitmap.createBitmap(2049, 1152, Bitmap.Config.ALPHA_8);
-
-        final GlifPatternDrawable drawable = new GlifPatternDrawable(Color.RED);
-        drawable.setBounds(0, 0, 1366, 768);  // original viewbox size
-        drawable.scaleCanvasToBounds(canvas, mockBitmapCache, drawable.getBounds());
-
-        expected.postScale(1 / 1.5f, 1 / 1.5f);
-        expected.postTranslate(0f, 0f);
-
-        assertEquals("Matrices should match", expected, canvas.getMatrix());
-    }
-
-    @Test
-    public void testMemoryAllocation() {
-        Debug.MemoryInfo memoryInfo = new Debug.MemoryInfo();
-        Debug.getMemoryInfo(memoryInfo);
-        final long memoryBefore = memoryInfo.getTotalPss();  // Get memory usage in KB
-
-        final GlifPatternDrawable drawable = new GlifPatternDrawable(Color.RED);
-        drawable.setBounds(0, 0, 1366, 768);
-        drawable.createBitmapCache(2049, 1152);
-
-        Debug.getMemoryInfo(memoryInfo);
-        final long memoryAfter = memoryInfo.getTotalPss();
-        Log.i(TAG, "Memory allocated for bitmap cache: " + (memoryAfter - memoryBefore));
-        assertTrue("Memory allocation should not exceed 5MB", memoryAfter < memoryBefore + 5000);
-    }
-
-    private void assertSameColor(String message, int expected, int actual) {
-        try {
-            assertEquals(expected, actual);
-        } catch (AssertionFailedError e) {
-            throw new AssertionFailedError(message + " expected <#" + Integer.toHexString(expected)
-                    + "> but found <#" + Integer.toHexString(actual) + "> instead");
-        }
-    }
+  }
 }
diff --git a/library/test/instrumentation/src/com/android/setupwizardlib/test/IllustrationTest.java b/library/test/instrumentation/src/com/android/setupwizardlib/test/IllustrationTest.java
index a4b6f27..253893e 100644
--- a/library/test/instrumentation/src/com/android/setupwizardlib/test/IllustrationTest.java
+++ b/library/test/instrumentation/src/com/android/setupwizardlib/test/IllustrationTest.java
@@ -23,13 +23,11 @@
 import android.graphics.Color;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
+import android.view.View;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.view.View;
-
 import com.android.setupwizardlib.view.Illustration;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -37,28 +35,29 @@
 @SmallTest
 public class IllustrationTest {
 
-    @Test
-    public void testWillDraw() {
-        final Illustration illustration = new Illustration(InstrumentationRegistry.getContext());
-        assertFalse("The illustration needs to be drawn", illustration.willNotDraw());
-    }
+  @Test
+  public void testWillDraw() {
+    final Illustration illustration = new Illustration(InstrumentationRegistry.getContext());
+    assertFalse("The illustration needs to be drawn", illustration.willNotDraw());
+  }
 
-    @Test
-    public void testAspectRatio() {
-        final Context context = InstrumentationRegistry.getContext();
-        // Force the context to be xhdpi
-        context.getResources().getDisplayMetrics().density = 2.0f;
+  @Test
+  public void testAspectRatio() {
+    final Context context = InstrumentationRegistry.getContext();
+    // Force the context to be xhdpi
+    context.getResources().getDisplayMetrics().density = 2.0f;
 
-        final Illustration illustration = new Illustration(context);
-        illustration.setAspectRatio(3.0f);
-        final Drawable backgroundDrawable = new ColorDrawable(Color.RED);
-        final Drawable illustrationDrawable = new ColorDrawable(Color.BLUE);
-        illustration.setBackgroundDrawable(backgroundDrawable);
-        illustration.setIllustration(illustrationDrawable);
+    final Illustration illustration = new Illustration(context);
+    illustration.setAspectRatio(3.0f);
+    final Drawable backgroundDrawable = new ColorDrawable(Color.RED);
+    final Drawable illustrationDrawable = new ColorDrawable(Color.BLUE);
+    illustration.setBackgroundDrawable(backgroundDrawable);
+    illustration.setIllustration(illustrationDrawable);
 
-        illustration.measure(View.MeasureSpec.makeMeasureSpec(300, View.MeasureSpec.EXACTLY),
-                View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
-        // (300px / 3) round down to nearest mod (8dp = 16px) = 96px
-        assertEquals("Top padding should be 96", 96, illustration.getPaddingTop());
-    }
+    illustration.measure(
+        View.MeasureSpec.makeMeasureSpec(300, View.MeasureSpec.EXACTLY),
+        View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
+    // (300px / 3) round down to nearest mod (8dp = 16px) = 96px
+    assertEquals("Top padding should be 96", 96, illustration.getPaddingTop());
+  }
 }
diff --git a/library/test/instrumentation/src/com/android/setupwizardlib/test/ItemAdapterTest.java b/library/test/instrumentation/src/com/android/setupwizardlib/test/ItemAdapterTest.java
index e5875e4..63180dc 100644
--- a/library/test/instrumentation/src/com/android/setupwizardlib/test/ItemAdapterTest.java
+++ b/library/test/instrumentation/src/com/android/setupwizardlib/test/ItemAdapterTest.java
@@ -24,78 +24,74 @@
 import android.database.DataSetObserver;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-
 import com.android.setupwizardlib.items.Item;
 import com.android.setupwizardlib.items.ItemAdapter;
 import com.android.setupwizardlib.items.ItemGroup;
 import com.android.setupwizardlib.items.ItemHierarchy;
-
+import java.util.Arrays;
+import java.util.HashSet;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.InOrder;
 
-import java.util.Arrays;
-import java.util.HashSet;
-
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class ItemAdapterTest {
 
-    private Item[] mItems = new Item[5];
-    private ItemGroup mItemGroup = new ItemGroup();
+  private Item[] mItems = new Item[5];
+  private ItemGroup mItemGroup = new ItemGroup();
 
-    @Before
-    public void setUp() throws Exception {
-        for (int i = 0; i < 5; i++) {
-            Item item = new Item();
-            item.setTitle("TestTitle" + i);
-            item.setId(i);
-            item.setLayoutResource(((i % 3) + 1) * 10);
-            mItems[i] = item;
-            mItemGroup.addChild(item);
-        }
+  @Before
+  public void setUp() throws Exception {
+    for (int i = 0; i < 5; i++) {
+      Item item = new Item();
+      item.setTitle("TestTitle" + i);
+      item.setId(i);
+      item.setLayoutResource(((i % 3) + 1) * 10);
+      mItems[i] = item;
+      mItemGroup.addChild(item);
     }
+  }
 
-    @Test
-    public void testAdapter() {
-        ItemAdapter adapter = new ItemAdapter(mItemGroup);
-        assertEquals("Adapter should have 5 items", 5, adapter.getCount());
-        assertEquals("Adapter should return the first item", mItems[0], adapter.getItem(0));
-        assertEquals("ID should be same as position", 2, adapter.getItemId(2));
+  @Test
+  public void testAdapter() {
+    ItemAdapter adapter = new ItemAdapter(mItemGroup);
+    assertEquals("Adapter should have 5 items", 5, adapter.getCount());
+    assertEquals("Adapter should return the first item", mItems[0], adapter.getItem(0));
+    assertEquals("ID should be same as position", 2, adapter.getItemId(2));
 
-        // Each test item has its own layout resource, and therefore its own view type
-        assertEquals("Should have 3 different view types", 3, adapter.getViewTypeCount());
-        HashSet<Integer> viewTypes = new HashSet<>(3);
-        viewTypes.add(adapter.getItemViewType(0));
-        viewTypes.add(adapter.getItemViewType(1));
-        viewTypes.add(adapter.getItemViewType(2));
+    // Each test item has its own layout resource, and therefore its own view type
+    assertEquals("Should have 3 different view types", 3, adapter.getViewTypeCount());
+    HashSet<Integer> viewTypes = new HashSet<>(3);
+    viewTypes.add(adapter.getItemViewType(0));
+    viewTypes.add(adapter.getItemViewType(1));
+    viewTypes.add(adapter.getItemViewType(2));
 
-        assertEquals("View types should be 0, 1, 2",
-                new HashSet<>(Arrays.asList(0, 1, 2)), viewTypes);
-    }
+    assertEquals("View types should be 0, 1, 2", new HashSet<>(Arrays.asList(0, 1, 2)), viewTypes);
+  }
 
-    @Test
-    public void testGetRootItemHierarchy() {
-        ItemAdapter adapter = new ItemAdapter(mItemGroup);
-        ItemHierarchy root = adapter.getRootItemHierarchy();
-        assertSame("Root item hierarchy should be mItemGroup", mItemGroup, root);
-    }
+  @Test
+  public void testGetRootItemHierarchy() {
+    ItemAdapter adapter = new ItemAdapter(mItemGroup);
+    ItemHierarchy root = adapter.getRootItemHierarchy();
+    assertSame("Root item hierarchy should be mItemGroup", mItemGroup, root);
+  }
 
-    @Test
-    public void testAdapterNotifications() {
-        ItemAdapter adapter = new ItemAdapter(mItemGroup);
-        final DataSetObserver observer = mock(DataSetObserver.class);
-        adapter.registerDataSetObserver(observer);
-        final InOrder inOrder = inOrder(observer);
+  @Test
+  public void testAdapterNotifications() {
+    ItemAdapter adapter = new ItemAdapter(mItemGroup);
+    final DataSetObserver observer = mock(DataSetObserver.class);
+    adapter.registerDataSetObserver(observer);
+    final InOrder inOrder = inOrder(observer);
 
-        mItems[0].setTitle("Child 1");
-        inOrder.verify(observer).onChanged();
+    mItems[0].setTitle("Child 1");
+    inOrder.verify(observer).onChanged();
 
-        mItemGroup.removeChild(mItems[1]);
-        inOrder.verify(observer).onChanged();
+    mItemGroup.removeChild(mItems[1]);
+    inOrder.verify(observer).onChanged();
 
-        mItemGroup.addChild(mItems[1]);
-        inOrder.verify(observer).onChanged();
-    }
+    mItemGroup.addChild(mItems[1]);
+    inOrder.verify(observer).onChanged();
+  }
 }
diff --git a/library/test/instrumentation/src/com/android/setupwizardlib/test/ItemInflaterTest.java b/library/test/instrumentation/src/com/android/setupwizardlib/test/ItemInflaterTest.java
index 20fd2cc..9e96bae 100644
--- a/library/test/instrumentation/src/com/android/setupwizardlib/test/ItemInflaterTest.java
+++ b/library/test/instrumentation/src/com/android/setupwizardlib/test/ItemInflaterTest.java
@@ -22,12 +22,10 @@
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-
 import com.android.setupwizardlib.items.Item;
 import com.android.setupwizardlib.items.ItemGroup;
 import com.android.setupwizardlib.items.ItemHierarchy;
 import com.android.setupwizardlib.items.ItemInflater;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -35,25 +33,26 @@
 @SmallTest
 public class ItemInflaterTest {
 
-    @Test
-    public void testDefaultPackage() {
-        ItemInflater inflater = new ItemInflater(InstrumentationRegistry.getContext());
-        assertEquals("Default package should be the one containing Item class",
-                "com.android.setupwizardlib.items.", inflater.getDefaultPackage());
-    }
+  @Test
+  public void testDefaultPackage() {
+    ItemInflater inflater = new ItemInflater(InstrumentationRegistry.getContext());
+    assertEquals(
+        "Default package should be the one containing Item class",
+        "com.android.setupwizardlib.items.",
+        inflater.getDefaultPackage());
+  }
 
-    @Test
-    public void testInflate() {
-        ItemInflater inflater = new ItemInflater(InstrumentationRegistry.getContext());
-        ItemHierarchy item = inflater.inflate(R.xml.test_items);
-        assertTrue("Inflated item should be ItemGroup", item instanceof ItemGroup);
-        ItemGroup itemGroup = (ItemGroup) item;
+  @Test
+  public void testInflate() {
+    ItemInflater inflater = new ItemInflater(InstrumentationRegistry.getContext());
+    ItemHierarchy item = inflater.inflate(R.xml.test_items);
+    assertTrue("Inflated item should be ItemGroup", item instanceof ItemGroup);
+    ItemGroup itemGroup = (ItemGroup) item;
 
-        Item child0 = (Item) itemGroup.getItemAt(0);
-        Item child1 = (Item) itemGroup.getItemAt(1);
-        assertEquals("Title of first child should be Title1", "Title1", child0.getTitle());
-        assertEquals("ID of second child should be test_item_2", R.id.test_item_2, child1.getId());
-        assertEquals("Summary of second child should be Summary2", "Summary2",
-                child1.getSummary());
-    }
+    Item child0 = (Item) itemGroup.getItemAt(0);
+    Item child1 = (Item) itemGroup.getItemAt(1);
+    assertEquals("Title of first child should be Title1", "Title1", child0.getTitle());
+    assertEquals("ID of second child should be test_item_2", R.id.test_item_2, child1.getId());
+    assertEquals("Summary of second child should be Summary2", "Summary2", child1.getSummary());
+  }
 }
diff --git a/library/test/instrumentation/src/com/android/setupwizardlib/test/ItemLayoutTest.java b/library/test/instrumentation/src/com/android/setupwizardlib/test/ItemLayoutTest.java
index 85876b4..dbf71b2 100644
--- a/library/test/instrumentation/src/com/android/setupwizardlib/test/ItemLayoutTest.java
+++ b/library/test/instrumentation/src/com/android/setupwizardlib/test/ItemLayoutTest.java
@@ -17,26 +17,22 @@
 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 android.support.test.filters.SmallTest;
 import com.android.setupwizardlib.R;
 import com.android.setupwizardlib.items.Item;
-
+import java.util.ArrayList;
+import java.util.List;
 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.
@@ -45,50 +41,50 @@
 @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
+  @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
+    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;
+    // 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;
+  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);
-    }
+  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);
+  @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));
-    }
+    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/test/instrumentation/src/com/android/setupwizardlib/test/ItemTest.java b/library/test/instrumentation/src/com/android/setupwizardlib/test/ItemTest.java
index b4ebabb..84990dd 100644
--- a/library/test/instrumentation/src/com/android/setupwizardlib/test/ItemTest.java
+++ b/library/test/instrumentation/src/com/android/setupwizardlib/test/ItemTest.java
@@ -28,19 +28,17 @@
 import android.content.Context;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.ShapeDrawable;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.TextView;
-
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
 import com.android.setupwizardlib.R;
 import com.android.setupwizardlib.items.Item;
 import com.android.setupwizardlib.items.ItemHierarchy.Observer;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -52,149 +50,149 @@
 @SmallTest
 public class ItemTest {
 
-    private TextView mTitleView;
-    private TextView mSummaryView;
-    private ImageView mIconView;
-    private FrameLayout mIconContainer;
+  private TextView mTitleView;
+  private TextView mSummaryView;
+  private ImageView mIconView;
+  private FrameLayout mIconContainer;
 
-    @Mock
-    private Observer mObserver;
+  @Mock private Observer mObserver;
 
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-    }
+  @Before
+  public void setUp() {
+    MockitoAnnotations.initMocks(this);
+  }
 
-    @Test
-    public void testOnBindView() {
-        Item item = new Item();
-        item.setTitle("TestTitle");
-        item.setSummary("TestSummary");
-        Drawable icon = new ShapeDrawable();
-        icon.setLevel(4);
-        item.setIcon(icon);
-        View view = createLayout();
+  @Test
+  public void testOnBindView() {
+    Item item = new Item();
+    item.setTitle("TestTitle");
+    item.setSummary("TestSummary");
+    Drawable icon = new ShapeDrawable();
+    icon.setLevel(4);
+    item.setIcon(icon);
+    View view = createLayout();
 
-        mIconView.setImageLevel(1);
-        Drawable recycledIcon = new ShapeDrawable();
-        mIconView.setImageDrawable(recycledIcon);
+    mIconView.setImageLevel(1);
+    Drawable recycledIcon = new ShapeDrawable();
+    mIconView.setImageDrawable(recycledIcon);
 
-        item.onBindView(view);
+    item.onBindView(view);
 
-        assertEquals("Title should be \"TestTitle\"", "TestTitle", mTitleView.getText().toString());
-        assertEquals("Summary should be \"TestSummary\"", "TestSummary",
-                mSummaryView.getText().toString());
-        assertSame("Icon should be the icon shape drawable", icon, mIconView.getDrawable());
-        assertEquals("Recycled icon level should not change", 1, recycledIcon.getLevel());
-        assertEquals("Icon should be level 4", 4, icon.getLevel());
-    }
+    assertEquals("Title should be \"TestTitle\"", "TestTitle", mTitleView.getText().toString());
+    assertEquals(
+        "Summary should be \"TestSummary\"", "TestSummary", mSummaryView.getText().toString());
+    assertSame("Icon should be the icon shape drawable", icon, mIconView.getDrawable());
+    assertEquals("Recycled icon level should not change", 1, recycledIcon.getLevel());
+    assertEquals("Icon should be level 4", 4, icon.getLevel());
+  }
 
-    @Test
-    public void testSingleLineItem() {
-        Item item = new Item();
-        item.setTitle("TestTitle");
-        View view = createLayout();
+  @Test
+  public void testSingleLineItem() {
+    Item item = new Item();
+    item.setTitle("TestTitle");
+    View view = createLayout();
 
-        item.onBindView(view);
+    item.onBindView(view);
 
-        assertEquals("Title should be \"TestTitle\"", "TestTitle", mTitleView.getText().toString());
-        assertEquals("Summary should be gone", View.GONE, mSummaryView.getVisibility());
-        assertEquals("IconContainer should be gone", View.GONE, mIconContainer.getVisibility());
-    }
+    assertEquals("Title should be \"TestTitle\"", "TestTitle", mTitleView.getText().toString());
+    assertEquals("Summary should be gone", View.GONE, mSummaryView.getVisibility());
+    assertEquals("IconContainer should be gone", View.GONE, mIconContainer.getVisibility());
+  }
 
-    @Test
-    public void testProperties() {
-        Item item = new Item();
-        item.registerObserver(mObserver);
-        final InOrder inOrder = inOrder(mObserver);
+  @Test
+  public void testProperties() {
+    Item item = new Item();
+    item.registerObserver(mObserver);
+    final InOrder inOrder = inOrder(mObserver);
 
-        item.setTitle("TestTitle");
-        inOrder.verify(mObserver).onItemRangeChanged(eq(item), eq(0), eq(1));
+    item.setTitle("TestTitle");
+    inOrder.verify(mObserver).onItemRangeChanged(eq(item), eq(0), eq(1));
 
-        item.setSummary("TestSummary");
-        inOrder.verify(mObserver).onItemRangeChanged(eq(item), eq(0), eq(1));
+    item.setSummary("TestSummary");
+    inOrder.verify(mObserver).onItemRangeChanged(eq(item), eq(0), eq(1));
 
-        item.setEnabled(false);
-        inOrder.verify(mObserver).onItemRangeChanged(eq(item), eq(0), eq(1));
+    item.setEnabled(false);
+    inOrder.verify(mObserver).onItemRangeChanged(eq(item), eq(0), eq(1));
 
-        ShapeDrawable icon = new ShapeDrawable();
-        item.setIcon(icon);
-        inOrder.verify(mObserver).onItemRangeChanged(eq(item), eq(0), eq(1));
+    ShapeDrawable icon = new ShapeDrawable();
+    item.setIcon(icon);
+    inOrder.verify(mObserver).onItemRangeChanged(eq(item), eq(0), eq(1));
 
-        item.setId(12345);
+    item.setId(12345);
 
-        item.setLayoutResource(56789);
-        inOrder.verify(mObserver).onItemRangeChanged(eq(item), eq(0), eq(1));
+    item.setLayoutResource(56789);
+    inOrder.verify(mObserver).onItemRangeChanged(eq(item), eq(0), eq(1));
 
-        assertEquals("Title should be \"TestTitle\"", "TestTitle", item.getTitle());
-        assertEquals("Summary should be \"TestSummary\"", "TestSummary", item.getSummary());
-        assertFalse("Enabled should be false", item.isEnabled());
-        assertSame("Icon should be same as set", icon, item.getIcon());
-        assertEquals("ID should be 12345", 12345, item.getId());
-        assertEquals("Layout resource should be 56789", 56789, item.getLayoutResource());
-    }
+    assertEquals("Title should be \"TestTitle\"", "TestTitle", item.getTitle());
+    assertEquals("Summary should be \"TestSummary\"", "TestSummary", item.getSummary());
+    assertFalse("Enabled should be false", item.isEnabled());
+    assertSame("Icon should be same as set", icon, item.getIcon());
+    assertEquals("ID should be 12345", 12345, item.getId());
+    assertEquals("Layout resource should be 56789", 56789, item.getLayoutResource());
+  }
 
-    @Test
-    public void testDefaultValues() {
-        Item item = new Item();
+  @Test
+  public void testDefaultValues() {
+    Item item = new Item();
 
-        assertNull("Default title should be null", item.getTitle());
-        assertNull("Default summary should be null", item.getSummary());
-        assertNull("Default icon should be null", item.getIcon());
-        assertTrue("Default enabled should be true", item.isEnabled());
-        assertEquals("Default ID should be 0", 0, item.getId());
-        assertEquals("Default layout resource should be R.layout.suw_items_text",
-                R.layout.suw_items_default, item.getLayoutResource());
-        assertTrue("Default visible should be true", item.isVisible());
-    }
+    assertNull("Default title should be null", item.getTitle());
+    assertNull("Default summary should be null", item.getSummary());
+    assertNull("Default icon should be null", item.getIcon());
+    assertTrue("Default enabled should be true", item.isEnabled());
+    assertEquals("Default ID should be 0", 0, item.getId());
+    assertEquals(
+        "Default layout resource should be R.layout.suw_items_text",
+        R.layout.suw_items_default,
+        item.getLayoutResource());
+    assertTrue("Default visible should be true", item.isVisible());
+  }
 
-    @Test
-    public void testHierarchyImplementation() {
-        Item item = new Item();
-        item.setId(12345);
+  @Test
+  public void testHierarchyImplementation() {
+    Item item = new Item();
+    item.setId(12345);
 
-        assertEquals("getCount should be 1", 1, item.getCount());
-        assertSame("getItemAt should return itself", item, item.getItemAt(0));
-        assertSame("findItemById with same ID should return itself", item,
-                item.findItemById(12345));
-        assertNull("findItemById with different ID should return null", item.findItemById(34567));
-    }
+    assertEquals("getCount should be 1", 1, item.getCount());
+    assertSame("getItemAt should return itself", item, item.getItemAt(0));
+    assertSame("findItemById with same ID should return itself", item, item.findItemById(12345));
+    assertNull("findItemById with different ID should return null", item.findItemById(34567));
+  }
 
-    @Test
-    public void testVisible() {
-        Item item = new Item();
-        item.registerObserver(mObserver);
-        item.setVisible(false);
+  @Test
+  public void testVisible() {
+    Item item = new Item();
+    item.registerObserver(mObserver);
+    item.setVisible(false);
 
-        assertFalse("Item should not be visible", item.isVisible());
-        assertEquals("Item count should be 0 when not visible", 0, item.getCount());
+    assertFalse("Item should not be visible", item.isVisible());
+    assertEquals("Item count should be 0 when not visible", 0, item.getCount());
 
-        verify(mObserver).onItemRangeRemoved(eq(item), eq(0), eq(1));
+    verify(mObserver).onItemRangeRemoved(eq(item), eq(0), eq(1));
 
-        item.setVisible(true);
-        verify(mObserver).onItemRangeInserted(eq(item), eq(0), eq(1));
-    }
+    item.setVisible(true);
+    verify(mObserver).onItemRangeInserted(eq(item), eq(0), eq(1));
+  }
 
-    private ViewGroup createLayout() {
-        Context context = InstrumentationRegistry.getContext();
-        ViewGroup root = new FrameLayout(context);
+  private ViewGroup createLayout() {
+    Context context = InstrumentationRegistry.getContext();
+    ViewGroup root = new FrameLayout(context);
 
-        mTitleView = new TextView(context);
-        mTitleView.setId(R.id.suw_items_title);
-        root.addView(mTitleView);
+    mTitleView = new TextView(context);
+    mTitleView.setId(R.id.suw_items_title);
+    root.addView(mTitleView);
 
-        mSummaryView = new TextView(context);
-        mSummaryView.setId(R.id.suw_items_summary);
-        root.addView(mSummaryView);
+    mSummaryView = new TextView(context);
+    mSummaryView.setId(R.id.suw_items_summary);
+    root.addView(mSummaryView);
 
-        mIconContainer = new FrameLayout(context);
-        mIconContainer.setId(R.id.suw_items_icon_container);
-        root.addView(mIconContainer);
+    mIconContainer = new FrameLayout(context);
+    mIconContainer.setId(R.id.suw_items_icon_container);
+    root.addView(mIconContainer);
 
-        mIconView = new ImageView(context);
-        mIconView.setId(R.id.suw_items_icon);
-        mIconContainer.addView(mIconView);
+    mIconView = new ImageView(context);
+    mIconView.setId(R.id.suw_items_icon);
+    mIconContainer.addView(mIconView);
 
-        return root;
-    }
+    return root;
+  }
 }
diff --git a/library/test/instrumentation/src/com/android/setupwizardlib/test/ReflectionInflaterTest.java b/library/test/instrumentation/src/com/android/setupwizardlib/test/ReflectionInflaterTest.java
index 137a146..69e5882 100644
--- a/library/test/instrumentation/src/com/android/setupwizardlib/test/ReflectionInflaterTest.java
+++ b/library/test/instrumentation/src/com/android/setupwizardlib/test/ReflectionInflaterTest.java
@@ -20,64 +20,59 @@
 import static org.junit.Assert.assertTrue;
 
 import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.annotation.NonNull;
 import android.view.animation.Animation;
 import android.view.animation.AnimationSet;
 import android.view.animation.ScaleAnimation;
-
-import androidx.annotation.NonNull;
-
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
 import com.android.setupwizardlib.items.ReflectionInflater;
-
+import java.util.List;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.List;
-
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class ReflectionInflaterTest {
 
-    @Test
-    public void testInflateXml() {
-        final Context context = InstrumentationRegistry.getContext();
-        TestInflater inflater = new TestInflater(context);
-        final Animation result = inflater.inflate(R.xml.reflection_inflater_test);
+  @Test
+  public void testInflateXml() {
+    final Context context = InstrumentationRegistry.getContext();
+    TestInflater inflater = new TestInflater(context);
+    final Animation result = inflater.inflate(R.xml.reflection_inflater_test);
 
-        assertTrue(result instanceof AnimationSet);
-        final AnimationSet set = (AnimationSet) result;
-        final List<Animation> animations = set.getAnimations();
-        assertEquals(1, animations.size());
-        assertTrue(animations.get(0) instanceof ScaleAnimation);
+    assertTrue(result instanceof AnimationSet);
+    final AnimationSet set = (AnimationSet) result;
+    final List<Animation> animations = set.getAnimations();
+    assertEquals(1, animations.size());
+    assertTrue(animations.get(0) instanceof ScaleAnimation);
+  }
+
+  @Test
+  public void testDefaultPackage() {
+    final Context context = InstrumentationRegistry.getContext();
+    TestInflater inflater = new TestInflater(context);
+    inflater.setDefaultPackage("android.view.animation.");
+    final Animation result = inflater.inflate(R.xml.reflection_inflater_test_with_default_package);
+
+    assertTrue(result instanceof AnimationSet);
+    final AnimationSet set = (AnimationSet) result;
+    final List<Animation> animations = set.getAnimations();
+    assertEquals(1, animations.size());
+    assertTrue(animations.get(0) instanceof ScaleAnimation);
+  }
+
+  private static class TestInflater extends ReflectionInflater<Animation> {
+
+    protected TestInflater(@NonNull Context context) {
+      super(context);
     }
 
-    @Test
-    public void testDefaultPackage() {
-        final Context context = InstrumentationRegistry.getContext();
-        TestInflater inflater = new TestInflater(context);
-        inflater.setDefaultPackage("android.view.animation.");
-        final Animation result =
-                inflater.inflate(R.xml.reflection_inflater_test_with_default_package);
-
-        assertTrue(result instanceof AnimationSet);
-        final AnimationSet set = (AnimationSet) result;
-        final List<Animation> animations = set.getAnimations();
-        assertEquals(1, animations.size());
-        assertTrue(animations.get(0) instanceof ScaleAnimation);
+    @Override
+    protected void onAddChildItem(Animation parent, Animation child) {
+      final AnimationSet group = (AnimationSet) parent;
+      group.addAnimation(child);
     }
-
-    private static class TestInflater extends ReflectionInflater<Animation> {
-
-        protected TestInflater(@NonNull Context context) {
-            super(context);
-        }
-
-        @Override
-        protected void onAddChildItem(Animation parent, Animation child) {
-            final AnimationSet group = (AnimationSet) parent;
-            group.addAnimation(child);
-        }
-    }
+  }
 }
diff --git a/library/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardLayoutTest.java b/library/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardLayoutTest.java
index 531d69e..9d2f784 100644
--- a/library/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardLayoutTest.java
+++ b/library/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardLayoutTest.java
@@ -27,9 +27,7 @@
 import android.graphics.Color;
 import android.graphics.drawable.ColorDrawable;
 import android.os.Parcelable;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.annotation.IdRes;
 import android.util.SparseArray;
 import android.view.AbsSavedState;
 import android.view.ContextThemeWrapper;
@@ -37,15 +35,14 @@
 import android.view.View;
 import android.widget.ProgressBar;
 import android.widget.TextView;
-
-import androidx.annotation.IdRes;
-
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
 import com.android.setupwizardlib.SetupWizardLayout;
 import com.android.setupwizardlib.template.HeaderMixin;
 import com.android.setupwizardlib.template.NavigationBarMixin;
 import com.android.setupwizardlib.template.ProgressBarMixin;
 import com.android.setupwizardlib.view.NavigationBar;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -54,191 +51,196 @@
 @SmallTest
 public class SetupWizardLayoutTest {
 
-    @IdRes
-    private static final int ID1234 = 1234;
+  @IdRes private static final int ID1234 = 1234;
 
-    private Context mContext;
+  private Context mContext;
 
-    @Before
-    public void setUp() throws Exception {
-        mContext = new ContextThemeWrapper(InstrumentationRegistry.getContext(),
-                R.style.SuwThemeMaterial_Light);
-    }
+  @Before
+  public void setUp() throws Exception {
+    mContext =
+        new ContextThemeWrapper(
+            InstrumentationRegistry.getContext(), R.style.SuwThemeMaterial_Light);
+  }
 
-    @Test
-    public void testDefaultTemplate() {
-        SetupWizardLayout layout = new SetupWizardLayout(mContext);
-        assertDefaultTemplateInflated(layout);
-    }
+  @Test
+  public void testDefaultTemplate() {
+    SetupWizardLayout layout = new SetupWizardLayout(mContext);
+    assertDefaultTemplateInflated(layout);
+  }
 
-    @Test
-    public void testSetHeaderText() {
-        SetupWizardLayout layout = new SetupWizardLayout(mContext);
-        TextView title = (TextView) layout.findViewById(R.id.suw_layout_title);
-        layout.setHeaderText("Abracadabra");
-        assertEquals("Header text should be \"Abracadabra\"", "Abracadabra", title.getText());
-    }
+  @Test
+  public void testSetHeaderText() {
+    SetupWizardLayout layout = new SetupWizardLayout(mContext);
+    TextView title = (TextView) layout.findViewById(R.id.suw_layout_title);
+    layout.setHeaderText("Abracadabra");
+    assertEquals("Header text should be \"Abracadabra\"", "Abracadabra", title.getText());
+  }
 
-    @Test
-    public void testAddView() {
-        SetupWizardLayout layout = new SetupWizardLayout(mContext);
-        TextView tv = new TextView(mContext);
-        tv.setId(R.id.test_view_id);
-        layout.addView(tv);
-        assertDefaultTemplateInflated(layout);
-        View view = layout.findViewById(R.id.test_view_id);
-        assertSame("The view added should be the same text view", tv, view);
-    }
+  @Test
+  public void testAddView() {
+    SetupWizardLayout layout = new SetupWizardLayout(mContext);
+    TextView tv = new TextView(mContext);
+    tv.setId(R.id.test_view_id);
+    layout.addView(tv);
+    assertDefaultTemplateInflated(layout);
+    View view = layout.findViewById(R.id.test_view_id);
+    assertSame("The view added should be the same text view", tv, view);
+  }
 
-    @Test
-    public void testInflateFromXml() {
-        LayoutInflater inflater = LayoutInflater.from(mContext);
-        SetupWizardLayout layout = (SetupWizardLayout) inflater.inflate(R.layout.test_layout, null);
-        assertDefaultTemplateInflated(layout);
-        View content = layout.findViewById(R.id.test_content);
-        assertTrue("@id/test_content should be a TextView", content instanceof TextView);
-    }
+  @Test
+  public void testInflateFromXml() {
+    LayoutInflater inflater = LayoutInflater.from(mContext);
+    SetupWizardLayout layout = (SetupWizardLayout) inflater.inflate(R.layout.test_layout, null);
+    assertDefaultTemplateInflated(layout);
+    View content = layout.findViewById(R.id.test_content);
+    assertTrue("@id/test_content should be a TextView", content instanceof TextView);
+  }
 
-    @Test
-    public void testCustomTemplate() {
-        SetupWizardLayout layout = new SetupWizardLayout(mContext, R.layout.test_template);
-        View templateView = layout.findViewById(R.id.test_template_view);
-        assertNotNull("@id/test_template_view should exist in template", templateView);
+  @Test
+  public void testCustomTemplate() {
+    SetupWizardLayout layout = new SetupWizardLayout(mContext, R.layout.test_template);
+    View templateView = layout.findViewById(R.id.test_template_view);
+    assertNotNull("@id/test_template_view should exist in template", templateView);
 
-        TextView tv = new TextView(mContext);
-        tv.setId(R.id.test_view_id);
-        layout.addView(tv);
+    TextView tv = new TextView(mContext);
+    tv.setId(R.id.test_view_id);
+    layout.addView(tv);
 
-        templateView = layout.findViewById(R.id.test_template_view);
-        assertNotNull("@id/test_template_view should exist in template", templateView);
-        View contentView = layout.findViewById(R.id.test_view_id);
-        assertSame("The view added should be the same text view", tv, contentView);
+    templateView = layout.findViewById(R.id.test_template_view);
+    assertNotNull("@id/test_template_view should exist in template", templateView);
+    View contentView = layout.findViewById(R.id.test_view_id);
+    assertSame("The view added should be the same text view", tv, contentView);
 
-        // The following methods should be no-ops because the custom template doesn't contain the
-        // corresponding optional views. Just check that they don't throw exceptions.
-        layout.setHeaderText("Abracadabra");
-        layout.setIllustration(new ColorDrawable(Color.MAGENTA));
-        layout.setLayoutBackground(new ColorDrawable(Color.RED));
-    }
+    // The following methods should be no-ops because the custom template doesn't contain the
+    // corresponding optional views. Just check that they don't throw exceptions.
+    layout.setHeaderText("Abracadabra");
+    layout.setIllustration(new ColorDrawable(Color.MAGENTA));
+    layout.setLayoutBackground(new ColorDrawable(Color.RED));
+  }
 
-    @Test
-    public void testGetNavigationBar() {
-        final SetupWizardLayout layout = new SetupWizardLayout(mContext);
-        final NavigationBar navigationBar = layout.getNavigationBar();
-        assertEquals("Navigation bar should have ID = @id/suw_layout_navigation_bar",
-                R.id.suw_layout_navigation_bar, navigationBar.getId());
-    }
+  @Test
+  public void testGetNavigationBar() {
+    final SetupWizardLayout layout = new SetupWizardLayout(mContext);
+    final NavigationBar navigationBar = layout.getNavigationBar();
+    assertEquals(
+        "Navigation bar should have ID = @id/suw_layout_navigation_bar",
+        R.id.suw_layout_navigation_bar,
+        navigationBar.getId());
+  }
 
-    @Test
-    public void testGetNavigationBarNull() {
-        // test_template does not have navigation bar so getNavigationBar() should return null.
-        final SetupWizardLayout layout = new SetupWizardLayout(mContext, R.layout.test_template);
-        final NavigationBar navigationBar = layout.getNavigationBar();
-        assertNull("getNavigationBar() in test_template should return null", navigationBar);
-    }
+  @Test
+  public void testGetNavigationBarNull() {
+    // test_template does not have navigation bar so getNavigationBar() should return null.
+    final SetupWizardLayout layout = new SetupWizardLayout(mContext, R.layout.test_template);
+    final NavigationBar navigationBar = layout.getNavigationBar();
+    assertNull("getNavigationBar() in test_template should return null", navigationBar);
+  }
 
-    @Test
-    public void testShowProgressBar() {
-        final SetupWizardLayout layout = new SetupWizardLayout(mContext);
-        layout.showProgressBar();
-        assertTrue("Progress bar should be shown", layout.isProgressBarShown());
-        final View progressBar = layout.findViewById(R.id.suw_layout_progress);
-        assertTrue("Progress bar view should be shown",
-                progressBar instanceof ProgressBar && progressBar.getVisibility() == View.VISIBLE);
-    }
+  @Test
+  public void testShowProgressBar() {
+    final SetupWizardLayout layout = new SetupWizardLayout(mContext);
+    layout.showProgressBar();
+    assertTrue("Progress bar should be shown", layout.isProgressBarShown());
+    final View progressBar = layout.findViewById(R.id.suw_layout_progress);
+    assertTrue(
+        "Progress bar view should be shown",
+        progressBar instanceof ProgressBar && progressBar.getVisibility() == View.VISIBLE);
+  }
 
-    @Test
-    public void testHideProgressBar() {
-        final SetupWizardLayout layout = new SetupWizardLayout(mContext);
-        layout.showProgressBar();
-        assertTrue("Progress bar should be shown", layout.isProgressBarShown());
-        layout.hideProgressBar();
-        assertFalse("Progress bar should be hidden", layout.isProgressBarShown());
-        final View progressBar = layout.findViewById(R.id.suw_layout_progress);
-        assertTrue("Progress bar view should exist",
-                progressBar == null || progressBar.getVisibility() != View.VISIBLE);
-    }
+  @Test
+  public void testHideProgressBar() {
+    final SetupWizardLayout layout = new SetupWizardLayout(mContext);
+    layout.showProgressBar();
+    assertTrue("Progress bar should be shown", layout.isProgressBarShown());
+    layout.hideProgressBar();
+    assertFalse("Progress bar should be hidden", layout.isProgressBarShown());
+    final View progressBar = layout.findViewById(R.id.suw_layout_progress);
+    assertTrue(
+        "Progress bar view should exist",
+        progressBar == null || progressBar.getVisibility() != View.VISIBLE);
+  }
 
-    @Test
-    public void testShowProgressBarNotExist() {
-        // test_template does not have progress bar, so showNavigationBar() should do nothing.
-        final SetupWizardLayout layout = new SetupWizardLayout(mContext, R.layout.test_template);
-        layout.showProgressBar();
-        assertFalse("Progress bar should not be shown", layout.isProgressBarShown());
-    }
+  @Test
+  public void testShowProgressBarNotExist() {
+    // test_template does not have progress bar, so showNavigationBar() should do nothing.
+    final SetupWizardLayout layout = new SetupWizardLayout(mContext, R.layout.test_template);
+    layout.showProgressBar();
+    assertFalse("Progress bar should not be shown", layout.isProgressBarShown());
+  }
 
-    @Test
-    public void testNonMaterialTheme() {
-        mContext = new ContextThemeWrapper(InstrumentationRegistry.getContext(),
-                android.R.style.Theme);
-        new SetupWizardLayout(mContext);
-        // Inflating with a non-Material theme should not crash
-    }
+  @Test
+  public void testNonMaterialTheme() {
+    mContext = new ContextThemeWrapper(InstrumentationRegistry.getContext(), android.R.style.Theme);
+    new SetupWizardLayout(mContext);
+    // Inflating with a non-Material theme should not crash
+  }
 
-    @Test
-    public void testOnRestoreFromInstanceState() {
-        final SetupWizardLayout layout = new SetupWizardLayout(mContext);
-        layout.setId(ID1234);
+  @Test
+  public void testOnRestoreFromInstanceState() {
+    final SetupWizardLayout layout = new SetupWizardLayout(mContext);
+    layout.setId(ID1234);
 
-        SparseArray<Parcelable> container = new SparseArray<>();
-        layout.saveHierarchyState(container);
+    SparseArray<Parcelable> container = new SparseArray<>();
+    layout.saveHierarchyState(container);
 
-        final SetupWizardLayout layout2 = new SetupWizardLayout(mContext);
-        layout2.setId(ID1234);
-        layout2.restoreHierarchyState(container);
+    final SetupWizardLayout layout2 = new SetupWizardLayout(mContext);
+    layout2.setId(ID1234);
+    layout2.restoreHierarchyState(container);
 
-        assertFalse("Progress bar should not be shown", layout2.isProgressBarShown());
-    }
+    assertFalse("Progress bar should not be shown", layout2.isProgressBarShown());
+  }
 
-    @Test
-    public void testOnRestoreFromInstanceStateProgressBarShown() {
-        final SetupWizardLayout layout = new SetupWizardLayout(mContext);
-        layout.setId(ID1234);
+  @Test
+  public void testOnRestoreFromInstanceStateProgressBarShown() {
+    final SetupWizardLayout layout = new SetupWizardLayout(mContext);
+    layout.setId(ID1234);
 
-        layout.setProgressBarShown(true);
+    layout.setProgressBarShown(true);
 
-        SparseArray<Parcelable> container = new SparseArray<>();
-        layout.saveHierarchyState(container);
+    SparseArray<Parcelable> container = new SparseArray<>();
+    layout.saveHierarchyState(container);
 
-        final SetupWizardLayout layout2 = new SetupWizardLayout(mContext);
-        layout2.setId(ID1234);
-        layout2.restoreHierarchyState(container);
+    final SetupWizardLayout layout2 = new SetupWizardLayout(mContext);
+    layout2.setId(ID1234);
+    layout2.restoreHierarchyState(container);
 
-        assertTrue("Progress bar should be shown", layout2.isProgressBarShown());
-    }
+    assertTrue("Progress bar should be shown", layout2.isProgressBarShown());
+  }
 
-    @Test
-    public void testOnRestoreFromIncompatibleInstanceState() {
-        final SetupWizardLayout layout = new SetupWizardLayout(mContext);
-        layout.setId(ID1234);
+  @Test
+  public void testOnRestoreFromIncompatibleInstanceState() {
+    final SetupWizardLayout layout = new SetupWizardLayout(mContext);
+    layout.setId(ID1234);
 
-        SparseArray<Parcelable> container = new SparseArray<>();
-        container.put(1234, AbsSavedState.EMPTY_STATE);
-        layout.restoreHierarchyState(container);
+    SparseArray<Parcelable> container = new SparseArray<>();
+    container.put(1234, AbsSavedState.EMPTY_STATE);
+    layout.restoreHierarchyState(container);
 
-        // SetupWizardLayout shouldn't crash with incompatible Parcelable
+    // SetupWizardLayout shouldn't crash with incompatible Parcelable
 
-        assertFalse("Progress bar should not be shown", layout.isProgressBarShown());
-    }
+    assertFalse("Progress bar should not be shown", layout.isProgressBarShown());
+  }
 
-    @Test
-    public void testGetMixins() {
-        final SetupWizardLayout layout = new SetupWizardLayout(mContext);
-        assertNotNull("SetupWizardLayout should have header mixin",
-                layout.getMixin(HeaderMixin.class));
-        assertNotNull("SetupWizardLayout should have progress bar mixin",
-                layout.getMixin(ProgressBarMixin.class));
-        assertNotNull("SetupWizardLayout should have navigation bar mixin",
-                layout.getMixin(NavigationBarMixin.class));
-    }
+  @Test
+  public void testGetMixins() {
+    final SetupWizardLayout layout = new SetupWizardLayout(mContext);
+    assertNotNull("SetupWizardLayout should have header mixin", layout.getMixin(HeaderMixin.class));
+    assertNotNull(
+        "SetupWizardLayout should have progress bar mixin",
+        layout.getMixin(ProgressBarMixin.class));
+    assertNotNull(
+        "SetupWizardLayout should have navigation bar mixin",
+        layout.getMixin(NavigationBarMixin.class));
+  }
 
-    private void assertDefaultTemplateInflated(SetupWizardLayout layout) {
-        View decorView = layout.findViewById(R.id.suw_layout_decor);
-        View navbar = layout.findViewById(R.id.suw_layout_navigation_bar);
-        View title = layout.findViewById(R.id.suw_layout_title);
-        assertNotNull("@id/suw_layout_decor_view should not be null", decorView);
-        assertTrue("@id/suw_layout_navigation_bar should be an instance of NavigationBar",
-                navbar instanceof NavigationBar);
-        assertNotNull("@id/suw_layout_title should not be null", title);
-    }
+  private void assertDefaultTemplateInflated(SetupWizardLayout layout) {
+    View decorView = layout.findViewById(R.id.suw_layout_decor);
+    View navbar = layout.findViewById(R.id.suw_layout_navigation_bar);
+    View title = layout.findViewById(R.id.suw_layout_title);
+    assertNotNull("@id/suw_layout_decor_view should not be null", decorView);
+    assertTrue(
+        "@id/suw_layout_navigation_bar should be an instance of NavigationBar",
+        navbar instanceof NavigationBar);
+    assertNotNull("@id/suw_layout_title should not be null", title);
+  }
 }
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 5c34fe0..fc18a31 100644
--- a/library/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardListLayoutTest.java
+++ b/library/test/instrumentation/src/com/android/setupwizardlib/test/SetupWizardListLayoutTest.java
@@ -25,20 +25,18 @@
 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;
 import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.ListView;
 import android.widget.ProgressBar;
 import android.widget.TextView;
-
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
 import com.android.setupwizardlib.SetupWizardLayout;
 import com.android.setupwizardlib.SetupWizardListLayout;
 import com.android.setupwizardlib.view.NavigationBar;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -47,90 +45,93 @@
 @SmallTest
 public class SetupWizardListLayoutTest {
 
-    private Context mContext;
+  private Context mContext;
 
-    @Before
-    public void setUp() throws Exception {
-        mContext = new ContextThemeWrapper(InstrumentationRegistry.getContext(),
-                R.style.SuwThemeMaterial_Light);
+  @Before
+  public void setUp() throws Exception {
+    mContext =
+        new ContextThemeWrapper(
+            InstrumentationRegistry.getContext(), R.style.SuwThemeMaterial_Light);
+  }
+
+  @Test
+  public void testDefaultTemplate() {
+    SetupWizardListLayout layout = new SetupWizardListLayout(mContext);
+    assertListTemplateInflated(layout);
+  }
+
+  @Test
+  public void testAddView() {
+    SetupWizardListLayout layout = new SetupWizardListLayout(mContext);
+    TextView tv = new TextView(mContext);
+    try {
+      layout.addView(tv);
+      fail("Adding view to ListLayout should throw");
+    } catch (UnsupportedOperationException e) {
+      // Expected exception
     }
+  }
 
-    @Test
-    public void testDefaultTemplate() {
-        SetupWizardListLayout layout = new SetupWizardListLayout(mContext);
-        assertListTemplateInflated(layout);
+  @Test
+  public void testInflateFromXml() {
+    LayoutInflater inflater = LayoutInflater.from(mContext);
+    SetupWizardListLayout layout =
+        (SetupWizardListLayout) inflater.inflate(R.layout.test_list_layout, null);
+    assertListTemplateInflated(layout);
+  }
+
+  @Test
+  public void testShowProgressBar() {
+    final SetupWizardListLayout layout = new SetupWizardListLayout(mContext);
+    layout.showProgressBar();
+    assertTrue("Progress bar should be shown", layout.isProgressBarShown());
+    final View progressBar = layout.findViewById(R.id.suw_layout_progress);
+    assertTrue(
+        "Progress bar view should be shown",
+        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);
 
-    @Test
-    public void testAddView() {
-        SetupWizardListLayout layout = new SetupWizardListLayout(mContext);
-        TextView tv = new TextView(mContext);
-        try {
-            layout.addView(tv);
-            fail("Adding view to ListLayout should throw");
-        } catch (UnsupportedOperationException e) {
-            // Expected exception
-        }
+    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);
 
-    @Test
-    public void testInflateFromXml() {
-        LayoutInflater inflater = LayoutInflater.from(mContext);
-        SetupWizardListLayout layout = (SetupWizardListLayout)
-                inflater.inflate(R.layout.test_list_layout, null);
-        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());
 
-    @Test
-    public void testShowProgressBar() {
-        final SetupWizardListLayout layout = new SetupWizardListLayout(mContext);
-        layout.showProgressBar();
-        assertTrue("Progress bar should be shown", layout.isProgressBarShown());
-        final View progressBar = layout.findViewById(R.id.suw_layout_progress);
-        assertTrue("Progress bar view should be shown",
-                progressBar instanceof ProgressBar && progressBar.getVisibility() == View.VISIBLE);
-    }
+    final Drawable divider = layout.getDivider();
+    assertTrue("Divider should be instance of InsetDrawable", divider instanceof InsetDrawable);
+  }
 
-    @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);
-        View title = layout.findViewById(R.id.suw_layout_title);
-        View list = layout.findViewById(android.R.id.list);
-        assertNotNull("@id/suw_layout_decor_view should not be null", decorView);
-        assertTrue("@id/suw_layout_navigation_bar should be an instance of NavigationBar",
-                navbar instanceof NavigationBar);
-        assertNotNull("@id/suw_layout_title should not be null", title);
-        assertTrue("@android:id/list should be an instance of ListView", list instanceof ListView);
-    }
+  private void assertListTemplateInflated(SetupWizardLayout layout) {
+    View decorView = layout.findViewById(R.id.suw_layout_decor);
+    View navbar = layout.findViewById(R.id.suw_layout_navigation_bar);
+    View title = layout.findViewById(R.id.suw_layout_title);
+    View list = layout.findViewById(android.R.id.list);
+    assertNotNull("@id/suw_layout_decor_view should not be null", decorView);
+    assertTrue(
+        "@id/suw_layout_navigation_bar should be an instance of NavigationBar",
+        navbar instanceof NavigationBar);
+    assertNotNull("@id/suw_layout_title should not be null", title);
+    assertTrue("@android:id/list should be an instance of ListView", list instanceof ListView);
+  }
 }
diff --git a/library/test/instrumentation/src/com/android/setupwizardlib/test/SimpleInflaterTest.java b/library/test/instrumentation/src/com/android/setupwizardlib/test/SimpleInflaterTest.java
index f4738ca..da39a7b 100644
--- a/library/test/instrumentation/src/com/android/setupwizardlib/test/SimpleInflaterTest.java
+++ b/library/test/instrumentation/src/com/android/setupwizardlib/test/SimpleInflaterTest.java
@@ -20,15 +20,12 @@
 
 import android.content.Context;
 import android.content.res.Resources;
+import androidx.annotation.NonNull;
+import android.util.AttributeSet;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.util.AttributeSet;
-
-import androidx.annotation.NonNull;
-
 import com.android.setupwizardlib.items.SimpleInflater;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -36,30 +33,30 @@
 @RunWith(AndroidJUnit4.class)
 public class SimpleInflaterTest {
 
-    @Test
-    public void testInflateXml() {
-        final Context context = InstrumentationRegistry.getContext();
-        TestInflater inflater = new TestInflater(context.getResources());
-        final StringBuilder result = inflater.inflate(R.xml.simple_inflater_test);
+  @Test
+  public void testInflateXml() {
+    final Context context = InstrumentationRegistry.getContext();
+    TestInflater inflater = new TestInflater(context.getResources());
+    final StringBuilder result = inflater.inflate(R.xml.simple_inflater_test);
 
-        assertEquals("Parent[null] > Child[foobar]", result.toString());
+    assertEquals("Parent[null] > Child[foobar]", result.toString());
+  }
+
+  private static class TestInflater extends SimpleInflater<StringBuilder> {
+
+    protected TestInflater(@NonNull Resources resources) {
+      super(resources);
     }
 
-    private static class TestInflater extends SimpleInflater<StringBuilder> {
-
-        protected TestInflater(@NonNull Resources resources) {
-            super(resources);
-        }
-
-        @Override
-        protected StringBuilder onCreateItem(String tagName, AttributeSet attrs) {
-            final String attribute = attrs.getAttributeValue(null, "myattribute");
-            return new StringBuilder(tagName).append("[").append(attribute).append("]");
-        }
-
-        @Override
-        protected void onAddChildItem(StringBuilder parent, StringBuilder child) {
-            parent.append(" > ").append(child);
-        }
+    @Override
+    protected StringBuilder onCreateItem(String tagName, AttributeSet attrs) {
+      final String attribute = attrs.getAttributeValue(null, "myattribute");
+      return new StringBuilder(tagName).append("[").append(attribute).append("]");
     }
+
+    @Override
+    protected void onAddChildItem(StringBuilder parent, StringBuilder child) {
+      parent.append(" > ").append(child);
+    }
+  }
 }
diff --git a/library/test/instrumentation/src/com/android/setupwizardlib/test/SpanHelperTest.java b/library/test/instrumentation/src/com/android/setupwizardlib/test/SpanHelperTest.java
index 903cf5e..920d7ab 100644
--- a/library/test/instrumentation/src/com/android/setupwizardlib/test/SpanHelperTest.java
+++ b/library/test/instrumentation/src/com/android/setupwizardlib/test/SpanHelperTest.java
@@ -19,13 +19,11 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertSame;
 
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
 import android.text.Annotation;
 import android.text.SpannableStringBuilder;
-
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
 import com.android.setupwizardlib.span.SpanHelper;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -33,17 +31,17 @@
 @SmallTest
 public class SpanHelperTest {
 
-    @Test
-    public void testReplaceSpan() {
-        SpannableStringBuilder ssb = new SpannableStringBuilder("Hello world");
-        Annotation oldSpan = new Annotation("key", "value");
-        Annotation newSpan = new Annotation("newkey", "newvalue");
-        ssb.setSpan(oldSpan, 2, 5, 0 /* flags */);
+  @Test
+  public void testReplaceSpan() {
+    SpannableStringBuilder ssb = new SpannableStringBuilder("Hello world");
+    Annotation oldSpan = new Annotation("key", "value");
+    Annotation newSpan = new Annotation("newkey", "newvalue");
+    ssb.setSpan(oldSpan, 2, 5, 0 /* flags */);
 
-        SpanHelper.replaceSpan(ssb, oldSpan, newSpan);
+    SpanHelper.replaceSpan(ssb, oldSpan, newSpan);
 
-        final Object[] spans = ssb.getSpans(0, ssb.length(), Object.class);
-        assertEquals("There should be one span in the builder", 1, spans.length);
-        assertSame("The span should be newSpan", newSpan, spans[0]);
-    }
+    final Object[] spans = ssb.getSpans(0, ssb.length(), Object.class);
+    assertEquals("There should be one span in the builder", 1, spans.length);
+    assertSame("The span should be newSpan", newSpan, spans[0]);
+  }
 }
diff --git a/library/test/instrumentation/src/com/android/setupwizardlib/test/StatusBarBackgroundLayoutTest.java b/library/test/instrumentation/src/com/android/setupwizardlib/test/StatusBarBackgroundLayoutTest.java
index 006e5c4..e0fd49b 100644
--- a/library/test/instrumentation/src/com/android/setupwizardlib/test/StatusBarBackgroundLayoutTest.java
+++ b/library/test/instrumentation/src/com/android/setupwizardlib/test/StatusBarBackgroundLayoutTest.java
@@ -24,9 +24,7 @@
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-
 import com.android.setupwizardlib.view.StatusBarBackgroundLayout;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -34,46 +32,48 @@
 @SmallTest
 public class StatusBarBackgroundLayoutTest {
 
-    @Test
-    public void testSetStatusBarBackground() {
-        final StatusBarBackgroundLayout layout = new StatusBarBackgroundLayout(
-                InstrumentationRegistry.getContext());
-        final ShapeDrawable drawable = new ShapeDrawable();
-        layout.setStatusBarBackground(drawable);
-        assertSame("Status bar background drawable should be same as set",
-                drawable, layout.getStatusBarBackground());
+  @Test
+  public void testSetStatusBarBackground() {
+    final StatusBarBackgroundLayout layout =
+        new StatusBarBackgroundLayout(InstrumentationRegistry.getContext());
+    final ShapeDrawable drawable = new ShapeDrawable();
+    layout.setStatusBarBackground(drawable);
+    assertSame(
+        "Status bar background drawable should be same as set",
+        drawable,
+        layout.getStatusBarBackground());
+  }
+
+  @Test
+  public void testAttachedToWindow() {
+    // Attaching to window should request apply window inset
+    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
+      final TestStatusBarBackgroundLayout layout =
+          new TestStatusBarBackgroundLayout(InstrumentationRegistry.getContext());
+      layout.mRequestApplyInsets = false;
+      layout.onAttachedToWindow();
+
+      assertTrue("Attaching to window should apply window inset", layout.mRequestApplyInsets);
+    }
+  }
+
+  private static class TestStatusBarBackgroundLayout extends StatusBarBackgroundLayout {
+
+    boolean mRequestApplyInsets = false;
+
+    TestStatusBarBackgroundLayout(Context context) {
+      super(context);
     }
 
-    @Test
-    public void testAttachedToWindow() {
-        // Attaching to window should request apply window inset
-        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
-            final TestStatusBarBackgroundLayout layout =
-                    new TestStatusBarBackgroundLayout(InstrumentationRegistry.getContext());
-            layout.mRequestApplyInsets = false;
-            layout.onAttachedToWindow();
-
-            assertTrue("Attaching to window should apply window inset", layout.mRequestApplyInsets);
-        }
+    @Override
+    public void onAttachedToWindow() {
+      super.onAttachedToWindow();
     }
 
-    private static class TestStatusBarBackgroundLayout extends StatusBarBackgroundLayout {
-
-        boolean mRequestApplyInsets = false;
-
-        TestStatusBarBackgroundLayout(Context context) {
-            super(context);
-        }
-
-        @Override
-        public void onAttachedToWindow() {
-            super.onAttachedToWindow();
-        }
-
-        @Override
-        public void requestApplyInsets() {
-            super.requestApplyInsets();
-            mRequestApplyInsets = true;
-        }
+    @Override
+    public void requestApplyInsets() {
+      super.requestApplyInsets();
+      mRequestApplyInsets = true;
     }
+  }
 }
diff --git a/library/test/instrumentation/src/com/android/setupwizardlib/test/SystemBarHelperTest.java b/library/test/instrumentation/src/com/android/setupwizardlib/test/SystemBarHelperTest.java
index 98c28f6..1b534e1 100644
--- a/library/test/instrumentation/src/com/android/setupwizardlib/test/SystemBarHelperTest.java
+++ b/library/test/instrumentation/src/com/android/setupwizardlib/test/SystemBarHelperTest.java
@@ -17,7 +17,6 @@
 package com.android.setupwizardlib.test;
 
 import static com.google.common.truth.Truth.assertThat;
-
 import static org.junit.Assert.assertEquals;
 
 import android.annotation.SuppressLint;
@@ -28,19 +27,17 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.SystemClock;
+import android.view.ContextThemeWrapper;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.annotation.UiThreadTest;
 import android.support.test.filters.SmallTest;
 import android.support.test.rule.UiThreadTestRule;
 import android.support.test.runner.AndroidJUnit4;
-import android.view.ContextThemeWrapper;
-import android.view.View;
-import android.view.Window;
-import android.view.WindowManager;
-
 import com.android.setupwizardlib.test.util.MockWindow;
 import com.android.setupwizardlib.util.SystemBarHelper;
-
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -49,241 +46,240 @@
 @SmallTest
 public class SystemBarHelperTest {
 
-    @Rule
-    public UiThreadTestRule mUiThreadTestRule = new UiThreadTestRule();
+  @Rule public UiThreadTestRule mUiThreadTestRule = new UiThreadTestRule();
 
-    private static final int STATUS_BAR_DISABLE_BACK = 0x00400000;
+  private static final int STATUS_BAR_DISABLE_BACK = 0x00400000;
 
-    @SuppressLint("InlinedApi")
-    private static final int DEFAULT_IMMERSIVE_FLAGS =
-            View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
-            | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
-            | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
-            | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
+  @SuppressLint("InlinedApi")
+  private static final int DEFAULT_IMMERSIVE_FLAGS =
+      View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+          | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
+          | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+          | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
 
-    @SuppressLint("InlinedApi")
-    private static final int DIALOG_IMMERSIVE_FLAGS =
-            View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
-                    | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
+  @SuppressLint("InlinedApi")
+  private static final int DIALOG_IMMERSIVE_FLAGS =
+      View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
 
-    @UiThreadTest
-    @Test
-    public void testAddVisibilityFlagView() {
-        final View view = createViewWithSystemUiVisibility(0x456);
-        SystemBarHelper.addVisibilityFlag(view, 0x1400);
-        if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {
-            // Check that result is 0x1456, because 0x1400 | 0x456 = 0x1456.
-            assertEquals("View visibility should be 0x1456", 0x1456, view.getSystemUiVisibility());
-        }
+  @UiThreadTest
+  @Test
+  public void testAddVisibilityFlagView() {
+    final View view = createViewWithSystemUiVisibility(0x456);
+    SystemBarHelper.addVisibilityFlag(view, 0x1400);
+    if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {
+      // Check that result is 0x1456, because 0x1400 | 0x456 = 0x1456.
+      assertEquals("View visibility should be 0x1456", 0x1456, view.getSystemUiVisibility());
     }
+  }
 
-    @UiThreadTest
-    @Test
-    public void testRemoveVisibilityFlagView() {
-        final View view = createViewWithSystemUiVisibility(0x456);
-        SystemBarHelper.removeVisibilityFlag(view, 0x1400);
-        if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {
-            // Check that result is 0x56, because 0x456 & ~0x1400 = 0x56.
-            assertEquals("View visibility should be 0x56", 0x56, view.getSystemUiVisibility());
-        }
+  @UiThreadTest
+  @Test
+  public void testRemoveVisibilityFlagView() {
+    final View view = createViewWithSystemUiVisibility(0x456);
+    SystemBarHelper.removeVisibilityFlag(view, 0x1400);
+    if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {
+      // Check that result is 0x56, because 0x456 & ~0x1400 = 0x56.
+      assertEquals("View visibility should be 0x56", 0x56, view.getSystemUiVisibility());
     }
+  }
 
-    @UiThreadTest
-    @Test
-    public void testAddVisibilityFlagWindow() {
-        final Window window = createWindowWithSystemUiVisibility(0x456);
-        SystemBarHelper.addVisibilityFlag(window, 0x1400);
-        if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {
-            // Check that result is 0x1456 = 0x1400 | 0x456.
-            assertEquals("View visibility should be 0x1456", 0x1456,
-                    window.getAttributes().systemUiVisibility);
-        }
+  @UiThreadTest
+  @Test
+  public void testAddVisibilityFlagWindow() {
+    final Window window = createWindowWithSystemUiVisibility(0x456);
+    SystemBarHelper.addVisibilityFlag(window, 0x1400);
+    if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {
+      // Check that result is 0x1456 = 0x1400 | 0x456.
+      assertEquals(
+          "View visibility should be 0x1456", 0x1456, window.getAttributes().systemUiVisibility);
     }
+  }
 
-    @UiThreadTest
-    @Test
-    public void testRemoveVisibilityFlagWindow() {
-        final Window window = createWindowWithSystemUiVisibility(0x456);
-        SystemBarHelper.removeVisibilityFlag(window, 0x1400);
-        if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {
-            // Check that result is 0x56 = 0x456 & ~0x1400.
-            assertEquals("View visibility should be 0x56", 0x56,
-                    window.getAttributes().systemUiVisibility);
-        }
+  @UiThreadTest
+  @Test
+  public void testRemoveVisibilityFlagWindow() {
+    final Window window = createWindowWithSystemUiVisibility(0x456);
+    SystemBarHelper.removeVisibilityFlag(window, 0x1400);
+    if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {
+      // Check that result is 0x56 = 0x456 & ~0x1400.
+      assertEquals(
+          "View visibility should be 0x56", 0x56, window.getAttributes().systemUiVisibility);
     }
+  }
 
-    @UiThreadTest
-    @Test
-    public void testHideSystemBarsWindow() {
-        final Window window = createWindowWithSystemUiVisibility(0x456);
-        SystemBarHelper.hideSystemBars(window);
-        if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
-            assertEquals("DEFAULT_IMMERSIVE_FLAGS should be added to window's systemUiVisibility",
-                    DEFAULT_IMMERSIVE_FLAGS | 0x456,
-                    window.getAttributes().systemUiVisibility);
-            assertEquals(
-                    "DEFAULT_IMMERSIVE_FLAGS should be added to decorView's systemUiVisibility",
-                    DEFAULT_IMMERSIVE_FLAGS | 0x456,
-                    window.getDecorView().getSystemUiVisibility());
-            assertEquals("Navigation bar should be transparent", window.getNavigationBarColor(), 0);
-            assertEquals("Status bar should be transparent", window.getStatusBarColor(), 0);
-        }
+  @UiThreadTest
+  @Test
+  public void testHideSystemBarsWindow() {
+    final Window window = createWindowWithSystemUiVisibility(0x456);
+    SystemBarHelper.hideSystemBars(window);
+    if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+      assertEquals(
+          "DEFAULT_IMMERSIVE_FLAGS should be added to window's systemUiVisibility",
+          DEFAULT_IMMERSIVE_FLAGS | 0x456,
+          window.getAttributes().systemUiVisibility);
+      assertEquals(
+          "DEFAULT_IMMERSIVE_FLAGS should be added to decorView's systemUiVisibility",
+          DEFAULT_IMMERSIVE_FLAGS | 0x456,
+          window.getDecorView().getSystemUiVisibility());
+      assertEquals("Navigation bar should be transparent", window.getNavigationBarColor(), 0);
+      assertEquals("Status bar should be transparent", window.getStatusBarColor(), 0);
     }
+  }
 
-    @UiThreadTest
-    @Test
-    public void testShowSystemBarsWindow() {
-        final Window window = createWindowWithSystemUiVisibility(0x456);
-        Context context = new ContextThemeWrapper(
-                InstrumentationRegistry.getContext(), android.R.style.Theme);
-        SystemBarHelper.showSystemBars(window, context);
-        if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
-            assertEquals(
-                    "DEFAULT_IMMERSIVE_FLAGS should be removed from window's systemUiVisibility",
-                    0x456 & ~DEFAULT_IMMERSIVE_FLAGS,
-                    window.getAttributes().systemUiVisibility);
-            assertEquals(
-                    "DEFAULT_IMMERSIVE_FLAGS should be removed from decorView's systemUiVisibility",
-                    0x456 & ~DEFAULT_IMMERSIVE_FLAGS,
-                    window.getDecorView().getSystemUiVisibility());
-            assertEquals("Navigation bar should not be transparent",
-                    window.getNavigationBarColor(), 0xff000000);
-            assertEquals("Status bar should not be transparent",
-                    window.getStatusBarColor(), 0xff000000);
-        }
+  @UiThreadTest
+  @Test
+  public void testShowSystemBarsWindow() {
+    final Window window = createWindowWithSystemUiVisibility(0x456);
+    Context context =
+        new ContextThemeWrapper(InstrumentationRegistry.getContext(), android.R.style.Theme);
+    SystemBarHelper.showSystemBars(window, context);
+    if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+      assertEquals(
+          "DEFAULT_IMMERSIVE_FLAGS should be removed from window's systemUiVisibility",
+          0x456 & ~DEFAULT_IMMERSIVE_FLAGS,
+          window.getAttributes().systemUiVisibility);
+      assertEquals(
+          "DEFAULT_IMMERSIVE_FLAGS should be removed from decorView's systemUiVisibility",
+          0x456 & ~DEFAULT_IMMERSIVE_FLAGS,
+          window.getDecorView().getSystemUiVisibility());
+      assertEquals(
+          "Navigation bar should not be transparent", window.getNavigationBarColor(), 0xff000000);
+      assertEquals("Status bar should not be transparent", window.getStatusBarColor(), 0xff000000);
     }
+  }
 
-    @UiThreadTest
-    @Test
-    public void testHideSystemBarsNoInfiniteLoop() throws InterruptedException {
-        final TestWindow window = new TestWindow(InstrumentationRegistry.getContext(), null);
-        final HandlerThread thread = new HandlerThread("SystemBarHelperTest");
-        thread.start();
-        final Handler handler = new Handler(thread.getLooper());
-        handler.post(new Runnable() {
-            @Override
-            public void run() {
-                SystemBarHelper.hideSystemBars(window);
-            }
+  @UiThreadTest
+  @Test
+  public void testHideSystemBarsNoInfiniteLoop() throws InterruptedException {
+    final TestWindow window = new TestWindow(InstrumentationRegistry.getContext(), null);
+    final HandlerThread thread = new HandlerThread("SystemBarHelperTest");
+    thread.start();
+    final Handler handler = new Handler(thread.getLooper());
+    handler.post(
+        new Runnable() {
+          @Override
+          public void run() {
+            SystemBarHelper.hideSystemBars(window);
+          }
         });
-        SystemClock.sleep(500);  // Wait for the looper to drain all the messages
-        thread.quit();
-        // Initial peek + 3 retries = 4 tries total
-        if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
-            assertEquals("Peek decor view should give up after 4 tries", 4,
-                    window.peekDecorViewCount);
-        }
+    SystemClock.sleep(500); // Wait for the looper to drain all the messages
+    thread.quit();
+    // Initial peek + 3 retries = 4 tries total
+    if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+      assertEquals("Peek decor view should give up after 4 tries", 4, window.peekDecorViewCount);
+    }
+  }
+
+  @UiThreadTest
+  @Test
+  public void testHideSystemBarsDialog() {
+    final Dialog dialog = new Dialog(InstrumentationRegistry.getContext());
+    if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+      final WindowManager.LayoutParams attrs = dialog.getWindow().getAttributes();
+      attrs.systemUiVisibility = 0x456;
+      dialog.getWindow().setAttributes(attrs);
     }
 
-    @UiThreadTest
-    @Test
-    public void testHideSystemBarsDialog() {
-        final Dialog dialog = new Dialog(InstrumentationRegistry.getContext());
-        if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
-            final WindowManager.LayoutParams attrs = dialog.getWindow().getAttributes();
-            attrs.systemUiVisibility = 0x456;
-            dialog.getWindow().setAttributes(attrs);
-        }
+    SystemBarHelper.hideSystemBars(dialog);
+    if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+      assertEquals(
+          "DIALOG_IMMERSIVE_FLAGS should be added to window's systemUiVisibility",
+          DIALOG_IMMERSIVE_FLAGS | 0x456,
+          dialog.getWindow().getAttributes().systemUiVisibility);
+    }
+  }
 
-        SystemBarHelper.hideSystemBars(dialog);
-        if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
-            assertEquals("DIALOG_IMMERSIVE_FLAGS should be added to window's systemUiVisibility",
-                    DIALOG_IMMERSIVE_FLAGS | 0x456,
-                    dialog.getWindow().getAttributes().systemUiVisibility);
-        }
+  @UiThreadTest
+  @Test
+  public void testSetBackButtonVisibleTrue() {
+    final Window window = createWindowWithSystemUiVisibility(STATUS_BAR_DISABLE_BACK | 0x456);
+    SystemBarHelper.setBackButtonVisible(window, true);
+    if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {
+      assertThat(window.getAttributes().systemUiVisibility)
+          .named("window sysUiVisibility")
+          .isEqualTo(0x456);
+      assertThat(window.getDecorView().getSystemUiVisibility())
+          .named("decor view sysUiVisibility")
+          .isEqualTo(0x456);
+    }
+  }
+
+  @UiThreadTest
+  @Test
+  public void testSetBackButtonVisibleFalse() {
+    final Window window = createWindowWithSystemUiVisibility(0x456);
+    SystemBarHelper.setBackButtonVisible(window, false);
+    if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {
+      assertThat(window.getAttributes().systemUiVisibility)
+          .named("window sysUiVisibility")
+          .isEqualTo(0x456 | STATUS_BAR_DISABLE_BACK);
+      assertThat(window.getDecorView().getSystemUiVisibility())
+          .named("decor view sysUiVisibility")
+          .isEqualTo(0x456 | STATUS_BAR_DISABLE_BACK);
+    }
+  }
+
+  private View createViewWithSystemUiVisibility(int vis) {
+    final View view = new View(InstrumentationRegistry.getContext());
+    if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {
+      view.setSystemUiVisibility(vis);
+    }
+    return view;
+  }
+
+  private Window createWindowWithSystemUiVisibility(int vis) {
+    final Window window =
+        new TestWindow(InstrumentationRegistry.getContext(), createViewWithSystemUiVisibility(vis));
+    if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {
+      WindowManager.LayoutParams attrs = window.getAttributes();
+      attrs.systemUiVisibility = vis;
+      window.setAttributes(attrs);
+    }
+    return window;
+  }
+
+  private static class TestWindow extends MockWindow {
+
+    private View mDecorView;
+    public int peekDecorViewCount = 0;
+
+    private int mNavigationBarColor = -1;
+    private int mStatusBarColor = -1;
+
+    TestWindow(Context context, View decorView) {
+      super(context);
+      mDecorView = decorView;
     }
 
-    @UiThreadTest
-    @Test
-    public void testSetBackButtonVisibleTrue() {
-        final Window window = createWindowWithSystemUiVisibility(STATUS_BAR_DISABLE_BACK | 0x456);
-        SystemBarHelper.setBackButtonVisible(window, true);
-        if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {
-            assertThat(window.getAttributes().systemUiVisibility)
-                    .named("window sysUiVisibility")
-                    .isEqualTo(0x456);
-            assertThat(window.getDecorView().getSystemUiVisibility())
-                    .named("decor view sysUiVisibility")
-                    .isEqualTo(0x456);
-        }
+    @Override
+    public View getDecorView() {
+      return mDecorView;
     }
 
-    @UiThreadTest
-    @Test
-    public void testSetBackButtonVisibleFalse() {
-        final Window window = createWindowWithSystemUiVisibility(0x456);
-        SystemBarHelper.setBackButtonVisible(window, false);
-        if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {
-            assertThat(window.getAttributes().systemUiVisibility)
-                    .named("window sysUiVisibility")
-                    .isEqualTo(0x456 | STATUS_BAR_DISABLE_BACK);
-            assertThat(window.getDecorView().getSystemUiVisibility())
-                    .named("decor view sysUiVisibility")
-                    .isEqualTo(0x456 | STATUS_BAR_DISABLE_BACK);
-        }
+    @Override
+    public View peekDecorView() {
+      peekDecorViewCount++;
+      return mDecorView;
     }
 
-    private View createViewWithSystemUiVisibility(int vis) {
-        final View view = new View(InstrumentationRegistry.getContext());
-        if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {
-            view.setSystemUiVisibility(vis);
-        }
-        return view;
+    @Override
+    public void setNavigationBarColor(int i) {
+      mNavigationBarColor = i;
     }
 
-    private Window createWindowWithSystemUiVisibility(int vis) {
-        final Window window = new TestWindow(InstrumentationRegistry.getContext(),
-                createViewWithSystemUiVisibility(vis));
-        if (VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB) {
-            WindowManager.LayoutParams attrs = window.getAttributes();
-            attrs.systemUiVisibility = vis;
-            window.setAttributes(attrs);
-        }
-        return window;
+    @Override
+    public int getNavigationBarColor() {
+      return mNavigationBarColor;
     }
 
-    private static class TestWindow extends MockWindow {
-
-        private View mDecorView;
-        public int peekDecorViewCount = 0;
-
-        private int mNavigationBarColor = -1;
-        private int mStatusBarColor = -1;
-
-        TestWindow(Context context, View decorView) {
-            super(context);
-            mDecorView = decorView;
-        }
-
-        @Override
-        public View getDecorView() {
-            return mDecorView;
-        }
-
-        @Override
-        public View peekDecorView() {
-            peekDecorViewCount++;
-            return mDecorView;
-        }
-
-        @Override
-        public void setNavigationBarColor(int i) {
-            mNavigationBarColor = i;
-        }
-
-        @Override
-        public int getNavigationBarColor() {
-            return mNavigationBarColor;
-        }
-
-        @Override
-        public void setStatusBarColor(int i) {
-            mStatusBarColor = i;
-        }
-
-        @Override
-        public int getStatusBarColor() {
-            return mStatusBarColor;
-        }
+    @Override
+    public void setStatusBarColor(int i) {
+      mStatusBarColor = i;
     }
+
+    @Override
+    public int getStatusBarColor() {
+      return mStatusBarColor;
+    }
+  }
 }
diff --git a/library/test/instrumentation/src/com/android/setupwizardlib/test/util/DrawingTestHelper.java b/library/test/instrumentation/src/com/android/setupwizardlib/test/util/DrawingTestHelper.java
index 6910513..918d63a 100644
--- a/library/test/instrumentation/src/com/android/setupwizardlib/test/util/DrawingTestHelper.java
+++ b/library/test/instrumentation/src/com/android/setupwizardlib/test/util/DrawingTestHelper.java
@@ -24,69 +24,69 @@
 import android.content.pm.ActivityInfo;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
-import android.support.test.InstrumentationRegistry;
+import androidx.annotation.StyleRes;
 import android.view.View;
 import android.view.View.MeasureSpec;
-
-import androidx.annotation.StyleRes;
+import android.support.test.InstrumentationRegistry;
 
 public class DrawingTestHelper {
 
-    /**
-     * Creates an activity of which to inflate views and drawables for drawing tests. This method
-     * will return an instance of AppCompatActivity which allows testing of drawing behavior
-     * injected by support libraries (like drawable tinting) as well.
-     */
-    public static Activity createCanvasActivity(@StyleRes int theme)
-            throws IllegalAccessException, InstantiationException {
-        final Context context = InstrumentationRegistry.getTargetContext();
-        final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+  /**
+   * Creates an activity of which to inflate views and drawables for drawing tests. This method will
+   * return an instance of AppCompatActivity which allows testing of drawing behavior injected by
+   * support libraries (like drawable tinting) as well.
+   */
+  public static Activity createCanvasActivity(@StyleRes int theme)
+      throws IllegalAccessException, InstantiationException {
+    final Context context = InstrumentationRegistry.getTargetContext();
+    final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
 
-        final Intent intent = new Intent(context, DrawingTestActivity.class);
-        final Activity activity = instrumentation.newActivity(
-                DrawingTestActivity.class,
-                context,
-                null, /* token */
-                new Application(),
-                intent,
-                new ActivityInfo(),
-                "", /* title */
-                null, /* parent */
-                null, /* id */
-                null /* lastNonConfigurationInstance */);
-        instrumentation.callActivityOnCreate(activity, null);
-        activity.setTheme(theme);
-        return activity;
-    }
+    final Intent intent = new Intent(context, DrawingTestActivity.class);
+    final Activity activity =
+        instrumentation.newActivity(
+            DrawingTestActivity.class,
+            context,
+            null, /* token */
+            new Application(),
+            intent,
+            new ActivityInfo(),
+            "", /* title */
+            null, /* parent */
+            null, /* id */
+            null /* lastNonConfigurationInstance */);
+    instrumentation.callActivityOnCreate(activity, null);
+    activity.setTheme(theme);
+    return activity;
+  }
 
-    private final int mWidth;
-    private final int mHeight;
-    private final Canvas mCanvas;
-    private final Bitmap mBitmap;
+  private final int mWidth;
+  private final int mHeight;
+  private final Canvas mCanvas;
+  private final Bitmap mBitmap;
 
-    public DrawingTestHelper(int width, int height) {
-        mWidth = width;
-        mHeight = height;
+  public DrawingTestHelper(int width, int height) {
+    mWidth = width;
+    mHeight = height;
 
-        mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
-        mCanvas = new Canvas(mBitmap);
-    }
+    mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+    mCanvas = new Canvas(mBitmap);
+  }
 
-    public void drawView(View view) {
-        view.measure(
-                MeasureSpec.makeMeasureSpec(mWidth, MeasureSpec.EXACTLY),
-                MeasureSpec.makeMeasureSpec(mHeight, MeasureSpec.EXACTLY));
-        view.layout(0, 0, mWidth, mHeight);
-        view.draw(mCanvas);
-    }
+  public void drawView(View view) {
+    view.measure(
+        MeasureSpec.makeMeasureSpec(mWidth, MeasureSpec.EXACTLY),
+        MeasureSpec.makeMeasureSpec(mHeight, MeasureSpec.EXACTLY));
+    view.layout(0, 0, mWidth, mHeight);
+    view.draw(mCanvas);
+  }
 
-    public int[] getPixels() {
-        int[] out = new int[mWidth * mHeight];
-        mBitmap.getPixels(out, 0, mWidth, 0, 0, mWidth, mHeight);
-        return out;
-    }
+  public int[] getPixels() {
+    int[] out = new int[mWidth * mHeight];
+    mBitmap.getPixels(out, 0, mWidth, 0, 0, mWidth, mHeight);
+    return out;
+  }
 
-    public int getPixel(int x, int y) {
-        return mBitmap.getPixel(x, y);
-    }
+  public int getPixel(int x, int y) {
+    return mBitmap.getPixel(x, y);
+  }
 }
diff --git a/library/test/instrumentation/src/com/android/setupwizardlib/test/util/MockWindow.java b/library/test/instrumentation/src/com/android/setupwizardlib/test/util/MockWindow.java
index 7af20eb..1e096eb 100644
--- a/library/test/instrumentation/src/com/android/setupwizardlib/test/util/MockWindow.java
+++ b/library/test/instrumentation/src/com/android/setupwizardlib/test/util/MockWindow.java
@@ -21,6 +21,7 @@
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Bundle;
+import androidx.annotation.NonNull;
 import android.view.InputQueue;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
@@ -30,252 +31,250 @@
 import android.view.ViewGroup;
 import android.view.Window;
 
-import androidx.annotation.NonNull;
-
 public class MockWindow extends Window {
 
-    public MockWindow(Context context) {
-        super(context);
-    }
+  public MockWindow(Context context) {
+    super(context);
+  }
 
-    @Override
-    public void takeSurface(SurfaceHolder.Callback2 callback2) {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  public void takeSurface(SurfaceHolder.Callback2 callback2) {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @Override
-    public void takeInputQueue(InputQueue.Callback callback) {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  public void takeInputQueue(InputQueue.Callback callback) {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @Override
-    public boolean isFloating() {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  public boolean isFloating() {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @Override
-    public void setContentView(int i) {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  public void setContentView(int i) {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @Override
-    public void setContentView(View view) {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  public void setContentView(View view) {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @Override
-    public void setContentView(View view, ViewGroup.LayoutParams layoutParams) {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  public void setContentView(View view, ViewGroup.LayoutParams layoutParams) {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @Override
-    public void addContentView(View view, ViewGroup.LayoutParams layoutParams) {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  public void addContentView(View view, ViewGroup.LayoutParams layoutParams) {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @Override
-    public View getCurrentFocus() {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  public View getCurrentFocus() {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @NonNull
-    @Override
-    public LayoutInflater getLayoutInflater() {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @NonNull
+  @Override
+  public LayoutInflater getLayoutInflater() {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @Override
-    public void setTitle(CharSequence charSequence) {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  public void setTitle(CharSequence charSequence) {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @Override
-    public void setTitleColor(int i) {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  public void setTitleColor(int i) {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @Override
-    public void openPanel(int i, KeyEvent keyEvent) {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  public void openPanel(int i, KeyEvent keyEvent) {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @Override
-    public void closePanel(int i) {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  public void closePanel(int i) {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @Override
-    public void togglePanel(int i, KeyEvent keyEvent) {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  public void togglePanel(int i, KeyEvent keyEvent) {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @Override
-    public void invalidatePanelMenu(int i) {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  public void invalidatePanelMenu(int i) {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @Override
-    public boolean performPanelShortcut(int i, int i1, KeyEvent keyEvent, int i2) {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  public boolean performPanelShortcut(int i, int i1, KeyEvent keyEvent, int i2) {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @Override
-    public boolean performPanelIdentifierAction(int i, int i1, int i2) {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  public boolean performPanelIdentifierAction(int i, int i1, int i2) {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @Override
-    public void closeAllPanels() {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  public void closeAllPanels() {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @Override
-    public boolean performContextMenuIdentifierAction(int i, int i1) {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  public boolean performContextMenuIdentifierAction(int i, int i1) {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @Override
-    public void onConfigurationChanged(Configuration configuration) {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  public void onConfigurationChanged(Configuration configuration) {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @Override
-    public void setBackgroundDrawable(Drawable drawable) {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  public void setBackgroundDrawable(Drawable drawable) {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @Override
-    public void setFeatureDrawableResource(int i, int i1) {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  public void setFeatureDrawableResource(int i, int i1) {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @Override
-    public void setFeatureDrawableUri(int i, Uri uri) {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  public void setFeatureDrawableUri(int i, Uri uri) {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @Override
-    public void setFeatureDrawable(int i, Drawable drawable) {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  public void setFeatureDrawable(int i, Drawable drawable) {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @Override
-    public void setFeatureDrawableAlpha(int i, int i1) {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  public void setFeatureDrawableAlpha(int i, int i1) {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @Override
-    public void setFeatureInt(int i, int i1) {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  public void setFeatureInt(int i, int i1) {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @Override
-    public void takeKeyEvents(boolean b) {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  public void takeKeyEvents(boolean b) {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @Override
-    public boolean superDispatchKeyEvent(KeyEvent keyEvent) {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  public boolean superDispatchKeyEvent(KeyEvent keyEvent) {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @Override
-    public boolean superDispatchKeyShortcutEvent(KeyEvent keyEvent) {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  public boolean superDispatchKeyShortcutEvent(KeyEvent keyEvent) {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @Override
-    public boolean superDispatchTouchEvent(MotionEvent motionEvent) {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  public boolean superDispatchTouchEvent(MotionEvent motionEvent) {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @Override
-    public boolean superDispatchTrackballEvent(MotionEvent motionEvent) {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  public boolean superDispatchTrackballEvent(MotionEvent motionEvent) {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @Override
-    public boolean superDispatchGenericMotionEvent(MotionEvent motionEvent) {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  public boolean superDispatchGenericMotionEvent(MotionEvent motionEvent) {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @Override
-    public View getDecorView() {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  public View getDecorView() {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @Override
-    public View peekDecorView() {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  public View peekDecorView() {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @Override
-    public Bundle saveHierarchyState() {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  public Bundle saveHierarchyState() {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @Override
-    public void restoreHierarchyState(Bundle bundle) {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  public void restoreHierarchyState(Bundle bundle) {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @Override
-    protected void onActive() {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  protected void onActive() {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @Override
-    public void setChildDrawable(int i, Drawable drawable) {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  public void setChildDrawable(int i, Drawable drawable) {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @Override
-    public void setChildInt(int i, int i1) {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  public void setChildInt(int i, int i1) {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @Override
-    public boolean isShortcutKey(int i, KeyEvent keyEvent) {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  public boolean isShortcutKey(int i, KeyEvent keyEvent) {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @Override
-    public void setVolumeControlStream(int i) {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  public void setVolumeControlStream(int i) {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @Override
-    public int getVolumeControlStream() {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  public int getVolumeControlStream() {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @Override
-    public int getStatusBarColor() {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  public int getStatusBarColor() {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @Override
-    public void setStatusBarColor(int i) {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  public void setStatusBarColor(int i) {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @Override
-    public int getNavigationBarColor() {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  public int getNavigationBarColor() {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @Override
-    public void setNavigationBarColor(int i) {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  public void setNavigationBarColor(int i) {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @Override
-    public void setDecorCaptionShade(int i) {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  public void setDecorCaptionShade(int i) {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 
-    @Override
-    public void setResizingCaptionDrawable(Drawable drawable) {
-        throw new UnsupportedOperationException("Unexpected method call on mock");
-    }
+  @Override
+  public void setResizingCaptionDrawable(Drawable drawable) {
+    throw new UnsupportedOperationException("Unexpected method call on mock");
+  }
 }
diff --git a/library/test/instrumentation/src/com/android/setupwizardlib/util/FallbackThemeWrapperTest.java b/library/test/instrumentation/src/com/android/setupwizardlib/util/FallbackThemeWrapperTest.java
index d492765..99d997b 100644
--- a/library/test/instrumentation/src/com/android/setupwizardlib/util/FallbackThemeWrapperTest.java
+++ b/library/test/instrumentation/src/com/android/setupwizardlib/util/FallbackThemeWrapperTest.java
@@ -20,13 +20,11 @@
 
 import android.content.Context;
 import android.content.res.TypedArray;
+import android.view.ContextThemeWrapper;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.view.ContextThemeWrapper;
-
 import com.android.setupwizardlib.test.R;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -35,37 +33,35 @@
 @SmallTest
 public class FallbackThemeWrapperTest {
 
-    private FallbackThemeWrapper mThemedContext;
+  private FallbackThemeWrapper mThemedContext;
 
-    @Before
-    public void setUp() {
-        Context baseContext = new ContextThemeWrapper(
-                InstrumentationRegistry.getContext(),
-                R.style.TestBaseTheme);
-        mThemedContext = new FallbackThemeWrapper(baseContext, R.style.TestFallbackTheme);
-    }
+  @Before
+  public void setUp() {
+    Context baseContext =
+        new ContextThemeWrapper(InstrumentationRegistry.getContext(), R.style.TestBaseTheme);
+    mThemedContext = new FallbackThemeWrapper(baseContext, R.style.TestFallbackTheme);
+  }
 
-    @Test
-    public void testThemeValueOnlyInBase() {
-        final TypedArray a =
-                mThemedContext.obtainStyledAttributes(new int[] {android.R.attr.background});
-        assertEquals(0xffff0000, a.getColor(0, 0));
-        a.recycle();
-    }
+  @Test
+  public void testThemeValueOnlyInBase() {
+    final TypedArray a =
+        mThemedContext.obtainStyledAttributes(new int[] {android.R.attr.background});
+    assertEquals(0xffff0000, a.getColor(0, 0));
+    a.recycle();
+  }
 
-    @Test
-    public void testThemeValueOnlyInFallback() {
-        final TypedArray a =
-                mThemedContext.obtainStyledAttributes(new int[] {android.R.attr.foreground});
-        assertEquals(0xff0000ff, a.getColor(0, 0));
-        a.recycle();
-    }
+  @Test
+  public void testThemeValueOnlyInFallback() {
+    final TypedArray a =
+        mThemedContext.obtainStyledAttributes(new int[] {android.R.attr.foreground});
+    assertEquals(0xff0000ff, a.getColor(0, 0));
+    a.recycle();
+  }
 
-    @Test
-    public void testThemeValueInBoth() {
-        final TypedArray a =
-                mThemedContext.obtainStyledAttributes(new int[] {android.R.attr.theme});
-        assertEquals(R.style.TestBaseTheme, a.getResourceId(0, 0));
-        a.recycle();
-    }
+  @Test
+  public void testThemeValueInBoth() {
+    final TypedArray a = mThemedContext.obtainStyledAttributes(new int[] {android.R.attr.theme});
+    assertEquals(R.style.TestBaseTheme, a.getResourceId(0, 0));
+    a.recycle();
+  }
 }
diff --git a/library/test/robotest/src/com/android/setupwizardlib/GlifLayoutTest.java b/library/test/robotest/src/com/android/setupwizardlib/GlifLayoutTest.java
index 2a4a8c0..d77838f 100644
--- a/library/test/robotest/src/com/android/setupwizardlib/GlifLayoutTest.java
+++ b/library/test/robotest/src/com/android/setupwizardlib/GlifLayoutTest.java
@@ -16,14 +16,8 @@
 
 package com.android.setupwizardlib;
 
-import static org.hamcrest.Matchers.instanceOf;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 import static org.robolectric.RuntimeEnvironment.application;
 
 import android.content.Context;
@@ -34,328 +28,337 @@
 import android.os.Build;
 import android.os.Build.VERSION;
 import android.os.Build.VERSION_CODES;
+import androidx.annotation.IdRes;
 import android.view.ContextThemeWrapper;
 import android.view.View;
 import android.widget.ProgressBar;
 import android.widget.ScrollView;
 import android.widget.TextView;
-
-import androidx.annotation.IdRes;
-
-import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
 import com.android.setupwizardlib.template.ColoredHeaderMixin;
 import com.android.setupwizardlib.template.HeaderMixin;
 import com.android.setupwizardlib.template.IconMixin;
 import com.android.setupwizardlib.template.ProgressBarMixin;
 import com.android.setupwizardlib.view.StatusBarBackgroundLayout;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.annotation.Config;
 
-@RunWith(SuwLibRobolectricTestRunner.class)
-@Config(sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
+@RunWith(RobolectricTestRunner.class)
+@Config(sdk = {Config.OLDEST_SDK, Config.NEWEST_SDK})
 public class GlifLayoutTest {
 
-    private Context mContext;
+  private Context context;
 
-    @Before
-    public void setUp() throws Exception {
-        mContext = new ContextThemeWrapper(application, R.style.SuwThemeGlif_Light);
+  @Before
+  public void setUpContext() {
+    context = new ContextThemeWrapper(application, R.style.SuwThemeGlif_Light);
+  }
+
+  @Test
+  public void testDefaultTemplate() {
+    GlifLayout layout = new GlifLayout(context);
+    assertDefaultTemplateInflated(layout);
+  }
+
+  @Test
+  public void testSetHeaderText() {
+    GlifLayout layout = new GlifLayout(context);
+    TextView title = layout.findViewById(R.id.suw_layout_title);
+    layout.setHeaderText("Abracadabra");
+    assertWithMessage("Header text should be \"Abracadabra\"")
+        .that(title.getText().toString())
+        .isEqualTo("Abracadabra");
+  }
+
+  @Test
+  public void testAddView() {
+    @IdRes int testViewId = 123456;
+    GlifLayout layout = new GlifLayout(context);
+    TextView tv = new TextView(context);
+    tv.setId(testViewId);
+    layout.addView(tv);
+    assertDefaultTemplateInflated(layout);
+    View view = layout.findViewById(testViewId);
+    assertThat(view).named("Text view added").isSameAs(tv);
+  }
+
+  @Test
+  public void testGetScrollView() {
+    GlifLayout layout = new GlifLayout(context);
+    assertWithMessage("Get scroll view should not be null with default template")
+        .that(layout.getScrollView())
+        .isNotNull();
+  }
+
+  @Test
+  public void testSetPrimaryColor() {
+    GlifLayout layout = new GlifLayout(context);
+    layout.setProgressBarShown(true);
+    layout.setPrimaryColor(ColorStateList.valueOf(Color.RED));
+    assertWithMessage("Primary color should be red")
+        .that(layout.getPrimaryColor())
+        .isEqualTo(ColorStateList.valueOf(Color.RED));
+
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+      ProgressBar progressBar = layout.findViewById(R.id.suw_layout_progress);
+      assertThat(progressBar.getIndeterminateTintList())
+          .named("indeterminate progress bar tint")
+          .isEqualTo(ColorStateList.valueOf(Color.RED));
+      assertThat(progressBar.getProgressBackgroundTintList())
+          .named("determinate progress bar tint")
+          .isEqualTo(ColorStateList.valueOf(Color.RED));
+    }
+  }
+
+  @Config(qualifiers = "sw600dp")
+  @Test
+  public void testSetPrimaryColorTablet() {
+    GlifLayout layout = new GlifLayout(context);
+    layout.setProgressBarShown(true);
+    layout.setPrimaryColor(ColorStateList.valueOf(Color.RED));
+    assertWithMessage("Primary color should be red")
+        .that(layout.getPrimaryColor())
+        .isEqualTo(ColorStateList.valueOf(Color.RED));
+
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+      ProgressBar progressBar = layout.findViewById(R.id.suw_layout_progress);
+      assertWithMessage("Progress bar should be tinted red")
+          .that(progressBar.getIndeterminateTintList())
+          .isEqualTo(ColorStateList.valueOf(Color.RED));
+      assertWithMessage("Determinate progress bar should also be tinted red")
+          .that(progressBar.getProgressBackgroundTintList())
+          .isEqualTo(ColorStateList.valueOf(Color.RED));
     }
 
-    @Test
-    public void testDefaultTemplate() {
-        GlifLayout layout = new GlifLayout(mContext);
-        assertDefaultTemplateInflated(layout);
+    assertThat(((GlifPatternDrawable) getTabletBackground(layout)).getColor()).isEqualTo(Color.RED);
+  }
+
+  @Test
+  public void testSetBackgroundBaseColor() {
+    GlifLayout layout = new GlifLayout(context);
+    layout.setPrimaryColor(ColorStateList.valueOf(Color.BLUE));
+    layout.setBackgroundBaseColor(ColorStateList.valueOf(Color.RED));
+
+    assertThat(((GlifPatternDrawable) getPhoneBackground(layout)).getColor()).isEqualTo(Color.RED);
+    assertThat(layout.getBackgroundBaseColor().getDefaultColor()).isEqualTo(Color.RED);
+  }
+
+  @Config(qualifiers = "sw600dp")
+  @Test
+  public void testSetBackgroundBaseColorTablet() {
+    GlifLayout layout = new GlifLayout(context);
+    layout.setPrimaryColor(ColorStateList.valueOf(Color.BLUE));
+    layout.setBackgroundBaseColor(ColorStateList.valueOf(Color.RED));
+
+    assertThat(((GlifPatternDrawable) getTabletBackground(layout)).getColor()).isEqualTo(Color.RED);
+    assertThat(layout.getBackgroundBaseColor().getDefaultColor()).isEqualTo(Color.RED);
+  }
+
+  @Test
+  public void testSetBackgroundPatternedTrue() {
+    GlifLayout layout = new GlifLayout(context);
+    layout.setBackgroundPatterned(true);
+
+    assertThat(getPhoneBackground(layout)).isInstanceOf(GlifPatternDrawable.class);
+    assertThat(layout.isBackgroundPatterned()).named("background is patterned").isTrue();
+  }
+
+  @Test
+  public void testSetBackgroundPatternedFalse() {
+    GlifLayout layout = new GlifLayout(context);
+    layout.setBackgroundPatterned(false);
+
+    assertThat(getPhoneBackground(layout)).isInstanceOf(ColorDrawable.class);
+    assertThat(layout.isBackgroundPatterned()).named("background is patterned").isFalse();
+  }
+
+  @Config(qualifiers = "sw600dp")
+  @Test
+  public void testSetBackgroundPatternedTrueTablet() {
+    GlifLayout layout = new GlifLayout(context);
+    layout.setBackgroundPatterned(true);
+
+    assertThat(getTabletBackground(layout)).isInstanceOf(GlifPatternDrawable.class);
+    assertThat(layout.isBackgroundPatterned()).named("background is patterned").isTrue();
+  }
+
+  @Config(qualifiers = "sw600dp")
+  @Test
+  public void testSetBackgroundPatternedFalseTablet() {
+    GlifLayout layout = new GlifLayout(context);
+    layout.setBackgroundPatterned(false);
+
+    assertThat(getTabletBackground(layout)).isInstanceOf(ColorDrawable.class);
+    assertThat(layout.isBackgroundPatterned()).named("background is patterned").isFalse();
+  }
+
+  @Test
+  public void testNonGlifTheme() {
+    context = new ContextThemeWrapper(application, android.R.style.Theme);
+    new GlifLayout(context);
+    // Inflating with a non-GLIF theme should not crash
+  }
+
+  @Test
+  public void testPeekProgressBarNull() {
+    GlifLayout layout = new GlifLayout(context);
+    assertWithMessage("PeekProgressBar should return null initially")
+        .that(layout.peekProgressBar())
+        .isNull();
+  }
+
+  @Test
+  public void testPeekProgressBar() {
+    GlifLayout layout = new GlifLayout(context);
+    layout.setProgressBarShown(true);
+    assertWithMessage("Peek progress bar should return the bar after setProgressBarShown(true)")
+        .that(layout.peekProgressBar())
+        .isNotNull();
+  }
+
+  @Test
+  public void testMixins() {
+    GlifLayout layout = new GlifLayout(context);
+    final HeaderMixin header = layout.getMixin(HeaderMixin.class);
+    assertThat(header).named("header").isInstanceOf(ColoredHeaderMixin.class);
+
+    assertWithMessage("GlifLayout should have icon mixin")
+        .that(layout.getMixin(IconMixin.class))
+        .isNotNull();
+    assertWithMessage("GlifLayout should have progress bar mixin")
+        .that(layout.getMixin(ProgressBarMixin.class))
+        .isNotNull();
+  }
+
+  @Test
+  public void testInflateFooter() {
+    GlifLayout layout = new GlifLayout(context);
+
+    final View view = layout.inflateFooter(android.R.layout.simple_list_item_1);
+    assertThat(view.getId()).isEqualTo(android.R.id.text1);
+    assertThat((View) layout.findViewById(android.R.id.text1)).isNotNull();
+  }
+
+  @Config(qualifiers = "sw600dp")
+  @Test
+  public void testInflateFooterTablet() {
+    testInflateFooter();
+  }
+
+  @Test
+  public void testInflateFooterBlankTemplate() {
+    GlifLayout layout = new GlifLayout(context, R.layout.suw_glif_blank_template);
+
+    final View view = layout.inflateFooter(android.R.layout.simple_list_item_1);
+    assertThat(view.getId()).isEqualTo(android.R.id.text1);
+    assertThat((View) layout.findViewById(android.R.id.text1)).isNotNull();
+  }
+
+  @Config(qualifiers = "sw600dp")
+  @Test
+  public void testInflateFooterBlankTemplateTablet() {
+    testInflateFooterBlankTemplate();
+  }
+
+  @Test
+  public void testFooterXml() {
+    GlifLayout layout =
+        new GlifLayout(
+            context,
+            Robolectric.buildAttributeSet()
+                .addAttribute(R.attr.suwFooter, "@android:layout/simple_list_item_1")
+                .build());
+
+    assertThat((View) layout.findViewById(android.R.id.text1)).isNotNull();
+  }
+
+  @Test
+  public void inflateStickyHeader_shouldAddViewToLayout() {
+    GlifLayout layout = new GlifLayout(context);
+
+    final View view = layout.inflateStickyHeader(android.R.layout.simple_list_item_1);
+    assertThat(view.getId()).isEqualTo(android.R.id.text1);
+    assertThat((View) layout.findViewById(android.R.id.text1)).isNotNull();
+  }
+
+  @Config(qualifiers = "sw600dp")
+  @Test
+  public void inflateStickyHeader_whenOnTablets_shouldAddViewToLayout() {
+    inflateStickyHeader_shouldAddViewToLayout();
+  }
+
+  @Test
+  public void inflateStickyHeader_whenInXml_shouldAddViewToLayout() {
+    GlifLayout layout =
+        new GlifLayout(
+            context,
+            Robolectric.buildAttributeSet()
+                .addAttribute(R.attr.suwStickyHeader, "@android:layout/simple_list_item_1")
+                .build());
+
+    assertThat((View) layout.findViewById(android.R.id.text1)).isNotNull();
+  }
+
+  @Test
+  public void inflateStickyHeader_whenOnBlankTemplate_shouldAddViewToLayout() {
+    GlifLayout layout = new GlifLayout(context, R.layout.suw_glif_blank_template);
+
+    final View view = layout.inflateStickyHeader(android.R.layout.simple_list_item_1);
+    assertThat(view.getId()).isEqualTo(android.R.id.text1);
+    assertThat((View) layout.findViewById(android.R.id.text1)).isNotNull();
+  }
+
+  @Config(qualifiers = "sw600dp")
+  @Test
+  public void inflateStickyHeader_whenOnBlankTemplateTablet_shouldAddViewToLayout() {
+    inflateStickyHeader_whenOnBlankTemplate_shouldAddViewToLayout();
+  }
+
+  @Config(minSdk = Config.OLDEST_SDK, maxSdk = Config.NEWEST_SDK)
+  @Test
+  public void createFromXml_shouldSetLayoutFullscreen_whenLayoutFullscreenIsNotSet() {
+    GlifLayout layout = new GlifLayout(context, Robolectric.buildAttributeSet().build());
+    if (VERSION.SDK_INT >= VERSION_CODES.M) {
+      assertThat(layout.getSystemUiVisibility() & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)
+          .isEqualTo(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
     }
+  }
 
-    @Test
-    public void testSetHeaderText() {
-        GlifLayout layout = new GlifLayout(mContext);
-        TextView title = (TextView) layout.findViewById(R.id.suw_layout_title);
-        layout.setHeaderText("Abracadabra");
-        assertEquals("Header text should be \"Abracadabra\"", "Abracadabra", title.getText());
-    }
+  @Test
+  public void createFromXml_shouldNotSetLayoutFullscreen_whenLayoutFullscreenIsFalse() {
+    GlifLayout layout =
+        new GlifLayout(
+            context,
+            Robolectric.buildAttributeSet()
+                .addAttribute(R.attr.suwLayoutFullscreen, "false")
+                .build());
 
-    @Test
-    public void testAddView() {
-        @IdRes int testViewId = 123456;
-        GlifLayout layout = new GlifLayout(mContext);
-        TextView tv = new TextView(mContext);
-        tv.setId(testViewId);
-        layout.addView(tv);
-        assertDefaultTemplateInflated(layout);
-        View view = layout.findViewById(testViewId);
-        assertSame("The view added should be the same text view", tv, view);
-    }
+    assertThat(layout.getSystemUiVisibility() & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN).isEqualTo(0);
+  }
 
-    @Test
-    public void testGetScrollView() {
-        GlifLayout layout = new GlifLayout(mContext);
-        assertNotNull("Get scroll view should not be null with default template",
-                layout.getScrollView());
-    }
+  private Drawable getPhoneBackground(GlifLayout layout) {
+    final StatusBarBackgroundLayout patternBg = layout.findManagedViewById(R.id.suw_pattern_bg);
+    return patternBg.getStatusBarBackground();
+  }
 
-    @Test
-    public void testSetPrimaryColor() {
-        GlifLayout layout = new GlifLayout(mContext);
-        layout.setProgressBarShown(true);
-        layout.setPrimaryColor(ColorStateList.valueOf(Color.RED));
-        assertEquals("Primary color should be red",
-                ColorStateList.valueOf(Color.RED), layout.getPrimaryColor());
+  private Drawable getTabletBackground(GlifLayout layout) {
+    final View patternBg = layout.findManagedViewById(R.id.suw_pattern_bg);
+    return patternBg.getBackground();
+  }
 
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
-            ProgressBar progressBar = (ProgressBar) layout.findViewById(R.id.suw_layout_progress);
-            assertEquals("Progress bar should be tinted red",
-                    ColorStateList.valueOf(Color.RED), progressBar.getIndeterminateTintList());
-            assertEquals("Determinate progress bar should also be tinted red",
-                    ColorStateList.valueOf(Color.RED), progressBar.getProgressBackgroundTintList());
-        }
-    }
+  private void assertDefaultTemplateInflated(GlifLayout layout) {
+    View title = layout.findViewById(R.id.suw_layout_title);
+    assertWithMessage("@id/suw_layout_title should not be null").that(title).isNotNull();
 
-    @Config(qualifiers = "sw600dp")
-    @Test
-    public void testSetPrimaryColorTablet() {
-        GlifLayout layout = new GlifLayout(mContext);
-        layout.setProgressBarShown(true);
-        layout.setPrimaryColor(ColorStateList.valueOf(Color.RED));
-        assertEquals("Primary color should be red",
-                ColorStateList.valueOf(Color.RED), layout.getPrimaryColor());
+    View icon = layout.findViewById(R.id.suw_layout_icon);
+    assertWithMessage("@id/suw_layout_icon should not be null").that(icon).isNotNull();
 
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
-            ProgressBar progressBar = (ProgressBar) layout.findViewById(R.id.suw_layout_progress);
-            assertEquals("Progress bar should be tinted red",
-                    ColorStateList.valueOf(Color.RED), progressBar.getIndeterminateTintList());
-            assertEquals("Determinate progress bar should also be tinted red",
-                    ColorStateList.valueOf(Color.RED), progressBar.getProgressBackgroundTintList());
-        }
-
-        assertEquals(Color.RED, ((GlifPatternDrawable) getTabletBackground(layout)).getColor());
-    }
-
-    @Test
-    public void testSetBackgroundBaseColor() {
-        GlifLayout layout = new GlifLayout(mContext);
-        layout.setPrimaryColor(ColorStateList.valueOf(Color.BLUE));
-        layout.setBackgroundBaseColor(ColorStateList.valueOf(Color.RED));
-
-        assertEquals(Color.RED, ((GlifPatternDrawable) getPhoneBackground(layout)).getColor());
-        assertEquals(Color.RED, layout.getBackgroundBaseColor().getDefaultColor());
-    }
-
-    @Config(qualifiers = "sw600dp")
-    @Test
-    public void testSetBackgroundBaseColorTablet() {
-        GlifLayout layout = new GlifLayout(mContext);
-        layout.setPrimaryColor(ColorStateList.valueOf(Color.BLUE));
-        layout.setBackgroundBaseColor(ColorStateList.valueOf(Color.RED));
-
-        assertEquals(Color.RED, ((GlifPatternDrawable) getTabletBackground(layout)).getColor());
-        assertEquals(Color.RED, layout.getBackgroundBaseColor().getDefaultColor());
-    }
-
-    @Test
-    public void testSetBackgroundPatternedTrue() {
-        GlifLayout layout = new GlifLayout(mContext);
-        layout.setBackgroundPatterned(true);
-
-        assertThat(getPhoneBackground(layout), instanceOf(GlifPatternDrawable.class));
-        assertTrue("Background should be patterned", layout.isBackgroundPatterned());
-    }
-
-    @Test
-    public void testSetBackgroundPatternedFalse() {
-        GlifLayout layout = new GlifLayout(mContext);
-        layout.setBackgroundPatterned(false);
-
-        assertThat(getPhoneBackground(layout), instanceOf(ColorDrawable.class));
-        assertFalse("Background should not be patterned", layout.isBackgroundPatterned());
-    }
-
-    @Config(qualifiers = "sw600dp")
-    @Test
-    public void testSetBackgroundPatternedTrueTablet() {
-        GlifLayout layout = new GlifLayout(mContext);
-        layout.setBackgroundPatterned(true);
-
-        assertThat(getTabletBackground(layout), instanceOf(GlifPatternDrawable.class));
-        assertTrue("Background should be patterned", layout.isBackgroundPatterned());
-    }
-
-    @Config(qualifiers = "sw600dp")
-    @Test
-    public void testSetBackgroundPatternedFalseTablet() {
-        GlifLayout layout = new GlifLayout(mContext);
-        layout.setBackgroundPatterned(false);
-
-        assertThat(getTabletBackground(layout), instanceOf(ColorDrawable.class));
-        assertFalse("Background should not be patterned", layout.isBackgroundPatterned());
-    }
-
-    @Test
-    public void testNonGlifTheme() {
-        mContext = new ContextThemeWrapper(application, android.R.style.Theme);
-        new GlifLayout(mContext);
-        // Inflating with a non-GLIF theme should not crash
-    }
-
-    @Test
-    public void testPeekProgressBarNull() {
-        GlifLayout layout = new GlifLayout(mContext);
-        assertNull("PeekProgressBar should return null initially", layout.peekProgressBar());
-    }
-
-    @Test
-    public void testPeekProgressBar() {
-        GlifLayout layout = new GlifLayout(mContext);
-        layout.setProgressBarShown(true);
-        assertNotNull("Peek progress bar should return the bar after setProgressBarShown(true)",
-                layout.peekProgressBar());
-    }
-
-    @Test
-    public void testMixins() {
-        GlifLayout layout = new GlifLayout(mContext);
-        final HeaderMixin header = layout.getMixin(HeaderMixin.class);
-        assertTrue("Header should be instance of ColoredHeaderMixin. "
-                + "Found " + header.getClass() + " instead.", header instanceof ColoredHeaderMixin);
-
-        assertNotNull("GlifLayout should have icon mixin", layout.getMixin(IconMixin.class));
-        assertNotNull("GlifLayout should have progress bar mixin",
-                layout.getMixin(ProgressBarMixin.class));
-    }
-
-    @Test
-    public void testInflateFooter() {
-        GlifLayout layout = new GlifLayout(mContext);
-
-        final View view = layout.inflateFooter(android.R.layout.simple_list_item_1);
-        assertEquals(android.R.id.text1, view.getId());
-        assertNotNull(layout.findViewById(android.R.id.text1));
-    }
-
-    @Config(qualifiers = "sw600dp")
-    @Test
-    public void testInflateFooterTablet() {
-        testInflateFooter();
-    }
-
-    @Test
-    public void testInflateFooterBlankTemplate() {
-        GlifLayout layout = new GlifLayout(mContext, R.layout.suw_glif_blank_template);
-
-        final View view = layout.inflateFooter(android.R.layout.simple_list_item_1);
-        assertEquals(android.R.id.text1, view.getId());
-        assertNotNull(layout.findViewById(android.R.id.text1));
-    }
-
-    @Config(qualifiers = "sw600dp")
-    @Test
-    public void testInflateFooterBlankTemplateTablet() {
-        testInflateFooterBlankTemplate();
-    }
-
-    @Test
-    public void testFooterXml() {
-        GlifLayout layout = new GlifLayout(
-                mContext,
-                Robolectric.buildAttributeSet()
-                        .addAttribute(R.attr.suwFooter, "@android:layout/simple_list_item_1")
-                        .build());
-
-        assertNotNull(layout.findViewById(android.R.id.text1));
-    }
-
-    @Test
-    public void inflateStickyHeader_shouldAddViewToLayout() {
-        GlifLayout layout = new GlifLayout(mContext);
-
-        final View view = layout.inflateStickyHeader(android.R.layout.simple_list_item_1);
-        assertEquals(android.R.id.text1, view.getId());
-        assertNotNull(layout.findViewById(android.R.id.text1));
-    }
-
-    @Config(qualifiers = "sw600dp")
-    @Test
-    public void inflateStickyHeader_whenOnTablets_shouldAddViewToLayout() {
-        inflateStickyHeader_shouldAddViewToLayout();
-    }
-
-    @Test
-    public void inflateStickyHeader_whenInXml_shouldAddViewToLayout() {
-        GlifLayout layout = new GlifLayout(
-                mContext,
-                Robolectric.buildAttributeSet()
-                        .addAttribute(R.attr.suwStickyHeader, "@android:layout/simple_list_item_1")
-                        .build());
-
-        assertNotNull(layout.findViewById(android.R.id.text1));
-    }
-
-    @Test
-    public void inflateStickyHeader_whenOnBlankTemplate_shouldAddViewToLayout() {
-        GlifLayout layout = new GlifLayout(mContext, R.layout.suw_glif_blank_template);
-
-        final View view = layout.inflateStickyHeader(android.R.layout.simple_list_item_1);
-        assertEquals(android.R.id.text1, view.getId());
-        assertNotNull(layout.findViewById(android.R.id.text1));
-    }
-
-    @Config(qualifiers = "sw600dp")
-    @Test
-    public void inflateStickyHeader_whenOnBlankTemplateTablet_shouldAddViewToLayout() {
-        inflateStickyHeader_whenOnBlankTemplate_shouldAddViewToLayout();
-    }
-
-    @Config(minSdk = Config.OLDEST_SDK, maxSdk = Config.NEWEST_SDK)
-    @Test
-    public void createFromXml_shouldSetLayoutFullscreen_whenLayoutFullscreenIsNotSet() {
-        GlifLayout layout = new GlifLayout(
-                mContext,
-                Robolectric.buildAttributeSet()
-                        .build());
-        if (VERSION.SDK_INT >= VERSION_CODES.M) {
-            assertEquals(
-                    View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN,
-                    layout.getSystemUiVisibility() & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
-        }
-    }
-
-    @Test
-    public void createFromXml_shouldNotSetLayoutFullscreen_whenLayoutFullscreenIsFalse() {
-        GlifLayout layout = new GlifLayout(
-                mContext,
-                Robolectric.buildAttributeSet()
-                        .addAttribute(R.attr.suwLayoutFullscreen, "false")
-                        .build());
-
-        assertEquals(
-                0,
-                layout.getSystemUiVisibility() & View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
-    }
-
-    private Drawable getPhoneBackground(GlifLayout layout) {
-        final StatusBarBackgroundLayout patternBg =
-                (StatusBarBackgroundLayout) layout.findManagedViewById(R.id.suw_pattern_bg);
-        return patternBg.getStatusBarBackground();
-    }
-
-    private Drawable getTabletBackground(GlifLayout layout) {
-        final View patternBg = layout.findManagedViewById(R.id.suw_pattern_bg);
-        return patternBg.getBackground();
-    }
-
-    private void assertDefaultTemplateInflated(GlifLayout layout) {
-        View title = layout.findViewById(R.id.suw_layout_title);
-        assertNotNull("@id/suw_layout_title should not be null", title);
-
-        View icon = layout.findViewById(R.id.suw_layout_icon);
-        assertNotNull("@id/suw_layout_icon should not be null", icon);
-
-        View scrollView = layout.findViewById(R.id.suw_scroll_view);
-        assertTrue("@id/suw_scroll_view should be a ScrollView", scrollView instanceof ScrollView);
-    }
+    View scrollView = layout.findViewById(R.id.suw_scroll_view);
+    assertWithMessage("@id/suw_scroll_view should be a ScrollView")
+        .that(scrollView instanceof ScrollView)
+        .isTrue();
+  }
 }
diff --git a/library/test/robotest/src/com/android/setupwizardlib/gesture/ConsecutiveTapsGestureDetectorTest.java b/library/test/robotest/src/com/android/setupwizardlib/gesture/ConsecutiveTapsGestureDetectorTest.java
index aa2cce3..ba3e2c5 100644
--- a/library/test/robotest/src/com/android/setupwizardlib/gesture/ConsecutiveTapsGestureDetectorTest.java
+++ b/library/test/robotest/src/com/android/setupwizardlib/gesture/ConsecutiveTapsGestureDetectorTest.java
@@ -23,96 +23,93 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewConfiguration;
-
-import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.annotation.Config;
 
-@Config(sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
-@RunWith(SuwLibRobolectricTestRunner.class)
+@Config(sdk = {Config.OLDEST_SDK, Config.NEWEST_SDK})
+@RunWith(RobolectricTestRunner.class)
 public class ConsecutiveTapsGestureDetectorTest {
 
-    @Mock
-    private ConsecutiveTapsGestureDetector.OnConsecutiveTapsListener mListener;
+  @Mock private ConsecutiveTapsGestureDetector.OnConsecutiveTapsListener listener;
 
-    private ConsecutiveTapsGestureDetector mDetector;
-    private int mSlop;
-    private int mTapTimeout;
+  private ConsecutiveTapsGestureDetector detector;
+  private int slop;
+  private int tapTimeout;
 
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
+  @Before
+  public void setUp() {
+    MockitoAnnotations.initMocks(this);
 
-        View view = new View(application);
-        view.measure(500, 500);
-        view.layout(0, 0, 500, 500);
-        mDetector = new ConsecutiveTapsGestureDetector(mListener, view);
+    View view = new View(application);
+    view.measure(500, 500);
+    view.layout(0, 0, 500, 500);
+    detector = new ConsecutiveTapsGestureDetector(listener, view);
 
-        mSlop = ViewConfiguration.get(application).getScaledDoubleTapSlop();
-        mTapTimeout = ViewConfiguration.getDoubleTapTimeout();
-    }
+    slop = ViewConfiguration.get(application).getScaledDoubleTapSlop();
+    tapTimeout = ViewConfiguration.getDoubleTapTimeout();
+  }
 
-    @Test
-    public void onTouchEvent_shouldTriggerCallbackOnFourTaps() {
-        InOrder inOrder = inOrder(mListener);
+  @Test
+  public void onTouchEvent_shouldTriggerCallbackOnFourTaps() {
+    InOrder inOrder = inOrder(listener);
 
-        tap(0, 25f, 25f);
-        inOrder.verify(mListener).onConsecutiveTaps(eq(1));
+    tap(0, 25f, 25f);
+    inOrder.verify(listener).onConsecutiveTaps(eq(1));
 
-        tap(100, 25f, 25f);
-        inOrder.verify(mListener).onConsecutiveTaps(eq(2));
+    tap(100, 25f, 25f);
+    inOrder.verify(listener).onConsecutiveTaps(eq(2));
 
-        tap(200, 25f, 25f);
-        inOrder.verify(mListener).onConsecutiveTaps(eq(3));
+    tap(200, 25f, 25f);
+    inOrder.verify(listener).onConsecutiveTaps(eq(3));
 
-        tap(300, 25f, 25f);
-        inOrder.verify(mListener).onConsecutiveTaps(eq(4));
-    }
+    tap(300, 25f, 25f);
+    inOrder.verify(listener).onConsecutiveTaps(eq(4));
+  }
 
-    @Test
-    public void onTouchEvent_tapOnDifferentLocation_shouldResetCounter() {
-        InOrder inOrder = inOrder(mListener);
+  @Test
+  public void onTouchEvent_tapOnDifferentLocation_shouldResetCounter() {
+    InOrder inOrder = inOrder(listener);
 
-        tap(0, 25f, 25f);
-        inOrder.verify(mListener).onConsecutiveTaps(eq(1));
+    tap(0, 25f, 25f);
+    inOrder.verify(listener).onConsecutiveTaps(eq(1));
 
-        tap(100, 25f, 25f);
-        inOrder.verify(mListener).onConsecutiveTaps(eq(2));
+    tap(100, 25f, 25f);
+    inOrder.verify(listener).onConsecutiveTaps(eq(2));
 
-        tap(200, 25f + mSlop * 2, 25f);
-        inOrder.verify(mListener).onConsecutiveTaps(eq(1));
+    tap(200, 25f + slop * 2, 25f);
+    inOrder.verify(listener).onConsecutiveTaps(eq(1));
 
-        tap(300, 25f + mSlop * 2, 25f);
-        inOrder.verify(mListener).onConsecutiveTaps(eq(2));
-    }
+    tap(300, 25f + slop * 2, 25f);
+    inOrder.verify(listener).onConsecutiveTaps(eq(2));
+  }
 
-    @Test
-    public void onTouchEvent_tapAfterTimeout_shouldResetCounter() {
-        InOrder inOrder = inOrder(mListener);
+  @Test
+  public void onTouchEvent_tapAfterTimeout_shouldResetCounter() {
+    InOrder inOrder = inOrder(listener);
 
-        tap(0, 25f, 25f);
-        inOrder.verify(mListener).onConsecutiveTaps(eq(1));
+    tap(0, 25f, 25f);
+    inOrder.verify(listener).onConsecutiveTaps(eq(1));
 
-        tap(100, 25f, 25f);
-        inOrder.verify(mListener).onConsecutiveTaps(eq(2));
+    tap(100, 25f, 25f);
+    inOrder.verify(listener).onConsecutiveTaps(eq(2));
 
-        tap(200 + mTapTimeout, 25f, 25f);
-        inOrder.verify(mListener).onConsecutiveTaps(eq(1));
+    tap(200 + tapTimeout, 25f, 25f);
+    inOrder.verify(listener).onConsecutiveTaps(eq(1));
 
-        tap(300 + mTapTimeout, 25f, 25f);
-        inOrder.verify(mListener).onConsecutiveTaps(eq(2));
-    }
+    tap(300 + tapTimeout, 25f, 25f);
+    inOrder.verify(listener).onConsecutiveTaps(eq(2));
+  }
 
-    private void tap(int timeMillis, float x, float y) {
-        mDetector.onTouchEvent(
-                MotionEvent.obtain(timeMillis, timeMillis, MotionEvent.ACTION_DOWN, x, y, 0));
-        mDetector.onTouchEvent(
-                MotionEvent.obtain(timeMillis, timeMillis + 10, MotionEvent.ACTION_UP, x, y, 0));
-    }
+  private void tap(int timeMillis, float x, float y) {
+    detector.onTouchEvent(
+        MotionEvent.obtain(timeMillis, timeMillis, MotionEvent.ACTION_DOWN, x, y, 0));
+    detector.onTouchEvent(
+        MotionEvent.obtain(timeMillis, timeMillis + 10, MotionEvent.ACTION_UP, x, y, 0));
+  }
 }
diff --git a/library/test/robotest/src/com/android/setupwizardlib/items/ButtonItemTest.java b/library/test/robotest/src/com/android/setupwizardlib/items/ButtonItemTest.java
index 40e5da8..b51e875 100644
--- a/library/test/robotest/src/com/android/setupwizardlib/items/ButtonItemTest.java
+++ b/library/test/robotest/src/com/android/setupwizardlib/items/ButtonItemTest.java
@@ -16,12 +16,8 @@
 
 package com.android.setupwizardlib.items;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 import static org.junit.Assert.fail;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.same;
@@ -31,152 +27,157 @@
 import static org.robolectric.RuntimeEnvironment.application;
 
 import android.content.Context;
-import android.text.TextUtils;
 import android.view.ContextThemeWrapper;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.Button;
 import android.widget.FrameLayout;
 import android.widget.LinearLayout;
-
 import com.android.setupwizardlib.R;
 import com.android.setupwizardlib.items.ButtonItem.OnClickListener;
-import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.annotation.Config;
 
-@RunWith(SuwLibRobolectricTestRunner.class)
-@Config(sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
+@RunWith(RobolectricTestRunner.class)
+@Config(sdk = {Config.OLDEST_SDK, Config.NEWEST_SDK})
 public class ButtonItemTest {
 
-    private ViewGroup mParent;
-    private Context mContext;
+  private ViewGroup parent;
+  private Context context;
 
-    @Before
-    public void setUp() {
-        mContext = new ContextThemeWrapper(application, R.style.SuwThemeGlif_Light);
-        mParent = new LinearLayout(mContext);
+  @Before
+  public void setUp() {
+    context = new ContextThemeWrapper(application, R.style.SuwThemeGlif_Light);
+    parent = new LinearLayout(context);
+  }
+
+  @Test
+  public void testDefaultItem() {
+    ButtonItem item = new ButtonItem();
+
+    assertThat(item.isEnabled()).named("enabled").isTrue();
+    assertThat(item.getCount()).named("count").isEqualTo(0);
+    assertThat(item.getLayoutResource()).named("layout resource").isEqualTo(0);
+    assertThat(item.getTheme()).named("theme").isEqualTo(R.style.SuwButtonItem);
+    assertThat(item.getText()).named("text").isNull();
+  }
+
+  @Test
+  public void testOnBindView() {
+    ButtonItem item = new ButtonItem();
+
+    try {
+      item.onBindView(new View(context));
+      fail("Calling onBindView on ButtonItem should throw UnsupportedOperationException");
+    } catch (UnsupportedOperationException e) {
+      // pass
     }
+  }
 
-    @Test
-    public void testDefaultItem() {
-        ButtonItem item = new ButtonItem();
+  @Test
+  public void testCreateButton() {
+    TestButtonItem item = new TestButtonItem();
+    final Button button = item.createButton(parent);
 
-        assertTrue("ButtonItem should be enabled by default", item.isEnabled());
-        assertEquals("ButtonItem should return count = 0", 0, item.getCount());
-        assertEquals("ButtonItem should return layout resource = 0", 0, item.getLayoutResource());
-        assertEquals("Default theme should be @style/SuwButtonItem", R.style.SuwButtonItem,
-                item.getTheme());
-        assertNull("Default text should be null", item.getText());
+    assertThat(button.isEnabled()).named("enabled").isTrue();
+    assertThat(button.getText().toString()).isEmpty();
+  }
+
+  @Test
+  public void testButtonItemSetsItsId() {
+    TestButtonItem item = new TestButtonItem();
+    final int id = 12345;
+    item.setId(id);
+
+    assertWithMessage("Button's id should be set")
+        .that(item.createButton(parent).getId())
+        .isEqualTo(id);
+  }
+
+  @Test
+  public void testCreateButtonTwice() {
+    TestButtonItem item = new TestButtonItem();
+    final Button button = item.createButton(parent);
+
+    FrameLayout frameLayout = new FrameLayout(context);
+    frameLayout.addView(button);
+
+    final Button button2 = item.createButton(parent);
+    assertWithMessage("createButton should be reused").that(button2).isSameAs(button);
+    assertWithMessage("Should be removed from parent after createButton")
+        .that(button2.getParent())
+        .isNull();
+  }
+
+  @Test
+  public void testSetEnabledTrue() {
+    TestButtonItem item = new TestButtonItem();
+    item.setEnabled(true);
+
+    final Button button = item.createButton(parent);
+    assertWithMessage("ButtonItem should be enabled").that(item.isEnabled()).isTrue();
+    assertWithMessage("Button should be enabled").that(button.isEnabled()).isTrue();
+  }
+
+  @Test
+  public void testSetEnabledFalse() {
+    TestButtonItem item = new TestButtonItem();
+    item.setEnabled(false);
+
+    final Button button = item.createButton(parent);
+    assertWithMessage("ButtonItem should be disabled").that(item.isEnabled()).isFalse();
+    assertWithMessage("Button should be disabled").that(button.isEnabled()).isFalse();
+  }
+
+  @Test
+  public void testSetText() {
+    TestButtonItem item = new TestButtonItem();
+    item.setText("lorem ipsum");
+
+    final Button button = item.createButton(parent);
+    assertWithMessage("ButtonItem text should be \"lorem ipsum\"")
+        .that(item.getText().toString())
+        .isEqualTo("lorem ipsum");
+    assertWithMessage("Button text should be \"lorem ipsum\"")
+        .that(button.getText().toString())
+        .isEqualTo("lorem ipsum");
+  }
+
+  @Test
+  public void testSetTheme() {
+    TestButtonItem item = new TestButtonItem();
+    item.setTheme(R.style.SuwButtonItem_Colored);
+
+    final Button button = item.createButton(parent);
+    assertWithMessage("ButtonItem theme should be SuwButtonItem.Colored")
+        .that(item.getTheme())
+        .isEqualTo(R.style.SuwButtonItem_Colored);
+    assertThat(button.getContext().getTheme()).isNotNull();
+  }
+
+  @Test
+  public void testOnClickListener() {
+    TestButtonItem item = new TestButtonItem();
+    final OnClickListener listener = mock(OnClickListener.class);
+    item.setOnClickListener(listener);
+
+    verify(listener, never()).onClick(any(ButtonItem.class));
+
+    final Button button = item.createButton(parent);
+    button.performClick();
+
+    verify(listener).onClick(same(item));
+  }
+
+  private static class TestButtonItem extends ButtonItem {
+
+    @Override
+    public Button createButton(ViewGroup parent) {
+      // Make this method public for testing
+      return super.createButton(parent);
     }
-
-    @Test
-    public void testOnBindView() {
-        ButtonItem item = new ButtonItem();
-
-        try {
-            item.onBindView(new View(mContext));
-            fail("Calling onBindView on ButtonItem should throw UnsupportedOperationException");
-        } catch (UnsupportedOperationException e) {
-            // pass
-        }
-    }
-
-    @Test
-    public void testCreateButton() {
-        TestButtonItem item = new TestButtonItem();
-        final Button button = item.createButton(mParent);
-
-        assertTrue("Default button should be enabled", button.isEnabled());
-        assertTrue("Default button text should be empty", TextUtils.isEmpty(button.getText()));
-    }
-
-    @Test
-    public void testButtonItemSetsItsId() {
-        TestButtonItem item = new TestButtonItem();
-        final int id = 12345;
-        item.setId(id);
-
-        assertEquals("Button's id should be set", item.createButton(mParent).getId(), id);
-    }
-
-    @Test
-    public void testCreateButtonTwice() {
-        TestButtonItem item = new TestButtonItem();
-        final Button button = item.createButton(mParent);
-
-        FrameLayout frameLayout = new FrameLayout(mContext);
-        frameLayout.addView(button);
-
-        final Button button2 = item.createButton(mParent);
-        assertSame("createButton should be reused", button, button2);
-        assertNull("Should be removed from parent after createButton", button2.getParent());
-    }
-
-    @Test
-    public void testSetEnabledTrue() {
-        TestButtonItem item = new TestButtonItem();
-        item.setEnabled(true);
-
-        final Button button = item.createButton(mParent);
-        assertTrue("ButtonItem should be enabled", item.isEnabled());
-        assertTrue("Button should be enabled", button.isEnabled());
-    }
-
-    @Test
-    public void testSetEnabledFalse() {
-        TestButtonItem item = new TestButtonItem();
-        item.setEnabled(false);
-
-        final Button button = item.createButton(mParent);
-        assertFalse("ButtonItem should be disabled", item.isEnabled());
-        assertFalse("Button should be disabled", button.isEnabled());
-    }
-
-    @Test
-    public void testSetText() {
-        TestButtonItem item = new TestButtonItem();
-        item.setText("lorem ipsum");
-
-        final Button button = item.createButton(mParent);
-        assertEquals("ButtonItem text should be \"lorem ipsum\"", "lorem ipsum", item.getText());
-        assertEquals("Button text should be \"lorem ipsum\"", "lorem ipsum", button.getText());
-    }
-
-    @Test
-    public void testSetTheme() {
-        TestButtonItem item = new TestButtonItem();
-        item.setTheme(R.style.SuwButtonItem_Colored);
-
-        final Button button = item.createButton(mParent);
-        assertEquals("ButtonItem theme should be SuwButtonItem.Colored",
-                R.style.SuwButtonItem_Colored, item.getTheme());
-        assertNotNull(button.getContext().getTheme());
-    }
-
-    @Test
-    public void testOnClickListener() {
-        TestButtonItem item = new TestButtonItem();
-        final OnClickListener listener = mock(OnClickListener.class);
-        item.setOnClickListener(listener);
-
-        verify(listener, never()).onClick(any(ButtonItem.class));
-
-        final Button button = item.createButton(mParent);
-        button.performClick();
-
-        verify(listener).onClick(same(item));
-    }
-
-    private static class TestButtonItem extends ButtonItem {
-
-        @Override
-        public Button createButton(ViewGroup parent) {
-            // Make this method public for testing
-            return super.createButton(parent);
-        }
-    }
+  }
 }
diff --git a/library/test/robotest/src/com/android/setupwizardlib/items/ItemGroupTest.java b/library/test/robotest/src/com/android/setupwizardlib/items/ItemGroupTest.java
index ecaec71..3cbc576 100644
--- a/library/test/robotest/src/com/android/setupwizardlib/items/ItemGroupTest.java
+++ b/library/test/robotest/src/com/android/setupwizardlib/items/ItemGroupTest.java
@@ -16,289 +16,308 @@
 
 package com.android.setupwizardlib.items;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
+import static com.google.common.truth.Truth.assertWithMessage;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 
-import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.annotation.Config;
 
-@RunWith(SuwLibRobolectricTestRunner.class)
-@Config(sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
+@RunWith(RobolectricTestRunner.class)
+@Config(sdk = {Config.OLDEST_SDK, Config.NEWEST_SDK})
 public class ItemGroupTest {
 
-    private static final Item CHILD_1 = new EqualsItem("Child 1");
-    private static final Item CHILD_2 = new EqualsItem("Child 2");
-    private static final Item CHILD_3 = new EqualsItem("Child 3");
-    private static final Item CHILD_4 = new EqualsItem("Child 4");
+  private static final Item CHILD_1 = new EqualsItem("Child 1");
+  private static final Item CHILD_2 = new EqualsItem("Child 2");
+  private static final Item CHILD_3 = new EqualsItem("Child 3");
+  private static final Item CHILD_4 = new EqualsItem("Child 4");
 
-    private ItemGroup mItemGroup;
+  private ItemGroup itemGroup;
 
-    @Mock
-    private ItemHierarchy.Observer mObserver;
+  @Mock private ItemHierarchy.Observer observer;
 
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mItemGroup = new ItemGroup();
-        mItemGroup.registerObserver(mObserver);
+  @Before
+  public void setUp() {
+    MockitoAnnotations.initMocks(this);
+    itemGroup = new ItemGroup();
+    itemGroup.registerObserver(observer);
+  }
+
+  @Test
+  public void testGroup() {
+    itemGroup.addChild(CHILD_1);
+    itemGroup.addChild(CHILD_2);
+
+    assertWithMessage("Item at position 0 should be child1")
+        .that(itemGroup.getItemAt(0))
+        .isSameAs(CHILD_1);
+    assertWithMessage("Item at position 1 should be child2")
+        .that(itemGroup.getItemAt(1))
+        .isSameAs(CHILD_2);
+    assertWithMessage("Should have 2 children").that(itemGroup.getCount()).isEqualTo(2);
+
+    final InOrder inOrder = inOrder(observer);
+    inOrder.verify(observer).onItemRangeInserted(eq(itemGroup), eq(0), eq(1));
+    inOrder.verify(observer).onItemRangeInserted(eq(itemGroup), eq(1), eq(1));
+  }
+
+  @Test
+  public void testRemoveChild() {
+    itemGroup.addChild(CHILD_1);
+    itemGroup.addChild(CHILD_2);
+    itemGroup.addChild(CHILD_3);
+
+    itemGroup.removeChild(CHILD_2);
+
+    assertWithMessage("Item at position 0 should be child1")
+        .that(itemGroup.getItemAt(0))
+        .isSameAs(CHILD_1);
+    assertWithMessage("Item at position 1 should be child3")
+        .that(itemGroup.getItemAt(1))
+        .isSameAs(CHILD_3);
+    assertWithMessage("Should have 2 children").that(itemGroup.getCount()).isEqualTo(2);
+
+    verify(observer).onItemRangeRemoved(eq(itemGroup), eq(1), eq(1));
+  }
+
+  @Test
+  public void testClear() {
+    itemGroup.addChild(CHILD_1);
+    itemGroup.addChild(CHILD_2);
+
+    itemGroup.clear();
+
+    assertWithMessage("Should have 0 child").that(itemGroup.getCount()).isEqualTo(0);
+
+    verify(observer).onItemRangeRemoved(eq(itemGroup), eq(0), eq(2));
+  }
+
+  @Test
+  public void testNestedGroup() {
+    ItemGroup parentGroup = new ItemGroup();
+    ItemGroup childGroup = new ItemGroup();
+    parentGroup.registerObserver(observer);
+
+    parentGroup.addChild(CHILD_1);
+    childGroup.addChild(CHILD_2);
+    childGroup.addChild(CHILD_3);
+    parentGroup.addChild(childGroup);
+    parentGroup.addChild(CHILD_4);
+
+    assertWithMessage("Position 0 should be child 1")
+        .that(parentGroup.getItemAt(0))
+        .isSameAs(CHILD_1);
+    assertWithMessage("Position 1 should be child 2")
+        .that(parentGroup.getItemAt(1))
+        .isSameAs(CHILD_2);
+    assertWithMessage("Position 2 should be child 3")
+        .that(parentGroup.getItemAt(2))
+        .isSameAs(CHILD_3);
+    assertWithMessage("Position 3 should be child 4")
+        .that(parentGroup.getItemAt(3))
+        .isSameAs(CHILD_4);
+
+    final InOrder inOrder = inOrder(observer);
+    inOrder.verify(observer).onItemRangeInserted(eq(parentGroup), eq(0), eq(1));
+    inOrder.verify(observer).onItemRangeInserted(eq(parentGroup), eq(1), eq(2));
+    inOrder.verify(observer).onItemRangeInserted(eq(parentGroup), eq(3), eq(1));
+    verifyNoMoreInteractions(observer);
+  }
+
+  @Test
+  public void testNestedGroupClearNotification() {
+    ItemGroup parentGroup = new ItemGroup();
+    ItemGroup childGroup = new ItemGroup();
+    parentGroup.registerObserver(observer);
+
+    parentGroup.addChild(CHILD_1);
+    childGroup.addChild(CHILD_2);
+    childGroup.addChild(CHILD_3);
+    parentGroup.addChild(childGroup);
+    parentGroup.addChild(CHILD_4);
+
+    childGroup.clear();
+
+    final InOrder inOrder = inOrder(observer);
+    inOrder.verify(observer).onItemRangeInserted(eq(parentGroup), eq(0), eq(1));
+    inOrder.verify(observer).onItemRangeInserted(eq(parentGroup), eq(1), eq(2));
+    inOrder.verify(observer).onItemRangeInserted(eq(parentGroup), eq(3), eq(1));
+    verify(observer).onItemRangeRemoved(eq(parentGroup), eq(1), eq(2));
+    verifyNoMoreInteractions(observer);
+  }
+
+  @Test
+  public void testNestedGroupRemoveNotification() {
+    ItemGroup parentGroup = new ItemGroup();
+    ItemGroup childGroup = new ItemGroup();
+    parentGroup.registerObserver(observer);
+
+    parentGroup.addChild(CHILD_1);
+    childGroup.addChild(CHILD_2);
+    childGroup.addChild(CHILD_3);
+    parentGroup.addChild(childGroup);
+    parentGroup.addChild(CHILD_4);
+
+    childGroup.removeChild(CHILD_3);
+    childGroup.removeChild(CHILD_2);
+
+    final InOrder inOrder = inOrder(observer);
+    inOrder.verify(observer).onItemRangeInserted(eq(parentGroup), eq(0), eq(1));
+    inOrder.verify(observer).onItemRangeInserted(eq(parentGroup), eq(1), eq(2));
+    inOrder.verify(observer).onItemRangeInserted(eq(parentGroup), eq(3), eq(1));
+    inOrder.verify(observer).onItemRangeRemoved(eq(parentGroup), eq(2), eq(1));
+    inOrder.verify(observer).onItemRangeRemoved(eq(parentGroup), eq(1), eq(1));
+    verifyNoMoreInteractions(observer);
+  }
+
+  @Test
+  public void testNestedGroupClear() {
+    ItemGroup parentGroup = new ItemGroup();
+    ItemGroup childGroup = new ItemGroup();
+    parentGroup.registerObserver(observer);
+
+    parentGroup.addChild(CHILD_1);
+    childGroup.addChild(CHILD_2);
+    childGroup.addChild(CHILD_3);
+    parentGroup.addChild(childGroup);
+
+    childGroup.clear();
+
+    final InOrder inOrder = inOrder(observer);
+    inOrder.verify(observer).onItemRangeInserted(eq(parentGroup), eq(0), eq(1));
+    inOrder.verify(observer).onItemRangeInserted(eq(parentGroup), eq(1), eq(2));
+    inOrder.verify(observer).onItemRangeRemoved(eq(parentGroup), eq(1), eq(2));
+    verifyNoMoreInteractions(observer);
+  }
+
+  @Test
+  public void testNestedGroupRemoveLastChild() {
+    ItemGroup parentGroup = new ItemGroup();
+    ItemGroup childGroup1 = new ItemGroup();
+    ItemGroup childGroup2 = new ItemGroup();
+    parentGroup.registerObserver(observer);
+
+    childGroup1.addChild(CHILD_1);
+    childGroup1.addChild(CHILD_2);
+    parentGroup.addChild(childGroup1);
+    childGroup2.addChild(CHILD_3);
+    childGroup2.addChild(CHILD_4);
+    parentGroup.addChild(childGroup2);
+
+    childGroup2.removeChild(CHILD_4);
+    childGroup2.removeChild(CHILD_3);
+
+    final InOrder inOrder = inOrder(observer);
+    inOrder.verify(observer).onItemRangeInserted(eq(parentGroup), eq(0), eq(2));
+    inOrder.verify(observer).onItemRangeInserted(eq(parentGroup), eq(2), eq(2));
+    inOrder.verify(observer).onItemRangeRemoved(eq(parentGroup), eq(3), eq(1));
+    inOrder.verify(observer).onItemRangeRemoved(eq(parentGroup), eq(2), eq(1));
+    verifyNoMoreInteractions(observer);
+  }
+
+  @Test
+  public void testNestedGroupClearOnlyChild() {
+    ItemGroup parentGroup = new ItemGroup();
+    ItemGroup childGroup = new ItemGroup();
+    parentGroup.registerObserver(observer);
+
+    childGroup.addChild(CHILD_1);
+    childGroup.addChild(CHILD_2);
+    parentGroup.addChild(childGroup);
+
+    childGroup.clear();
+
+    final InOrder inOrder = inOrder(observer);
+    inOrder.verify(observer).onItemRangeInserted(eq(parentGroup), eq(0), eq(2));
+    inOrder.verify(observer).onItemRangeRemoved(eq(parentGroup), eq(0), eq(2));
+    verifyNoMoreInteractions(observer);
+  }
+
+  @Test
+  public void testNotifyChange() {
+    itemGroup.addChild(CHILD_1);
+    itemGroup.addChild(CHILD_2);
+
+    CHILD_2.setTitle("Child 2 modified");
+
+    verify(observer).onItemRangeChanged(eq(itemGroup), eq(1), eq(1));
+  }
+
+  @Test
+  public void testEmptyChildGroup() {
+    ItemGroup parentGroup = new ItemGroup();
+    ItemGroup childGroup = new ItemGroup();
+
+    parentGroup.addChild(CHILD_1);
+    parentGroup.addChild(childGroup);
+    parentGroup.addChild(CHILD_2);
+
+    assertWithMessage("Position 0 should be child 1")
+        .that(parentGroup.getItemAt(0))
+        .isSameAs(CHILD_1);
+    assertWithMessage("Position 1 should be child 2")
+        .that(parentGroup.getItemAt(1))
+        .isSameAs(CHILD_2);
+  }
+
+  @Test
+  public void testFindItemById() {
+    CHILD_1.setId(12345);
+    CHILD_2.setId(23456);
+
+    itemGroup.addChild(CHILD_1);
+    itemGroup.addChild(CHILD_2);
+
+    assertWithMessage("Find item 23456 should return child 2")
+        .that(itemGroup.findItemById(23456))
+        .isSameAs(CHILD_2);
+  }
+
+  @Test
+  public void testFindItemByIdNotFound() {
+    CHILD_1.setId(12345);
+    CHILD_2.setId(23456);
+
+    itemGroup.addChild(CHILD_1);
+    itemGroup.addChild(CHILD_2);
+
+    assertWithMessage("ID not found should return null")
+        .that(itemGroup.findItemById(56789))
+        .isNull();
+  }
+
+  /**
+   * This class will always return true on {@link #equals(Object)}. Used to ensure that ItemGroup is
+   * using identity rather than equals(). Be sure to use assertSame rather than assertEquals when
+   * comparing items of this class.
+   */
+  private static class EqualsItem extends Item {
+
+    EqualsItem(String name) {
+      setTitle(name);
     }
 
-    @Test
-    public void testGroup() {
-        mItemGroup.addChild(CHILD_1);
-        mItemGroup.addChild(CHILD_2);
-
-        assertSame("Item at position 0 should be child1", CHILD_1, mItemGroup.getItemAt(0));
-        assertSame("Item at position 1 should be child2", CHILD_2, mItemGroup.getItemAt(1));
-        assertEquals("Should have 2 children", 2, mItemGroup.getCount());
-
-        final InOrder inOrder = inOrder(mObserver);
-        inOrder.verify(mObserver).onItemRangeInserted(eq(mItemGroup), eq(0), eq(1));
-        inOrder.verify(mObserver).onItemRangeInserted(eq(mItemGroup), eq(1), eq(1));
+    @Override
+    public int hashCode() {
+      return 1;
     }
 
-    @Test
-    public void testRemoveChild() {
-        mItemGroup.addChild(CHILD_1);
-        mItemGroup.addChild(CHILD_2);
-        mItemGroup.addChild(CHILD_3);
-
-        mItemGroup.removeChild(CHILD_2);
-
-        assertSame("Item at position 0 should be child1", CHILD_1, mItemGroup.getItemAt(0));
-        assertSame("Item at position 1 should be child3", CHILD_3, mItemGroup.getItemAt(1));
-        assertEquals("Should have 2 children", 2, mItemGroup.getCount());
-
-        verify(mObserver).onItemRangeRemoved(eq(mItemGroup), eq(1), eq(1));
+    @Override
+    public boolean equals(Object obj) {
+      return obj instanceof Item;
     }
 
-    @Test
-    public void testClear() {
-        mItemGroup.addChild(CHILD_1);
-        mItemGroup.addChild(CHILD_2);
-
-        mItemGroup.clear();
-
-        assertEquals("Should have 0 child", 0, mItemGroup.getCount());
-
-        verify(mObserver).onItemRangeRemoved(eq(mItemGroup), eq(0), eq(2));
+    @Override
+    public String toString() {
+      return "EqualsItem{title=" + getTitle() + "}";
     }
-
-    @Test
-    public void testNestedGroup() {
-        ItemGroup parentGroup = new ItemGroup();
-        ItemGroup childGroup = new ItemGroup();
-        parentGroup.registerObserver(mObserver);
-
-        parentGroup.addChild(CHILD_1);
-        childGroup.addChild(CHILD_2);
-        childGroup.addChild(CHILD_3);
-        parentGroup.addChild(childGroup);
-        parentGroup.addChild(CHILD_4);
-
-        assertSame("Position 0 should be child 1", CHILD_1, parentGroup.getItemAt(0));
-        assertSame("Position 1 should be child 2", CHILD_2, parentGroup.getItemAt(1));
-        assertSame("Position 2 should be child 3", CHILD_3, parentGroup.getItemAt(2));
-        assertSame("Position 3 should be child 4", CHILD_4, parentGroup.getItemAt(3));
-
-        final InOrder inOrder = inOrder(mObserver);
-        inOrder.verify(mObserver).onItemRangeInserted(eq(parentGroup), eq(0), eq(1));
-        inOrder.verify(mObserver).onItemRangeInserted(eq(parentGroup), eq(1), eq(2));
-        inOrder.verify(mObserver).onItemRangeInserted(eq(parentGroup), eq(3), eq(1));
-        verifyNoMoreInteractions(mObserver);
-    }
-
-    @Test
-    public void testNestedGroupClearNotification() {
-        ItemGroup parentGroup = new ItemGroup();
-        ItemGroup childGroup = new ItemGroup();
-        parentGroup.registerObserver(mObserver);
-
-        parentGroup.addChild(CHILD_1);
-        childGroup.addChild(CHILD_2);
-        childGroup.addChild(CHILD_3);
-        parentGroup.addChild(childGroup);
-        parentGroup.addChild(CHILD_4);
-
-        childGroup.clear();
-
-        final InOrder inOrder = inOrder(mObserver);
-        inOrder.verify(mObserver).onItemRangeInserted(eq(parentGroup), eq(0), eq(1));
-        inOrder.verify(mObserver).onItemRangeInserted(eq(parentGroup), eq(1), eq(2));
-        inOrder.verify(mObserver).onItemRangeInserted(eq(parentGroup), eq(3), eq(1));
-        verify(mObserver).onItemRangeRemoved(eq(parentGroup), eq(1), eq(2));
-        verifyNoMoreInteractions(mObserver);
-    }
-
-    @Test
-    public void testNestedGroupRemoveNotification() {
-        ItemGroup parentGroup = new ItemGroup();
-        ItemGroup childGroup = new ItemGroup();
-        parentGroup.registerObserver(mObserver);
-
-        parentGroup.addChild(CHILD_1);
-        childGroup.addChild(CHILD_2);
-        childGroup.addChild(CHILD_3);
-        parentGroup.addChild(childGroup);
-        parentGroup.addChild(CHILD_4);
-
-        childGroup.removeChild(CHILD_3);
-        childGroup.removeChild(CHILD_2);
-
-        final InOrder inOrder = inOrder(mObserver);
-        inOrder.verify(mObserver).onItemRangeInserted(eq(parentGroup), eq(0), eq(1));
-        inOrder.verify(mObserver).onItemRangeInserted(eq(parentGroup), eq(1), eq(2));
-        inOrder.verify(mObserver).onItemRangeInserted(eq(parentGroup), eq(3), eq(1));
-        inOrder.verify(mObserver).onItemRangeRemoved(eq(parentGroup), eq(2), eq(1));
-        inOrder.verify(mObserver).onItemRangeRemoved(eq(parentGroup), eq(1), eq(1));
-        verifyNoMoreInteractions(mObserver);
-    }
-
-    @Test
-    public void testNestedGroupClear() {
-        ItemGroup parentGroup = new ItemGroup();
-        ItemGroup childGroup = new ItemGroup();
-        parentGroup.registerObserver(mObserver);
-
-        parentGroup.addChild(CHILD_1);
-        childGroup.addChild(CHILD_2);
-        childGroup.addChild(CHILD_3);
-        parentGroup.addChild(childGroup);
-
-        childGroup.clear();
-
-        final InOrder inOrder = inOrder(mObserver);
-        inOrder.verify(mObserver).onItemRangeInserted(eq(parentGroup), eq(0), eq(1));
-        inOrder.verify(mObserver).onItemRangeInserted(eq(parentGroup), eq(1), eq(2));
-        inOrder.verify(mObserver).onItemRangeRemoved(eq(parentGroup), eq(1), eq(2));
-        verifyNoMoreInteractions(mObserver);
-    }
-
-    @Test
-    public void testNestedGroupRemoveLastChild() {
-        ItemGroup parentGroup = new ItemGroup();
-        ItemGroup childGroup1 = new ItemGroup();
-        ItemGroup childGroup2 = new ItemGroup();
-        parentGroup.registerObserver(mObserver);
-
-        childGroup1.addChild(CHILD_1);
-        childGroup1.addChild(CHILD_2);
-        parentGroup.addChild(childGroup1);
-        childGroup2.addChild(CHILD_3);
-        childGroup2.addChild(CHILD_4);
-        parentGroup.addChild(childGroup2);
-
-        childGroup2.removeChild(CHILD_4);
-        childGroup2.removeChild(CHILD_3);
-
-        final InOrder inOrder = inOrder(mObserver);
-        inOrder.verify(mObserver).onItemRangeInserted(eq(parentGroup), eq(0), eq(2));
-        inOrder.verify(mObserver).onItemRangeInserted(eq(parentGroup), eq(2), eq(2));
-        inOrder.verify(mObserver).onItemRangeRemoved(eq(parentGroup), eq(3), eq(1));
-        inOrder.verify(mObserver).onItemRangeRemoved(eq(parentGroup), eq(2), eq(1));
-        verifyNoMoreInteractions(mObserver);
-    }
-
-    @Test
-    public void testNestedGroupClearOnlyChild() {
-        ItemGroup parentGroup = new ItemGroup();
-        ItemGroup childGroup = new ItemGroup();
-        parentGroup.registerObserver(mObserver);
-
-        childGroup.addChild(CHILD_1);
-        childGroup.addChild(CHILD_2);
-        parentGroup.addChild(childGroup);
-
-        childGroup.clear();
-
-        final InOrder inOrder = inOrder(mObserver);
-        inOrder.verify(mObserver).onItemRangeInserted(eq(parentGroup), eq(0), eq(2));
-        inOrder.verify(mObserver).onItemRangeRemoved(eq(parentGroup), eq(0), eq(2));
-        verifyNoMoreInteractions(mObserver);
-    }
-
-    @Test
-    public void testNotifyChange() {
-        mItemGroup.addChild(CHILD_1);
-        mItemGroup.addChild(CHILD_2);
-
-        CHILD_2.setTitle("Child 2 modified");
-
-        verify(mObserver).onItemRangeChanged(eq(mItemGroup), eq(1), eq(1));
-    }
-
-    @Test
-    public void testEmptyChildGroup() {
-        ItemGroup parentGroup = new ItemGroup();
-        ItemGroup childGroup = new ItemGroup();
-
-        parentGroup.addChild(CHILD_1);
-        parentGroup.addChild(childGroup);
-        parentGroup.addChild(CHILD_2);
-
-        assertSame("Position 0 should be child 1", CHILD_1, parentGroup.getItemAt(0));
-        assertSame("Position 1 should be child 2", CHILD_2, parentGroup.getItemAt(1));
-    }
-
-    @Test
-    public void testFindItemById() {
-        CHILD_1.setId(12345);
-        CHILD_2.setId(23456);
-
-        mItemGroup.addChild(CHILD_1);
-        mItemGroup.addChild(CHILD_2);
-
-        assertSame("Find item 23456 should return child 2",
-                CHILD_2, mItemGroup.findItemById(23456));
-    }
-
-    @Test
-    public void testFindItemByIdNotFound() {
-        CHILD_1.setId(12345);
-        CHILD_2.setId(23456);
-
-        mItemGroup.addChild(CHILD_1);
-        mItemGroup.addChild(CHILD_2);
-
-        assertNull("ID not found should return null", mItemGroup.findItemById(56789));
-    }
-
-    /**
-     * This class will always return true on {@link #equals(Object)}. Used to ensure that ItemGroup
-     * is using identity rather than equals(). Be sure to use assertSame rather than assertEquals
-     * when comparing items of this class.
-     */
-    private static class EqualsItem extends Item {
-
-        EqualsItem(String name) {
-            setTitle(name);
-        }
-
-        @Override
-        public int hashCode() {
-            return 1;
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            return obj instanceof Item;
-        }
-
-        @Override
-        public String toString() {
-            return "EqualsItem{title=" + getTitle() + "}";
-        }
-    }
+  }
 }
diff --git a/library/test/robotest/src/com/android/setupwizardlib/robolectric/ExternalResources.java b/library/test/robotest/src/com/android/setupwizardlib/robolectric/ExternalResources.java
new file mode 100644
index 0000000..06ef508
--- /dev/null
+++ b/library/test/robotest/src/com/android/setupwizardlib/robolectric/ExternalResources.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.setupwizardlib.robolectric;
+
+import static org.robolectric.RuntimeEnvironment.application;
+import static org.robolectric.Shadows.shadowOf;
+
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.AssetManager;
+import android.content.res.Configuration;
+import androidx.annotation.AnyRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import android.util.DisplayMetrics;
+import java.util.HashMap;
+import java.util.Map;
+import org.robolectric.res.ResName;
+import org.robolectric.res.ResType;
+import org.robolectric.res.TypedResource;
+import org.robolectric.shadows.ShadowPackageManager;
+
+/**
+ * Utility class to inject resources for an "external" application in Robolectric tests. This can be
+ * used with {@link org.robolectric.shadows.ShadowPackageManager#resources} to simulate loading
+ * resources from another package.
+ */
+public final class ExternalResources {
+
+  public static Resources injectExternalResources(String packageName) {
+    return injectExternalResources(createPackageInfo(packageName));
+  }
+
+  public static Resources injectExternalResources(PackageInfo packageInfo) {
+    try {
+      application.getPackageManager().getPackageInfo(packageInfo.packageName, 0);
+    } catch (NameNotFoundException e) {
+      // Add the package if it does not exist
+      shadowOf(application.getPackageManager()).addPackage(packageInfo);
+    }
+    Resources resources = Resources.forPackageName(packageInfo.packageName);
+    ShadowPackageManager.resources.put(packageInfo.packageName, resources);
+    return resources;
+  }
+
+  /**
+   * Constructed resources for testing, representing resources external to the current package under
+   * test.
+   */
+  public static class Resources extends android.content.res.Resources {
+
+    private final String packageName;
+
+    public static Resources forPackageName(String packageName) {
+      android.content.res.Resources res = application.getResources();
+      return new Resources(
+          packageName, res.getAssets(), res.getDisplayMetrics(), res.getConfiguration());
+    }
+
+    private Resources(
+        String packageName, AssetManager assets, DisplayMetrics metrics, Configuration config) {
+      super(assets, metrics, config);
+      this.packageName = packageName;
+    }
+
+    @Override
+    public int getIdentifier(String name, String defType, String defPackage) {
+      Integer resourceId = resourceIds.get(ResName.qualifyResName(name, defPackage, defType));
+      if (resourceId == null) {
+        return 0;
+      }
+      return resourceId;
+    }
+
+    @Override
+    public int getInteger(int id) {
+      return (int) get(id, ResType.INTEGER);
+    }
+
+    public void putInteger(String name, int value) {
+      put(
+          ResName.qualifyResName(name, packageName, "integer"),
+          new TypedResource<>(value, ResType.INTEGER, null));
+    }
+
+    @Override
+    public int getColor(int id) {
+      return (int) get(id, ResType.COLOR);
+    }
+
+    @Override
+    public int getColor(int id, @Nullable Theme theme) {
+      return (int) get(id, ResType.COLOR);
+    }
+
+    public void putColor(String name, int value) {
+      put(
+          ResName.qualifyResName(name, packageName, "color"),
+          new TypedResource<>(value, ResType.COLOR, null));
+    }
+
+    @NonNull
+    @Override
+    public CharSequence getText(int id) {
+      return (CharSequence) get(id, ResType.CHAR_SEQUENCE);
+    }
+
+    @NonNull
+    @Override
+    public String getString(int id) {
+      return get(id, ResType.CHAR_SEQUENCE).toString();
+    }
+
+    public void putText(String name, CharSequence value) {
+      put(
+          ResName.qualifyResName(name, packageName, "string"),
+          new TypedResource<>(value, ResType.CHAR_SEQUENCE, null));
+    }
+
+    private final Map<Integer, TypedResource<?>> overrideResources = new HashMap<>();
+    private final Map<ResName, Integer> resourceIds = new HashMap<>();
+    private int nextId = 1;
+
+    private <T> void put(ResName resName, TypedResource<T> value) {
+      int id = nextId++;
+      overrideResources.put(id, value);
+      resourceIds.put(resName, id);
+    }
+
+    private Object get(@AnyRes int id, ResType type) {
+      TypedResource<?> override = overrideResources.get(id);
+      if (override != null && override.getResType() == type) {
+        return override.getData();
+      }
+      throw new NotFoundException();
+    }
+  }
+
+  private static PackageInfo createPackageInfo(String packageName) {
+    PackageInfo packageInfo = new PackageInfo();
+    packageInfo.packageName = packageName;
+    return packageInfo;
+  }
+
+  private ExternalResources() {}
+}
diff --git a/library/test/robotest/src/com/android/setupwizardlib/robolectric/SuwLibRobolectricTestRunner.java b/library/test/robotest/src/com/android/setupwizardlib/robolectric/SuwLibRobolectricTestRunner.java
deleted file mode 100644
index 61baa23..0000000
--- a/library/test/robotest/src/com/android/setupwizardlib/robolectric/SuwLibRobolectricTestRunner.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.setupwizardlib.robolectric;
-
-import org.junit.runner.notification.RunNotifier;
-import org.junit.runners.model.FrameworkMethod;
-import org.junit.runners.model.InitializationError;
-import org.robolectric.RobolectricTestRunner;
-
-public class SuwLibRobolectricTestRunner extends RobolectricTestRunner {
-
-    public SuwLibRobolectricTestRunner(Class<?> testClass) throws InitializationError {
-        super(testClass);
-    }
-
-    @Override
-    protected void runChild(FrameworkMethod method, RunNotifier notifier) {
-        System.out.println("===== Running " + method + " =====");
-        super.runChild(method, notifier);
-    }
-}
diff --git a/library/test/robotest/src/com/android/setupwizardlib/shadow/ShadowLog.java b/library/test/robotest/src/com/android/setupwizardlib/shadow/ShadowLog.java
deleted file mode 100644
index f1d37c8..0000000
--- a/library/test/robotest/src/com/android/setupwizardlib/shadow/ShadowLog.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.setupwizardlib.shadow;
-
-import android.util.Log;
-
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-
-@Implements(Log.class)
-public class ShadowLog extends org.robolectric.shadows.ShadowLog {
-
-    public static boolean sWtfIsFatal = true;
-
-    public static class TerribleFailure extends RuntimeException {
-
-        public TerribleFailure(String msg, Throwable cause) {
-            super(msg, cause);
-        }
-    }
-
-    @Implementation
-    public static void wtf(String tag, String msg) {
-        org.robolectric.shadows.ShadowLog.wtf(tag, msg);
-        if (sWtfIsFatal) {
-            throw new TerribleFailure(msg, null);
-        }
-    }
-
-    @Implementation
-    public static void wtf(String tag, String msg, Throwable throwable) {
-        org.robolectric.shadows.ShadowLog.wtf(tag, msg, throwable);
-        if (sWtfIsFatal) {
-            throw new TerribleFailure(msg, throwable);
-        }
-    }
-}
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 3aafa7d..3bc9fd1 100644
--- a/library/test/robotest/src/com/android/setupwizardlib/span/LinkSpanTest.java
+++ b/library/test/robotest/src/com/android/setupwizardlib/span/LinkSpanTest.java
@@ -17,8 +17,7 @@
 package com.android.setupwizardlib.span;
 
 import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assert.assertSame;
+import static com.google.common.truth.Truth.assertWithMessage;
 import static org.robolectric.RuntimeEnvironment.application;
 
 import android.content.Context;
@@ -27,82 +26,83 @@
 import android.text.SpannableStringBuilder;
 import android.text.method.LinkMovementMethod;
 import android.widget.TextView;
-
-import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 
-@RunWith(SuwLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 public class LinkSpanTest {
 
-    @Test
-    public void onClick_shouldCallListenerOnContext() {
-        final TestContext context = new TestContext(application);
-        final TextView textView = new TextView(context);
-        final LinkSpan linkSpan = new LinkSpan("test_id");
+  @Test
+  public void onClick_shouldCallListenerOnContext() {
+    final TestContext context = new TestContext(application);
+    final TextView textView = new TextView(context);
+    final LinkSpan linkSpan = new LinkSpan("test_id");
 
-        linkSpan.onClick(textView);
+    linkSpan.onClick(textView);
 
-        assertSame("Clicked LinkSpan should be passed to setup", linkSpan, context.clickedSpan);
+    assertWithMessage("Clicked LinkSpan should be passed to setup")
+        .that(context.clickedSpan)
+        .isSameAs(linkSpan);
+  }
+
+  @Test
+  public void onClick_contextDoesNotImplementOnClickListener_shouldBeNoOp() {
+    final TextView textView = new TextView(application);
+    final LinkSpan linkSpan = new LinkSpan("test_id");
+
+    linkSpan.onClick(textView);
+
+    // This would be no-op, because the context doesn't implement LinkSpan.OnClickListener.
+    // Just check that no uncaught exception here.
+  }
+
+  @Test
+  public void onClick_contextWrapsOnClickListener_shouldCallWrappedListener() {
+    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);
+    assertWithMessage("Clicked LinkSpan should be passed to setup")
+        .that(context.clickedSpan)
+        .isSameAs(linkSpan);
+  }
+
+  @Test
+  public void onClick_shouldClearSelection() {
+    final TestContext context = new TestContext(application);
+    final TextView textView = new TextView(context);
+    textView.setMovementMethod(LinkMovementMethod.getInstance());
+    textView.setFocusable(true);
+    textView.setFocusableInTouchMode(true);
+    final LinkSpan linkSpan = new LinkSpan("test_id");
+
+    SpannableStringBuilder text = new SpannableStringBuilder("Lorem ipsum dolor sit");
+    textView.setText(text);
+    text.setSpan(linkSpan, /* start= */ 0, /* end= */ 5, /* flags= */ 0);
+    // Simulate the touch effect set by TextView when touched.
+    Selection.setSelection(text, /* start= */ 0, /* stop= */ 5);
+
+    linkSpan.onClick(textView);
+
+    assertThat(Selection.getSelectionStart(textView.getText())).isEqualTo(0);
+    assertThat(Selection.getSelectionEnd(textView.getText())).isEqualTo(0);
+  }
+
+  @SuppressWarnings("deprecation")
+  private static class TestContext extends ContextWrapper implements LinkSpan.OnClickListener {
+
+    public LinkSpan clickedSpan = null;
+
+    TestContext(Context base) {
+      super(base);
     }
 
-    @Test
-    public void onClick_contextDoesNotImplementOnClickListener_shouldBeNoOp() {
-        final TextView textView = new TextView(application);
-        final LinkSpan linkSpan = new LinkSpan("test_id");
-
-        linkSpan.onClick(textView);
-
-        // This would be no-op, because the context doesn't implement LinkSpan.OnClickListener.
-        // Just check that no uncaught exception here.
+    @Override
+    public void onClick(LinkSpan span) {
+      clickedSpan = span;
     }
-
-    @Test
-    public void onClick_contextWrapsOnClickListener_shouldCallWrappedListener() {
-        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);
-    }
-
-    @Test
-    public void onClick_shouldClearSelection() {
-        final TestContext context = new TestContext(application);
-        final TextView textView = new TextView(context);
-        textView.setMovementMethod(LinkMovementMethod.getInstance());
-        textView.setFocusable(true);
-        textView.setFocusableInTouchMode(true);
-        final LinkSpan linkSpan = new LinkSpan("test_id");
-
-        SpannableStringBuilder text = new SpannableStringBuilder("Lorem ipsum dolor sit");
-        textView.setText(text);
-        text.setSpan(linkSpan, /* start= */ 0, /* end= */ 5, /* flags= */ 0);
-        // Simulate the touch effect set by TextView when touched.
-        Selection.setSelection(text, /* start= */ 0, /* end= */ 5);
-
-        linkSpan.onClick(textView);
-
-        assertThat(Selection.getSelectionStart(textView.getText())).isEqualTo(0);
-        assertThat(Selection.getSelectionEnd(textView.getText())).isEqualTo(0);
-    }
-
-    @SuppressWarnings("deprecation")
-    private static class TestContext extends ContextWrapper implements LinkSpan.OnClickListener {
-
-        public LinkSpan clickedSpan = null;
-
-        TestContext(Context base) {
-            super(base);
-        }
-
-        @Override
-        public void onClick(LinkSpan span) {
-            clickedSpan = span;
-        }
-    }
+  }
 }
diff --git a/library/test/robotest/src/com/android/setupwizardlib/template/ListViewScrollHandlingDelegateTest.java b/library/test/robotest/src/com/android/setupwizardlib/template/ListViewScrollHandlingDelegateTest.java
index ec3622d..9b99a68 100644
--- a/library/test/robotest/src/com/android/setupwizardlib/template/ListViewScrollHandlingDelegateTest.java
+++ b/library/test/robotest/src/com/android/setupwizardlib/template/ListViewScrollHandlingDelegateTest.java
@@ -16,105 +16,93 @@
 
 package com.android.setupwizardlib.template;
 
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.spy;
+import static com.google.common.truth.Truth.assertThat;
 import static org.mockito.Mockito.verify;
 import static org.robolectric.RuntimeEnvironment.application;
 
 import android.content.Context;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.AbsListView.OnScrollListener;
 import android.widget.BaseAdapter;
 import android.widget.ListView;
-
-import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.Shadows;
 import org.robolectric.annotation.Config;
 
-@Config(sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
-@RunWith(SuwLibRobolectricTestRunner.class)
+@Config(sdk = {Config.OLDEST_SDK, Config.NEWEST_SDK})
+@RunWith(RobolectricTestRunner.class)
 public class ListViewScrollHandlingDelegateTest {
 
-    @Mock
-    private RequireScrollMixin mRequireScrollMixin;
+  @Mock private RequireScrollMixin requireScrollMixin;
 
-    private ListView mListView;
-    private ListViewScrollHandlingDelegate mDelegate;
-    private ArgumentCaptor<OnScrollListener> mListenerCaptor;
+  private ListView listView;
+  private ListViewScrollHandlingDelegate delegate;
 
-    @Before
-    public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
+  @Before
+  public void setUp() throws Exception {
+    MockitoAnnotations.initMocks(this);
 
-        mListView = spy(new TestListView(application));
-        mDelegate = new ListViewScrollHandlingDelegate(mRequireScrollMixin, mListView);
+    listView = new TestListView(application);
+    delegate = new ListViewScrollHandlingDelegate(requireScrollMixin, listView);
 
-        mListenerCaptor = ArgumentCaptor.forClass(OnScrollListener.class);
-        doNothing().when(mListView).setOnScrollListener(mListenerCaptor.capture());
+    listView.layout(0, 0, 50, 50);
+  }
 
-        mListView.layout(0, 0, 50, 50);
+  @Test
+  public void testRequireScroll() throws Throwable {
+    delegate.startListening();
+
+    verify(requireScrollMixin).notifyScrollabilityChange(true);
+  }
+
+  @Test
+  public void testScrolledToBottom() throws Throwable {
+    delegate.startListening();
+
+    verify(requireScrollMixin).notifyScrollabilityChange(true);
+
+    Shadows.shadowOf(listView).getOnScrollListener().onScroll(listView, 2, 20, 20);
+
+    verify(requireScrollMixin).notifyScrollabilityChange(false);
+  }
+
+  @Test
+  public void testPageScrollDown() throws Throwable {
+    delegate.pageScrollDown();
+    assertThat(Shadows.shadowOf(listView).getLastSmoothScrollByDistance()).isEqualTo(50);
+  }
+
+  private static class TestListView extends ListView {
+
+    TestListView(Context context) {
+      super(context);
+      setAdapter(
+          new BaseAdapter() {
+            @Override
+            public int getCount() {
+              return 20;
+            }
+
+            @Override
+            public Object getItem(int position) {
+              return null;
+            }
+
+            @Override
+            public long getItemId(int position) {
+              return position;
+            }
+
+            @Override
+            public View getView(int position, View convertView, ViewGroup parent) {
+              return new View(parent.getContext());
+            }
+          });
     }
-
-    @Test
-    public void testRequireScroll() throws Throwable {
-        mDelegate.startListening();
-
-        verify(mRequireScrollMixin).notifyScrollabilityChange(true);
-    }
-
-    @Test
-    public void testScrolledToBottom() throws Throwable {
-        mDelegate.startListening();
-
-        verify(mRequireScrollMixin).notifyScrollabilityChange(true);
-
-        doReturn(20).when(mListView).getLastVisiblePosition();
-        mListenerCaptor.getValue().onScroll(mListView, 2, 20, 20);
-
-        verify(mRequireScrollMixin).notifyScrollabilityChange(false);
-    }
-
-    @Test
-    public void testPageScrollDown() throws Throwable {
-        mDelegate.pageScrollDown();
-        verify(mListView).smoothScrollBy(eq(50), anyInt());
-    }
-
-    private static class TestListView extends ListView {
-
-        TestListView(Context context) {
-            super(context);
-            setAdapter(new BaseAdapter() {
-                @Override
-                public int getCount() {
-                    return 20;
-                }
-
-                @Override
-                public Object getItem(int position) {
-                    return null;
-                }
-
-                @Override
-                public long getItemId(int position) {
-                    return position;
-                }
-
-                @Override
-                public View getView(int position, View convertView, ViewGroup parent) {
-                    return new View(parent.getContext());
-                }
-            });
-        }
-    }
+  }
 }
diff --git a/library/test/robotest/src/com/android/setupwizardlib/template/RequireScrollMixinTest.java b/library/test/robotest/src/com/android/setupwizardlib/template/RequireScrollMixinTest.java
index c641449..fe45d5f 100644
--- a/library/test/robotest/src/com/android/setupwizardlib/template/RequireScrollMixinTest.java
+++ b/library/test/robotest/src/com/android/setupwizardlib/template/RequireScrollMixinTest.java
@@ -16,13 +16,10 @@
 
 package com.android.setupwizardlib.template;
 
-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 com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
@@ -33,135 +30,138 @@
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.widget.Button;
-
+import com.android.setupwizardlib.GlifLayout;
 import com.android.setupwizardlib.TemplateLayout;
-import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
 import com.android.setupwizardlib.template.RequireScrollMixin.OnRequireScrollStateChangedListener;
 import com.android.setupwizardlib.template.RequireScrollMixin.ScrollHandlingDelegate;
 import com.android.setupwizardlib.view.NavigationBar;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.annotation.Config;
 
-@Config(sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
-@RunWith(SuwLibRobolectricTestRunner.class)
+@Config(sdk = {Config.OLDEST_SDK, Config.NEWEST_SDK})
+@RunWith(RobolectricTestRunner.class)
 public class RequireScrollMixinTest {
 
-    @Mock
-    private TemplateLayout mTemplateLayout;
+  @Mock private ScrollHandlingDelegate delegate;
 
-    @Mock
-    private ScrollHandlingDelegate mDelegate;
+  private RequireScrollMixin requireScrollMixin;
 
-    private RequireScrollMixin mRequireScrollMixin;
+  @Before
+  public void setUp() {
+    MockitoAnnotations.initMocks(this);
 
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
+    TemplateLayout templateLayout = new GlifLayout(application);
+    requireScrollMixin = new RequireScrollMixin(templateLayout);
+    requireScrollMixin.setScrollHandlingDelegate(delegate);
+  }
 
-        doReturn(application).when(mTemplateLayout).getContext();
-        mRequireScrollMixin = new RequireScrollMixin(mTemplateLayout);
-        mRequireScrollMixin.setScrollHandlingDelegate(mDelegate);
-    }
+  @Test
+  public void testRequireScroll() {
+    requireScrollMixin.requireScroll();
 
-    @Test
-    public void testRequireScroll() {
-        mRequireScrollMixin.requireScroll();
+    verify(delegate).startListening();
+  }
 
-        verify(mDelegate).startListening();
-    }
+  @Test
+  public void testScrollStateChangedListener() {
+    OnRequireScrollStateChangedListener listener = mock(OnRequireScrollStateChangedListener.class);
+    requireScrollMixin.setOnRequireScrollStateChangedListener(listener);
+    assertWithMessage("Scrolling should not be required initially")
+        .that(requireScrollMixin.isScrollingRequired())
+        .isFalse();
 
-    @Test
-    public void testScrollStateChangedListener() {
-        OnRequireScrollStateChangedListener listener =
-                mock(OnRequireScrollStateChangedListener.class);
-        mRequireScrollMixin.setOnRequireScrollStateChangedListener(listener);
-        assertFalse("Scrolling should not be required initially",
-                mRequireScrollMixin.isScrollingRequired());
+    requireScrollMixin.notifyScrollabilityChange(true);
+    verify(listener).onRequireScrollStateChanged(true);
+    assertWithMessage("Scrolling should be required when there is more content below the fold")
+        .that(requireScrollMixin.isScrollingRequired())
+        .isTrue();
 
-        mRequireScrollMixin.notifyScrollabilityChange(true);
-        verify(listener).onRequireScrollStateChanged(true);
-        assertTrue("Scrolling should be required when there is more content below the fold",
-                mRequireScrollMixin.isScrollingRequired());
+    requireScrollMixin.notifyScrollabilityChange(false);
+    verify(listener).onRequireScrollStateChanged(false);
+    assertWithMessage("Scrolling should not be required after scrolling to bottom")
+        .that(requireScrollMixin.isScrollingRequired())
+        .isFalse();
 
-        mRequireScrollMixin.notifyScrollabilityChange(false);
-        verify(listener).onRequireScrollStateChanged(false);
-        assertFalse("Scrolling should not be required after scrolling to bottom",
-                mRequireScrollMixin.isScrollingRequired());
+    // Once the user has scrolled to the bottom, they should not be forced to scroll down again
+    requireScrollMixin.notifyScrollabilityChange(true);
+    verifyNoMoreInteractions(listener);
 
-        // Once the user has scrolled to the bottom, they should not be forced to scroll down again
-        mRequireScrollMixin.notifyScrollabilityChange(true);
-        verifyNoMoreInteractions(listener);
+    assertWithMessage("Scrolling should not be required after scrolling to bottom once")
+        .that(requireScrollMixin.isScrollingRequired())
+        .isFalse();
 
-        assertFalse("Scrolling should not be required after scrolling to bottom once",
-                mRequireScrollMixin.isScrollingRequired());
+    assertThat(requireScrollMixin.getOnRequireScrollStateChangedListener()).isSameAs(listener);
+  }
 
-        assertSame(listener, mRequireScrollMixin.getOnRequireScrollStateChangedListener());
-    }
+  @Test
+  public void testCreateOnClickListener() {
+    OnClickListener wrappedListener = mock(OnClickListener.class);
+    final OnClickListener onClickListener =
+        requireScrollMixin.createOnClickListener(wrappedListener);
 
-    @Test
-    public void testCreateOnClickListener() {
-        OnClickListener wrappedListener = mock(OnClickListener.class);
-        final OnClickListener onClickListener =
-                mRequireScrollMixin.createOnClickListener(wrappedListener);
+    requireScrollMixin.notifyScrollabilityChange(true);
+    onClickListener.onClick(null);
 
-        mRequireScrollMixin.notifyScrollabilityChange(true);
-        onClickListener.onClick(null);
+    verify(wrappedListener, never()).onClick(any(View.class));
+    verify(delegate).pageScrollDown();
 
-        verify(wrappedListener, never()).onClick(any(View.class));
-        verify(mDelegate).pageScrollDown();
+    requireScrollMixin.notifyScrollabilityChange(false);
+    onClickListener.onClick(null);
 
-        mRequireScrollMixin.notifyScrollabilityChange(false);
-        onClickListener.onClick(null);
+    verify(wrappedListener).onClick(any(View.class));
+  }
 
-        verify(wrappedListener).onClick(any(View.class));
-    }
+  @Test
+  public void testRequireScrollWithNavigationBar() {
+    final NavigationBar navigationBar = new NavigationBar(application);
+    requireScrollMixin.requireScrollWithNavigationBar(navigationBar);
 
-    @Test
-    public void testRequireScrollWithNavigationBar() {
-        final NavigationBar navigationBar = new NavigationBar(application);
-        mRequireScrollMixin.requireScrollWithNavigationBar(navigationBar);
+    requireScrollMixin.notifyScrollabilityChange(true);
+    assertWithMessage("More button should be visible")
+        .that(navigationBar.getMoreButton().getVisibility())
+        .isEqualTo(View.VISIBLE);
+    assertWithMessage("Next button should be hidden")
+        .that(navigationBar.getNextButton().getVisibility())
+        .isEqualTo(View.GONE);
 
-        mRequireScrollMixin.notifyScrollabilityChange(true);
-        assertEquals("More button should be visible",
-                View.VISIBLE, navigationBar.getMoreButton().getVisibility());
-        assertEquals("Next button should be hidden",
-                View.GONE, navigationBar.getNextButton().getVisibility());
+    navigationBar.getMoreButton().performClick();
+    verify(delegate).pageScrollDown();
 
-        navigationBar.getMoreButton().performClick();
-        verify(mDelegate).pageScrollDown();
+    requireScrollMixin.notifyScrollabilityChange(false);
+    assertWithMessage("More button should be hidden")
+        .that(navigationBar.getMoreButton().getVisibility())
+        .isEqualTo(View.GONE);
+    assertWithMessage("Next button should be visible")
+        .that(navigationBar.getNextButton().getVisibility())
+        .isEqualTo(View.VISIBLE);
+  }
 
-        mRequireScrollMixin.notifyScrollabilityChange(false);
-        assertEquals("More button should be hidden",
-                View.GONE, navigationBar.getMoreButton().getVisibility());
-        assertEquals("Next button should be visible",
-                View.VISIBLE, navigationBar.getNextButton().getVisibility());
-    }
+  @SuppressLint("SetTextI18n") // It's OK for testing
+  @Test
+  public void testRequireScrollWithButton() {
+    final Button button = new Button(application);
+    button.setText("OriginalLabel");
+    OnClickListener wrappedListener = mock(OnClickListener.class);
+    requireScrollMixin.requireScrollWithButton(button, "TestMoreLabel", wrappedListener);
 
-    @SuppressLint("SetTextI18n") // It's OK for testing
-    @Test
-    public void testRequireScrollWithButton() {
-        final Button button = new Button(application);
-        button.setText("OriginalLabel");
-        OnClickListener wrappedListener = mock(OnClickListener.class);
-        mRequireScrollMixin.requireScrollWithButton(
-                button, "TestMoreLabel", wrappedListener);
+    assertWithMessage("Button label should be kept initially")
+        .that(button.getText().toString())
+        .isEqualTo("OriginalLabel");
 
-        assertEquals("Button label should be kept initially", "OriginalLabel", button.getText());
+    requireScrollMixin.notifyScrollabilityChange(true);
+    assertThat(button.getText().toString()).isEqualTo("TestMoreLabel");
+    button.performClick();
+    verify(wrappedListener, never()).onClick(eq(button));
+    verify(delegate).pageScrollDown();
 
-        mRequireScrollMixin.notifyScrollabilityChange(true);
-        assertEquals("TestMoreLabel", button.getText());
-        button.performClick();
-        verify(wrappedListener, never()).onClick(eq(button));
-        verify(mDelegate).pageScrollDown();
-
-        mRequireScrollMixin.notifyScrollabilityChange(false);
-        assertEquals("OriginalLabel", button.getText());
-        button.performClick();
-        verify(wrappedListener).onClick(eq(button));
-    }
+    requireScrollMixin.notifyScrollabilityChange(false);
+    assertThat(button.getText().toString()).isEqualTo("OriginalLabel");
+    button.performClick();
+    verify(wrappedListener).onClick(eq(button));
+  }
 }
diff --git a/library/test/robotest/src/com/android/setupwizardlib/template/ScrollViewScrollHandlingDelegateTest.java b/library/test/robotest/src/com/android/setupwizardlib/template/ScrollViewScrollHandlingDelegateTest.java
index 429445c..b07e2a3 100644
--- a/library/test/robotest/src/com/android/setupwizardlib/template/ScrollViewScrollHandlingDelegateTest.java
+++ b/library/test/robotest/src/com/android/setupwizardlib/template/ScrollViewScrollHandlingDelegateTest.java
@@ -16,72 +16,65 @@
 
 package com.android.setupwizardlib.template;
 
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.spy;
+import static com.google.common.truth.Truth.assertThat;
 import static org.mockito.Mockito.verify;
 import static org.robolectric.RuntimeEnvironment.application;
 
-import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
+import android.view.View;
 import com.android.setupwizardlib.view.BottomScrollView;
-import com.android.setupwizardlib.view.BottomScrollView.BottomScrollListener;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.annotation.Config;
 
-@Config(sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
-@RunWith(SuwLibRobolectricTestRunner.class)
+@Config(sdk = {Config.OLDEST_SDK, Config.NEWEST_SDK})
+@RunWith(RobolectricTestRunner.class)
 public class ScrollViewScrollHandlingDelegateTest {
 
-    @Mock
-    private RequireScrollMixin mRequireScrollMixin;
+  @Mock private RequireScrollMixin requireScrollMixin;
 
-    private BottomScrollView mScrollView;
-    private ScrollViewScrollHandlingDelegate mDelegate;
-    private ArgumentCaptor<BottomScrollListener> mListenerCaptor;
+  private BottomScrollView scrollView;
+  private ScrollViewScrollHandlingDelegate delegate;
 
-    @Before
-    public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
+  @Before
+  public void setUp() throws Exception {
+    MockitoAnnotations.initMocks(this);
 
-        mScrollView = spy(new BottomScrollView(application));
-        mDelegate = new ScrollViewScrollHandlingDelegate(mRequireScrollMixin, mScrollView);
+    scrollView = new BottomScrollView(application);
+    View childView = new View(application);
+    scrollView.addView(childView);
+    delegate = new ScrollViewScrollHandlingDelegate(requireScrollMixin, scrollView);
 
-        mListenerCaptor = ArgumentCaptor.forClass(BottomScrollListener.class);
-        doNothing().when(mScrollView).setBottomScrollListener(mListenerCaptor.capture());
+    scrollView.layout(0, 0, 500, 500);
+    childView.layout(0, 0, 1000, 1000);
+  }
 
-        mScrollView.layout(0, 0, 50, 50);
-    }
+  @Test
+  public void testRequireScroll() throws Throwable {
+    delegate.startListening();
 
-    @Test
-    public void testRequireScroll() throws Throwable {
-        mDelegate.startListening();
+    scrollView.getBottomScrollListener().onRequiresScroll();
+    verify(requireScrollMixin).notifyScrollabilityChange(true);
+  }
 
-        mListenerCaptor.getValue().onRequiresScroll();
-        verify(mRequireScrollMixin).notifyScrollabilityChange(true);
-    }
+  @Test
+  public void testScrolledToBottom() throws Throwable {
+    delegate.startListening();
 
-    @Test
-    public void testScrolledToBottom() throws Throwable {
-        mDelegate.startListening();
+    scrollView.getBottomScrollListener().onRequiresScroll();
+    verify(requireScrollMixin).notifyScrollabilityChange(true);
 
-        mListenerCaptor.getValue().onRequiresScroll();
-        verify(mRequireScrollMixin).notifyScrollabilityChange(true);
+    scrollView.getBottomScrollListener().onScrolledToBottom();
 
-        mListenerCaptor.getValue().onScrolledToBottom();
+    verify(requireScrollMixin).notifyScrollabilityChange(false);
+  }
 
-        verify(mRequireScrollMixin).notifyScrollabilityChange(false);
-    }
-
-    @Test
-    public void testPageScrollDown() throws Throwable {
-        mDelegate.pageScrollDown();
-        verify(mScrollView).smoothScrollBy(anyInt(), eq(50));
-    }
+  @Test
+  public void testPageScrollDown() throws Throwable {
+    delegate.pageScrollDown();
+    assertThat(scrollView.getScrollY()).isEqualTo(500);
+  }
 }
diff --git a/library/test/robotest/src/com/android/setupwizardlib/util/GlifDimensionTest.java b/library/test/robotest/src/com/android/setupwizardlib/util/GlifDimensionTest.java
index c10c122..a0c688c 100644
--- a/library/test/robotest/src/com/android/setupwizardlib/util/GlifDimensionTest.java
+++ b/library/test/robotest/src/com/android/setupwizardlib/util/GlifDimensionTest.java
@@ -16,7 +16,7 @@
 
 package com.android.setupwizardlib.util;
 
-import static org.junit.Assert.assertEquals;
+import static com.google.common.truth.Truth.assertWithMessage;
 import static org.robolectric.RuntimeEnvironment.application;
 
 import android.content.Context;
@@ -25,90 +25,88 @@
 import android.util.DisplayMetrics;
 import android.util.TypedValue;
 import android.view.ContextThemeWrapper;
-
 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.RobolectricTestRunner;
 import org.robolectric.annotation.Config;
 
-@RunWith(SuwLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 @Config(sdk = Config.ALL_SDKS)
 public class GlifDimensionTest {
 
-    private Context mContext;
+  private Context context;
 
-    @Before
-    public void setUp() {
-        mContext = new ContextThemeWrapper(application, R.style.SuwThemeGlif_Light);
-    }
+  @Before
+  public void setUp() {
+    context = new ContextThemeWrapper(application, R.style.SuwThemeGlif_Light);
+  }
 
-    @Test
-    public void testDividerInsetPhone() {
-        assertDividerInset();
-    }
+  @Test
+  public void testDividerInsetPhone() {
+    assertDividerInset();
+  }
 
-    @Config(qualifiers = "sw600dp")
-    @Test
-    public void testDividerInsetSw600dp() {
-        assertDividerInset();
-    }
+  @Config(qualifiers = "sw600dp")
+  @Test
+  public void testDividerInsetSw600dp() {
+    assertDividerInset();
+  }
 
-    private void assertDividerInset() {
-        final Resources res = mContext.getResources();
+  private void assertDividerInset() {
+    final Resources res = context.getResources();
 
-        final TypedArray a = mContext.obtainStyledAttributes(new int[]{R.attr.suwMarginSides});
-        final int marginSides = a.getDimensionPixelSize(0, 0);
-        a.recycle();
+    final TypedArray a = context.obtainStyledAttributes(new int[] {R.attr.suwMarginSides});
+    final int marginSides = a.getDimensionPixelSize(0, 0);
+    a.recycle();
 
-        assertEquals(
-                "Dimensions should satisfy constraint: "
-                        + "?attr/suwMarginSides = suw_items_glif_text_divider_inset",
-                marginSides,
-                res.getDimensionPixelSize(R.dimen.suw_items_glif_text_divider_inset));
+    assertWithMessage(
+            "Dimensions should satisfy constraint: "
+                + "?attr/suwMarginSides = suw_items_glif_text_divider_inset")
+        .that(res.getDimensionPixelSize(R.dimen.suw_items_glif_text_divider_inset))
+        .isEqualTo(marginSides);
 
-        assertEquals(
-                "Dimensions should satisfy constraint: ?attr/suwMarginSides + "
-                        + "suw_items_icon_container_width = suw_items_glif_icon_divider_inset",
-                marginSides + res.getDimensionPixelSize(R.dimen.suw_items_icon_container_width),
-                res.getDimensionPixelSize(R.dimen.suw_items_glif_icon_divider_inset));
-    }
+    assertWithMessage(
+            "Dimensions should satisfy constraint: ?attr/suwMarginSides + "
+                + "suw_items_icon_container_width = suw_items_glif_icon_divider_inset")
+        .that(res.getDimensionPixelSize(R.dimen.suw_items_glif_icon_divider_inset))
+        .isEqualTo(marginSides + res.getDimensionPixelSize(R.dimen.suw_items_icon_container_width));
+  }
 
-    @Test
-    public void testButtonMargin() {
-        assertButtonMargin();
-    }
+  @Test
+  public void testButtonMargin() {
+    assertButtonMargin();
+  }
 
-    @Config(qualifiers = "sw600dp")
-    @Test
-    public void testButtonMarginSw600dp() {
-        assertButtonMargin();
-    }
+  @Config(qualifiers = "sw600dp")
+  @Test
+  public void testButtonMarginSw600dp() {
+    assertButtonMargin();
+  }
 
-    private void assertButtonMargin() {
-        final Resources res = mContext.getResources();
+  private void assertButtonMargin() {
+    final Resources res = context.getResources();
 
-        final TypedArray a = mContext.obtainStyledAttributes(new int[]{R.attr.suwMarginSides});
-        final int marginSides = a.getDimensionPixelSize(0, 0);
-        a.recycle();
+    final TypedArray a = context.obtainStyledAttributes(new int[] {R.attr.suwMarginSides});
+    final int marginSides = a.getDimensionPixelSize(0, 0);
+    a.recycle();
 
-        assertEquals(
-                "Dimensions should satisfy constraint: ?attr/suwMarginSides - "
-                        + "4dp (internal padding of button) = suw_glif_button_margin_end",
-                marginSides - dp2Px(4),
-                res.getDimensionPixelSize(R.dimen.suw_glif_button_margin_end));
+    assertWithMessage(
+            "Dimensions should satisfy constraint: ?attr/suwMarginSides - "
+                + "4dp (internal padding of button) = suw_glif_button_margin_end")
+        .that(res.getDimensionPixelSize(R.dimen.suw_glif_button_margin_end))
+        .isEqualTo(marginSides - dp2Px(4));
 
-        assertEquals(
-                "Dimensions should satisfy constraint: ?attr/suwMarginSides - "
-                        + "suw_glif_button_padding = suw_glif_button_margin_start",
-                marginSides - res.getDimensionPixelSize(R.dimen.suw_glif_button_padding),
-                res.getDimensionPixelSize(R.dimen.suw_glif_button_margin_start));
-    }
+    assertWithMessage(
+            "Dimensions should satisfy constraint: ?attr/suwMarginSides - "
+                + "suw_glif_button_padding = suw_glif_button_margin_start")
+        .that(res.getDimensionPixelSize(R.dimen.suw_glif_button_margin_start))
+        .isEqualTo(marginSides - res.getDimensionPixelSize(R.dimen.suw_glif_button_padding));
+  }
 
-    private int dp2Px(float dp) {
-        DisplayMetrics displayMetrics = mContext.getResources().getDisplayMetrics();
-        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, displayMetrics);
-    }
+  private int dp2Px(float dp) {
+    DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
+    return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, displayMetrics);
+  }
 }
diff --git a/library/test/robotest/src/com/android/setupwizardlib/util/GlifStyleTest.java b/library/test/robotest/src/com/android/setupwizardlib/util/GlifStyleTest.java
index d8e318d..61b84bb 100644
--- a/library/test/robotest/src/com/android/setupwizardlib/util/GlifStyleTest.java
+++ b/library/test/robotest/src/com/android/setupwizardlib/util/GlifStyleTest.java
@@ -17,9 +17,6 @@
 package com.android.setupwizardlib.util;
 
 import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
 import static org.robolectric.RuntimeEnvironment.application;
 
 import android.annotation.TargetApi;
@@ -28,71 +25,82 @@
 import android.os.Build.VERSION;
 import android.os.Build.VERSION_CODES;
 import android.os.Bundle;
+import androidx.annotation.Nullable;
+import android.util.AttributeSet;
 import android.view.ContextThemeWrapper;
+import android.view.View;
 import android.widget.Button;
 import android.widget.ProgressBar;
-
-import androidx.annotation.Nullable;
-
 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.Robolectric;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.annotation.Config;
+import org.robolectric.util.ReflectionHelpers;
+import org.robolectric.util.ReflectionHelpers.ClassParameter;
 
-@RunWith(SuwLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 @Config(sdk = {Config.OLDEST_SDK, Config.NEWEST_SDK})
 public class GlifStyleTest {
 
-    private Context mContext;
-
-    @Before
-    public void setUp() {
-        mContext = new ContextThemeWrapper(application, R.style.SuwThemeGlif_Light);
+  @Test
+  public void testSuwGlifButtonTertiary() {
+    Button button =
+        createButton(
+            new ContextThemeWrapper(application, R.style.SuwThemeGlif_Light),
+            Robolectric.buildAttributeSet()
+                .setStyleAttribute("@style/SuwGlifButton.Tertiary")
+                .build());
+    assertThat(button.getBackground()).named("background").isNotNull();
+    assertThat(button.getTransformationMethod()).named("transformation method").isNull();
+    if (VERSION.SDK_INT < VERSION_CODES.M) {
+      // Robolectric resolved the wrong theme attribute on versions >= M
+      // https://github.com/robolectric/robolectric/issues/2940
+      assertThat(Integer.toHexString(button.getTextColors().getDefaultColor()))
+          .isEqualTo("ff4285f4");
     }
+  }
 
-    @Test
-    public void testSuwGlifButtonTertiary() {
-        Button button = new Button(
-                mContext,
-                Robolectric.buildAttributeSet()
-                        .setStyleAttribute("@style/SuwGlifButton.Tertiary")
-                        .build());
-        assertThat(button.getBackground()).named("background").isNotNull();
-        assertThat(button.getTransformationMethod()).named("transformation method").isNull();
-        if (VERSION.SDK_INT < VERSION_CODES.M) {
-            // Robolectric resolved the wrong theme attribute on versions >= M
-            // https://github.com/robolectric/robolectric/issues/2940
-            assertEquals("ff4285f4", Integer.toHexString(button.getTextColors().getDefaultColor()));
-        }
+  @TargetApi(VERSION_CODES.LOLLIPOP)
+  @Config(sdk = Config.NEWEST_SDK)
+  @Test
+  public void glifThemeLight_statusBarColorShouldBeTransparent() {
+    GlifThemeActivity activity = Robolectric.setupActivity(GlifThemeActivity.class);
+    assertThat(activity.getWindow().getStatusBarColor()).isEqualTo(0x00000000);
+  }
+
+  @Test
+  public void glifLoadingScreen_shouldHaveProgressBar() {
+    GlifThemeActivity activity = Robolectric.setupActivity(GlifThemeActivity.class);
+    activity.setContentView(R.layout.suw_glif_loading_screen);
+
+    assertThat((View) activity.findViewById(R.id.suw_large_progress_bar))
+        .isInstanceOf(ProgressBar.class);
+  }
+
+  private Button createButton(Context context, AttributeSet attrs) {
+    Class<? extends Button> buttonClass;
+    try {
+      // Use AppCompatButton in builds that have them (i.e. gingerbreadCompat)
+      // noinspection unchecked
+      buttonClass =
+          (Class<? extends Button>) Class.forName("androidx.appcompat.widget.AppCompatButton");
+    } catch (ClassNotFoundException e) {
+      buttonClass = Button.class;
     }
+    return ReflectionHelpers.callConstructor(
+        buttonClass,
+        ClassParameter.from(Context.class, context),
+        ClassParameter.from(AttributeSet.class, attrs));
+  }
 
-    @TargetApi(VERSION_CODES.LOLLIPOP)
-    @Config(sdk = Config.NEWEST_SDK)
-    @Test
-    public void glifThemeLight_statusBarColorShouldBeTransparent() {
-        GlifThemeActivity activity = Robolectric.setupActivity(GlifThemeActivity.class);
-        assertEquals(0x00000000, activity.getWindow().getStatusBarColor());
+  private static class GlifThemeActivity extends Activity {
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+      setTheme(R.style.SuwThemeGlif_Light);
+      super.onCreate(savedInstanceState);
     }
-
-    @Test
-    public void glifLoadingScreen_shouldHaveProgressBar() {
-        GlifThemeActivity activity = Robolectric.setupActivity(GlifThemeActivity.class);
-        activity.setContentView(R.layout.suw_glif_loading_screen);
-
-        assertTrue("Progress bar should exist",
-                activity.findViewById(R.id.suw_large_progress_bar) instanceof ProgressBar);
-    }
-
-    private static class GlifThemeActivity extends Activity {
-
-        @Override
-        protected void onCreate(@Nullable Bundle savedInstanceState) {
-            setTheme(R.style.SuwThemeGlif_Light);
-            super.onCreate(savedInstanceState);
-        }
-    }
+  }
 }
diff --git a/library/test/robotest/src/com/android/setupwizardlib/util/GlifV3StyleTest.java b/library/test/robotest/src/com/android/setupwizardlib/util/GlifV3StyleTest.java
index 44b8886..13e29f6 100644
--- a/library/test/robotest/src/com/android/setupwizardlib/util/GlifV3StyleTest.java
+++ b/library/test/robotest/src/com/android/setupwizardlib/util/GlifV3StyleTest.java
@@ -18,68 +18,59 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
 import android.app.Activity;
 import android.graphics.Color;
 import android.graphics.Typeface;
 import android.os.Build.VERSION;
 import android.os.Build.VERSION_CODES;
 import android.os.Bundle;
+import androidx.annotation.Nullable;
 import android.view.View;
 import android.widget.Button;
-
-import androidx.annotation.Nullable;
-
 import com.android.setupwizardlib.R;
-import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.annotation.Config;
 
-@RunWith(SuwLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 @Config(minSdk = Config.OLDEST_SDK, maxSdk = Config.NEWEST_SDK)
 public class GlifV3StyleTest {
 
-    @Test
-    public void activityWithGlifV3Theme_shouldUseLightNavBarOnV27OrAbove() {
-        GlifThemeActivity activity = Robolectric.setupActivity(GlifThemeActivity.class);
-        if (VERSION.SDK_INT >= VERSION_CODES.O_MR1) {
-            assertEquals(
-                    activity.getWindow().getNavigationBarColor(),
-                    Color.WHITE);
-            int vis = activity.getWindow().getDecorView().getSystemUiVisibility();
-            assertTrue((vis & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR) != 0);
-        } else if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
-            assertEquals(
-                    activity.getWindow().getNavigationBarColor(),
-                    Color.BLACK);
-        }
-        // Nav bar color is not customizable pre-L
+  @Test
+  public void activityWithGlifV3Theme_shouldUseLightNavBarOnV27OrAbove() {
+    GlifThemeActivity activity = Robolectric.setupActivity(GlifThemeActivity.class);
+    if (VERSION.SDK_INT >= VERSION_CODES.O_MR1) {
+      assertThat(activity.getWindow().getNavigationBarColor()).isEqualTo(Color.WHITE);
+      int vis = activity.getWindow().getDecorView().getSystemUiVisibility();
+      assertThat((vis & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR) != 0).isTrue();
+    } else if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+      assertThat(activity.getWindow().getNavigationBarColor()).isEqualTo(Color.BLACK);
     }
+    // Nav bar color is not customizable pre-L
+  }
 
-    @Test
-    public void buttonWithGlifV3_shouldBeGoogleSans() {
-        GlifThemeActivity activity = Robolectric.setupActivity(GlifThemeActivity.class);
-        Button button = new Button(
-                activity,
-                Robolectric.buildAttributeSet()
-                        .setStyleAttribute("@style/SuwGlifButton.Primary")
-                        .build());
-        assertThat(button.getTypeface()).isEqualTo(Typeface.create("google-sans", 0));
-        // Button should not be all caps
-        assertThat(button.getTransformationMethod()).isNull();
+  @Test
+  public void buttonWithGlifV3_shouldBeGoogleSans() {
+    GlifThemeActivity activity = Robolectric.setupActivity(GlifThemeActivity.class);
+    Button button =
+        new Button(
+            activity,
+            Robolectric.buildAttributeSet()
+                .setStyleAttribute("@style/SuwGlifButton.Primary")
+                .build());
+    assertThat(button.getTypeface()).isEqualTo(Typeface.create("google-sans", 0));
+    // Button should not be all caps
+    assertThat(button.getTransformationMethod()).isNull();
+  }
+
+  private static class GlifThemeActivity extends Activity {
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+      setTheme(R.style.SuwThemeGlifV3_Light);
+      super.onCreate(savedInstanceState);
     }
-
-    private static class GlifThemeActivity extends Activity {
-
-        @Override
-        protected void onCreate(@Nullable Bundle savedInstanceState) {
-            setTheme(R.style.SuwThemeGlifV3_Light);
-            super.onCreate(savedInstanceState);
-        }
-    }
+  }
 }
diff --git a/library/test/robotest/src/com/android/setupwizardlib/util/PartnerTest.java b/library/test/robotest/src/com/android/setupwizardlib/util/PartnerTest.java
index 2285cd5..f301e43 100644
--- a/library/test/robotest/src/com/android/setupwizardlib/util/PartnerTest.java
+++ b/library/test/robotest/src/com/android/setupwizardlib/util/PartnerTest.java
@@ -17,211 +17,164 @@
 package com.android.setupwizardlib.util;
 
 import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.spy;
 import static org.robolectric.RuntimeEnvironment.application;
+import static org.robolectric.Shadows.shadowOf;
 
-import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
-import android.content.res.Resources;
 import android.os.Build.VERSION;
 import android.os.Build.VERSION_CODES;
-
 import com.android.setupwizardlib.R;
-import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
+import com.android.setupwizardlib.robolectric.ExternalResources;
+import com.android.setupwizardlib.robolectric.ExternalResources.Resources;
 import com.android.setupwizardlib.util.Partner.ResourceEntry;
-import com.android.setupwizardlib.util.PartnerTest.ShadowApplicationPackageManager;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.robolectric.Shadows;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.annotation.Config;
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.shadows.ShadowResources;
 
-import java.util.Arrays;
-import java.util.Collections;
-
-@RunWith(SuwLibRobolectricTestRunner.class)
-@Config(
-        sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK },
-        shadows = ShadowApplicationPackageManager.class)
+@RunWith(RobolectricTestRunner.class)
+@Config(sdk = {Config.OLDEST_SDK, Config.NEWEST_SDK})
 public class PartnerTest {
 
-    private static final String ACTION_PARTNER_CUSTOMIZATION =
-            "com.android.setupwizard.action.PARTNER_CUSTOMIZATION";
+  private static final String ACTION_PARTNER_CUSTOMIZATION =
+      "com.android.setupwizard.action.PARTNER_CUSTOMIZATION";
 
-    private Context mContext;
-    private Resources mPartnerResources;
+  @Before
+  public void setUp() throws Exception {
+    MockitoAnnotations.initMocks(this);
+    Partner.resetForTesting();
+  }
 
-    private ShadowApplicationPackageManager mPackageManager;
+  @Test
+  public void get_withPartnerPackage_shouldReturnNonNull() {
+    new PartnerPackageBuilder("foo.bar")
+        .setIsSystem(false)
+        .setDirectBootAware(true)
+        .injectResources();
+    new PartnerPackageBuilder("test.partner.package").setDirectBootAware(true).injectResources();
 
-    @Before
-    public void setUp() throws Exception {
-        Partner.resetForTesting();
+    Partner partner = Partner.get(application);
+    assertThat(partner).isNotNull();
+    assertThat(partner.getPackageName()).isEqualTo("test.partner.package");
+  }
 
-        mContext = spy(application);
-        mPartnerResources = spy(ShadowResources.getSystem());
+  @Test
+  public void get_noPartnerPackage_shouldReturnNull() {
+    Partner partner = Partner.get(application);
+    assertThat(partner).isNull();
+  }
 
-        mPackageManager =
-                (ShadowApplicationPackageManager) Shadows.shadowOf(application.getPackageManager());
-        mPackageManager.partnerResources = mPartnerResources;
+  @Test
+  public void get_nonSystemPartnerPackage_shouldIgnoreAndReturnNull() {
+    new PartnerPackageBuilder("foo.bar")
+        .setIsSystem(false)
+        .setDirectBootAware(true)
+        .injectResources();
+    new PartnerPackageBuilder("test.partner.package")
+        .setIsSystem(false)
+        .setDirectBootAware(true)
+        .injectResources();
+
+    Partner partner = Partner.get(application);
+    assertThat(partner).isNull();
+  }
+
+  @Test
+  public void getResourceEntry_hasOverlay_shouldReturnOverlayValue() {
+    new PartnerPackageBuilder("test.partner.package")
+        .injectResources()
+        .putInteger("suwTransitionDuration", 5000);
+
+    ResourceEntry entry = Partner.getResourceEntry(application, R.integer.suwTransitionDuration);
+    int partnerValue = entry.resources.getInteger(entry.id);
+    assertThat(partnerValue).named("partner value").isEqualTo(5000);
+    assertThat(entry.isOverlay).isTrue();
+  }
+
+  @Test
+  public void getColor_partnerValuePresent_shouldReturnPartnerValue() {
+    new PartnerPackageBuilder("test.partner.package")
+        .injectResources()
+        .putColor("suw_color_accent_dark", 0xffff00ff);
+
+    final int color = Partner.getColor(application, R.color.suw_color_accent_dark);
+    assertThat(color).isEqualTo(0xffff00ff);
+  }
+
+  @Test
+  public void getText_partnerValuePresent_shouldReturnPartnerValue() {
+    new PartnerPackageBuilder("test.partner.package")
+        .injectResources()
+        .putText("suw_next_button_label", "partner");
+
+    final CharSequence partnerText = Partner.getText(application, R.string.suw_next_button_label);
+    assertThat(partnerText.toString()).isEqualTo("partner");
+  }
+
+  @Test
+  public void getResourceEntry_partnerValueNotPresent_shouldReturnDefault() {
+    new PartnerPackageBuilder("test.partner.package").injectResources();
+
+    ResourceEntry entry = Partner.getResourceEntry(application, R.color.suw_color_accent_dark);
+    int partnerValue = entry.resources.getColor(entry.id);
+    assertThat(partnerValue).isEqualTo(0xff448aff);
+    assertThat(entry.isOverlay).isFalse();
+  }
+
+  @Test
+  public void getResourceEntry_directBootUnawareNoValueDefined_shouldReturnDefaultValue() {
+    new PartnerPackageBuilder("test.partner.package").injectResources();
+
+    ResourceEntry entry = Partner.getResourceEntry(application, R.color.suw_color_accent_dark);
+    int partnerValue = entry.resources.getColor(entry.id);
+    assertThat(partnerValue).isEqualTo(0xff448aff);
+    assertThat(entry.isOverlay).isFalse();
+  }
+
+  private static class PartnerPackageBuilder {
+    private final String packageName;
+    private final ResolveInfo resolveInfo;
+
+    PartnerPackageBuilder(String packageName) {
+      this.packageName = packageName;
+
+      resolveInfo = new ResolveInfo();
+      resolveInfo.resolvePackageName = packageName;
+      ActivityInfo activityInfo = new ActivityInfo();
+      ApplicationInfo appInfo = new ApplicationInfo();
+      appInfo.flags = ApplicationInfo.FLAG_SYSTEM;
+      appInfo.packageName = packageName;
+      activityInfo.applicationInfo = appInfo;
+      activityInfo.packageName = packageName;
+      activityInfo.name = packageName;
+      resolveInfo.activityInfo = activityInfo;
     }
 
-    @Test
-    public void testLoadPartner() {
-        mPackageManager.addResolveInfoForIntent(
-                new Intent(ACTION_PARTNER_CUSTOMIZATION),
-                Arrays.asList(
-                        createResolveInfo("foo.bar", false, true),
-                        createResolveInfo("test.partner.package", true, true))
-        );
-
-        Partner partner = Partner.get(mContext);
-        assertNotNull("Partner should not be null", partner);
+    PartnerPackageBuilder setIsSystem(boolean isSystem) {
+      if (isSystem) {
+        resolveInfo.activityInfo.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+      } else {
+        resolveInfo.activityInfo.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;
+      }
+      return this;
     }
 
-    @Test
-    public void testLoadNoPartner() {
-        Partner partner = Partner.get(mContext);
-        assertNull("Partner should be null", partner);
+    PartnerPackageBuilder setDirectBootAware(boolean directBootAware) {
+      if (VERSION.SDK_INT >= VERSION_CODES.N) {
+        resolveInfo.activityInfo.directBootAware = directBootAware;
+      }
+      return this;
     }
 
-    @Test
-    public void testLoadNonSystemPartner() {
-        mPackageManager.addResolveInfoForIntent(
-                new Intent(ACTION_PARTNER_CUSTOMIZATION),
-                Arrays.asList(
-                        createResolveInfo("foo.bar", false, true),
-                        createResolveInfo("test.partner.package", false, true))
-        );
-
-        Partner partner = Partner.get(mContext);
-        assertNull("Partner should be null", partner);
+    Resources injectResources() {
+      shadowOf(application.getPackageManager())
+          .addResolveInfoForIntent(new Intent(ACTION_PARTNER_CUSTOMIZATION), resolveInfo);
+      return ExternalResources.injectExternalResources(packageName);
     }
-
-    @Test
-    public void testLoadPartnerValue() {
-        doReturn(0x7f010000).when(mPartnerResources)
-                .getIdentifier(eq("suwTransitionDuration"), eq("integer"), anyString());
-        doReturn(5000).when(mPartnerResources).getInteger(eq(0x7f010000));
-
-        mPackageManager.addResolveInfoForIntent(
-                new Intent(ACTION_PARTNER_CUSTOMIZATION),
-                Arrays.asList(
-                        createResolveInfo("foo.bar", false, true),
-                        createResolveInfo("test.partner.package", true, true))
-        );
-
-        ResourceEntry entry = Partner.getResourceEntry(mContext, R.integer.suwTransitionDuration);
-        int partnerValue = entry.resources.getInteger(entry.id);
-        assertEquals("Partner value should be overlaid to 5000", 5000, partnerValue);
-        assertTrue("Partner value should come from overlay", entry.isOverlay);
-    }
-
-    @Test
-    public void getColor_shouldReturnPartnerValueIfPresent() {
-        final int expectedPartnerColor = 1111;
-        doReturn(12345).when(mPartnerResources)
-                .getIdentifier(eq("suw_color_accent_dark"), eq("color"), anyString());
-        doReturn(expectedPartnerColor).when(mPartnerResources).getColor(eq(12345));
-        mPackageManager.addResolveInfoForIntent(
-                new Intent(ACTION_PARTNER_CUSTOMIZATION),
-                Arrays.asList(createResolveInfo("test.partner.package", true, true)));
-        final int foundColor = Partner.getColor(mContext, R.color.suw_color_accent_dark);
-        assertEquals("Partner color should be overlayed to: " + expectedPartnerColor,
-                expectedPartnerColor, foundColor);
-    }
-
-    @Test
-    public void getText_shouldReturnPartnerValueIfPresent() {
-        final CharSequence expectedPartnerText = "partner";
-        doReturn(12345).when(mPartnerResources)
-                .getIdentifier(eq("suw_next_button_label"), eq("string"), anyString());
-        doReturn(expectedPartnerText).when(mPartnerResources).getText(eq(12345));
-        mPackageManager.addResolveInfoForIntent(
-                new Intent(ACTION_PARTNER_CUSTOMIZATION),
-                Collections.singletonList(createResolveInfo("test.partner.package", true, true)));
-        final CharSequence partnerText = Partner.getText(mContext, R.string.suw_next_button_label);
-        assertThat(partnerText).isEqualTo(expectedPartnerText);
-    }
-
-    @Test
-    public void testLoadDefaultValue() {
-        mPackageManager.addResolveInfoForIntent(
-                new Intent(ACTION_PARTNER_CUSTOMIZATION),
-                Arrays.asList(
-                        createResolveInfo("foo.bar", false, true),
-                        createResolveInfo("test.partner.package", true, true))
-        );
-
-        ResourceEntry entry = Partner.getResourceEntry(mContext, R.color.suw_color_accent_dark);
-        int partnerValue = entry.resources.getColor(entry.id);
-        assertEquals("Partner value should default to 0xff448aff", 0xff448aff, partnerValue);
-        assertFalse("Partner value should come from fallback", entry.isOverlay);
-    }
-
-    @Test
-    public void testNotDirectBootAware() {
-        mPackageManager.addResolveInfoForIntent(
-                new Intent(ACTION_PARTNER_CUSTOMIZATION),
-                Collections.singletonList(createResolveInfo("test.partner.package", true, false)));
-
-        ResourceEntry entry = Partner.getResourceEntry(mContext, R.color.suw_color_accent_dark);
-        int partnerValue = entry.resources.getColor(entry.id);
-        assertEquals("Partner value should default to 0xff448aff", 0xff448aff, partnerValue);
-        assertFalse("Partner value should come from fallback", entry.isOverlay);
-    }
-
-    private ResolveInfo createResolveInfo(
-            String packageName,
-            boolean isSystem,
-            boolean directBootAware) {
-        ResolveInfo info = new ResolveInfo();
-        info.resolvePackageName = packageName;
-        ActivityInfo activityInfo = new ActivityInfo();
-        ApplicationInfo appInfo = new ApplicationInfo();
-        appInfo.flags = isSystem ? ApplicationInfo.FLAG_SYSTEM : 0;
-        appInfo.packageName = packageName;
-        activityInfo.applicationInfo = appInfo;
-        activityInfo.packageName = packageName;
-        activityInfo.name = packageName;
-        if (VERSION.SDK_INT >= VERSION_CODES.N) {
-            activityInfo.directBootAware = directBootAware;
-        }
-        info.activityInfo = activityInfo;
-        return info;
-    }
-
-    @Implements(className = "android.app.ApplicationPackageManager")
-    public static class ShadowApplicationPackageManager extends
-            org.robolectric.shadows.ShadowApplicationPackageManager {
-
-        public Resources partnerResources;
-
-        @Implementation
-        @Override
-        public Resources getResourcesForApplication(ApplicationInfo app)
-                throws NameNotFoundException {
-            if (app != null && "test.partner.package".equals(app.packageName)) {
-                return partnerResources;
-            } else {
-                return super.getResourcesForApplication(app);
-            }
-        }
-    }
+  }
 }
diff --git a/library/test/robotest/src/com/android/setupwizardlib/util/ThemeResolverTest.java b/library/test/robotest/src/com/android/setupwizardlib/util/ThemeResolverTest.java
new file mode 100644
index 0000000..6489961
--- /dev/null
+++ b/library/test/robotest/src/com/android/setupwizardlib/util/ThemeResolverTest.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.setupwizardlib.util;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.robolectric.Shadows.shadowOf;
+
+import android.app.Activity;
+import android.content.Intent;
+import androidx.annotation.StyleRes;
+import com.android.setupwizardlib.R;
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(sdk = Config.NEWEST_SDK)
+public class ThemeResolverTest {
+
+  @After
+  public void resetDefaultThemeResolver() {
+    ThemeResolver.setDefault(null);
+  }
+
+  @Test
+  public void resolve_nonDayNight_shouldReturnCorrespondingTheme() {
+    @StyleRes int defaultTheme = 12345;
+    ThemeResolver themeResolver =
+        new ThemeResolver.Builder().setDefaultTheme(defaultTheme).setUseDayNight(false).build();
+    assertThat(themeResolver.resolve("material")).isEqualTo(R.style.SuwThemeMaterial);
+    assertThat(themeResolver.resolve("material_light")).isEqualTo(R.style.SuwThemeMaterial_Light);
+    assertThat(themeResolver.resolve("glif")).isEqualTo(R.style.SuwThemeGlif);
+    assertThat(themeResolver.resolve("glif_light")).isEqualTo(R.style.SuwThemeGlif_Light);
+    assertThat(themeResolver.resolve("glif_v2")).isEqualTo(R.style.SuwThemeGlifV2);
+    assertThat(themeResolver.resolve("glif_v2_light")).isEqualTo(R.style.SuwThemeGlifV2_Light);
+    assertThat(themeResolver.resolve("glif_v3")).isEqualTo(R.style.SuwThemeGlifV3);
+    assertThat(themeResolver.resolve("glif_v3_light")).isEqualTo(R.style.SuwThemeGlifV3_Light);
+    assertThat(themeResolver.resolve("unknown_theme")).isEqualTo(defaultTheme);
+  }
+
+  @Test
+  public void resolve_dayNight_shouldReturnDayNightTheme() {
+    @StyleRes int defaultTheme = 12345;
+    ThemeResolver themeResolver = new ThemeResolver.Builder().setDefaultTheme(defaultTheme).build();
+    assertThat(themeResolver.resolve("material")).isEqualTo(R.style.SuwThemeMaterial_DayNight);
+    assertThat(themeResolver.resolve("material_light"))
+        .isEqualTo(R.style.SuwThemeMaterial_DayNight);
+    assertThat(themeResolver.resolve("glif")).isEqualTo(R.style.SuwThemeGlif_DayNight);
+    assertThat(themeResolver.resolve("glif_light")).isEqualTo(R.style.SuwThemeGlif_DayNight);
+    assertThat(themeResolver.resolve("glif_v2")).isEqualTo(R.style.SuwThemeGlifV2_DayNight);
+    assertThat(themeResolver.resolve("glif_v2_light")).isEqualTo(R.style.SuwThemeGlifV2_DayNight);
+    assertThat(themeResolver.resolve("glif_v3")).isEqualTo(R.style.SuwThemeGlifV3_DayNight);
+    assertThat(themeResolver.resolve("glif_v3_light")).isEqualTo(R.style.SuwThemeGlifV3_DayNight);
+    assertThat(themeResolver.resolve("unknown_theme")).isEqualTo(defaultTheme);
+  }
+
+  @Test
+  public void resolve_newerThanOldestSupportedTheme_shouldReturnSpecifiedTheme() {
+    ThemeResolver themeResolver =
+        new ThemeResolver.Builder()
+            .setOldestSupportedTheme(WizardManagerHelper.THEME_GLIF_V2)
+            .build();
+    assertThat(themeResolver.resolve("glif_v2")).isEqualTo(R.style.SuwThemeGlifV2_DayNight);
+    assertThat(themeResolver.resolve("glif_v2_light")).isEqualTo(R.style.SuwThemeGlifV2_DayNight);
+    assertThat(themeResolver.resolve("glif_v3")).isEqualTo(R.style.SuwThemeGlifV3_DayNight);
+    assertThat(themeResolver.resolve("glif_v3_light")).isEqualTo(R.style.SuwThemeGlifV3_DayNight);
+  }
+
+  @Test
+  public void resolve_olderThanOldestSupportedTheme_shouldReturnDefault() {
+    @StyleRes int defaultTheme = 12345;
+    ThemeResolver themeResolver =
+        new ThemeResolver.Builder()
+            .setDefaultTheme(defaultTheme)
+            .setOldestSupportedTheme(WizardManagerHelper.THEME_GLIF_V2)
+            .build();
+    assertThat(themeResolver.resolve("material")).isEqualTo(defaultTheme);
+    assertThat(themeResolver.resolve("material_light")).isEqualTo(defaultTheme);
+    assertThat(themeResolver.resolve("glif")).isEqualTo(defaultTheme);
+    assertThat(themeResolver.resolve("glif_light")).isEqualTo(defaultTheme);
+  }
+
+  @Test
+  public void resolve_intentTheme_shouldReturnCorrespondingTheme() {
+    @StyleRes int defaultTheme = 12345;
+    ThemeResolver themeResolver =
+        new ThemeResolver.Builder().setDefaultTheme(defaultTheme).setUseDayNight(false).build();
+    assertThat(
+            themeResolver.resolve(
+                new Intent().putExtra(WizardManagerHelper.EXTRA_THEME, "material")))
+        .isEqualTo(R.style.SuwThemeMaterial);
+    assertThat(
+            themeResolver.resolve(
+                new Intent().putExtra(WizardManagerHelper.EXTRA_THEME, "material_light")))
+        .isEqualTo(R.style.SuwThemeMaterial_Light);
+    assertThat(
+            themeResolver.resolve(new Intent().putExtra(WizardManagerHelper.EXTRA_THEME, "glif")))
+        .isEqualTo(R.style.SuwThemeGlif);
+    assertThat(
+            themeResolver.resolve(
+                new Intent().putExtra(WizardManagerHelper.EXTRA_THEME, "glif_light")))
+        .isEqualTo(R.style.SuwThemeGlif_Light);
+    assertThat(
+            themeResolver.resolve(
+                new Intent().putExtra(WizardManagerHelper.EXTRA_THEME, "glif_v2")))
+        .isEqualTo(R.style.SuwThemeGlifV2);
+    assertThat(
+            themeResolver.resolve(
+                new Intent().putExtra(WizardManagerHelper.EXTRA_THEME, "glif_v2_light")))
+        .isEqualTo(R.style.SuwThemeGlifV2_Light);
+    assertThat(
+            themeResolver.resolve(
+                new Intent().putExtra(WizardManagerHelper.EXTRA_THEME, "glif_v3")))
+        .isEqualTo(R.style.SuwThemeGlifV3);
+    assertThat(
+            themeResolver.resolve(
+                new Intent().putExtra(WizardManagerHelper.EXTRA_THEME, "glif_v3_light")))
+        .isEqualTo(R.style.SuwThemeGlifV3_Light);
+    assertThat(
+            themeResolver.resolve(
+                new Intent().putExtra(WizardManagerHelper.EXTRA_THEME, "unknown_theme")))
+        .isEqualTo(defaultTheme);
+  }
+
+  @Test
+  public void resolve_suwIntent_shouldForceNonDayNightTheme() {
+    @StyleRes int defaultTheme = 12345;
+    ThemeResolver themeResolver =
+        new ThemeResolver.Builder().setDefaultTheme(defaultTheme).setUseDayNight(true).build();
+    Intent originalIntent = new Intent().putExtra(WizardManagerHelper.EXTRA_IS_FIRST_RUN, true);
+    assertThat(
+            themeResolver.resolve(
+                new Intent(originalIntent).putExtra(WizardManagerHelper.EXTRA_THEME, "material")))
+        .isEqualTo(R.style.SuwThemeMaterial);
+    assertThat(
+            themeResolver.resolve(
+                new Intent(originalIntent)
+                    .putExtra(WizardManagerHelper.EXTRA_THEME, "material_light")))
+        .isEqualTo(R.style.SuwThemeMaterial_Light);
+    assertThat(
+            themeResolver.resolve(
+                new Intent(originalIntent).putExtra(WizardManagerHelper.EXTRA_THEME, "glif")))
+        .isEqualTo(R.style.SuwThemeGlif);
+    assertThat(
+            themeResolver.resolve(
+                new Intent(originalIntent).putExtra(WizardManagerHelper.EXTRA_THEME, "glif_light")))
+        .isEqualTo(R.style.SuwThemeGlif_Light);
+    assertThat(
+            themeResolver.resolve(
+                new Intent(originalIntent).putExtra(WizardManagerHelper.EXTRA_THEME, "glif_v2")))
+        .isEqualTo(R.style.SuwThemeGlifV2);
+    assertThat(
+            themeResolver.resolve(
+                new Intent(originalIntent)
+                    .putExtra(WizardManagerHelper.EXTRA_THEME, "glif_v2_light")))
+        .isEqualTo(R.style.SuwThemeGlifV2_Light);
+    assertThat(
+            themeResolver.resolve(
+                new Intent(originalIntent).putExtra(WizardManagerHelper.EXTRA_THEME, "glif_v3")))
+        .isEqualTo(R.style.SuwThemeGlifV3);
+    assertThat(
+            themeResolver.resolve(
+                new Intent(originalIntent)
+                    .putExtra(WizardManagerHelper.EXTRA_THEME, "glif_v3_light")))
+        .isEqualTo(R.style.SuwThemeGlifV3_Light);
+    assertThat(
+            themeResolver.resolve(
+                new Intent(originalIntent)
+                    .putExtra(WizardManagerHelper.EXTRA_THEME, "unknown_theme")))
+        .isEqualTo(defaultTheme);
+  }
+
+  @Test
+  public void applyTheme_glifV3_shouldSetActivityThemeToGlifV3() {
+    @StyleRes int defaultTheme = 12345;
+    ThemeResolver themeResolver =
+        new ThemeResolver.Builder().setUseDayNight(false).setDefaultTheme(defaultTheme).build();
+
+    Activity activity =
+        Robolectric.buildActivity(
+                Activity.class,
+                new Intent()
+                    .putExtra(WizardManagerHelper.EXTRA_THEME, WizardManagerHelper.THEME_GLIF_V3))
+            .setup()
+            .get();
+
+    themeResolver.applyTheme(activity);
+
+    assertThat(shadowOf(activity).callGetThemeResId()).isEqualTo(R.style.SuwThemeGlifV3);
+  }
+
+  @Test
+  public void setDefault_shouldSetDefaultResolver() {
+    ThemeResolver themeResolver = new ThemeResolver.Builder().setUseDayNight(false).build();
+
+    ThemeResolver.setDefault(themeResolver);
+    assertThat(ThemeResolver.getDefault()).isSameAs(themeResolver);
+  }
+}
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 616ccdd..e302cab 100644
--- a/library/test/robotest/src/com/android/setupwizardlib/util/WizardManagerHelperTest.java
+++ b/library/test/robotest/src/com/android/setupwizardlib/util/WizardManagerHelperTest.java
@@ -16,10 +16,10 @@
 
 package com.android.setupwizardlib.util;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 import static org.robolectric.RuntimeEnvironment.application;
+import static org.robolectric.Shadows.shadowOf;
 
 import android.annotation.TargetApi;
 import android.app.Activity;
@@ -29,299 +29,343 @@
 import android.provider.Settings;
 import android.provider.Settings.Global;
 import android.provider.Settings.Secure;
-
 import androidx.annotation.StyleRes;
-
 import com.android.setupwizardlib.R;
-import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.annotation.Config;
-
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
 
-@RunWith(SuwLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 @Config(sdk = Config.NEWEST_SDK)
 public class WizardManagerHelperTest {
 
-    @Test
-    public void testGetNextIntent() {
-        final Intent intent = new Intent("test.intent.ACTION");
-        intent.putExtra("scriptUri", "android-resource://test-script");
-        intent.putExtra("actionId", "test_action_id");
-        intent.putExtra("theme", "test_theme");
-        intent.putExtra("ignoreExtra", "poof"); // extra is ignored because it's not known
+  @Test
+  public void testGetNextIntent() {
+    final Intent intent = new Intent("test.intent.ACTION");
+    intent.putExtra("scriptUri", "android-resource://test-script");
+    intent.putExtra("actionId", "test_action_id");
+    intent.putExtra("theme", "test_theme");
+    intent.putExtra("ignoreExtra", "poof"); // extra is ignored because it's not known
 
-        final Intent data = new Intent();
-        data.putExtra("extraData", "shazam");
+    final Intent data = new Intent();
+    data.putExtra("extraData", "shazam");
 
-        final Intent nextIntent =
-                WizardManagerHelper.getNextIntent(intent, Activity.RESULT_OK, data);
-        assertEquals("Next intent action should be NEXT", "com.android.wizard.NEXT",
-                nextIntent.getAction());
-        assertEquals("Script URI should be the same as original intent",
-                "android-resource://test-script", nextIntent.getStringExtra("scriptUri"));
-        assertEquals("Action ID should be the same as original intent", "test_action_id",
-                nextIntent.getStringExtra("actionId"));
-        assertEquals("Theme extra should be the same as original intent", "test_theme",
-                nextIntent.getStringExtra("theme"));
-        assertFalse("ignoreExtra should not be in nextIntent", nextIntent.hasExtra("ignoreExtra"));
-        assertEquals("Result code extra should be RESULT_OK", Activity.RESULT_OK,
-                nextIntent.getIntExtra("com.android.setupwizard.ResultCode", 0));
-        assertEquals("Extra data should surface as extra in nextIntent", "shazam",
-                nextIntent.getStringExtra("extraData"));
+    final Intent nextIntent = WizardManagerHelper.getNextIntent(intent, Activity.RESULT_OK, data);
+    assertWithMessage("Next intent action should be NEXT")
+        .that(nextIntent.getAction())
+        .isEqualTo("com.android.wizard.NEXT");
+    assertWithMessage("Script URI should be the same as original intent")
+        .that(nextIntent.getStringExtra("scriptUri"))
+        .isEqualTo("android-resource://test-script");
+    assertWithMessage("Action ID should be the same as original intent")
+        .that(nextIntent.getStringExtra("actionId"))
+        .isEqualTo("test_action_id");
+    assertWithMessage("Theme extra should be the same as original intent")
+        .that(nextIntent.getStringExtra("theme"))
+        .isEqualTo("test_theme");
+    assertWithMessage("ignoreExtra should not be in nextIntent")
+        .that(nextIntent.hasExtra("ignoreExtra"))
+        .isFalse();
+    assertWithMessage("Result code extra should be RESULT_OK")
+        .that(nextIntent.getIntExtra("com.android.setupwizard.ResultCode", 0))
+        .isEqualTo(Activity.RESULT_OK);
+    assertWithMessage("Extra data should surface as extra in nextIntent")
+        .that(nextIntent.getStringExtra("extraData"))
+        .isEqualTo("shazam");
+  }
+
+  @Test
+  public void testIsSetupWizardTrue() {
+    final Intent intent = new Intent();
+    intent.putExtra("firstRun", true);
+    assertWithMessage("Is setup wizard should be true")
+        .that(WizardManagerHelper.isSetupWizardIntent(intent))
+        .isTrue();
+  }
+
+  @Test
+  public void testIsDeferredSetupTrue() {
+    final Intent intent = new Intent();
+    intent.putExtra("deferredSetup", true);
+    assertWithMessage("Is deferred setup wizard should be true")
+        .that(WizardManagerHelper.isDeferredSetupWizard(intent))
+        .isTrue();
+  }
+
+  @Test
+  public void testIsPreDeferredSetupTrue() {
+    final Intent intent = new Intent();
+    intent.putExtra("preDeferredSetup", true);
+    assertWithMessage("Is pre-deferred setup wizard should be true")
+        .that(WizardManagerHelper.isPreDeferredSetupWizard(intent))
+        .isTrue();
+  }
+
+  @Test
+  public void testIsSetupWizardFalse() {
+    final Intent intent = new Intent();
+    intent.putExtra("firstRun", false);
+    assertWithMessage("Is setup wizard should be true")
+        .that(WizardManagerHelper.isSetupWizardIntent(intent))
+        .isFalse();
+  }
+
+  @Test
+  public void isLightTheme_shouldReturnTrue_whenThemeIsLight() {
+    List<String> lightThemes =
+        Arrays.asList(
+            "holo_light", "material_light", "glif_light", "glif_v2_light", "glif_v3_light");
+    ArrayList<String> unexpectedIntentThemes = new ArrayList<>();
+    ArrayList<String> unexpectedStringThemes = new ArrayList<>();
+    for (final String theme : lightThemes) {
+      Intent intent = new Intent();
+      intent.putExtra(WizardManagerHelper.EXTRA_THEME, theme);
+      if (!WizardManagerHelper.isLightTheme(intent, false)) {
+        unexpectedIntentThemes.add(theme);
+      }
+      if (!WizardManagerHelper.isLightTheme(theme, false)) {
+        unexpectedStringThemes.add(theme);
+      }
     }
+    assertWithMessage("Intent themes " + unexpectedIntentThemes + " should be light")
+        .that(unexpectedIntentThemes.isEmpty())
+        .isTrue();
+    assertWithMessage("String themes " + unexpectedStringThemes + " should be light")
+        .that(unexpectedStringThemes.isEmpty())
+        .isTrue();
+  }
 
-    @Test
-    public void testIsSetupWizardTrue() {
-        final Intent intent = new Intent();
-        intent.putExtra("firstRun", true);
-        assertTrue("Is setup wizard should be true",
-                WizardManagerHelper.isSetupWizardIntent(intent));
+  @Test
+  public void isLightTheme_shouldReturnFalse_whenThemeIsNotLight() {
+    List<String> lightThemes = Arrays.asList("holo", "material", "glif", "glif_v2", "glif_v3");
+    ArrayList<String> unexpectedIntentThemes = new ArrayList<>();
+    ArrayList<String> unexpectedStringThemes = new ArrayList<>();
+    for (final String theme : lightThemes) {
+      Intent intent = new Intent();
+      intent.putExtra(WizardManagerHelper.EXTRA_THEME, theme);
+      if (WizardManagerHelper.isLightTheme(intent, true)) {
+        unexpectedIntentThemes.add(theme);
+      }
+      if (WizardManagerHelper.isLightTheme(theme, true)) {
+        unexpectedStringThemes.add(theme);
+      }
     }
+    assertWithMessage("Intent themes " + unexpectedIntentThemes + " should not be light")
+        .that(unexpectedIntentThemes.isEmpty())
+        .isTrue();
+    assertWithMessage("String themes " + unexpectedStringThemes + " should not be light")
+        .that(unexpectedStringThemes.isEmpty())
+        .isTrue();
+  }
 
-    @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 getThemeRes_whenOldestSupportedThemeTakeEffect_shouldReturnDefault() {
+    Intent intent = new Intent();
+    intent.putExtra(WizardManagerHelper.EXTRA_THEME, "material");
+    assertThat(WizardManagerHelper.getThemeRes(intent, 0, WizardManagerHelper.THEME_GLIF_V2))
+        .isEqualTo(0);
+  }
 
-    @Test
-    public void testIsPreDeferredSetupTrue() {
-        final Intent intent = new Intent();
-        intent.putExtra("preDeferredSetup", true);
-        assertTrue("Is pre-deferred setup wizard should be true",
-                WizardManagerHelper.isPreDeferredSetupWizard(intent));
-    }
+  @Test
+  public void getThemeRes_whenOldestSupportedThemeNotTakeEffect_shouldReturnCurrent() {
+    Intent intent = new Intent();
+    intent.putExtra(WizardManagerHelper.EXTRA_THEME, "glif_v3");
+    assertThat(WizardManagerHelper.getThemeRes(intent, 0, WizardManagerHelper.THEME_GLIF_V2))
+        .isEqualTo(R.style.SuwThemeGlifV3);
+  }
 
-    @Test
-    public void testIsSetupWizardFalse() {
-        final Intent intent = new Intent();
-        intent.putExtra("firstRun", false);
-        assertFalse("Is setup wizard should be true",
-                WizardManagerHelper.isSetupWizardIntent(intent));
-    }
+  @Test
+  public void testIsLightThemeDefault() {
+    final Intent intent = new Intent();
+    intent.putExtra("theme", "abracadabra");
+    assertWithMessage("isLightTheme should return default value true")
+        .that(WizardManagerHelper.isLightTheme(intent, true))
+        .isTrue();
+    assertWithMessage("isLightTheme should return default value false")
+        .that(WizardManagerHelper.isLightTheme(intent, false))
+        .isFalse();
+  }
 
-    @Test
-    public void isLightTheme_shouldReturnTrue_whenThemeIsLight() {
-        List<String> lightThemes = Arrays.asList(
-                "holo_light",
-                "material_light",
-                "glif_light",
-                "glif_v2_light",
-                "glif_v3_light"
-        );
-        ArrayList<String> unexpectedIntentThemes = new ArrayList<>();
-        ArrayList<String> unexpectedStringThemes = new ArrayList<>();
-        for (final String theme : lightThemes) {
-            Intent intent = new Intent();
-            intent.putExtra(WizardManagerHelper.EXTRA_THEME, theme);
-            if (!WizardManagerHelper.isLightTheme(intent, false)) {
-                unexpectedIntentThemes.add(theme);
-            }
-            if (!WizardManagerHelper.isLightTheme(theme, false)) {
-                unexpectedStringThemes.add(theme);
-            }
-        }
-        assertTrue("Intent themes " + unexpectedIntentThemes + " should be light",
-                unexpectedIntentThemes.isEmpty());
-        assertTrue("String themes " + unexpectedStringThemes + " should be light",
-                unexpectedStringThemes.isEmpty());
-    }
+  @Test
+  public void testIsLightThemeUnspecified() {
+    final Intent intent = new Intent();
+    assertWithMessage("isLightTheme should return default value true")
+        .that(WizardManagerHelper.isLightTheme(intent, true))
+        .isTrue();
+    assertWithMessage("isLightTheme should return default value false")
+        .that(WizardManagerHelper.isLightTheme(intent, false))
+        .isFalse();
+  }
 
-    @Test
-    public void isLightTheme_shouldReturnFalse_whenThemeIsNotLight() {
-        List<String> lightThemes = Arrays.asList(
-                "holo",
-                "material",
-                "glif",
-                "glif_v2",
-                "glif_v3"
-        );
-        ArrayList<String> unexpectedIntentThemes = new ArrayList<>();
-        ArrayList<String> unexpectedStringThemes = new ArrayList<>();
-        for (final String theme : lightThemes) {
-            Intent intent = new Intent();
-            intent.putExtra(WizardManagerHelper.EXTRA_THEME, theme);
-            if (WizardManagerHelper.isLightTheme(intent, true)) {
-                unexpectedIntentThemes.add(theme);
-            }
-            if (WizardManagerHelper.isLightTheme(theme, true)) {
-                unexpectedStringThemes.add(theme);
-            }
-        }
-        assertTrue("Intent themes " + unexpectedIntentThemes + " should not be light",
-                unexpectedIntentThemes.isEmpty());
-        assertTrue("String themes " + unexpectedStringThemes + " should not be light",
-                unexpectedStringThemes.isEmpty());
-    }
+  @Test
+  public void testGetThemeResGlifV3Light() {
+    assertThat(WizardManagerHelper.getThemeRes("glif_v3_light", 0))
+        .isEqualTo(R.style.SuwThemeGlifV3_Light);
+  }
 
-    @Test
-    public void testIsLightThemeDefault() {
-        final Intent intent = new Intent();
-        intent.putExtra("theme", "abracadabra");
-        assertTrue("isLightTheme should return default value true",
-                WizardManagerHelper.isLightTheme(intent, true));
-        assertFalse("isLightTheme should return default value false",
-                WizardManagerHelper.isLightTheme(intent, false));
-    }
+  @Test
+  public void testGetThemeResGlifV3() {
+    assertThat(WizardManagerHelper.getThemeRes("glif_v3", 0)).isEqualTo(R.style.SuwThemeGlifV3);
+  }
 
-    @Test
-    public void testIsLightThemeUnspecified() {
-        final Intent intent = new Intent();
-        assertTrue("isLightTheme should return default value true",
-                WizardManagerHelper.isLightTheme(intent, true));
-        assertFalse("isLightTheme should return default value false",
-                WizardManagerHelper.isLightTheme(intent, false));
-    }
+  @Test
+  public void testGetThemeResGlifV2Light() {
+    assertThat(WizardManagerHelper.getThemeRes("glif_v2_light", 0))
+        .isEqualTo(R.style.SuwThemeGlifV2_Light);
+  }
 
-    @Test
-    public void testGetThemeResGlifV3Light() {
-        assertEquals(R.style.SuwThemeGlifV3_Light,
-                WizardManagerHelper.getThemeRes("glif_v3_light", 0));
-    }
+  @Test
+  public void testGetThemeResGlifV2() {
+    assertThat(WizardManagerHelper.getThemeRes("glif_v2", 0)).isEqualTo(R.style.SuwThemeGlifV2);
+  }
 
-    @Test
-    public void testGetThemeResGlifV3() {
-        assertEquals(R.style.SuwThemeGlifV3,
-                WizardManagerHelper.getThemeRes("glif_v3", 0));
-    }
+  @Test
+  public void testGetThemeResGlifLight() {
+    assertThat(WizardManagerHelper.getThemeRes("glif_light", 0))
+        .isEqualTo(R.style.SuwThemeGlif_Light);
+  }
 
-    @Test
-    public void testGetThemeResGlifV2Light() {
-        assertEquals(R.style.SuwThemeGlifV2_Light,
-                WizardManagerHelper.getThemeRes("glif_v2_light", 0));
-    }
+  @Test
+  public void testGetThemeResGlif() {
+    assertThat(WizardManagerHelper.getThemeRes("glif", 0)).isEqualTo(R.style.SuwThemeGlif);
+  }
 
-    @Test
-    public void testGetThemeResGlifV2() {
-        assertEquals(R.style.SuwThemeGlifV2,
-                WizardManagerHelper.getThemeRes("glif_v2", 0));
-    }
+  @Test
+  public void testGetThemeResMaterialLight() {
+    assertThat(WizardManagerHelper.getThemeRes("material_light", 0))
+        .isEqualTo(R.style.SuwThemeMaterial_Light);
+  }
 
-    @Test
-    public void testGetThemeResGlifLight() {
-        assertEquals(R.style.SuwThemeGlif_Light,
-                WizardManagerHelper.getThemeRes("glif_light", 0));
-    }
+  @Test
+  public void testGetThemeResMaterial() {
+    assertThat(WizardManagerHelper.getThemeRes("material", 0)).isEqualTo(R.style.SuwThemeMaterial);
+  }
 
-    @Test
-    public void testGetThemeResGlif() {
-        assertEquals(R.style.SuwThemeGlif,
-                WizardManagerHelper.getThemeRes("glif", 0));
-    }
+  @Test
+  public void testGetThemeResDefault() {
+    @StyleRes int def = 123;
+    assertThat(WizardManagerHelper.getThemeRes("abracadabra", def)).isEqualTo(def);
+  }
 
-    @Test
-    public void testGetThemeResMaterialLight() {
-        assertEquals(R.style.SuwThemeMaterial_Light,
-                WizardManagerHelper.getThemeRes("material_light", 0));
-    }
+  @Test
+  public void testGetThemeResNull() {
+    @StyleRes int def = 123;
+    assertThat(WizardManagerHelper.getThemeRes((String) null, def)).isEqualTo(def);
+  }
 
-    @Test
-    public void testGetThemeResMaterial() {
-        assertEquals(R.style.SuwThemeMaterial,
-                WizardManagerHelper.getThemeRes("material", 0));
-    }
+  @Test
+  public void testGetThemeResFromIntent() {
+    Intent intent = new Intent();
+    intent.putExtra(WizardManagerHelper.EXTRA_THEME, "material");
+    assertThat(WizardManagerHelper.getThemeRes(intent, 0)).isEqualTo(R.style.SuwThemeMaterial);
+  }
 
-    @Test
-    public void testGetThemeResDefault() {
-        @StyleRes int def = 123;
-        assertEquals(def, WizardManagerHelper.getThemeRes("abracadabra", def));
-    }
+  @Test
+  public void testCopyWizardManagerIntent() {
+    Bundle wizardBundle = new Bundle();
+    wizardBundle.putString("foo", "bar");
+    Intent originalIntent =
+        new Intent()
+            .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)
+            .putExtra(WizardManagerHelper.EXTRA_IS_PRE_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");
 
-    @Test
-    public void testGetThemeResNull() {
-        @StyleRes int def = 123;
-        assertEquals(def, WizardManagerHelper.getThemeRes((String) null, def));
-    }
+    Intent intent = new Intent("test.intent.action");
+    WizardManagerHelper.copyWizardManagerExtras(originalIntent, intent);
 
-    @Test
-    public void testGetThemeResFromIntent() {
-        Intent intent = new Intent();
-        intent.putExtra(WizardManagerHelper.EXTRA_THEME, "material");
-        assertEquals(R.style.SuwThemeMaterial, WizardManagerHelper.getThemeRes(intent, 0));
-    }
+    assertWithMessage("Intent action should be kept")
+        .that(intent.getAction())
+        .isEqualTo("test.intent.action");
+    assertWithMessage("EXTRA_THEME should be copied")
+        .that(intent.getStringExtra(WizardManagerHelper.EXTRA_THEME))
+        .isEqualTo("test_theme");
+    Bundle copiedWizardBundle = intent.getParcelableExtra(WizardManagerHelper.EXTRA_WIZARD_BUNDLE);
+    assertWithMessage("Wizard bundle should be copied")
+        .that(copiedWizardBundle.getString("foo"))
+        .isEqualTo("bar");
 
-    @Test
-    public void testCopyWizardManagerIntent() {
-        Bundle wizardBundle = new Bundle();
-        wizardBundle.putString("foo", "bar");
-        Intent originalIntent = new Intent()
-                .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)
-                .putExtra(WizardManagerHelper.EXTRA_IS_PRE_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");
+    assertWithMessage("EXTRA_IS_FIRST_RUN should be copied")
+        .that(intent.getBooleanExtra(WizardManagerHelper.EXTRA_IS_FIRST_RUN, false))
+        .isTrue();
+    assertWithMessage("EXTRA_IS_DEFERRED_SETUP should be copied")
+        .that(intent.getBooleanExtra(WizardManagerHelper.EXTRA_IS_DEFERRED_SETUP, false))
+        .isTrue();
+    assertWithMessage("EXTRA_IS_PRE_DEFERRED_SETUP should be copied")
+        .that(intent.getBooleanExtra(WizardManagerHelper.EXTRA_IS_PRE_DEFERRED_SETUP, false))
+        .isTrue();
 
-        Intent intent = new Intent("test.intent.action");
-        WizardManagerHelper.copyWizardManagerExtras(originalIntent, intent);
+    // Script URI and Action ID are replaced by Wizard Bundle in M, but are kept for backwards
+    // compatibility
+    assertWithMessage("EXTRA_SCRIPT_URI should be copied")
+        .that(intent.getStringExtra(WizardManagerHelper.EXTRA_SCRIPT_URI))
+        .isEqualTo("test_script_uri");
+    assertWithMessage("EXTRA_ACTION_ID should be copied")
+        .that(intent.getStringExtra(WizardManagerHelper.EXTRA_ACTION_ID))
+        .isEqualTo("test_action_id");
+  }
 
-        assertEquals("Intent action should be kept", "test.intent.action", intent.getAction());
-        assertEquals("EXTRA_THEME should be copied",
-                "test_theme", intent.getStringExtra(WizardManagerHelper.EXTRA_THEME));
-        Bundle copiedWizardBundle =
-                intent.getParcelableExtra(WizardManagerHelper.EXTRA_WIZARD_BUNDLE);
-        assertEquals("Wizard bundle should be copied", "bar", copiedWizardBundle.getString("foo"));
+  @TargetApi(VERSION_CODES.JELLY_BEAN_MR1)
+  @Test
+  public void testIsUserSetupComplete() {
+    Settings.Global.putInt(application.getContentResolver(), Global.DEVICE_PROVISIONED, 1);
+    Settings.Secure.putInt(application.getContentResolver(), "user_setup_complete", 1);
+    assertThat(WizardManagerHelper.isUserSetupComplete(application)).isTrue();
 
-        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));
-        assertTrue("EXTRA_IS_PRE_DEFERRED_SETUP should be copied",
-                intent.getBooleanExtra(WizardManagerHelper.EXTRA_IS_PRE_DEFERRED_SETUP, false));
+    Settings.Secure.putInt(application.getContentResolver(), "user_setup_complete", 0);
+    assertThat(WizardManagerHelper.isUserSetupComplete(application)).isFalse();
+  }
 
-        // Script URI and Action ID are replaced by Wizard Bundle in M, but are kept for backwards
-        // compatibility
-        assertEquals("EXTRA_SCRIPT_URI should be copied",
-                "test_script_uri", intent.getStringExtra(WizardManagerHelper.EXTRA_SCRIPT_URI));
-        assertEquals("EXTRA_ACTION_ID should be copied",
-                "test_action_id", intent.getStringExtra(WizardManagerHelper.EXTRA_ACTION_ID));
-    }
+  @Test
+  @Config(sdk = VERSION_CODES.JELLY_BEAN)
+  public void testIsUserSetupCompleteCompat() {
+    Settings.Secure.putInt(application.getContentResolver(), Secure.DEVICE_PROVISIONED, 1);
+    assertThat(WizardManagerHelper.isUserSetupComplete(application)).isTrue();
 
-    @TargetApi(VERSION_CODES.JELLY_BEAN_MR1)
-    @Test
-    public void testIsUserSetupComplete() {
-        Settings.Secure.putInt(application.getContentResolver(), Global.DEVICE_PROVISIONED, 1);
-        Settings.Secure.putInt(application.getContentResolver(), "user_setup_complete", 1);
-        assertTrue(WizardManagerHelper.isUserSetupComplete(application));
+    Settings.Secure.putInt(application.getContentResolver(), Secure.DEVICE_PROVISIONED, 0);
+    assertThat(WizardManagerHelper.isUserSetupComplete(application)).isFalse();
+  }
 
-        Settings.Secure.putInt(application.getContentResolver(), "user_setup_complete", 0);
-        assertFalse(WizardManagerHelper.isUserSetupComplete(application));
-    }
+  @TargetApi(VERSION_CODES.JELLY_BEAN_MR1)
+  @Test
+  public void testIsDeviceProvisioned() {
+    Settings.Global.putInt(application.getContentResolver(), Global.DEVICE_PROVISIONED, 1);
+    assertThat(WizardManagerHelper.isDeviceProvisioned(application)).isTrue();
+    Settings.Global.putInt(application.getContentResolver(), Global.DEVICE_PROVISIONED, 0);
+    assertThat(WizardManagerHelper.isDeviceProvisioned(application)).isFalse();
+  }
 
-    @Test
-    @Config(sdk = VERSION_CODES.JELLY_BEAN)
-    public void testIsUserSetupCompleteCompat() {
-        Settings.Secure.putInt(application.getContentResolver(), Secure.DEVICE_PROVISIONED, 1);
-        assertTrue(WizardManagerHelper.isUserSetupComplete(application));
+  @Test
+  @Config(sdk = VERSION_CODES.JELLY_BEAN)
+  public void testIsDeviceProvisionedCompat() {
+    Settings.Secure.putInt(application.getContentResolver(), Secure.DEVICE_PROVISIONED, 1);
+    assertThat(WizardManagerHelper.isDeviceProvisioned(application)).isTrue();
+    Settings.Secure.putInt(application.getContentResolver(), Secure.DEVICE_PROVISIONED, 0);
+    assertThat(WizardManagerHelper.isDeviceProvisioned(application)).isFalse();
+  }
 
-        Settings.Secure.putInt(application.getContentResolver(), Secure.DEVICE_PROVISIONED, 0);
-        assertFalse(WizardManagerHelper.isUserSetupComplete(application));
-    }
+  @Test
+  public void applyTheme_glifDayNight_shouldApplyThemeToActivity() {
+    Activity activity =
+        Robolectric.buildActivity(
+                Activity.class,
+                new Intent()
+                    .putExtra(
+                        WizardManagerHelper.EXTRA_THEME, WizardManagerHelper.THEME_GLIF_LIGHT))
+            .setup()
+            .get();
 
-    @TargetApi(VERSION_CODES.JELLY_BEAN_MR1)
-    @Test
-    public void testIsDeviceProvisioned() {
-        Settings.Secure.putInt(application.getContentResolver(), Global.DEVICE_PROVISIONED, 1);
-        assertTrue(WizardManagerHelper.isDeviceProvisioned(application));
-        Settings.Secure.putInt(application.getContentResolver(), Global.DEVICE_PROVISIONED, 0);
-        assertFalse(WizardManagerHelper.isDeviceProvisioned(application));
-    }
+    WizardManagerHelper.applyTheme(activity);
 
-    @Test
-    @Config(sdk = VERSION_CODES.JELLY_BEAN)
-    public void testIsDeviceProvisionedCompat() {
-        Settings.Secure.putInt(application.getContentResolver(), Secure.DEVICE_PROVISIONED, 1);
-        assertTrue(WizardManagerHelper.isDeviceProvisioned(application));
-        Settings.Secure.putInt(application.getContentResolver(), Secure.DEVICE_PROVISIONED, 0);
-        assertFalse(WizardManagerHelper.isDeviceProvisioned(application));
-    }
+    assertThat(shadowOf(activity).callGetThemeResId()).isEqualTo(R.style.SuwThemeGlif_DayNight);
+  }
 }
diff --git a/library/test/robotest/src/com/android/setupwizardlib/view/FillContentLayoutTest.java b/library/test/robotest/src/com/android/setupwizardlib/view/FillContentLayoutTest.java
index ae4f3d1..001f1da 100644
--- a/library/test/robotest/src/com/android/setupwizardlib/view/FillContentLayoutTest.java
+++ b/library/test/robotest/src/com/android/setupwizardlib/view/FillContentLayoutTest.java
@@ -16,72 +16,73 @@
 
 package com.android.setupwizardlib.view;
 
-import static org.junit.Assert.assertEquals;
+import static com.google.common.truth.Truth.assertThat;
 import static org.robolectric.RuntimeEnvironment.application;
 
 import android.view.View;
 import android.view.View.MeasureSpec;
-
-import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.annotation.Config;
 
-@RunWith(SuwLibRobolectricTestRunner.class)
+@RunWith(RobolectricTestRunner.class)
 @Config(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));
+  @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());
-    }
+    assertThat(layout.getMeasuredWidth()).isEqualTo(123);
+    assertThat(layout.getMeasuredHeight()).isEqualTo(123);
+  }
 
-    @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));
+  @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());
-    }
+    assertThat(child.getMeasuredWidth()).isEqualTo(123);
+    assertThat(child.getMeasuredHeight()).isEqualTo(123);
+  }
 
-    @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));
+  @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());
-    }
+    assertThat(child.getMeasuredWidth()).isEqualTo(88);
+    assertThat(child.getMeasuredHeight()).isEqualTo(88);
+  }
 }
diff --git a/library/test/robotest/src/com/android/setupwizardlib/view/IllustrationVideoViewTest.java b/library/test/robotest/src/com/android/setupwizardlib/view/IllustrationVideoViewTest.java
index 0e0e99c..e980563 100644
--- a/library/test/robotest/src/com/android/setupwizardlib/view/IllustrationVideoViewTest.java
+++ b/library/test/robotest/src/com/android/setupwizardlib/view/IllustrationVideoViewTest.java
@@ -17,204 +17,223 @@
 package com.android.setupwizardlib.view;
 
 import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.fail;
 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.app.Activity;
 import android.graphics.SurfaceTexture;
-import android.media.MediaPlayer;
-import android.os.Build.VERSION_CODES;
-import android.view.Surface;
-import android.view.View;
-
+import android.net.Uri;
 import androidx.annotation.RawRes;
-
+import android.view.View;
 import com.android.setupwizardlib.R;
-import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
-import com.android.setupwizardlib.shadow.ShadowLog;
-import com.android.setupwizardlib.shadow.ShadowLog.TerribleFailure;
-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.RobolectricTestRunner;
+import org.robolectric.Shadows;
 import org.robolectric.annotation.Config;
-import org.robolectric.annotation.Implementation;
-import org.robolectric.annotation.Implements;
-import org.robolectric.annotation.RealObject;
-import org.robolectric.shadow.api.Shadow;
 import org.robolectric.shadows.ShadowMediaPlayer;
+import org.robolectric.shadows.ShadowMediaPlayer.InvalidStateBehavior;
+import org.robolectric.shadows.ShadowMediaPlayer.MediaInfo;
+import org.robolectric.shadows.util.DataSource;
 import org.robolectric.util.ReflectionHelpers;
+import org.robolectric.util.ReflectionHelpers.ClassParameter;
 
-@RunWith(SuwLibRobolectricTestRunner.class)
-@Config(
-        sdk = Config.NEWEST_SDK,
-        shadows = {
-                ShadowLog.class,
-                ShadowMockMediaPlayer.class,
-                ShadowSurface.class
-        })
+@RunWith(RobolectricTestRunner.class)
+@Config(sdk = Config.NEWEST_SDK)
 public class IllustrationVideoViewTest {
 
-    @Mock
-    private SurfaceTexture mSurfaceTexture;
+  @Mock private SurfaceTexture surfaceTexture;
 
-    private IllustrationVideoView mView;
+  private IllustrationVideoView view;
 
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-    }
+  @Before
+  public void setUp() {
+    MockitoAnnotations.initMocks(this);
+    ShadowMediaPlayer.addMediaInfo(
+        DataSource.toDataSource(
+            "android.resource://" + application.getPackageName() + "/" + android.R.color.white),
+        new ShadowMediaPlayer.MediaInfo(100, 10));
+    ShadowMediaPlayer.addMediaInfo(
+        DataSource.toDataSource(
+            "android.resource://" + application.getPackageName() + "/" + android.R.color.black),
+        new ShadowMediaPlayer.MediaInfo(100, 10));
+  }
 
-    @After
-    public void tearDown() {
-        ShadowMockMediaPlayer.reset();
-    }
+  @Test
+  public void testPausedWhenWindowFocusLost() {
+    createDefaultView();
+    Robolectric.flushForegroundThreadScheduler();
+    view.start();
 
-    @Test
-    public void nullMediaPlayer_shouldThrowWtf() {
-        ShadowMockMediaPlayer.sMediaPlayer = null;
-        try {
-            createDefaultView();
-            fail("WTF should be thrown for null media player");
-        } catch (TerribleFailure e) {
-            // pass
-        }
-    }
+    assertThat(view.mMediaPlayer).isNotNull();
+    assertThat(view.surface).isNotNull();
 
-    @Test
-    public void onVisibilityChanged_notVisible_shouldRelease() {
-        createDefaultView();
-        mView.onWindowVisibilityChanged(View.GONE);
+    view.onWindowFocusChanged(false);
+    assertThat(getShadowMediaPlayer().getState()).isEqualTo(ShadowMediaPlayer.State.PAUSED);
+  }
 
-        verify(ShadowMockMediaPlayer.sMediaPlayer).release();
-        assertThat(mView.mSurface).isNull();
-        assertThat(mView.mMediaPlayer).isNull();
-    }
+  @Test
+  public void testStartedWhenWindowFocusRegained() {
+    testPausedWhenWindowFocusLost();
+    Robolectric.flushForegroundThreadScheduler();
 
-    @Test
-    public void onVisibilityChanged_visible_shouldPlay() {
-        createDefaultView();
+    view.onWindowFocusChanged(true);
+    assertThat(getShadowMediaPlayer().getState()).isEqualTo(ShadowMediaPlayer.State.STARTED);
+  }
 
-        mView.onWindowVisibilityChanged(View.GONE);
-        assertThat(mView.mSurface).isNull();
-        assertThat(mView.mMediaPlayer).isNull();
+  @Test
+  public void testSurfaceReleasedWhenTextureDestroyed() {
+    createDefaultView();
+    view.start();
 
-        mView.onWindowVisibilityChanged(View.VISIBLE);
+    assertThat(view.mMediaPlayer).isNotNull();
+    assertThat(view.surface).isNotNull();
 
-        assertThat(mView.mSurface).isNotNull();
-        assertThat(mView.mMediaPlayer).isNotNull();
-    }
+    // MediaPlayer is set to null after destroy. Retrieve it first before we call destroy.
+    ShadowMediaPlayer shadowMediaPlayer = getShadowMediaPlayer();
+    view.onSurfaceTextureDestroyed(surfaceTexture);
+    assertThat(shadowMediaPlayer.getState()).isEqualTo(ShadowMediaPlayer.State.END);
+  }
 
-    @Test
-    public void testPausedWhenWindowFocusLost() {
-        createDefaultView();
-        mView.start();
+  @Test
+  public void testXmlSetVideoResId() {
+    createDefaultView();
+    assertThat(getShadowMediaPlayer().getSourceUri().toString())
+        .isEqualTo("android.resource://com.android.setupwizardlib/" + android.R.color.white);
+  }
 
-        assertNotNull(mView.mMediaPlayer);
-        assertNotNull(mView.mSurface);
+  @Test
+  public void testSetVideoResId() {
+    createDefaultView();
 
-        mView.onWindowFocusChanged(false);
-        verify(ShadowMockMediaPlayer.getMock()).pause();
-    }
+    @RawRes int black = android.R.color.black;
+    view.setVideoResource(black);
 
-    @Test
-    public void testStartedWhenWindowFocusRegained() {
-        testPausedWhenWindowFocusLost();
+    assertThat(getShadowMediaPlayer().getSourceUri().toString())
+        .isEqualTo("android.resource://com.android.setupwizardlib/" + android.R.color.black);
+  }
 
-        // Clear verifications for calls in the other test
-        reset(ShadowMockMediaPlayer.getMock());
+  @Test
+  public void prepareVideo_shouldSetAspectRatio() {
+    createDefaultView();
 
-        mView.onWindowFocusChanged(true);
-        verify(ShadowMockMediaPlayer.getMock()).start();
-    }
+    ReflectionHelpers.setField(getShadowMediaPlayer(), "videoWidth", 720);
+    ReflectionHelpers.setField(getShadowMediaPlayer(), "videoHeight", 1280);
 
-    @Test
-    public void testSurfaceReleasedWhenTextureDestroyed() {
-        createDefaultView();
-        mView.start();
+    Robolectric.flushForegroundThreadScheduler();
+    view.start();
 
-        assertNotNull(mView.mMediaPlayer);
-        assertNotNull(mView.mSurface);
+    view.measure(
+        View.MeasureSpec.makeMeasureSpec(720, View.MeasureSpec.EXACTLY),
+        View.MeasureSpec.makeMeasureSpec(720, View.MeasureSpec.EXACTLY));
 
-        mView.onSurfaceTextureDestroyed(mSurfaceTexture);
-        verify(ShadowMockMediaPlayer.getMock()).release();
-    }
+    final float aspectRatio = (float) view.getMeasuredHeight() / view.getMeasuredWidth();
+    assertThat(aspectRatio).isWithin(0.001f).of(1280f / 720f);
+  }
 
-    @Test
-    public void testXmlSetVideoResId() {
-        createDefaultView();
-        assertEquals(android.R.color.white, ShadowMockMediaPlayer.sResId);
-    }
+  @Test
+  public void prepareVideo_zeroHeight_shouldSetAspectRatioToZero() {
+    createDefaultView();
 
-    @Test
-    public void testSetVideoResId() {
-        createDefaultView();
+    ReflectionHelpers.setField(getShadowMediaPlayer(), "videoWidth", 720);
+    ReflectionHelpers.setField(getShadowMediaPlayer(), "videoHeight", 0);
 
-        @RawRes int black = android.R.color.black;
-        mView.setVideoResource(black);
+    Robolectric.flushForegroundThreadScheduler();
+    view.start();
 
-        assertEquals(android.R.color.black, ShadowMockMediaPlayer.sResId);
-    }
+    final float aspectRatio = (float) view.getHeight() / view.getWidth();
+    assertThat(aspectRatio).isEqualTo(0.0f);
+  }
 
-    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.setSurfaceTexture(mock(SurfaceTexture.class));
-        mView.onSurfaceTextureAvailable(mSurfaceTexture, 500, 500);
-    }
+  @Test
+  public void setVideoResId_resetDiffVideoResFromDiffPackage_videoResShouldBeSet() {
+    // VideoRes default set as android.R.color.white with
+    // default package(com.android.setupwizardlib)
+    createDefaultView();
 
-    @Implements(MediaPlayer.class)
-    public static class ShadowMockMediaPlayer extends ShadowMediaPlayer {
+    // reset different videoRes from different package
+    String newPackageName = "com.android.fakepackage";
+    @RawRes int black = android.R.color.black;
+    addMediaInfo(black, newPackageName);
+    view.setVideoResource(black, newPackageName);
 
-        private static MediaPlayer sMediaPlayer = mock(MediaPlayer.class);
-        private static int sResId;
+    // should be reset to black with the new package
+    assertThat(getShadowMediaPlayer().getSourceUri().toString())
+        .isEqualTo("android.resource://" + newPackageName + "/" + android.R.color.black);
+  }
 
-        public static void reset() {
-            sMediaPlayer = mock(MediaPlayer.class);
-            sResId = 0;
-        }
+  @Test
+  public void setVideoResId_resetDiffVideoResFromSamePackage_videoResShouldBeSet() {
+    // VideoRes default set as android.R.color.white with
+    // default package(com.android.setupwizardlib)
+    createDefaultView();
 
-        @Implementation
-        public static MediaPlayer create(Context context, int resId) {
-            sResId = resId;
-            return sMediaPlayer;
-        }
+    // reset different videoRes from the same package(default package)
+    String defaultPackageName = "com.android.setupwizardlib";
+    @RawRes int black = android.R.color.black;
+    addMediaInfo(black, defaultPackageName);
+    view.setVideoResource(black, defaultPackageName);
 
-        public static MediaPlayer getMock() {
-            return sMediaPlayer;
-        }
-    }
+    // should be reset to black with the same package(default package)
+    assertThat(getShadowMediaPlayer().getSourceUri().toString())
+        .isEqualTo("android.resource://" + defaultPackageName + "/" + android.R.color.black);
+  }
 
-    @Implements(Surface.class)
-    @TargetApi(VERSION_CODES.HONEYCOMB)
-    public static class ShadowSurface extends org.robolectric.shadows.ShadowSurface {
+  @Test
+  public void setVideoResId_resetSameVideoResFromDifferentPackage_videoResShouldBeSet() {
+    // VideoRes default set as android.R.color.white with
+    // default package(com.android.setupwizardlib)
+    createDefaultView();
 
-        @RealObject
-        private Surface mRealSurface;
+    // reset same videoRes from different package
+    @RawRes int white = android.R.color.white;
+    String newPackageName = "com.android.fakepackage";
+    addMediaInfo(white, newPackageName);
+    view.setVideoResource(white, newPackageName);
 
-        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);
-        }
-    }
+    // should be white with the new package
+    assertThat(getShadowMediaPlayer().getSourceUri().toString())
+        .isEqualTo("android.resource://" + newPackageName + "/" + android.R.color.white);
+  }
+
+  private ShadowMediaPlayer getShadowMediaPlayer() {
+    return Shadows.shadowOf(view.mMediaPlayer);
+  }
+
+  private void createDefaultView() {
+    view =
+        new IllustrationVideoView(
+            application,
+            Robolectric.buildAttributeSet()
+                // Any resource attribute should work, since the data source is fake
+                .addAttribute(R.attr.suwVideo, "@android:color/white")
+                .build());
+
+    Activity activity = Robolectric.setupActivity(Activity.class);
+    activity.setContentView(view);
+    setWindowVisible();
+
+    view.setSurfaceTexture(mock(SurfaceTexture.class));
+    view.onSurfaceTextureAvailable(surfaceTexture, 500, 500);
+    getShadowMediaPlayer().setInvalidStateBehavior(InvalidStateBehavior.EMULATE);
+  }
+
+  private void setWindowVisible() {
+    Object viewRootImpl = ReflectionHelpers.callInstanceMethod(view, "getViewRootImpl");
+    ReflectionHelpers.callInstanceMethod(
+        viewRootImpl, "handleAppVisibility", ClassParameter.from(boolean.class, true));
+    assertThat(view.isAttachedToWindow()).isTrue();
+    assertThat(view.getWindowVisibility()).isEqualTo(View.VISIBLE);
+  }
+
+  private void addMediaInfo(@RawRes int res, String packageName) {
+    ShadowMediaPlayer.addMediaInfo(
+        DataSource.toDataSource(
+            application, Uri.parse("android.resource://" + packageName + "/" + res), null),
+        new MediaInfo(5000, 1));
+  }
 }
diff --git a/library/test/robotest/src/com/android/setupwizardlib/view/RichTextViewTest.java b/library/test/robotest/src/com/android/setupwizardlib/view/RichTextViewTest.java
index f77de68..477c42a 100644
--- a/library/test/robotest/src/com/android/setupwizardlib/view/RichTextViewTest.java
+++ b/library/test/robotest/src/com/android/setupwizardlib/view/RichTextViewTest.java
@@ -17,15 +17,10 @@
 package com.android.setupwizardlib.view;
 
 import static com.google.common.truth.Truth.assertThat;
-
-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 com.google.common.truth.Truth.assertWithMessage;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.robolectric.RuntimeEnvironment.application;
 
@@ -39,201 +34,196 @@
 import android.text.Spanned;
 import android.text.style.TextAppearanceSpan;
 import android.view.MotionEvent;
-
-import com.android.setupwizardlib.robolectric.SuwLibRobolectricTestRunner;
 import com.android.setupwizardlib.span.LinkSpan;
 import com.android.setupwizardlib.span.LinkSpan.OnLinkClickListener;
 import com.android.setupwizardlib.view.TouchableMovementMethod.TouchableLinkMovementMethod;
-
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
 import org.robolectric.annotation.Config;
 
-import java.util.Arrays;
-
-@RunWith(SuwLibRobolectricTestRunner.class)
-@Config(sdk = { Config.OLDEST_SDK, Config.NEWEST_SDK })
+@RunWith(RobolectricTestRunner.class)
+@Config(sdk = {Config.OLDEST_SDK, Config.NEWEST_SDK})
 public class RichTextViewTest {
 
-    @Test
-    public void testLinkAnnotation() {
-        Annotation link = new Annotation("link", "foobar");
-        SpannableStringBuilder ssb = new SpannableStringBuilder("Hello world");
-        ssb.setSpan(link, 1, 2, 0 /* flags */);
+  @Test
+  public void testLinkAnnotation() {
+    Annotation link = new Annotation("link", "foobar");
+    SpannableStringBuilder ssb = new SpannableStringBuilder("Hello world");
+    ssb.setSpan(link, 1, 2, 0 /* flags */);
 
-        RichTextView textView = new RichTextView(application);
-        textView.setText(ssb);
+    RichTextView textView = new RichTextView(application);
+    textView.setText(ssb);
 
-        final CharSequence text = textView.getText();
-        assertTrue("Text should be spanned", text instanceof Spanned);
+    final CharSequence text = textView.getText();
+    assertThat(text).isInstanceOf(Spanned.class);
 
-        assertThat(textView.getMovementMethod()).isInstanceOf(TouchableLinkMovementMethod.class);
+    assertThat(textView.getMovementMethod()).isInstanceOf(TouchableLinkMovementMethod.class);
 
-        Object[] spans = ((Spanned) text).getSpans(0, text.length(), Annotation.class);
-        assertEquals("Annotation should be removed " + Arrays.toString(spans), 0, spans.length);
+    Object[] spans = ((Spanned) text).getSpans(0, text.length(), Annotation.class);
+    assertThat(spans).isEmpty();
 
-        spans = ((Spanned) text).getSpans(0, text.length(), LinkSpan.class);
-        assertEquals("There should be one span " + Arrays.toString(spans), 1, spans.length);
-        assertTrue("The span should be a LinkSpan", spans[0] instanceof LinkSpan);
-        assertEquals("The LinkSpan should have id \"foobar\"",
-                "foobar", ((LinkSpan) spans[0]).getId());
+    spans = ((Spanned) text).getSpans(0, text.length(), LinkSpan.class);
+    assertThat(spans).hasLength(1);
+    assertThat(spans[0]).isInstanceOf(LinkSpan.class);
+    assertWithMessage("The LinkSpan should have id \"foobar\"")
+        .that(((LinkSpan) spans[0]).getId())
+        .isEqualTo("foobar");
+  }
+
+  @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(application);
+    textView.setText(ssb);
+
+    OnLinkClickListener listener = mock(OnLinkClickListener.class);
+    textView.setOnLinkClickListener(listener);
+
+    assertThat(textView.getOnLinkClickListener()).isSameAs(listener);
+
+    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 = new TestContext(application);
+    context.delegate = mock(LinkSpan.OnClickListener.class);
+    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.delegate).onClick(eq(spans[0]));
+  }
+
+  @Test
+  public void onTouchEvent_clickOnLinks_shouldReturnTrue() {
+    Annotation link = new Annotation("link", "foobar");
+    SpannableStringBuilder ssb = new SpannableStringBuilder("Hello world");
+    ssb.setSpan(link, 0, 2, 0 /* flags */);
+
+    RichTextView textView = new RichTextView(application);
+    textView.setText(ssb);
+
+    TouchableLinkMovementMethod mockMovementMethod = mock(TouchableLinkMovementMethod.class);
+    textView.setMovementMethod(mockMovementMethod);
+
+    MotionEvent motionEvent = MotionEvent.obtain(123, 22, MotionEvent.ACTION_DOWN, 0, 0, 0);
+    doReturn(motionEvent).when(mockMovementMethod).getLastTouchEvent();
+    doReturn(true).when(mockMovementMethod).isLastTouchEventHandled();
+    assertThat(textView.onTouchEvent(motionEvent)).isTrue();
+  }
+
+  @Test
+  public void onTouchEvent_clickOutsideLinks_shouldReturnFalse() {
+    Annotation link = new Annotation("link", "foobar");
+    SpannableStringBuilder ssb = new SpannableStringBuilder("Hello world");
+    ssb.setSpan(link, 0, 2, 0 /* flags */);
+
+    RichTextView textView = new RichTextView(application);
+    textView.setText(ssb);
+
+    TouchableLinkMovementMethod mockMovementMethod = mock(TouchableLinkMovementMethod.class);
+    textView.setMovementMethod(mockMovementMethod);
+
+    MotionEvent motionEvent = MotionEvent.obtain(123, 22, MotionEvent.ACTION_DOWN, 0, 0, 0);
+    doReturn(motionEvent).when(mockMovementMethod).getLastTouchEvent();
+    doReturn(false).when(mockMovementMethod).isLastTouchEventHandled();
+    assertThat(textView.onTouchEvent(motionEvent)).isFalse();
+  }
+
+  @Test
+  public void testTextStyle() {
+    Annotation link = new Annotation("textAppearance", "foobar");
+    SpannableStringBuilder ssb = new SpannableStringBuilder("Hello world");
+    ssb.setSpan(link, 1, 2, 0 /* flags */);
+
+    RichTextView textView = new RichTextView(application);
+    textView.setText(ssb);
+
+    final CharSequence text = textView.getText();
+    assertThat(text).isInstanceOf(Spanned.class);
+
+    Object[] spans = ((Spanned) text).getSpans(0, text.length(), Annotation.class);
+    assertThat(spans).isEmpty();
+
+    spans = ((Spanned) text).getSpans(0, text.length(), TextAppearanceSpan.class);
+    assertThat(spans).hasLength(1);
+    assertThat(spans[0]).isInstanceOf(TextAppearanceSpan.class);
+  }
+
+  @Test
+  public void testTextContainingLinksAreFocusable() {
+    Annotation testLink = new Annotation("link", "value");
+    SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder("Linked");
+    spannableStringBuilder.setSpan(testLink, 0, 3, 0);
+
+    RichTextView view = new RichTextView(application);
+    view.setText(spannableStringBuilder);
+
+    assertThat(view.isFocusable()).named("view focusable").isTrue();
+  }
+
+  @SuppressLint("SetTextI18n") // It's OK. This is just a test.
+  @Test
+  public void testTextContainingNoLinksAreNotFocusable() {
+    RichTextView textView = new RichTextView(application);
+    textView.setText("Thou shall not be focusable!");
+
+    assertThat(textView.isFocusable()).named("view focusable").isFalse();
+  }
+
+  // Based on the text contents of the text view, the "focusable" property of the element
+  // should also be automatically changed.
+  @SuppressLint("SetTextI18n") // It's OK. This is just a test.
+  @Test
+  public void testRichTextViewFocusChangesWithTextChange() {
+    RichTextView textView = new RichTextView(application);
+    textView.setText("Thou shall not be focusable!");
+
+    assertThat(textView.isFocusable()).isFalse();
+    assertThat(textView.isFocusableInTouchMode()).isFalse();
+
+    SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder("I am focusable");
+    spannableStringBuilder.setSpan(new Annotation("link", "focus:on_me"), 0, 1, 0);
+    textView.setText(spannableStringBuilder);
+    assertThat(textView.isFocusable()).isTrue();
+    if (VERSION.SDK_INT >= VERSION_CODES.N_MR1) {
+      assertThat(textView.isFocusableInTouchMode()).isTrue();
+      assertThat(textView.getRevealOnFocusHint()).isFalse();
+    } else {
+      assertThat(textView.isFocusableInTouchMode()).isFalse();
+    }
+  }
+
+  public static class TestContext extends ContextWrapper implements LinkSpan.OnClickListener {
+
+    LinkSpan.OnClickListener delegate;
+
+    public TestContext(Context base) {
+      super(base);
     }
 
-    @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(application);
-        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]));
+    @Override
+    public void onClick(LinkSpan span) {
+      if (delegate != null) {
+        delegate.onClick(span);
+      }
     }
-
-    @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(application));
-        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 onTouchEvent_clickOnLinks_shouldReturnTrue() {
-        Annotation link = new Annotation("link", "foobar");
-        SpannableStringBuilder ssb = new SpannableStringBuilder("Hello world");
-        ssb.setSpan(link, 0, 2, 0 /* flags */);
-
-        RichTextView textView = new RichTextView(application);
-        textView.setText(ssb);
-
-        TouchableLinkMovementMethod mockMovementMethod = mock(TouchableLinkMovementMethod.class);
-        textView.setMovementMethod(mockMovementMethod);
-
-        MotionEvent motionEvent =
-                MotionEvent.obtain(123, 22, MotionEvent.ACTION_DOWN, 0, 0, 0);
-        doReturn(motionEvent).when(mockMovementMethod).getLastTouchEvent();
-        doReturn(true).when(mockMovementMethod).isLastTouchEventHandled();
-        assertThat(textView.onTouchEvent(motionEvent)).isTrue();
-    }
-
-    @Test
-    public void onTouchEvent_clickOutsideLinks_shouldReturnFalse() {
-        Annotation link = new Annotation("link", "foobar");
-        SpannableStringBuilder ssb = new SpannableStringBuilder("Hello world");
-        ssb.setSpan(link, 0, 2, 0 /* flags */);
-
-        RichTextView textView = new RichTextView(application);
-        textView.setText(ssb);
-
-        TouchableLinkMovementMethod mockMovementMethod = mock(TouchableLinkMovementMethod.class);
-        textView.setMovementMethod(mockMovementMethod);
-
-        MotionEvent motionEvent =
-                MotionEvent.obtain(123, 22, MotionEvent.ACTION_DOWN, 0, 0, 0);
-        doReturn(motionEvent).when(mockMovementMethod).getLastTouchEvent();
-        doReturn(false).when(mockMovementMethod).isLastTouchEventHandled();
-        assertThat(textView.onTouchEvent(motionEvent)).isFalse();
-    }
-
-    @Test
-    public void testTextStyle() {
-        Annotation link = new Annotation("textAppearance", "foobar");
-        SpannableStringBuilder ssb = new SpannableStringBuilder("Hello world");
-        ssb.setSpan(link, 1, 2, 0 /* flags */);
-
-        RichTextView textView = new RichTextView(application);
-        textView.setText(ssb);
-
-        final CharSequence text = textView.getText();
-        assertTrue("Text should be spanned", text instanceof Spanned);
-
-        Object[] spans = ((Spanned) text).getSpans(0, text.length(), Annotation.class);
-        assertEquals("Annotation should be removed " + Arrays.toString(spans), 0, spans.length);
-
-        spans = ((Spanned) text).getSpans(0, text.length(), TextAppearanceSpan.class);
-        assertEquals("There should be one span " + Arrays.toString(spans), 1, spans.length);
-        assertTrue("The span should be a TextAppearanceSpan",
-                spans[0] instanceof TextAppearanceSpan);
-    }
-
-    @Test
-    public void testTextContainingLinksAreFocusable() {
-        Annotation testLink = new Annotation("link", "value");
-        SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder("Linked");
-        spannableStringBuilder.setSpan(testLink, 0, 3, 0);
-
-        RichTextView view = new RichTextView(application);
-        view.setText(spannableStringBuilder);
-
-        assertTrue("TextView should be focusable since it contains spans", view.isFocusable());
-    }
-
-
-    @SuppressLint("SetTextI18n")  // It's OK. This is just a test.
-    @Test
-    public void testTextContainingNoLinksAreNotFocusable() {
-        RichTextView textView = new RichTextView(application);
-        textView.setText("Thou shall not be focusable!");
-
-        assertFalse("TextView should not be focusable since it does not contain any span",
-                textView.isFocusable());
-    }
-
-
-    // Based on the text contents of the text view, the "focusable" property of the element
-    // should also be automatically changed.
-    @SuppressLint("SetTextI18n")  // It's OK. This is just a test.
-    @Test
-    public void testRichTextViewFocusChangesWithTextChange() {
-        RichTextView textView = new RichTextView(application);
-        textView.setText("Thou shall not be focusable!");
-
-        assertFalse(textView.isFocusable());
-        assertFalse(textView.isFocusableInTouchMode());
-
-        SpannableStringBuilder spannableStringBuilder =
-                new SpannableStringBuilder("I am focusable");
-        spannableStringBuilder.setSpan(new Annotation("link", "focus:on_me"), 0, 1, 0);
-        textView.setText(spannableStringBuilder);
-        assertTrue(textView.isFocusable());
-        if (VERSION.SDK_INT >= VERSION_CODES.N_MR1) {
-            assertTrue(textView.isFocusableInTouchMode());
-            assertFalse(textView.getRevealOnFocusHint());
-        } else {
-            assertFalse(textView.isFocusableInTouchMode());
-        }
-    }
-
-    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/navigationbar/Android.bp b/navigationbar/Android.bp
deleted file mode 100644
index 3868fe1..0000000
--- a/navigationbar/Android.bp
+++ /dev/null
@@ -1,7 +0,0 @@
-android_library {
-    name: "setup-wizard-navbar",
-
-    sdk_version: "current",
-    resource_dirs: ["res"],
-    srcs: ["src/**/*.java"],
-}
diff --git a/navigationbar/AndroidManifest.xml b/navigationbar/AndroidManifest.xml
deleted file mode 100644
index f1029cf..0000000
--- a/navigationbar/AndroidManifest.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.android.setupwizard.navigationbar">
-</manifest>
diff --git a/navigationbar/common.mk b/navigationbar/common.mk
deleted file mode 100644
index bacc931..0000000
--- a/navigationbar/common.mk
+++ /dev/null
@@ -1,17 +0,0 @@
-#
-# Include this make file to build your application against this module.
-#
-# Make sure to include it after you've set all your desired LOCAL variables.
-# Note that you must explicitly set your LOCAL_RESOURCE_DIR before including this file.
-#
-# For example:
-#
-#   LOCAL_RESOURCE_DIR := \
-#        $(LOCAL_PATH)/res
-#
-#   include frameworks/opt/setupwizard/navigationbar/common.mk
-#
-
-LOCAL_RESOURCE_DIR += $(call my-dir)/res
-LOCAL_AAPT_FLAGS += --auto-add-overlay --extra-packages com.android.setupwizard.navigationbar
-LOCAL_STATIC_JAVA_LIBRARIES += setup-wizard-navbar
diff --git a/navigationbar/project.properties b/navigationbar/project.properties
deleted file mode 100644
index 823f52e..0000000
--- a/navigationbar/project.properties
+++ /dev/null
@@ -1 +0,0 @@
-android.library=true
diff --git a/navigationbar/res/drawable/setup_wizard_navbar_btn_bg.xml b/navigationbar/res/drawable/setup_wizard_navbar_btn_bg.xml
deleted file mode 100644
index 0fc46be..0000000
--- a/navigationbar/res/drawable/setup_wizard_navbar_btn_bg.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="?android:attr/colorControlHighlight">
-</ripple>
diff --git a/navigationbar/res/drawable/setup_wizard_navbar_ic_back.xml b/navigationbar/res/drawable/setup_wizard_navbar_ic_back.xml
deleted file mode 100644
index 260e913..0000000
--- a/navigationbar/res/drawable/setup_wizard_navbar_ic_back.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-    Copyright (C) 2014 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-        http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:autoMirrored="true"
-    android:width="@dimen/setup_wizard_navbar_ic_intrinsic_size"
-    android:height="@dimen/setup_wizard_navbar_ic_intrinsic_size"
-    android:viewportWidth="24"
-    android:viewportHeight="24">
-
-    <path
-        android:fillColor="?attr/setup_wizard_navbar_text_color"
-        android:pathData="M15.4,7.4l-1.4,-1.4 -6,6 6,6 1.4,-1.4 -4.6,-4.6z" />
-
-</vector>
diff --git a/navigationbar/res/drawable/setup_wizard_navbar_ic_next.xml b/navigationbar/res/drawable/setup_wizard_navbar_ic_next.xml
deleted file mode 100644
index 1b40776..0000000
--- a/navigationbar/res/drawable/setup_wizard_navbar_ic_next.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-    Copyright (C) 2014 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-        http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:autoMirrored="true"
-    android:width="@dimen/setup_wizard_navbar_ic_intrinsic_size"
-    android:height="@dimen/setup_wizard_navbar_ic_intrinsic_size"
-    android:viewportWidth="24"
-    android:viewportHeight="24">
-
-    <path
-        android:fillColor="?attr/setup_wizard_navbar_text_color"
-        android:pathData="M10,6 l-1.4,1.4 4.6,4.6 -4.6,4.6 1.4,1.4 6,-6z" />
-
-</vector>
diff --git a/navigationbar/res/layout/setup_wizard_navbar_layout.xml b/navigationbar/res/layout/setup_wizard_navbar_layout.xml
deleted file mode 100644
index 6bdb02d..0000000
--- a/navigationbar/res/layout/setup_wizard_navbar_layout.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    style="@style/setup_wizard_navbar_style" >
-
-    <view
-        class="com.android.setupwizard.navigationbar.SetupWizardNavBar$NavButton"
-        style="@style/setup_wizard_navbar_button_style"
-        android:id="@+id/setup_wizard_navbar_back"
-        android:contentDescription="@string/setup_wizard_back_button_label"
-        android:drawableStart="@drawable/setup_wizard_navbar_ic_back" />
-
-    <View
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:layout_weight="1"
-        android:visibility="invisible" />
-
-    <view
-        class="com.android.setupwizard.navigationbar.SetupWizardNavBar$NavButton"
-        style="@style/setup_wizard_navbar_button_style"
-        android:text="@string/setup_wizard_next_button_label"
-        android:gravity="end|center_vertical"
-        android:drawableEnd="@drawable/setup_wizard_navbar_ic_next"
-        android:id="@+id/setup_wizard_navbar_next" />
-
-</LinearLayout>
\ No newline at end of file
diff --git a/navigationbar/res/values-af/strings.xml b/navigationbar/res/values-af/strings.xml
deleted file mode 100644
index 4f7af72..0000000
--- a/navigationbar/res/values-af/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Volgende"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Terug"</string>
-</resources>
diff --git a/navigationbar/res/values-am/strings.xml b/navigationbar/res/values-am/strings.xml
deleted file mode 100644
index d7c3353..0000000
--- a/navigationbar/res/values-am/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"ቀጣይ"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"ተመለስ"</string>
-</resources>
diff --git a/navigationbar/res/values-ar/strings.xml b/navigationbar/res/values-ar/strings.xml
deleted file mode 100644
index b84a0a7..0000000
--- a/navigationbar/res/values-ar/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"التالي"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"رجوع"</string>
-</resources>
diff --git a/navigationbar/res/values-az/strings.xml b/navigationbar/res/values-az/strings.xml
deleted file mode 100644
index 99f211c..0000000
--- a/navigationbar/res/values-az/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Növbəti"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Geri"</string>
-</resources>
diff --git a/navigationbar/res/values-b+sr+Latn/strings.xml b/navigationbar/res/values-b+sr+Latn/strings.xml
deleted file mode 100644
index 4dbd175..0000000
--- a/navigationbar/res/values-b+sr+Latn/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Dalje"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Nazad"</string>
-</resources>
diff --git a/navigationbar/res/values-be/strings.xml b/navigationbar/res/values-be/strings.xml
deleted file mode 100644
index 3612fce..0000000
--- a/navigationbar/res/values-be/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Далей"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Назад"</string>
-</resources>
diff --git a/navigationbar/res/values-bg/strings.xml b/navigationbar/res/values-bg/strings.xml
deleted file mode 100644
index e28abf9..0000000
--- a/navigationbar/res/values-bg/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Напред"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Назад"</string>
-</resources>
diff --git a/navigationbar/res/values-bn/strings.xml b/navigationbar/res/values-bn/strings.xml
deleted file mode 100644
index b7db1f2..0000000
--- a/navigationbar/res/values-bn/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"পরবর্তী"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"পিছনে"</string>
-</resources>
diff --git a/navigationbar/res/values-bs/strings.xml b/navigationbar/res/values-bs/strings.xml
deleted file mode 100644
index 1475017..0000000
--- a/navigationbar/res/values-bs/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Naprijed"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Nazad"</string>
-</resources>
diff --git a/navigationbar/res/values-ca/strings.xml b/navigationbar/res/values-ca/strings.xml
deleted file mode 100644
index 697af97..0000000
--- a/navigationbar/res/values-ca/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Següent"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Enrere"</string>
-</resources>
diff --git a/navigationbar/res/values-cs/strings.xml b/navigationbar/res/values-cs/strings.xml
deleted file mode 100644
index 2f3938a..0000000
--- a/navigationbar/res/values-cs/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Další"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Zpět"</string>
-</resources>
diff --git a/navigationbar/res/values-da/strings.xml b/navigationbar/res/values-da/strings.xml
deleted file mode 100644
index 487c575..0000000
--- a/navigationbar/res/values-da/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Næste"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Tilbage"</string>
-</resources>
diff --git a/navigationbar/res/values-de/strings.xml b/navigationbar/res/values-de/strings.xml
deleted file mode 100644
index e1e009f..0000000
--- a/navigationbar/res/values-de/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Weiter"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Zurück"</string>
-</resources>
diff --git a/navigationbar/res/values-el/strings.xml b/navigationbar/res/values-el/strings.xml
deleted file mode 100644
index 77aa32b..0000000
--- a/navigationbar/res/values-el/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Επόμενο"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Πίσω"</string>
-</resources>
diff --git a/navigationbar/res/values-en-rAU/strings.xml b/navigationbar/res/values-en-rAU/strings.xml
deleted file mode 100644
index b06dc86..0000000
--- a/navigationbar/res/values-en-rAU/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Next"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Back"</string>
-</resources>
diff --git a/navigationbar/res/values-en-rCA/strings.xml b/navigationbar/res/values-en-rCA/strings.xml
deleted file mode 100644
index b06dc86..0000000
--- a/navigationbar/res/values-en-rCA/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Next"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Back"</string>
-</resources>
diff --git a/navigationbar/res/values-en-rGB/strings.xml b/navigationbar/res/values-en-rGB/strings.xml
deleted file mode 100644
index b06dc86..0000000
--- a/navigationbar/res/values-en-rGB/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Next"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Back"</string>
-</resources>
diff --git a/navigationbar/res/values-en-rIN/strings.xml b/navigationbar/res/values-en-rIN/strings.xml
deleted file mode 100644
index b06dc86..0000000
--- a/navigationbar/res/values-en-rIN/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Next"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Back"</string>
-</resources>
diff --git a/navigationbar/res/values-en-rXC/strings.xml b/navigationbar/res/values-en-rXC/strings.xml
deleted file mode 100644
index 5c7c658..0000000
--- a/navigationbar/res/values-en-rXC/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‎‏‏‏‎‎‎‏‎‏‎‏‏‏‎‏‏‎‏‎‎‏‎‎‏‏‎‏‏‏‎‎‏‏‎‎‏‏‏‏‎‎‏‏‏‏‎‏‎‏‏‎‏‏‏‎Next‎‏‎‎‏‎"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‏‏‏‏‏‏‎‏‎‎‏‏‏‏‎‏‏‏‏‏‎‎‏‎‏‏‎‏‏‎‏‎‏‎‎‏‎‏‎‎‏‎‏‎‏‎‏‏‏‎‎‏‏‎‏‎‏‎‏‎‎‏‎‏‏‏‎‏‎‎Back‎‏‎‎‏‎"</string>
-</resources>
diff --git a/navigationbar/res/values-es-rUS/strings.xml b/navigationbar/res/values-es-rUS/strings.xml
deleted file mode 100644
index 3a23344..0000000
--- a/navigationbar/res/values-es-rUS/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Siguiente"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Atrás"</string>
-</resources>
diff --git a/navigationbar/res/values-es/strings.xml b/navigationbar/res/values-es/strings.xml
deleted file mode 100644
index 3a23344..0000000
--- a/navigationbar/res/values-es/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Siguiente"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Atrás"</string>
-</resources>
diff --git a/navigationbar/res/values-et/strings.xml b/navigationbar/res/values-et/strings.xml
deleted file mode 100644
index 7413fec..0000000
--- a/navigationbar/res/values-et/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Järgmine"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Tagasi"</string>
-</resources>
diff --git a/navigationbar/res/values-eu/strings.xml b/navigationbar/res/values-eu/strings.xml
deleted file mode 100644
index c108751..0000000
--- a/navigationbar/res/values-eu/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Hurrengoa"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Aurrekoa"</string>
-</resources>
diff --git a/navigationbar/res/values-fa/strings.xml b/navigationbar/res/values-fa/strings.xml
deleted file mode 100644
index 88c2a83..0000000
--- a/navigationbar/res/values-fa/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"بعدی"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"برگشت"</string>
-</resources>
diff --git a/navigationbar/res/values-fi/strings.xml b/navigationbar/res/values-fi/strings.xml
deleted file mode 100644
index 1bf32f2..0000000
--- a/navigationbar/res/values-fi/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Seuraava"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Edellinen"</string>
-</resources>
diff --git a/navigationbar/res/values-fr-rCA/strings.xml b/navigationbar/res/values-fr-rCA/strings.xml
deleted file mode 100644
index cea38c0..0000000
--- a/navigationbar/res/values-fr-rCA/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Suivant"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Précédent"</string>
-</resources>
diff --git a/navigationbar/res/values-fr/strings.xml b/navigationbar/res/values-fr/strings.xml
deleted file mode 100644
index cea38c0..0000000
--- a/navigationbar/res/values-fr/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Suivant"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Précédent"</string>
-</resources>
diff --git a/navigationbar/res/values-gl/strings.xml b/navigationbar/res/values-gl/strings.xml
deleted file mode 100644
index 52e9248..0000000
--- a/navigationbar/res/values-gl/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Seguinte"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Atrás"</string>
-</resources>
diff --git a/navigationbar/res/values-gu/strings.xml b/navigationbar/res/values-gu/strings.xml
deleted file mode 100644
index aa3efe8..0000000
--- a/navigationbar/res/values-gu/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"આગલું"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"પાછળ"</string>
-</resources>
diff --git a/navigationbar/res/values-hi/strings.xml b/navigationbar/res/values-hi/strings.xml
deleted file mode 100644
index f8e9f9d..0000000
--- a/navigationbar/res/values-hi/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"आगे बढ़ें"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"पीछे"</string>
-</resources>
diff --git a/navigationbar/res/values-hr/strings.xml b/navigationbar/res/values-hr/strings.xml
deleted file mode 100644
index 071e451..0000000
--- a/navigationbar/res/values-hr/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Dalje"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Natrag"</string>
-</resources>
diff --git a/navigationbar/res/values-hu/strings.xml b/navigationbar/res/values-hu/strings.xml
deleted file mode 100644
index ed8349d..0000000
--- a/navigationbar/res/values-hu/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Következő"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Vissza"</string>
-</resources>
diff --git a/navigationbar/res/values-hy/strings.xml b/navigationbar/res/values-hy/strings.xml
deleted file mode 100644
index 4dd2fd5..0000000
--- a/navigationbar/res/values-hy/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Հաջորդը"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Հետ"</string>
-</resources>
diff --git a/navigationbar/res/values-in/strings.xml b/navigationbar/res/values-in/strings.xml
deleted file mode 100644
index bc6bc1e..0000000
--- a/navigationbar/res/values-in/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Berikutnya"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Kembali"</string>
-</resources>
diff --git a/navigationbar/res/values-is/strings.xml b/navigationbar/res/values-is/strings.xml
deleted file mode 100644
index 22d32b7..0000000
--- a/navigationbar/res/values-is/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Áfram"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Til baka"</string>
-</resources>
diff --git a/navigationbar/res/values-it/strings.xml b/navigationbar/res/values-it/strings.xml
deleted file mode 100644
index 0b61a83..0000000
--- a/navigationbar/res/values-it/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Avanti"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Indietro"</string>
-</resources>
diff --git a/navigationbar/res/values-iw/strings.xml b/navigationbar/res/values-iw/strings.xml
deleted file mode 100644
index 93f956e..0000000
--- a/navigationbar/res/values-iw/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"הבא"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"הקודם"</string>
-</resources>
diff --git a/navigationbar/res/values-ja/strings.xml b/navigationbar/res/values-ja/strings.xml
deleted file mode 100644
index 430a0cf..0000000
--- a/navigationbar/res/values-ja/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"次へ"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"戻る"</string>
-</resources>
diff --git a/navigationbar/res/values-ka/strings.xml b/navigationbar/res/values-ka/strings.xml
deleted file mode 100644
index 8263905..0000000
--- a/navigationbar/res/values-ka/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"შემდეგი"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"უკან"</string>
-</resources>
diff --git a/navigationbar/res/values-kk/strings.xml b/navigationbar/res/values-kk/strings.xml
deleted file mode 100644
index 358dadb..0000000
--- a/navigationbar/res/values-kk/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Келесі"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Артқа"</string>
-</resources>
diff --git a/navigationbar/res/values-km/strings.xml b/navigationbar/res/values-km/strings.xml
deleted file mode 100644
index 480e4d4..0000000
--- a/navigationbar/res/values-km/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"បន្ទាប់"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"ថយក្រោយ"</string>
-</resources>
diff --git a/navigationbar/res/values-kn/strings.xml b/navigationbar/res/values-kn/strings.xml
deleted file mode 100644
index 333e1a2..0000000
--- a/navigationbar/res/values-kn/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"ಮುಂದೆ"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"ಹಿಂದೆ"</string>
-</resources>
diff --git a/navigationbar/res/values-ko/strings.xml b/navigationbar/res/values-ko/strings.xml
deleted file mode 100644
index 66510cc..0000000
--- a/navigationbar/res/values-ko/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"다음"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"뒤로"</string>
-</resources>
diff --git a/navigationbar/res/values-ky/strings.xml b/navigationbar/res/values-ky/strings.xml
deleted file mode 100644
index ee8e164..0000000
--- a/navigationbar/res/values-ky/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Кийинки"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Артка"</string>
-</resources>
diff --git a/navigationbar/res/values-lo/strings.xml b/navigationbar/res/values-lo/strings.xml
deleted file mode 100644
index fc2ce83..0000000
--- a/navigationbar/res/values-lo/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"ຕໍ່ໄປ"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"ກັບຄືນ"</string>
-</resources>
diff --git a/navigationbar/res/values-lt/strings.xml b/navigationbar/res/values-lt/strings.xml
deleted file mode 100644
index 183404c..0000000
--- a/navigationbar/res/values-lt/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Kitas"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Atgal"</string>
-</resources>
diff --git a/navigationbar/res/values-lv/strings.xml b/navigationbar/res/values-lv/strings.xml
deleted file mode 100644
index 2aa72ee..0000000
--- a/navigationbar/res/values-lv/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Tālāk"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Atpakaļ"</string>
-</resources>
diff --git a/navigationbar/res/values-mk/strings.xml b/navigationbar/res/values-mk/strings.xml
deleted file mode 100644
index 52c8324..0000000
--- a/navigationbar/res/values-mk/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Следно"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Назад"</string>
-</resources>
diff --git a/navigationbar/res/values-ml/strings.xml b/navigationbar/res/values-ml/strings.xml
deleted file mode 100644
index a1373bc..0000000
--- a/navigationbar/res/values-ml/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"അടുത്തത്"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"മടങ്ങുക"</string>
-</resources>
diff --git a/navigationbar/res/values-mn/strings.xml b/navigationbar/res/values-mn/strings.xml
deleted file mode 100644
index e8099ce..0000000
--- a/navigationbar/res/values-mn/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Дараах"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Буцах"</string>
-</resources>
diff --git a/navigationbar/res/values-mr/strings.xml b/navigationbar/res/values-mr/strings.xml
deleted file mode 100644
index 3668c64..0000000
--- a/navigationbar/res/values-mr/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"पुढील"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"मागे"</string>
-</resources>
diff --git a/navigationbar/res/values-ms/strings.xml b/navigationbar/res/values-ms/strings.xml
deleted file mode 100644
index d801b6d..0000000
--- a/navigationbar/res/values-ms/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Seterusnya"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Kembali"</string>
-</resources>
diff --git a/navigationbar/res/values-my/strings.xml b/navigationbar/res/values-my/strings.xml
deleted file mode 100644
index 8c5944f..0000000
--- a/navigationbar/res/values-my/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"ရှေ့သို့"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"နောက်သို့"</string>
-</resources>
diff --git a/navigationbar/res/values-nb/strings.xml b/navigationbar/res/values-nb/strings.xml
deleted file mode 100644
index c1b7b0c..0000000
--- a/navigationbar/res/values-nb/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Neste"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Tilbake"</string>
-</resources>
diff --git a/navigationbar/res/values-ne/strings.xml b/navigationbar/res/values-ne/strings.xml
deleted file mode 100644
index d806032..0000000
--- a/navigationbar/res/values-ne/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"अर्को"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"पछाडि"</string>
-</resources>
diff --git a/navigationbar/res/values-nl/strings.xml b/navigationbar/res/values-nl/strings.xml
deleted file mode 100644
index 4f7af72..0000000
--- a/navigationbar/res/values-nl/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Volgende"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Terug"</string>
-</resources>
diff --git a/navigationbar/res/values-pa/strings.xml b/navigationbar/res/values-pa/strings.xml
deleted file mode 100644
index f1a53bc..0000000
--- a/navigationbar/res/values-pa/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"ਅੱਗੇ"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"ਪਿੱਛੇ"</string>
-</resources>
diff --git a/navigationbar/res/values-pl/strings.xml b/navigationbar/res/values-pl/strings.xml
deleted file mode 100644
index f47a9a7..0000000
--- a/navigationbar/res/values-pl/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Dalej"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Wstecz"</string>
-</resources>
diff --git a/navigationbar/res/values-pt-rBR/strings.xml b/navigationbar/res/values-pt-rBR/strings.xml
deleted file mode 100644
index 181fafe..0000000
--- a/navigationbar/res/values-pt-rBR/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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óxima"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Voltar"</string>
-</resources>
diff --git a/navigationbar/res/values-pt-rPT/strings.xml b/navigationbar/res/values-pt-rPT/strings.xml
deleted file mode 100644
index 6be511f..0000000
--- a/navigationbar/res/values-pt-rPT/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Seguinte"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Anterior"</string>
-</resources>
diff --git a/navigationbar/res/values-pt/strings.xml b/navigationbar/res/values-pt/strings.xml
deleted file mode 100644
index 181fafe..0000000
--- a/navigationbar/res/values-pt/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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óxima"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Voltar"</string>
-</resources>
diff --git a/navigationbar/res/values-ro/strings.xml b/navigationbar/res/values-ro/strings.xml
deleted file mode 100644
index 1c75ba8..0000000
--- a/navigationbar/res/values-ro/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Înainte"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Înapoi"</string>
-</resources>
diff --git a/navigationbar/res/values-ru/strings.xml b/navigationbar/res/values-ru/strings.xml
deleted file mode 100644
index 454613a..0000000
--- a/navigationbar/res/values-ru/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Далее"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Назад"</string>
-</resources>
diff --git a/navigationbar/res/values-si/strings.xml b/navigationbar/res/values-si/strings.xml
deleted file mode 100644
index 892a100..0000000
--- a/navigationbar/res/values-si/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"මීළඟ"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"ආපසු"</string>
-</resources>
diff --git a/navigationbar/res/values-sk/strings.xml b/navigationbar/res/values-sk/strings.xml
deleted file mode 100644
index 6d8e36d..0000000
--- a/navigationbar/res/values-sk/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Ďalej"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Späť"</string>
-</resources>
diff --git a/navigationbar/res/values-sl/strings.xml b/navigationbar/res/values-sl/strings.xml
deleted file mode 100644
index ebc916a..0000000
--- a/navigationbar/res/values-sl/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Naprej"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Nazaj"</string>
-</resources>
diff --git a/navigationbar/res/values-sq/strings.xml b/navigationbar/res/values-sq/strings.xml
deleted file mode 100644
index 7f4f95d..0000000
--- a/navigationbar/res/values-sq/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Përpara"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Prapa"</string>
-</resources>
diff --git a/navigationbar/res/values-sr/strings.xml b/navigationbar/res/values-sr/strings.xml
deleted file mode 100644
index 531d772..0000000
--- a/navigationbar/res/values-sr/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Даље"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Назад"</string>
-</resources>
diff --git a/navigationbar/res/values-sv/strings.xml b/navigationbar/res/values-sv/strings.xml
deleted file mode 100644
index db88fdc..0000000
--- a/navigationbar/res/values-sv/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Nästa"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Föregående"</string>
-</resources>
diff --git a/navigationbar/res/values-sw/strings.xml b/navigationbar/res/values-sw/strings.xml
deleted file mode 100644
index c02d768..0000000
--- a/navigationbar/res/values-sw/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Endelea"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Rudi nyuma"</string>
-</resources>
diff --git a/navigationbar/res/values-ta/strings.xml b/navigationbar/res/values-ta/strings.xml
deleted file mode 100644
index 3e3ef59..0000000
--- a/navigationbar/res/values-ta/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"அடுத்து"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"பின்செல்"</string>
-</resources>
diff --git a/navigationbar/res/values-te/strings.xml b/navigationbar/res/values-te/strings.xml
deleted file mode 100644
index fdf5c75..0000000
--- a/navigationbar/res/values-te/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"తర్వాత"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"వెనుకకు"</string>
-</resources>
diff --git a/navigationbar/res/values-th/strings.xml b/navigationbar/res/values-th/strings.xml
deleted file mode 100644
index d926d6d..0000000
--- a/navigationbar/res/values-th/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"ถัดไป"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"กลับ"</string>
-</resources>
diff --git a/navigationbar/res/values-tl/strings.xml b/navigationbar/res/values-tl/strings.xml
deleted file mode 100644
index 68b8a43..0000000
--- a/navigationbar/res/values-tl/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Susunod"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Bumalik"</string>
-</resources>
diff --git a/navigationbar/res/values-tr/strings.xml b/navigationbar/res/values-tr/strings.xml
deleted file mode 100644
index 5e667b6..0000000
--- a/navigationbar/res/values-tr/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"İleri"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Geri"</string>
-</resources>
diff --git a/navigationbar/res/values-uk/strings.xml b/navigationbar/res/values-uk/strings.xml
deleted file mode 100644
index 9133704..0000000
--- a/navigationbar/res/values-uk/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Далі"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Назад"</string>
-</resources>
diff --git a/navigationbar/res/values-ur/strings.xml b/navigationbar/res/values-ur/strings.xml
deleted file mode 100644
index 00825ab..0000000
--- a/navigationbar/res/values-ur/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"آگے"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"پیچھے"</string>
-</resources>
diff --git a/navigationbar/res/values-uz/strings.xml b/navigationbar/res/values-uz/strings.xml
deleted file mode 100644
index 0f9e086..0000000
--- a/navigationbar/res/values-uz/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Keyingisi"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Orqaga"</string>
-</resources>
diff --git a/navigationbar/res/values-vi/strings.xml b/navigationbar/res/values-vi/strings.xml
deleted file mode 100644
index 3e822ec..0000000
--- a/navigationbar/res/values-vi/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Tiếp theo"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Quay lại"</string>
-</resources>
diff --git a/navigationbar/res/values-zh-rCN/strings.xml b/navigationbar/res/values-zh-rCN/strings.xml
deleted file mode 100644
index e7e2394..0000000
--- a/navigationbar/res/values-zh-rCN/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"下一步"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"返回"</string>
-</resources>
diff --git a/navigationbar/res/values-zh-rHK/strings.xml b/navigationbar/res/values-zh-rHK/strings.xml
deleted file mode 100644
index e7e2394..0000000
--- a/navigationbar/res/values-zh-rHK/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"下一步"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"返回"</string>
-</resources>
diff --git a/navigationbar/res/values-zh-rTW/strings.xml b/navigationbar/res/values-zh-rTW/strings.xml
deleted file mode 100644
index 56ae800..0000000
--- a/navigationbar/res/values-zh-rTW/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"繼續"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"返回"</string>
-</resources>
diff --git a/navigationbar/res/values-zu/strings.xml b/navigationbar/res/values-zu/strings.xml
deleted file mode 100644
index b5b0eee..0000000
--- a/navigationbar/res/values-zu/strings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?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">"Okulandelayo"</string>
-    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"Phindela emuva"</string>
-</resources>
diff --git a/navigationbar/res/values/colors.xml b/navigationbar/res/values/colors.xml
deleted file mode 100644
index 1cdf964..0000000
--- a/navigationbar/res/values/colors.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
-    <color name="setup_wizard_navbar_bg_dark">#ff21272b</color>
-    <color name="setup_wizard_navbar_bg_light">#ffe4e7e9</color>
-    <color name="setup_wizard_navbar_text_dark">#deffffff</color>
-    <color name="setup_wizard_navbar_text_light">#de000000</color>
-</resources>
diff --git a/navigationbar/res/values/dimens.xml b/navigationbar/res/values/dimens.xml
deleted file mode 100644
index df67d9f..0000000
--- a/navigationbar/res/values/dimens.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
-    <dimen name="setup_wizard_navbar_button_drawable_padding">6dp</dimen>
-    <dimen name="setup_wizard_navbar_button_padding_sides">10dp</dimen>
-    <dimen name="setup_wizard_navbar_height">56dp</dimen>
-    <dimen name="setup_wizard_navbar_ic_intrinsic_size">24dp</dimen>
-    <dimen name="setup_wizard_navbar_padding_sides">24dp</dimen>
-    <dimen name="setup_wizard_navbar_text_size">16sp</dimen>
-</resources>
diff --git a/navigationbar/res/values/strings.xml b/navigationbar/res/values/strings.xml
deleted file mode 100644
index 7f01c4e..0000000
--- a/navigationbar/res/values/strings.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
-    <!-- Button for going to the next screen or step [CHAR LIMIT=40] -->
-    <string name="setup_wizard_next_button_label">Next</string>
-
-    <!-- Button for going to the previous screen or step [CHAR LIMIT=40] -->
-    <string name="setup_wizard_back_button_label">Back</string>
-
-</resources>
diff --git a/navigationbar/res/values/styles.xml b/navigationbar/res/values/styles.xml
deleted file mode 100644
index 9aa9a85..0000000
--- a/navigationbar/res/values/styles.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
-
-    <attr name="setup_wizard_navbar_bg_color" format="color" />
-    <attr name="setup_wizard_navbar_text_color" format="color" />
-    <attr name="setup_wizard_navbar_theme" format="reference" />
-
-    <style name="setup_wizard_navbar_style">
-        <item name="android:orientation">horizontal</item>
-        <item name="android:layout_height">@dimen/setup_wizard_navbar_height</item>
-        <item name="android:layout_width">match_parent</item>
-        <item name="android:layout_alignParentBottom">true</item>
-        <item name="android:background">?attr/setup_wizard_navbar_bg_color</item>
-        <item name="android:paddingStart">@dimen/setup_wizard_navbar_padding_sides</item>
-        <item name="android:paddingEnd">@dimen/setup_wizard_navbar_padding_sides</item>
-    </style>
-
-    <style name="setup_wizard_navbar_theme_dark">
-        <item name="setup_wizard_navbar_bg_color">@color/setup_wizard_navbar_bg_dark</item>
-        <item name="setup_wizard_navbar_text_color">@color/setup_wizard_navbar_text_dark</item>
-    </style>
-
-    <style name="setup_wizard_navbar_theme_light">
-        <item name="setup_wizard_navbar_bg_color">@color/setup_wizard_navbar_bg_light</item>
-        <item name="setup_wizard_navbar_text_color">@color/setup_wizard_navbar_text_light</item>
-    </style>
-
-    <style name="setup_wizard_navbar_button_style" parent="@android:style/Widget.Material.Button.Borderless">
-        <item name="android:layout_width">wrap_content</item>
-        <item name="android:layout_height">match_parent</item>
-        <item name="android:layout_weight">0</item>
-        <item name="android:background">@drawable/setup_wizard_navbar_btn_bg</item>
-        <item name="android:drawablePadding">@dimen/setup_wizard_navbar_button_drawable_padding</item>
-        <item name="android:fontFamily">sans-serif</item>
-        <item name="android:minWidth">0dp</item>
-        <item name="android:paddingEnd">@dimen/setup_wizard_navbar_button_padding_sides</item>
-        <item name="android:paddingStart">@dimen/setup_wizard_navbar_button_padding_sides</item>
-        <item name="android:textAllCaps">true</item>
-        <item name="android:textColor">?attr/setup_wizard_navbar_text_color</item>
-        <item name="android:textSize">@dimen/setup_wizard_navbar_text_size</item>
-    </style>
-
-</resources>
diff --git a/navigationbar/src/com/android/setupwizard/navigationbar/SetupWizardNavBar.java b/navigationbar/src/com/android/setupwizard/navigationbar/SetupWizardNavBar.java
deleted file mode 100644
index e34ad6c..0000000
--- a/navigationbar/src/com/android/setupwizard/navigationbar/SetupWizardNavBar.java
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.setupwizard.navigationbar;
-
-import android.app.Activity;
-import android.app.Fragment;
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.os.Bundle;
-import android.util.AttributeSet;
-import android.view.ContextThemeWrapper;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewGroup;
-import android.view.ViewTreeObserver;
-import android.view.ViewTreeObserver.OnPreDrawListener;
-import android.widget.Button;
-
-/**
- * Fragment class for controlling the custom navigation bar shown during setup wizard. Apps in the
- * Android tree can use this by including the common.mk makefile. Apps outside of the tree can
- * create a library project out of the source.
- */
-public class SetupWizardNavBar extends Fragment implements OnPreDrawListener, OnClickListener {
-    private static final String TAG = "SetupWizardNavBar";
-    private static final int IMMERSIVE_FLAGS =
-            View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
-    private int mSystemUiFlags = IMMERSIVE_FLAGS | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
-
-    private ViewGroup mNavigationBarView;
-    private Button mNextButton;
-    private Button mBackButton;
-    private NavigationBarListener mCallback;
-
-    public interface NavigationBarListener {
-        public void onNavigationBarCreated(SetupWizardNavBar bar);
-        public void onNavigateBack();
-        public void onNavigateNext();
-    }
-
-    public SetupWizardNavBar() {
-        // no-arg constructor for fragments
-    }
-
-    @Override
-    public void onAttach(Activity activity) {
-        super.onAttach(activity);
-        mCallback = (NavigationBarListener) activity;
-    }
-
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container,
-            Bundle savedInstanceState) {
-        Context context = new ContextThemeWrapper(getActivity(), getNavbarTheme());
-        inflater = LayoutInflater.from(context);
-        mNavigationBarView = (ViewGroup) inflater.inflate(R.layout.setup_wizard_navbar_layout,
-                container, false);
-        mNextButton = (Button) mNavigationBarView.findViewById(R.id.setup_wizard_navbar_next);
-        mBackButton = (Button) mNavigationBarView.findViewById(R.id.setup_wizard_navbar_back);
-        mNextButton.setOnClickListener(this);
-        mBackButton.setOnClickListener(this);
-        return mNavigationBarView;
-    }
-
-    @Override
-    public void onViewCreated(View view, Bundle savedInstanceState) {
-        super.onViewCreated(view, savedInstanceState);
-        mCallback.onNavigationBarCreated(this);
-        mNavigationBarView.setSystemUiVisibility(mSystemUiFlags);
-
-        // Set the UI flags before draw because the visibility might change in unexpected /
-        // undetectable times, like transitioning from a finishing activity that had a keyboard
-        ViewTreeObserver viewTreeObserver = mNavigationBarView.getViewTreeObserver();
-        viewTreeObserver.addOnPreDrawListener(this);
-    }
-
-    @Override
-    public boolean onPreDraw() {
-        // View.setSystemUiVisibility checks if the visibility changes before applying them
-        // so the performance impact is contained
-        mNavigationBarView.setSystemUiVisibility(mSystemUiFlags);
-        return true;
-    }
-
-    /**
-     * Sets whether system navigation bar should be hidden.
-     * @param useImmersiveMode True to activate immersive mode and hide the system navigation bar
-     */
-    public void setUseImmersiveMode(boolean useImmersiveMode) {
-        // By default, enable layoutHideNavigation if immersive mode is used
-        setUseImmersiveMode(useImmersiveMode, useImmersiveMode);
-    }
-
-    public void setUseImmersiveMode(boolean useImmersiveMode, boolean layoutHideNavigation) {
-        if (useImmersiveMode) {
-            mSystemUiFlags |= IMMERSIVE_FLAGS;
-            if (layoutHideNavigation) {
-                mSystemUiFlags |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
-            }
-        } else {
-            mSystemUiFlags &= ~(IMMERSIVE_FLAGS | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
-        }
-        mNavigationBarView.setSystemUiVisibility(mSystemUiFlags);
-    }
-
-    private int getNavbarTheme() {
-        // Normally we can automatically guess the theme by comparing the foreground color against
-        // the background color. But we also allow specifying explicitly using
-        // setup_wizard_navbar_theme.
-        TypedArray attributes = getActivity().obtainStyledAttributes(
-                new int[] {
-                        R.attr.setup_wizard_navbar_theme,
-                        android.R.attr.colorForeground,
-                        android.R.attr.colorBackground });
-        int theme = attributes.getResourceId(0, 0);
-        if (theme == 0) {
-            // Compare the value of the foreground against the background color to see if current
-            // theme is light-on-dark or dark-on-light.
-            float[] foregroundHsv = new float[3];
-            float[] backgroundHsv = new float[3];
-            Color.colorToHSV(attributes.getColor(1, 0), foregroundHsv);
-            Color.colorToHSV(attributes.getColor(2, 0), backgroundHsv);
-            boolean isDarkBg = foregroundHsv[2] > backgroundHsv[2];
-            theme = isDarkBg ? R.style.setup_wizard_navbar_theme_dark :
-                    R.style.setup_wizard_navbar_theme_light;
-        }
-        attributes.recycle();
-        return theme;
-    }
-
-    @Override
-    public void onClick(View v) {
-        if (v == mBackButton) {
-            mCallback.onNavigateBack();
-        } else if (v == mNextButton) {
-            mCallback.onNavigateNext();
-        }
-    }
-
-    public Button getBackButton() {
-        return mBackButton;
-    }
-
-    public Button getNextButton() {
-        return mNextButton;
-    }
-
-    public static class NavButton extends Button {
-
-        public NavButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
-            super(context, attrs, defStyleAttr, defStyleRes);
-        }
-
-        public NavButton(Context context, AttributeSet attrs, int defStyleAttr) {
-            super(context, attrs, defStyleAttr);
-        }
-
-        public NavButton(Context context, AttributeSet attrs) {
-            super(context, attrs);
-        }
-
-        public NavButton(Context context) {
-            super(context);
-        }
-
-        @Override
-        public void setEnabled(boolean enabled) {
-            super.setEnabled(enabled);
-            // The color of the button is #de000000 / #deffffff when enabled. When disabled, apply
-            // additional 23% alpha, so the overall opacity is 20%.
-            setAlpha(enabled ? 1.0f : 0.23f);
-        }
-    }
-
-}
diff --git a/tools/build_for_build_server.sh b/tools/build_for_build_server.sh
index 7a8c942..1ed4ea7 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 buildProjectFull test coverage
+./gradlew buildProjectFull test coverage --info
diff --git a/tools/checkstyle/checkstyle.xml b/tools/checkstyle/checkstyle.xml
deleted file mode 100644
index 0dbccae..0000000
--- a/tools/checkstyle/checkstyle.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?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
deleted file mode 100644
index 6bf7b21..0000000
--- a/tools/checkstyle/checkstyle_suppression.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-<?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>