[automerger skipped] Migrate setup-wizard-lib to androidx. am: d533ee27e4  -s ours am: 216850a049  -s ours
am: 23e03d8d07  -s ours

Change-Id: If3386ef3ee0039ec4bec178f9e8ae4c3725f3973
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/util/LinkAccessibilityHelper.java b/library/gingerbread/src/com/android/setupwizardlib/util/LinkAccessibilityHelper.java
index 2a378e8..a0ea379 100644
--- a/library/gingerbread/src/com/android/setupwizardlib/util/LinkAccessibilityHelper.java
+++ b/library/gingerbread/src/com/android/setupwizardlib/util/LinkAccessibilityHelper.java
@@ -137,7 +137,7 @@
      *
      * @see ExploreByTouchHelper#dispatchHoverEvent(MotionEvent)
      */
-    public final boolean dispatchHoverEvent(MotionEvent event) {
+    public boolean dispatchHoverEvent(MotionEvent event) {
         return mDelegate instanceof ExploreByTouchHelper
                 && ((ExploreByTouchHelper) mDelegate).dispatchHoverEvent(event);
     }
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..da07b40 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
@@ -31,13 +31,13 @@
 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.AccessibilityDelegateCompat;
 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat;
 import androidx.customview.widget.ExploreByTouchHelper;
@@ -242,7 +242,7 @@
     @Test
     public void testMethodDelegation() {
         initTextView();
-        ExploreByTouchHelper delegate = mock(TestPreOLinkAccessibilityHelper.class);
+        AccessibilityDelegateCompat delegate = mock(AccessibilityDelegateCompat.class);
         LinkAccessibilityHelper helper = new LinkAccessibilityHelper(delegate);
 
         AccessibilityEvent accessibilityEvent =
@@ -272,10 +272,6 @@
                 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));
 
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..4716c4d 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
@@ -31,6 +31,7 @@
 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;
@@ -41,6 +42,7 @@
 import org.junit.Assume;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.robolectric.Robolectric;
 import org.robolectric.annotation.Config;
 
 @RunWith(SuwLibRobolectricTestRunner.class)
@@ -50,11 +52,24 @@
     private SwitchCompat mSwitch;
 
     @Test
-    public void testLayout() {
+    public void defaultLayout_baselineAligned_shouldBeFalse() {
         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);
+        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();
     }
 
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-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..6c813b8 100644
--- a/library/main/res/values/styles.xml
+++ b/library/main/res/values/styles.xml
@@ -166,7 +166,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 +204,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/util/WizardManagerHelper.java b/library/main/src/com/android/setupwizardlib/util/WizardManagerHelper.java
index 0628192..9230b67 100644
--- a/library/main/src/com/android/setupwizardlib/util/WizardManagerHelper.java
+++ b/library/main/src/com/android/setupwizardlib/util/WizardManagerHelper.java
@@ -23,6 +23,7 @@
 import android.os.Build.VERSION_CODES;
 import android.provider.Settings;
 
+import androidx.annotation.Nullable;
 import androidx.annotation.StyleRes;
 import androidx.annotation.VisibleForTesting;
 
@@ -279,7 +280,25 @@
      */
     public static @StyleRes int getThemeRes(Intent intent, @StyleRes int defaultTheme) {
         final String theme = intent.getStringExtra(EXTRA_THEME);
-        return getThemeRes(theme, defaultTheme);
+        return getThemeRes(theme, defaultTheme, null);
+    }
+
+    /**
+     * 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)
+     */
+    public static @StyleRes int getThemeRes(Intent intent, @StyleRes int defaultTheme,
+            @Nullable String oldestSupportedTheme) {
+        final String theme = intent.getStringExtra(EXTRA_THEME);
+        return getThemeRes(theme, defaultTheme, oldestSupportedTheme);
     }
 
     /**
@@ -302,28 +321,91 @@
      *         given theme is not recognized.
      */
     public static @StyleRes int getThemeRes(String theme, @StyleRes int defaultTheme) {
+        return getThemeRes(theme, defaultTheme, null);
+    }
+
+    /**
+     * 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,
+            @Nullable String oldestSupportedTheme) {
+        int returnedTheme = defaultTheme;
         if (theme != null) {
             switch (theme) {
                 case THEME_GLIF_V3_LIGHT:
-                    return R.style.SuwThemeGlifV3_Light;
+                    returnedTheme = R.style.SuwThemeGlifV3_Light;
+                    break;
                 case THEME_GLIF_V3:
-                    return R.style.SuwThemeGlifV3;
+                    returnedTheme = R.style.SuwThemeGlifV3;
+                    break;
                 case THEME_GLIF_V2_LIGHT:
-                    return R.style.SuwThemeGlifV2_Light;
+                    returnedTheme = R.style.SuwThemeGlifV2_Light;
+                    break;
                 case THEME_GLIF_V2:
-                    return R.style.SuwThemeGlifV2;
+                    returnedTheme = R.style.SuwThemeGlifV2;
+                    break;
                 case THEME_GLIF_LIGHT:
-                    return R.style.SuwThemeGlif_Light;
+                    returnedTheme = R.style.SuwThemeGlif_Light;
+                    break;
                 case THEME_GLIF:
-                    return R.style.SuwThemeGlif;
+                    returnedTheme = R.style.SuwThemeGlif;
+                    break;
                 case THEME_MATERIAL_LIGHT:
-                    return R.style.SuwThemeMaterial_Light;
+                    returnedTheme = R.style.SuwThemeMaterial_Light;
+                    break;
                 case THEME_MATERIAL:
-                    return R.style.SuwThemeMaterial;
+                    returnedTheme = R.style.SuwThemeMaterial;
+                    break;
+                default:
+                    // fall through
+            }
+
+            // b/79540471 Return the default theme if the specified theme
+            // is older than the oldest supported one.
+            if (oldestSupportedTheme != null
+                    && (getThemeVersion(theme) < getThemeVersion(oldestSupportedTheme))) {
+                returnedTheme = defaultTheme;
+            }
+        }
+
+        return returnedTheme;
+    }
+
+    private static int getThemeVersion(String theme) {
+        if (theme != null) {
+            switch (theme) {
+                case THEME_GLIF_V3_LIGHT:
+                case THEME_GLIF_V3:
+                    return 4;
+                case THEME_GLIF_V2_LIGHT:
+                case THEME_GLIF_V2:
+                    return 3;
+                case THEME_GLIF_LIGHT:
+                case THEME_GLIF:
+                    return 2;
+                case THEME_MATERIAL_LIGHT:
+                case THEME_MATERIAL:
+                    return 1;
                 default:
                     // fall through
             }
         }
-        return defaultTheme;
+        return -1;
     }
 }
diff --git a/library/main/src/com/android/setupwizardlib/view/IllustrationVideoView.java b/library/main/src/com/android/setupwizardlib/view/IllustrationVideoView.java
index 6d31583..3c188e8 100644
--- a/library/main/src/com/android/setupwizardlib/view/IllustrationVideoView.java
+++ b/library/main/src/com/android/setupwizardlib/view/IllustrationVideoView.java
@@ -22,11 +22,17 @@
 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 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;
@@ -35,6 +41,8 @@
 
 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
  * animated illustrations.
@@ -49,10 +57,11 @@
  */
 @TargetApi(VERSION_CODES.ICE_CREAM_SANDWICH)
 public class IllustrationVideoView extends TextureView implements Animatable,
-        TextureView.SurfaceTextureListener,
-        MediaPlayer.OnPreparedListener,
-        MediaPlayer.OnSeekCompleteListener,
-        MediaPlayer.OnInfoListener {
+        SurfaceTextureListener,
+        OnPreparedListener,
+        OnSeekCompleteListener,
+        OnInfoListener,
+        OnErrorListener {
 
     private static final String TAG = "IllustrationVideoView";
 
@@ -63,14 +72,19 @@
 
     private @RawRes int mVideoResId = 0;
 
+    private String mVideoResPackageName;
+
     @VisibleForTesting Surface mSurface;
 
+    private boolean mPrepared;
+
     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);
+        final int videoResId = a.getResourceId(R.styleable.SuwIllustrationVideoView_suwVideo, 0);
         a.recycle();
+        setVideoResource(videoResId);
 
         // 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
@@ -99,16 +113,30 @@
                 MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
     }
 
+
+    /**
+     * 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 != mVideoResId
+                || (videoResPackageName != null && !videoResPackageName.equals(
+                mVideoResPackageName))) {
+            mVideoResId = videoResId;
+            mVideoResPackageName = videoResPackageName;
+            createMediaPlayer();
+        }
+    }
+
     /**
      * 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();
-        }
+        setVideoResource(resId, getContext().getPackageName());
     }
 
     @Override
@@ -125,7 +153,7 @@
      * Creates a media player for the current URI. The media player will be started immediately if
      * the view's window is visible. If there is an existing media player, it will be released.
      */
-    private void createMediaPlayer() {
+    protected void createMediaPlayer() {
         if (mMediaPlayer != null) {
             mMediaPlayer.release();
         }
@@ -133,25 +161,47 @@
             return;
         }
 
-        mMediaPlayer = MediaPlayer.create(getContext(), mVideoResId);
+        mMediaPlayer = new MediaPlayer();
 
-        if (mMediaPlayer != null) {
-            mMediaPlayer.setSurface(mSurface);
-            mMediaPlayer.setOnPreparedListener(this);
-            mMediaPlayer.setOnSeekCompleteListener(this);
-            mMediaPlayer.setOnInfoListener(this);
+        mMediaPlayer.setSurface(mSurface);
+        mMediaPlayer.setOnPreparedListener(this);
+        mMediaPlayer.setOnSeekCompleteListener(this);
+        mMediaPlayer.setOnInfoListener(this);
+        mMediaPlayer.setOnErrorListener(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");
+        setVideoResourceInternal(mVideoResId, mVideoResPackageName);
+    }
+
+    private void setVideoResourceInternal(@RawRes int videoRes, String videoResPackageName) {
+        Uri uri = Uri.parse("android.resource://" + videoResPackageName + "/" + videoRes);
+        try {
+            mMediaPlayer.setDataSource(getContext(), uri);
+            mMediaPlayer.prepareAsync();
+        } catch (IOException e) {
+            Log.wtf(TAG, "Unable to set data source", e);
         }
-        if (getWindowVisibility() == View.VISIBLE) {
-            start();
+    }
+
+    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();
         }
     }
 
@@ -169,9 +219,9 @@
      */
     public void release() {
         if (mMediaPlayer != null) {
-            mMediaPlayer.stop();
             mMediaPlayer.release();
             mMediaPlayer = null;
+            mPrepared = false;
         }
         if (mSurface != null) {
             mSurface.release();
@@ -179,14 +229,34 @@
         }
     }
 
+    private void reattach() {
+        if (mSurface == null) {
+            initVideo();
+        }
+    }
+
+    private void initVideo() {
+        if (getWindowVisibility() != View.VISIBLE) {
+            return;
+        }
+        createSurface();
+        if (mSurface != null) {
+            createMediaPlayer();
+        } else {
+            Log.w(TAG, "Surface creation failed");
+        }
+    }
+
+    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);
-        mSurface = new Surface(surfaceTexture);
-        createMediaPlayer();
+        initVideo();
     }
 
     @Override
@@ -207,14 +277,14 @@
 
     @Override
     public void start() {
-        if (mMediaPlayer != null && !mMediaPlayer.isPlaying()) {
+        if (mPrepared && mMediaPlayer != null && !mMediaPlayer.isPlaying()) {
             mMediaPlayer.start();
         }
     }
 
     @Override
     public void stop() {
-        if (mMediaPlayer != null) {
+        if (mPrepared && mMediaPlayer != null) {
             mMediaPlayer.pause();
         }
     }
@@ -231,21 +301,52 @@
         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) {
+        mPrepared = 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());
+        }
+        if (Float.compare(mAspectRatio, aspectRatio) != 0) {
+            mAspectRatio = aspectRatio;
+            requestLayout();
+        }
+        if (getWindowVisibility() == View.VISIBLE) {
+            start();
+        }
     }
 
     @Override
     public void onSeekComplete(MediaPlayer mp) {
-        mp.start();
+        if (isPrepared()) {
+            mp.start();
+        } else {
+            Log.wtf(TAG, "Seek complete but media player not prepared");
+        }
     }
 
     public int getCurrentPosition() {
         return mMediaPlayer == null ? 0 : mMediaPlayer.getCurrentPosition();
     }
+
+    protected boolean isPrepared() {
+        return mPrepared;
+    }
+
+    @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/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/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..78e5011 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'
diff --git a/library/test/robotest/src/com/android/setupwizardlib/shadow/ShadowMediaPlayer.java b/library/test/robotest/src/com/android/setupwizardlib/shadow/ShadowMediaPlayer.java
new file mode 100644
index 0000000..7bcefd0
--- /dev/null
+++ b/library/test/robotest/src/com/android/setupwizardlib/shadow/ShadowMediaPlayer.java
@@ -0,0 +1,82 @@
+/*
+ * 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.content.Context;
+import android.media.MediaPlayer;
+import android.net.Uri;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+import java.io.IOException;
+import java.net.HttpCookie;
+import java.util.List;
+import java.util.Map;
+
+@Implements(MediaPlayer.class)
+public class ShadowMediaPlayer extends org.robolectric.shadows.ShadowMediaPlayer {
+
+    private int mVideoWidth;
+    private int mVideoHeight;
+
+    public ShadowMediaPlayer() {
+        super();
+        mVideoWidth = -1;
+        mVideoHeight = -1;
+    }
+
+    @Implementation
+    public void setDataSource(
+            @NonNull Context context,
+            @NonNull Uri uri,
+            @Nullable Map<String, String> headers,
+            @Nullable List<HttpCookie> cookies)
+            throws IOException {
+        setDataSource(context, uri, headers);
+    }
+
+    @Implementation
+    public void seekTo(long msec, int mode) {
+        seekTo((int) msec);
+    }
+
+    public void setVideoSize(int width, int height) {
+        if (width < 0) {
+            throw new IllegalArgumentException("Unexpected negative width=" + width);
+        }
+        if (height < 0) {
+            throw new IllegalArgumentException("Unexpected negative height=" + height);
+        }
+
+        mVideoWidth = width;
+        mVideoHeight = height;
+    }
+
+    @Override
+    public int getVideoWidth() {
+        return mVideoWidth;
+    }
+
+    @Override
+    public int getVideoHeight() {
+        return mVideoHeight;
+    }
+}
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..195fc9b 100644
--- a/library/test/robotest/src/com/android/setupwizardlib/util/GlifStyleTest.java
+++ b/library/test/robotest/src/com/android/setupwizardlib/util/GlifStyleTest.java
@@ -28,6 +28,7 @@
 import android.os.Build.VERSION;
 import android.os.Build.VERSION_CODES;
 import android.os.Bundle;
+import android.util.AttributeSet;
 import android.view.ContextThemeWrapper;
 import android.widget.Button;
 import android.widget.ProgressBar;
@@ -37,27 +38,21 @@
 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.annotation.Config;
+import org.robolectric.util.ReflectionHelpers;
+import org.robolectric.util.ReflectionHelpers.ClassParameter;
 
 @RunWith(SuwLibRobolectricTestRunner.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 = new Button(
-                mContext,
+        Button button = createButton(
+                new ContextThemeWrapper(application, R.style.SuwThemeGlif_Light),
                 Robolectric.buildAttributeSet()
                         .setStyleAttribute("@style/SuwGlifButton.Tertiary")
                         .build());
@@ -87,6 +82,22 @@
                 activity.findViewById(R.id.suw_large_progress_bar) instanceof ProgressBar);
     }
 
+    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));
+    }
+
     private static class GlifThemeActivity extends Activity {
 
         @Override
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..20549f2 100644
--- a/library/test/robotest/src/com/android/setupwizardlib/util/WizardManagerHelperTest.java
+++ b/library/test/robotest/src/com/android/setupwizardlib/util/WizardManagerHelperTest.java
@@ -162,6 +162,22 @@
     }
 
     @Test
+    public void getThemeRes_whenOldestSupportedThemeTakeEffect_shouldReturnDefault() {
+        Intent intent = new Intent();
+        intent.putExtra(WizardManagerHelper.EXTRA_THEME, "material");
+        assertEquals(0,
+                WizardManagerHelper.getThemeRes(intent, 0, WizardManagerHelper.THEME_GLIF_V2));
+    }
+
+    @Test
+    public void getThemeRes_whenOldestSupportedThemeNotTakeEffect_shouldReturnCurrent() {
+        Intent intent = new Intent();
+        intent.putExtra(WizardManagerHelper.EXTRA_THEME, "glif_v3");
+        assertEquals(R.style.SuwThemeGlifV3,
+                WizardManagerHelper.getThemeRes(intent, 0, WizardManagerHelper.THEME_GLIF_V2));
+    }
+
+    @Test
     public void testIsLightThemeDefault() {
         final Intent intent = new Intent();
         intent.putExtra("theme", "abracadabra");
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 1be822d..a6c1808 100644
--- a/library/test/robotest/src/com/android/setupwizardlib/view/IllustrationVideoViewTest.java
+++ b/library/test/robotest/src/com/android/setupwizardlib/view/IllustrationVideoViewTest.java
@@ -16,51 +16,52 @@
 
 package com.android.setupwizardlib.view;
 
-import static org.junit.Assert.assertEquals;
+import static com.google.common.truth.Truth.assertThat;
+
 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.net.Uri;
 import android.os.Build.VERSION_CODES;
 import android.view.Surface;
+import android.view.View;
 
 import androidx.annotation.RawRes;
 
 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.shadow.ShadowMediaPlayer;
 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.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.ShadowMediaPlayer.State;
+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,
+                ShadowMediaPlayer.class,
                 ShadowSurface.class
         })
 public class IllustrationVideoViewTest {
@@ -70,48 +71,58 @@
 
     private IllustrationVideoView mView;
 
+    private ShadowMediaPlayer mShadowMediaPlayer;
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-    }
-
-    @After
-    public void tearDown() {
-        ShadowMockMediaPlayer.reset();
+        addMediaInfo(android.R.color.white);
     }
 
     @Test
-    public void nullMediaPlayer_shouldThrowWtf() {
-        ShadowMockMediaPlayer.sMediaPlayer = null;
-        try {
-            createDefaultView();
-            fail("WTF should be thrown for null media player");
-        } catch (TerribleFailure e) {
-            // pass
-        }
+    public void onVisibilityChanged_notVisible_shouldRelease() {
+
+        createDefaultView();
+        mView.onWindowVisibilityChanged(View.GONE);
+
+        assertThat(mShadowMediaPlayer.getState()).isEqualTo(State.END);
+        assertThat(mView.mSurface).isNull();
+        assertThat(mView.mMediaPlayer).isNull();
+    }
+
+    @Test
+    public void onVisibilityChanged_visible_shouldPlay() {
+        createDefaultView();
+
+        mView.onWindowVisibilityChanged(View.GONE);
+        assertThat(mView.mSurface).isNull();
+        assertThat(mView.mMediaPlayer).isNull();
+
+        mView.onWindowVisibilityChanged(View.VISIBLE);
+
+        assertThat(mView.mSurface).isNotNull();
+        assertThat(mView.mMediaPlayer).isNotNull();
     }
 
     @Test
     public void testPausedWhenWindowFocusLost() {
         createDefaultView();
+        Robolectric.flushForegroundThreadScheduler();
         mView.start();
 
         assertNotNull(mView.mMediaPlayer);
         assertNotNull(mView.mSurface);
 
         mView.onWindowFocusChanged(false);
-        verify(ShadowMockMediaPlayer.getMock()).pause();
+        assertThat(mShadowMediaPlayer.getState()).isEqualTo(State.PAUSED);
     }
 
     @Test
     public void testStartedWhenWindowFocusRegained() {
         testPausedWhenWindowFocusLost();
 
-        // Clear verifications for calls in the other test
-        reset(ShadowMockMediaPlayer.getMock());
-
         mView.onWindowFocusChanged(true);
-        verify(ShadowMockMediaPlayer.getMock()).start();
+        assertThat(mShadowMediaPlayer.getState()).isEqualTo(State.STARTED);
     }
 
     @Test
@@ -123,55 +134,164 @@
         assertNotNull(mView.mSurface);
 
         mView.onSurfaceTextureDestroyed(mSurfaceTexture);
-        verify(ShadowMockMediaPlayer.getMock()).release();
+        assertThat(mShadowMediaPlayer.getState()).isEqualTo(State.END);
     }
 
     @Test
     public void testXmlSetVideoResId() {
         createDefaultView();
-        assertEquals(android.R.color.white, ShadowMockMediaPlayer.sResId);
+        assertThat(mShadowMediaPlayer.getSourceUri().toString())
+                .isEqualTo("android.resource://com.android.setupwizardlib/"
+                        + android.R.color.white);
     }
 
     @Test
     public void testSetVideoResId() {
+        addMediaInfo(android.R.color.black);
+
         createDefaultView();
 
         @RawRes int black = android.R.color.black;
         mView.setVideoResource(black);
 
-        assertEquals(android.R.color.black, ShadowMockMediaPlayer.sResId);
+        mShadowMediaPlayer = (ShadowMediaPlayer) Shadows.shadowOf(mView.mMediaPlayer);
+
+        assertThat(mShadowMediaPlayer.getSourceUri().toString())
+                .isEqualTo("android.resource://com.android.setupwizardlib/"
+                        + android.R.color.black);
+    }
+
+    @Test
+    public void prepareVideo_shouldSetAspectRatio() {
+        createDefaultView();
+
+        mShadowMediaPlayer.setVideoSize(720, 1280);
+
+        Robolectric.flushForegroundThreadScheduler();
+        mView.start();
+
+        mView.measure(View.MeasureSpec.makeMeasureSpec(720, View.MeasureSpec.EXACTLY),
+                View.MeasureSpec.makeMeasureSpec(720, View.MeasureSpec.EXACTLY));
+
+        final float aspectRatio = (float) mView.getMeasuredHeight() / mView.getMeasuredWidth();
+        assertThat(aspectRatio).isWithin(0.001f).of(1280f / 720f);
+    }
+
+    @Test
+    public void prepareVideo_zeroHeight_shouldSetAspectRatioToZero() {
+        createDefaultView();
+
+        mShadowMediaPlayer.setVideoSize(720, 0);
+
+        Robolectric.flushForegroundThreadScheduler();
+        mView.start();
+
+        final float aspectRatio = (float) mView.getHeight() / mView.getWidth();
+        assertThat(aspectRatio).isEqualTo(0.0f);
+    }
+
+    @Test
+    public void setVideoResId_resetDiffVideoResFromDiffPackage_videoResShouldBeSet() {
+        // VideoRes default set as android.R.color.white with
+        // default package(com.android.setupwizardlib)
+        createDefaultView();
+
+        // reset different videoRes from different package
+        String newPackageName = "com.android.fakepackage";
+        @RawRes int black = android.R.color.black;
+        addMediaInfo(black, newPackageName);
+        mView.setVideoResource(black, newPackageName);
+
+        // should be reset to black with the new package
+        mShadowMediaPlayer = (ShadowMediaPlayer) Shadows.shadowOf(mView.mMediaPlayer);
+        assertThat(mShadowMediaPlayer.getSourceUri().toString())
+                .isEqualTo("android.resource://" + newPackageName + "/"
+                        + android.R.color.black);
+    }
+
+    @Test
+    public void setVideoResId_resetDiffVideoResFromSamePackage_videoResShouldBeSet() {
+        // VideoRes default set as android.R.color.white with
+        // default package(com.android.setupwizardlib)
+        createDefaultView();
+
+        // reset different videoRes from the same package(default package)
+        String defaultPackageName = "com.android.setupwizardlib";
+        @RawRes int black = android.R.color.black;
+        addMediaInfo(black);
+        mView.setVideoResource(black, defaultPackageName);
+
+        // should be reset to black with the same package(default package)
+        mShadowMediaPlayer = (ShadowMediaPlayer) Shadows.shadowOf(mView.mMediaPlayer);
+        assertThat(mShadowMediaPlayer.getSourceUri().toString())
+                .isEqualTo("android.resource://" + defaultPackageName + "/"
+                        + android.R.color.black);
+    }
+
+    @Test
+    public void setVideoResId_resetSameVideoResFromDifferentPackage_videoResShouldBeSet() {
+        // VideoRes default set as android.R.color.white with
+        // default package(com.android.setupwizardlib)
+        createDefaultView();
+
+
+        // reset same videoRes from different package
+        @RawRes int white = android.R.color.white;
+        String newPackageName = "com.android.fakepackage";
+        addMediaInfo(white, newPackageName);
+        mView.setVideoResource(white, newPackageName);
+
+        // should be white with the new package
+        mShadowMediaPlayer = (ShadowMediaPlayer) Shadows.shadowOf(mView.mMediaPlayer);
+        assertThat(mShadowMediaPlayer.getSourceUri().toString())
+                .isEqualTo("android.resource://" + newPackageName + "/"
+                        + android.R.color.white);
     }
 
     private void createDefaultView() {
         mView = new IllustrationVideoView(
                 application,
                 Robolectric.buildAttributeSet()
-                        // Any resource attribute should work, since the media player is mocked
+                        // Any resource attribute should work, since the DataSource is fake
                         .addAttribute(R.attr.suwVideo, "@android:color/white")
                         .build());
+
+        Activity activity = Robolectric.setupActivity(Activity.class);
+        activity.setContentView(mView);
+        setWindowVisible();
+
+        mView.setSurfaceTexture(mock(SurfaceTexture.class));
         mView.onSurfaceTextureAvailable(mSurfaceTexture, 500, 500);
+        mShadowMediaPlayer = (ShadowMediaPlayer) Shadows.shadowOf(mView.mMediaPlayer);
+        mShadowMediaPlayer.setInvalidStateBehavior(InvalidStateBehavior.EMULATE);
     }
 
-    @Implements(MediaPlayer.class)
-    public static class ShadowMockMediaPlayer extends ShadowMediaPlayer {
+    private void setWindowVisible() {
+        Object viewRootImpl = ReflectionHelpers.callInstanceMethod(mView, "getViewRootImpl");
+        ReflectionHelpers.callInstanceMethod(
+                viewRootImpl,
+                "handleAppVisibility",
+                ClassParameter.from(boolean.class, true));
+        assertThat(mView.isAttachedToWindow()).isTrue();
+        assertThat(mView.getWindowVisibility()).isEqualTo(View.VISIBLE);
+    }
 
-        private static MediaPlayer sMediaPlayer = mock(MediaPlayer.class);
-        private static int sResId;
+    private void addMediaInfo(@RawRes int res) {
+        ShadowMediaPlayer.addMediaInfo(
+                DataSource.toDataSource(
+                        application,
+                        Uri.parse("android.resource://com.android.setupwizardlib/" + res),
+                        null),
+                new MediaInfo(5000, 1));
+    }
 
-        public static void reset() {
-            sMediaPlayer = mock(MediaPlayer.class);
-            sResId = 0;
-        }
-
-        @Implementation
-        public static MediaPlayer create(Context context, int resId) {
-            sResId = resId;
-            return sMediaPlayer;
-        }
-
-        public static MediaPlayer getMock() {
-            return sMediaPlayer;
-        }
+    private void addMediaInfo(@RawRes int res, String packageName) {
+        ShadowMediaPlayer.addMediaInfo(
+                DataSource.toDataSource(
+                        application,
+                        Uri.parse("android.resource://" + packageName + "/" + res),
+                        null),
+                new MediaInfo(5000, 1));
     }
 
     @Implements(Surface.class)
@@ -181,6 +301,7 @@
         @RealObject
         private Surface mRealSurface;
 
+        @Override
         public void __constructor__(SurfaceTexture surfaceTexture) {
             // Call the constructor on the real object, so that critical fields such as mLock is
             // initialized properly.
diff --git a/navigationbar/res/values-as/strings.xml b/navigationbar/res/values-as/strings.xml
new file mode 100644
index 0000000..398ff1f
--- /dev/null
+++ b/navigationbar/res/values-as/strings.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="setup_wizard_next_button_label" msgid="6681282266022780599">"পৰৱৰ্তী"</string>
+    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"উভতি যাওক"</string>
+</resources>
diff --git a/navigationbar/res/values-or/strings.xml b/navigationbar/res/values-or/strings.xml
new file mode 100644
index 0000000..4c70d06
--- /dev/null
+++ b/navigationbar/res/values-or/strings.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="setup_wizard_next_button_label" msgid="6681282266022780599">"ପରବର୍ତ୍ତୀ"</string>
+    <string name="setup_wizard_back_button_label" msgid="2863826823307023546">"ପଛକୁ ଫେରନ୍ତୁ"</string>
+</resources>
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