Snap for 7550640 from 379a98ee85f07d48394bbe17229574fd87578d01 to mainline-wifi-release

Change-Id: I3c173a5db3faa53cca429a960ddfe124311ee786
diff --git a/Android.bp b/Android.bp
index 776755b..4d416d3 100644
--- a/Android.bp
+++ b/Android.bp
@@ -2,6 +2,29 @@
 // Build the setup design library.
 //
 
+package {
+    default_applicable_licenses: ["external_setupdesign_license"],
+}
+
+// Added automatically by a large-scale-change
+//
+// large-scale-change included anything that looked like it might be a license
+// text as a license_text. e.g. LICENSE, NOTICE, COPYING etc.
+//
+// Please consider removing redundant or irrelevant files from 'license_text:'.
+// See: http://go/android-license-faq
+license {
+    name: "external_setupdesign_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "SPDX-license-identifier-Apache-2.0",
+    ],
+    license_text: [
+        "LICENSE",
+        "NOTICE",
+    ],
+}
+
 android_library {
     name: "setupdesign",
     static_libs: [
@@ -10,6 +33,7 @@
         "androidx.legacy_legacy-support-core-ui",
         "androidx.appcompat_appcompat",
         "androidx.recyclerview_recyclerview",
+        "com.google.android.material_material",
         "setupcompat",
         "setupdesign-strings",
     ],
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..d97975c
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,3 @@
+third_party {
+  license_type: NOTICE
+}
diff --git a/exempting_lint_checks.txt b/exempting_lint_checks.txt
new file mode 100644
index 0000000..201fd64
--- /dev/null
+++ b/exempting_lint_checks.txt
@@ -0,0 +1,49 @@
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/GlifLayout.java: CustomViewStyleable: getContext().obtainStyledAttributes(attrs, R.styleable.SudGlifLayout, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/SetupWizardLayout.java: CustomViewStyleable: .obtainStyledAttributes(attrs, R.styleable.SudSetupWizardLayout, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/FillContentLayout.java: CustomViewStyleable: context.obtainStyledAttributes(attrs, R.styleable.SudFillContentLayout, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/HeaderRecyclerView.java: CustomViewStyleable: .obtainStyledAttributes(attrs, R.styleable.SudHeaderRecyclerView, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/Illustration.java: CustomViewStyleable: getContext().obtainStyledAttributes(attrs, R.styleable.SudIllustration, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/IllustrationVideoView.java: CustomViewStyleable: context.obtainStyledAttributes(attrs, R.styleable.SudIllustrationVideoView);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/IntrinsicSizeFrameLayout.java: CustomViewStyleable: attrs, R.styleable.SudIntrinsicSizeFrameLayout, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/NavigationBar.java: ResourceType: @StyleableRes int colorBackground = 2;
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/NavigationBar.java: ResourceType: @StyleableRes int colorForeground = 1;
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/StickyHeaderListView.java: CustomViewStyleable: .obtainStyledAttributes(attrs, R.styleable.SudStickyHeaderListView, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/StickyHeaderScrollView.java: ObsoleteSdkInt: if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/GlifLayout.java: CustomViewStyleable: getContext().obtainStyledAttributes(attrs, R.styleable.SudGlifLayout, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/SetupWizardLayout.java: CustomViewStyleable: .obtainStyledAttributes(attrs, R.styleable.SudSetupWizardLayout, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/FillContentLayout.java: CustomViewStyleable: context.obtainStyledAttributes(attrs, R.styleable.SudFillContentLayout, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/HeaderRecyclerView.java: CustomViewStyleable: .obtainStyledAttributes(attrs, R.styleable.SudHeaderRecyclerView, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/Illustration.java: CustomViewStyleable: getContext().obtainStyledAttributes(attrs, R.styleable.SudIllustration, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/IllustrationVideoView.java: CustomViewStyleable: context.obtainStyledAttributes(attrs, R.styleable.SudIllustrationVideoView);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/IntrinsicSizeFrameLayout.java: CustomViewStyleable: attrs, R.styleable.SudIntrinsicSizeFrameLayout, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/NavigationBar.java: ResourceType: @StyleableRes int colorBackground = 2;
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/NavigationBar.java: ResourceType: @StyleableRes int colorForeground = 1;
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/StickyHeaderListView.java: CustomViewStyleable: .obtainStyledAttributes(attrs, R.styleable.SudStickyHeaderListView, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/StickyHeaderScrollView.java: ObsoleteSdkInt: if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/GlifLayout.java: CustomViewStyleable: getContext().obtainStyledAttributes(attrs, R.styleable.SudGlifLayout, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/SetupWizardLayout.java: CustomViewStyleable: .obtainStyledAttributes(attrs, R.styleable.SudSetupWizardLayout, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/items/RecyclerItemAdapter.java: NotifyDataSetChanged: notifyDataSetChanged();
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/FillContentLayout.java: CustomViewStyleable: context.obtainStyledAttributes(attrs, R.styleable.SudFillContentLayout, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/HeaderRecyclerView.java: CustomViewStyleable: .obtainStyledAttributes(attrs, R.styleable.SudHeaderRecyclerView, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/HeaderRecyclerView.java: NotifyDataSetChanged: notifyDataSetChanged();
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/Illustration.java: CustomViewStyleable: getContext().obtainStyledAttributes(attrs, R.styleable.SudIllustration, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/IllustrationVideoView.java: CustomViewStyleable: context.obtainStyledAttributes(attrs, R.styleable.SudIllustrationVideoView);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/IntrinsicSizeFrameLayout.java: CustomViewStyleable: attrs, R.styleable.SudIntrinsicSizeFrameLayout, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/NavigationBar.java: ResourceType: @StyleableRes int colorBackground = 2;
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/NavigationBar.java: ResourceType: @StyleableRes int colorForeground = 1;
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/StickyHeaderListView.java: CustomViewStyleable: .obtainStyledAttributes(attrs, R.styleable.SudStickyHeaderListView, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/StickyHeaderScrollView.java: ObsoleteSdkInt: if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/GlifLayout.java: CustomViewStyleable: getContext().obtainStyledAttributes(attrs, R.styleable.SudGlifLayout, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/SetupWizardLayout.java: CustomViewStyleable: .obtainStyledAttributes(attrs, R.styleable.SudSetupWizardLayout, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/items/RecyclerItemAdapter.java: NotifyDataSetChanged: notifyDataSetChanged();
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/FillContentLayout.java: CustomViewStyleable: context.obtainStyledAttributes(attrs, R.styleable.SudFillContentLayout, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/HeaderRecyclerView.java: CustomViewStyleable: .obtainStyledAttributes(attrs, R.styleable.SudHeaderRecyclerView, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/HeaderRecyclerView.java: NotifyDataSetChanged: notifyDataSetChanged();
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/IconUniformityAppImageView.java: AnnotateVersionCheck: private static final boolean ON_L_PLUS = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/Illustration.java: CustomViewStyleable: getContext().obtainStyledAttributes(attrs, R.styleable.SudIllustration, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/IllustrationVideoView.java: CustomViewStyleable: context.obtainStyledAttributes(attrs, R.styleable.SudIllustrationVideoView);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/IntrinsicSizeFrameLayout.java: CustomViewStyleable: attrs, R.styleable.SudIntrinsicSizeFrameLayout, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/NavigationBar.java: ResourceType: @StyleableRes int colorBackground = 2;
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/NavigationBar.java: ResourceType: @StyleableRes int colorForeground = 1;
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/StickyHeaderListView.java: CustomViewStyleable: .obtainStyledAttributes(attrs, R.styleable.SudStickyHeaderListView, defStyleAttr, 0);
+third_party/java_src/android_libs/setupdesign/main/src/com/google/android/setupdesign/view/StickyHeaderScrollView.java: ObsoleteSdkInt: if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
diff --git a/grandfathered_lint_checks.txt b/grandfathered_lint_checks.txt
deleted file mode 100644
index e69de29..0000000
--- a/grandfathered_lint_checks.txt
+++ /dev/null
diff --git a/lint-baseline.xml b/lint-baseline.xml
new file mode 100644
index 0000000..c369cea
--- /dev/null
+++ b/lint-baseline.xml
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0">
+
+    <issue
+        id="NewApi"
+        message="`android:datePickerDialogTheme` requires API level 21 (current min is 14)"
+        errorLine1="        &lt;item name=&quot;android:datePickerDialogTheme&quot;>@style/SudDateTimePickerDialogTheme&lt;/item>"
+        errorLine2="              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="external/setupdesign/main/res/values/styles.xml"
+            line="291"
+            column="15"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="`android:timePickerDialogTheme` requires API level 21 (current min is 14)"
+        errorLine1="        &lt;item name=&quot;android:timePickerDialogTheme&quot;>@style/SudDateTimePickerDialogTheme&lt;/item>"
+        errorLine2="              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="external/setupdesign/main/res/values/styles.xml"
+            line="292"
+            column="15"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="`android:datePickerDialogTheme` requires API level 21 (current min is 14)"
+        errorLine1="        &lt;item name=&quot;android:datePickerDialogTheme&quot;>@style/SudDateTimePickerDialogTheme.Light&lt;/item>"
+        errorLine2="              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="external/setupdesign/main/res/values/styles.xml"
+            line="302"
+            column="15"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="`android:timePickerDialogTheme` requires API level 21 (current min is 14)"
+        errorLine1="        &lt;item name=&quot;android:timePickerDialogTheme&quot;>@style/SudDateTimePickerDialogTheme.Light&lt;/item>"
+        errorLine2="              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="external/setupdesign/main/res/values/styles.xml"
+            line="303"
+            column="15"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="`android:stateListAnimator` requires API level 21 (current min is 14)"
+        errorLine1="        &lt;item name=&quot;android:stateListAnimator&quot;>@null&lt;/item>"
+        errorLine2="              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="external/setupdesign/main/res/values/styles.xml"
+            line="523"
+            column="15"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="`android:fontFamily` requires API level 16 (current min is 14)"
+        errorLine1="        &lt;item name=&quot;android:fontFamily&quot;>@string/sudFontSecondaryMedium&lt;/item>"
+        errorLine2="              ~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="external/setupdesign/main/res/values/styles.xml"
+            line="619"
+            column="15"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="Attribute `paddingStart` referenced here can result in a crash on some specific devices older than API 17 (current min is 14)"
+        errorLine1="        android:paddingStart=&quot;?attr/listPreferredItemPaddingLeft&quot;>"
+        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="external/setupdesign/main/res/layout/sud_items_expandable_switch.xml"
+            line="38"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="`@android:interpolator/linear_out_slow_in` requires API level 21 (current min is 14)"
+        errorLine1="        android:interpolator=&quot;@android:interpolator/linear_out_slow_in&quot;"
+        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="external/setupdesign/main/res/anim/sud_pre_p_activity_close_enter.xml"
+            line="21"
+            column="9"/>
+    </issue>
+
+    <issue
+        id="NewApi"
+        message="`@android:interpolator/fast_out_slow_in` requires API level 21 (current min is 14)"
+        errorLine1="        android:interpolator=&quot;@android:interpolator/fast_out_slow_in&quot;"
+        errorLine2="        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
+        <location
+            file="external/setupdesign/main/res/anim/sud_pre_p_activity_open_exit.xml"
+            line="22"
+            column="9"/>
+    </issue>
+
+</issues>
diff --git a/lottie_loading_layout/Android.bp b/lottie_loading_layout/Android.bp
new file mode 100644
index 0000000..6d41812
--- /dev/null
+++ b/lottie_loading_layout/Android.bp
@@ -0,0 +1,31 @@
+//
+// Build the setup design - lottie_loading_layout.
+//
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "external_setupdesign_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["external_setupdesign_license"],
+}
+
+android_library {
+    name: "setupdesign-lottie-loading-layout",
+    manifest: "AndroidManifest.xml",
+    static_libs: [
+        "androidx.annotation_annotation",
+        "lottie",
+        "setupcompat",
+        "setupdesign",
+    ],
+    srcs: [
+        "src/**/*.java",
+    ],
+    resource_dirs: [
+        "res",
+    ],
+    min_sdk_version: "16",
+    sdk_version: "current"
+}
diff --git a/lottie_loading_layout/AndroidManifest.xml b/lottie_loading_layout/AndroidManifest.xml
new file mode 100644
index 0000000..3cf1948
--- /dev/null
+++ b/lottie_loading_layout/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2020 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this 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.google.android.setupdesign.lottieloadinglayout">
+
+  <uses-sdk
+      android:minSdkVersion="14"
+      android:targetSdkVersion="30" />
+
+</manifest>
diff --git a/lottie_loading_layout/res/layout-land-v31/sud_glif_loading_template_content.xml b/lottie_loading_layout/res/layout-land-v31/sud_glif_loading_template_content.xml
new file mode 100644
index 0000000..65e20a9
--- /dev/null
+++ b/lottie_loading_layout/res/layout-land-v31/sud_glif_loading_template_content.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this 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"
+    android:id="@+id/sud_layout_template_content"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:orientation="horizontal">
+
+        <LinearLayout
+            android:id="@+id/sud_landscape_header_area"
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_weight="@dimen/sud_glif_land_header_area_weight"
+            android:orientation="vertical">
+
+            <ViewStub
+                android:id="@+id/sud_layout_sticky_header"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content" />
+
+            <com.google.android.setupdesign.view.BottomScrollView
+                android:id="@+id/sud_header_scroll_view"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:fillViewport="true"
+                android:scrollIndicators="?attr/sudScrollIndicators">
+
+                <include layout="@layout/sud_glif_header" />
+
+            </com.google.android.setupdesign.view.BottomScrollView>
+
+        </LinearLayout>
+
+        <LinearLayout
+            android:id="@+id/sud_landscape_content_area"
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_weight="@dimen/sud_glif_land_content_area_weight"
+            android:orientation="vertical">
+
+            <FrameLayout
+                android:id="@+id/sud_layout_loading_content"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent">
+
+                <ViewStub
+                    android:id="@+id/sud_loading_layout_lottie_stub"
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent"
+                    android:inflatedId="@+id/sud_layout_lottie_illustration"
+                    android:layout="@layout/sud_loading_lottie_layout" />
+
+                <ViewStub
+                    android:id="@+id/sud_loading_layout_illustration_stub"
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent"
+                    android:inflatedId="@+id/sud_layout_progress_illustration"
+                    android:layout="@layout/sud_loading_illustration_layout" />
+
+                <FrameLayout
+                    android:id="@+id/sud_layout_content"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:visibility="gone" />
+
+            </FrameLayout>
+
+        </LinearLayout>
+
+    </LinearLayout>
+
+    <ViewStub
+        android:id="@+id/suc_layout_footer"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+
+</LinearLayout>
diff --git a/lottie_loading_layout/res/layout-v31/sud_glif_loading_template_content.xml b/lottie_loading_layout/res/layout-v31/sud_glif_loading_template_content.xml
new file mode 100644
index 0000000..8415324
--- /dev/null
+++ b/lottie_loading_layout/res/layout-v31/sud_glif_loading_template_content.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this 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"
+    android:id="@+id/sud_layout_template_content"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <ViewStub
+        android:id="@+id/sud_layout_sticky_header"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:orientation="vertical">
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:orientation="vertical">
+
+            <!-- Ignore UnusedAttribute: scrollIndicators is new in M. Default to no indicators in older
+                versions. -->
+            <com.google.android.setupdesign.view.BottomScrollView
+                android:id="@+id/sud_header_scroll_view"
+                android:layout_width="match_parent"
+                android:layout_height="?attr/sudLoadingHeaderHeight"
+                android:fillViewport="true"
+                android:scrollIndicators="?attr/sudScrollIndicators"
+                tools:ignore="UnusedAttribute">
+
+                <include layout="@layout/sud_glif_header" />
+
+            </com.google.android.setupdesign.view.BottomScrollView>
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:orientation="vertical">
+
+                <ViewStub
+                    android:id="@+id/sud_loading_layout_lottie_stub"
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent"
+                    android:inflatedId="@+id/sud_layout_lottie_illustration"
+                    android:layout="@layout/sud_loading_lottie_layout" />
+
+                <ViewStub
+                    android:id="@+id/sud_loading_layout_illustration_stub"
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent"
+                    android:inflatedId="@+id/sud_layout_progress_illustration"
+                    android:layout="@layout/sud_loading_illustration_layout" />
+
+                <FrameLayout
+                    android:id="@+id/sud_layout_content"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:visibility="gone" />
+
+            </LinearLayout>
+
+        </LinearLayout>
+
+    </LinearLayout>
+
+    <ViewStub
+        android:id="@+id/suc_layout_footer"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/lottie_loading_layout/res/layout-v31/sud_loading_illustration_layout.xml b/lottie_loading_layout/res/layout-v31/sud_loading_illustration_layout.xml
new file mode 100644
index 0000000..0f60d02
--- /dev/null
+++ b/lottie_loading_layout/res/layout-v31/sud_loading_illustration_layout.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this 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/SudLoadingContentFrame"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <ProgressBar
+        android:id="@+id/sud_progress_bar"
+        android:visibility="gone"
+        style="@style/SudFourColorIndeterminateProgressBar"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+    <com.google.android.setupdesign.view.IllustrationVideoView
+        android:id="@+id/sud_progress_illustration"
+        android:visibility="gone"
+        style="@style/SudContentIllustration"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/lottie_loading_layout/res/layout-v31/sud_loading_lottie_layout.xml b/lottie_loading_layout/res/layout-v31/sud_loading_lottie_layout.xml
new file mode 100644
index 0000000..a38b791
--- /dev/null
+++ b/lottie_loading_layout/res/layout-v31/sud_loading_lottie_layout.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    style="@style/SudLoadingContentFrame"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <com.airbnb.lottie.LottieAnimationView
+        android:id="@+id/sud_lottie_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:importantForAccessibility="no"
+        style="@style/SudContentIllustration"
+        app:lottie_autoPlay="false"
+        app:lottie_loop="true" />
+
+</LinearLayout>
diff --git a/lottie_loading_layout/res/layout/sud_glif_loading_template_card.xml b/lottie_loading_layout/res/layout/sud_glif_loading_template_card.xml
new file mode 100644
index 0000000..2db095c
--- /dev/null
+++ b/lottie_loading_layout/res/layout/sud_glif_loading_template_card.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/suc_layout_status"
+    style="@style/SudGlifCardBackground"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:fitsSystemWindows="true"
+    android:gravity="center_horizontal"
+    android:orientation="vertical">
+
+    <View
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:visibility="invisible" />
+
+    <com.google.android.setupdesign.view.IntrinsicSizeFrameLayout
+        style="@style/SudGlifCardContainer"
+        android:layout_width="@dimen/sud_glif_card_width"
+        android:layout_height="wrap_content"
+        android:height="@dimen/sud_glif_card_height">
+
+        <include layout="@layout/sud_glif_loading_template_content" />
+
+    </com.google.android.setupdesign.view.IntrinsicSizeFrameLayout>
+
+    <View
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:visibility="invisible" />
+
+</LinearLayout>
diff --git a/lottie_loading_layout/res/layout/sud_glif_loading_template_compat.xml b/lottie_loading_layout/res/layout/sud_glif_loading_template_compat.xml
new file mode 100644
index 0000000..86003b9
--- /dev/null
+++ b/lottie_loading_layout/res/layout/sud_glif_loading_template_compat.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this 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.
+-->
+
+<com.google.android.setupcompat.view.StatusBarBackgroundLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/suc_layout_status"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+  <include layout="@layout/sud_glif_loading_template_content" />
+
+</com.google.android.setupcompat.view.StatusBarBackgroundLayout>
\ No newline at end of file
diff --git a/lottie_loading_layout/res/layout/sud_glif_loading_template_content.xml b/lottie_loading_layout/res/layout/sud_glif_loading_template_content.xml
new file mode 100644
index 0000000..e70edc1
--- /dev/null
+++ b/lottie_loading_layout/res/layout/sud_glif_loading_template_content.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this 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"
+    android:id="@+id/sud_layout_template_content"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <ViewStub
+        android:id="@+id/sud_layout_sticky_header"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+
+    <!-- Ignore UnusedAttribute: scrollIndicators is new in M. Default to no indicators in older
+        versions. -->
+    <com.google.android.setupdesign.view.BottomScrollView
+        android:id="@+id/sud_scroll_view"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:fillViewport="true"
+        android:scrollIndicators="?attr/sudScrollIndicators"
+        tools:ignore="UnusedAttribute">
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical">
+
+            <include layout="@layout/sud_glif_header" />
+
+            <ViewStub
+                android:id="@+id/sud_loading_layout_illustration_stub"
+                android:layout_width="match_parent"
+                android:layout_height="0dp"
+                android:layout_weight="1"
+                android:inflatedId="@+id/sud_layout_progress_illustration"
+                android:layout="@layout/sud_loading_illustration_layout" />
+
+            <ViewStub
+                android:id="@+id/sud_loading_layout_lottie_stub"
+                android:layout_width="match_parent"
+                android:layout_height="0dp"
+                android:layout_weight="1"
+                android:inflatedId="@+id/sud_layout_lottie_illustration"
+                android:layout="@layout/sud_loading_lottie_layout" />
+
+            <FrameLayout
+                android:id="@+id/sud_layout_content"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:visibility="gone" />
+
+        </LinearLayout>
+
+    </com.google.android.setupdesign.view.BottomScrollView>
+
+    <ViewStub
+        android:id="@+id/suc_layout_footer"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+
+</LinearLayout>
diff --git a/lottie_loading_layout/res/layout/sud_loading_illustration_layout.xml b/lottie_loading_layout/res/layout/sud_loading_illustration_layout.xml
new file mode 100644
index 0000000..772598e
--- /dev/null
+++ b/lottie_loading_layout/res/layout/sud_loading_illustration_layout.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this 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/SudContentFrame"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <com.google.android.setupdesign.view.FillContentLayout
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <ProgressBar
+            android:id="@+id/sud_progress_bar"
+            android:visibility="gone"
+            style="@style/SudFourColorIndeterminateProgressBar"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent" />
+
+        <com.google.android.setupdesign.view.IllustrationVideoView
+            android:id="@+id/sud_progress_illustration"
+            android:visibility="gone"
+            style="@style/SudContentIllustration"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" />
+
+    </com.google.android.setupdesign.view.FillContentLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/lottie_loading_layout/res/layout/sud_loading_lottie_layout.xml b/lottie_loading_layout/res/layout/sud_loading_lottie_layout.xml
new file mode 100644
index 0000000..6e00f98
--- /dev/null
+++ b/lottie_loading_layout/res/layout/sud_loading_lottie_layout.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    style="@style/SudContentFrame"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <com.google.android.setupdesign.view.FillContentLayout
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <com.airbnb.lottie.LottieAnimationView
+            android:id="@+id/sud_lottie_view"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:importantForAccessibility="no"
+            style="@style/SudContentIllustration"
+            app:lottie_autoPlay="false"
+            app:lottie_loop="true" />
+
+    </com.google.android.setupdesign.view.FillContentLayout>
+
+</LinearLayout>
diff --git a/lottie_loading_layout/res/values-sw600dp/layouts.xml b/lottie_loading_layout/res/values-sw600dp/layouts.xml
new file mode 100644
index 0000000..9fe5404
--- /dev/null
+++ b/lottie_loading_layout/res/values-sw600dp/layouts.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this 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">
+  <item name="sud_glif_loading_template" type="layout"  tools:ignore="UnusedResources">@layout/sud_glif_loading_template_card</item>
+</resources>
diff --git a/lottie_loading_layout/res/values/attrs.xml b/lottie_loading_layout/res/values/attrs.xml
new file mode 100644
index 0000000..57fcac7
--- /dev/null
+++ b/lottie_loading_layout/res/values/attrs.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<resources>
+    <declare-styleable name="SudGlifLoadingLayout">
+        <attr name="sudIllustrationType" format="string" />
+        <attr name="sudLottieRes" format="reference" />
+        <attr name="sudUsePartnerHeavyTheme" />
+    </declare-styleable>
+</resources>
\ No newline at end of file
diff --git a/lottie_loading_layout/res/values/layouts.xml b/lottie_loading_layout/res/values/layouts.xml
new file mode 100644
index 0000000..483f09d
--- /dev/null
+++ b/lottie_loading_layout/res/values/layouts.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this 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">
+  <item name="sud_glif_loading_template" type="layout"  tools:ignore="UnusedResources">@layout/sud_glif_loading_template_compat</item>
+</resources>
\ No newline at end of file
diff --git a/lottie_loading_layout/src/com/google/android/setupdesign/GlifLoadingLayout.java b/lottie_loading_layout/src/com/google/android/setupdesign/GlifLoadingLayout.java
new file mode 100644
index 0000000..d239e3d
--- /dev/null
+++ b/lottie_loading_layout/src/com/google/android/setupdesign/GlifLoadingLayout.java
@@ -0,0 +1,864 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this 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.google.android.setupdesign;
+
+import static com.google.android.setupcompat.partnerconfig.Util.isNightMode;
+
+import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.os.Build;
+import android.os.Build.VERSION_CODES;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings.Global;
+import android.provider.Settings.SettingNotFoundException;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewStub;
+import android.widget.LinearLayout;
+import android.widget.ProgressBar;
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RawRes;
+import androidx.annotation.StringDef;
+import androidx.annotation.VisibleForTesting;
+import com.airbnb.lottie.LottieAnimationView;
+import com.airbnb.lottie.LottieDrawable;
+import com.airbnb.lottie.LottieProperty;
+import com.airbnb.lottie.SimpleColorFilter;
+import com.airbnb.lottie.model.KeyPath;
+import com.airbnb.lottie.value.LottieValueCallback;
+import com.airbnb.lottie.value.SimpleLottieValueCallback;
+import com.google.android.setupcompat.partnerconfig.PartnerConfig;
+import com.google.android.setupcompat.partnerconfig.PartnerConfig.ResourceType;
+import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
+import com.google.android.setupcompat.partnerconfig.ResourceEntry;
+import com.google.android.setupcompat.template.FooterBarMixin;
+import com.google.android.setupcompat.util.BuildCompatUtils;
+import com.google.android.setupdesign.lottieloadinglayout.R;
+import com.google.android.setupdesign.view.IllustrationVideoView;
+import java.io.InputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A GLIF themed layout with a {@link com.airbnb.lottie.LottieAnimationView} to showing lottie
+ * illustration and a substitute {@link com.google.android.setupdesign.view.IllustrationVideoView}
+ * to showing mp4 illustration. {@code app:sudIllustrationType} can also be used to specify one of
+ * the set including "default", "account", "connection", "update", and "final_hold". {@code
+ * app:sudLottieRes} can assign the json file of Lottie resource.
+ */
+public class GlifLoadingLayout extends GlifLayout {
+
+  private static final String TAG = "GlifLoadingLayout";
+  View inflatedView;
+
+  @VisibleForTesting @IllustrationType String illustrationType = IllustrationType.DEFAULT;
+  @VisibleForTesting LottieAnimationConfig animationConfig = LottieAnimationConfig.CONFIG_DEFAULT;
+
+  @VisibleForTesting @RawRes int customLottieResource = 0;
+
+  @VisibleForTesting Map<KeyPath, SimpleColorFilter> customizationMap = new HashMap<>();
+
+  @VisibleForTesting
+  public List<LottieAnimationFinishListener> animationFinishListeners = new ArrayList<>();
+
+  public GlifLoadingLayout(Context context) {
+    this(context, 0, 0);
+  }
+
+  public GlifLoadingLayout(Context context, int template) {
+    this(context, template, 0);
+  }
+
+  public GlifLoadingLayout(Context context, int template, int containerId) {
+    super(context, template, containerId);
+    init(null, R.attr.sudLayoutTheme);
+  }
+
+  public GlifLoadingLayout(Context context, AttributeSet attrs) {
+    super(context, attrs);
+    init(attrs, R.attr.sudLayoutTheme);
+  }
+
+  @TargetApi(VERSION_CODES.HONEYCOMB)
+  public GlifLoadingLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+    super(context, attrs, defStyleAttr);
+    init(attrs, defStyleAttr);
+  }
+
+  private void init(AttributeSet attrs, int defStyleAttr) {
+    registerMixin(FooterBarMixin.class, new LoadingFooterBarMixin(this, attrs, defStyleAttr));
+
+    TypedArray a =
+        getContext()
+            .obtainStyledAttributes(attrs, R.styleable.SudGlifLoadingLayout, defStyleAttr, 0);
+    customLottieResource = a.getResourceId(R.styleable.SudGlifLoadingLayout_sudLottieRes, 0);
+    String illustrationType = a.getString(R.styleable.SudGlifLoadingLayout_sudIllustrationType);
+    boolean usePartnerHeavyTheme =
+        a.getBoolean(R.styleable.SudGlifLoadingLayout_sudUsePartnerHeavyTheme, false);
+    a.recycle();
+
+    if (customLottieResource != 0) {
+      inflateLottieView();
+      ViewGroup container = findContainer(0);
+      container.setVisibility(View.VISIBLE);
+    } else {
+      if (illustrationType != null) {
+        setIllustrationType(illustrationType);
+      }
+
+      if (BuildCompatUtils.isAtLeastS()) {
+        inflateLottieView();
+      } else {
+        inflateIllustrationStub();
+      }
+    }
+
+    boolean applyPartnerHeavyThemeResource = shouldApplyPartnerResource() && usePartnerHeavyTheme;
+    if (applyPartnerHeavyThemeResource) {
+      View view = findManagedViewById(R.id.sud_layout_loading_content);
+      if (view != null) {
+        applyPartnerCustomizationContentPaddingTopStyle(view);
+      }
+    }
+
+    updateHeaderHeight();
+    updateLandscapeMiddleHorizontalSpacing();
+  }
+
+  @Override
+  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+    if (inflatedView instanceof LinearLayout) {
+      updateContentPadding((LinearLayout) inflatedView);
+    }
+  }
+
+  public void setIllustrationType(@IllustrationType String type) {
+    if (customLottieResource != 0) {
+      throw new IllegalStateException(
+          "custom illustration already applied, should not set illustration.");
+    }
+
+    if (!illustrationType.equals(type)) {
+      illustrationType = type;
+      customizationMap.clear();
+    }
+
+    switch (type) {
+      case IllustrationType.ACCOUNT:
+        animationConfig = LottieAnimationConfig.CONFIG_ACCOUNT;
+        break;
+
+      case IllustrationType.CONNECTION:
+        animationConfig = LottieAnimationConfig.CONFIG_CONNECTION;
+        break;
+
+      case IllustrationType.UPDATE:
+        animationConfig = LottieAnimationConfig.CONFIG_UPDATE;
+        break;
+
+      case IllustrationType.FINAL_HOLD:
+        animationConfig = LottieAnimationConfig.CONFIG_FINAL_HOLD;
+        break;
+
+      default:
+        animationConfig = LottieAnimationConfig.CONFIG_DEFAULT;
+        break;
+    }
+
+    updateAnimationView();
+  }
+
+  // TODO: [GlifLoadingLayout] Should add testcase. LottieAnimationView was auto
+  // generated not able to mock. So we have no idea how to detected is the api pass to
+  // LottiAnimationView correctly.
+  public boolean setAnimation(InputStream inputStream, String keyCache) {
+    LottieAnimationView lottieAnimationView = findLottieAnimationView();
+    if (lottieAnimationView != null) {
+      lottieAnimationView.setAnimation(inputStream, keyCache);
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+  public boolean setAnimation(String assetName) {
+    LottieAnimationView lottieAnimationView = findLottieAnimationView();
+    if (lottieAnimationView != null) {
+      lottieAnimationView.setAnimation(assetName);
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+  public boolean setAnimation(@RawRes int rawRes) {
+    LottieAnimationView lottieAnimationView = findLottieAnimationView();
+    if (lottieAnimationView != null) {
+      lottieAnimationView.setAnimation(rawRes);
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+  private void updateAnimationView() {
+    if (BuildCompatUtils.isAtLeastS()) {
+      setLottieResource();
+    } else {
+      setIllustrationResource();
+    }
+  }
+
+  /**
+   * Call this when your activity is done and should be closed. The activity will be finished while
+   * animation finished.
+   */
+  public void finish(@NonNull Activity activity) {
+    if (activity == null) {
+      throw new NullPointerException("activity should not be null");
+    }
+    registerAnimationFinishRunnable(activity::finish, /* allowFinishWithMaximumDuration= */ true);
+  }
+
+  /**
+   * Launch a new activity after the animation finished.
+   *
+   * @param activity The activity which is GlifLoadingLayout attached to.
+   * @param intent The intent to start.
+   * @param options Additional options for how the Activity should be started. See {@link
+   *     android.content.Context#startActivity(Intent, Bundle)} for more details.
+   * @param finish Finish the activity after startActivity
+   * @see Activity#startActivity(Intent)
+   * @see Activity#startActivityForResult
+   */
+  public void startActivity(
+      @NonNull Activity activity,
+      @NonNull Intent intent,
+      @Nullable Bundle options,
+      boolean finish) {
+    if (activity == null) {
+      throw new NullPointerException("activity should not be null");
+    }
+
+    if (intent == null) {
+      throw new NullPointerException("intent should not be null");
+    }
+
+    registerAnimationFinishRunnable(
+        () -> {
+          if (options == null || Build.VERSION.SDK_INT < VERSION_CODES.JELLY_BEAN) {
+            activity.startActivity(intent);
+          } else {
+            activity.startActivity(intent, options);
+          }
+
+          if (finish) {
+            activity.finish();
+          }
+        },
+        /* allowFinishWithMaximumDuration= */ true);
+  }
+
+  /**
+   * Waiting for the animation finished and launch an activity for which you would like a result
+   * when it finished.
+   *
+   * @param activity The activity which the GlifLoadingLayout attached to.
+   * @param intent The intent to start.
+   * @param requestCode If >= 0, this code will be returned in onActivityResult() when the activity
+   *     exits.
+   * @param options Additional options for how the Activity should be started.
+   * @param finish Finish the activity after startActivityForResult. The onActivityResult might not
+   *     be called because the activity already finished.
+   *     <p>See {@link android.content.Context#startActivity(Intent, Bundle)}
+   *     Context.startActivity(Intent, Bundle)} for more details.
+   */
+  public void startActivityForResult(
+      @NonNull Activity activity,
+      @NonNull Intent intent,
+      int requestCode,
+      @Nullable Bundle options,
+      boolean finish) {
+    if (activity == null) {
+      throw new NullPointerException("activity should not be null");
+    }
+
+    if (intent == null) {
+      throw new NullPointerException("intent should not be null");
+    }
+
+    registerAnimationFinishRunnable(
+        () -> {
+          if (options == null || Build.VERSION.SDK_INT < VERSION_CODES.JELLY_BEAN) {
+            activity.startActivityForResult(intent, requestCode);
+          } else {
+            activity.startActivityForResult(intent, requestCode, options);
+          }
+
+          if (finish) {
+            activity.finish();
+          }
+        },
+        /* allowFinishWithMaximumDuration= */ true);
+  }
+
+  private void updateHeaderHeight() {
+    View headerView = findManagedViewById(R.id.sud_header_scroll_view);
+    if (headerView != null
+        && PartnerConfigHelper.get(getContext())
+            .isPartnerConfigAvailable(PartnerConfig.CONFIG_LOADING_LAYOUT_HEADER_HEIGHT)) {
+      float configHeaderHeight =
+          PartnerConfigHelper.get(getContext())
+              .getDimension(getContext(), PartnerConfig.CONFIG_LOADING_LAYOUT_HEADER_HEIGHT);
+      headerView.getLayoutParams().height = (int) configHeaderHeight;
+    }
+  }
+
+  private void updateContentPadding(LinearLayout linearLayout) {
+    int paddingTop = linearLayout.getPaddingTop();
+    int paddingLeft = linearLayout.getPaddingLeft();
+    int paddingRight = linearLayout.getPaddingRight();
+    int paddingBottom = linearLayout.getPaddingBottom();
+
+    if (PartnerConfigHelper.get(getContext())
+        .isPartnerConfigAvailable(PartnerConfig.CONFIG_LOADING_LAYOUT_PADDING_TOP)) {
+      float configPaddingTop =
+          PartnerConfigHelper.get(getContext())
+              .getDimension(getContext(), PartnerConfig.CONFIG_LOADING_LAYOUT_PADDING_TOP);
+      if (configPaddingTop >= 0) {
+        paddingTop = (int) configPaddingTop;
+      }
+    }
+
+    if (PartnerConfigHelper.get(getContext())
+        .isPartnerConfigAvailable(PartnerConfig.CONFIG_LOADING_LAYOUT_PADDING_START)) {
+      float configPaddingLeft =
+          PartnerConfigHelper.get(getContext())
+              .getDimension(getContext(), PartnerConfig.CONFIG_LOADING_LAYOUT_PADDING_START);
+      if (configPaddingLeft >= 0) {
+        paddingLeft = (int) configPaddingLeft;
+      }
+    }
+
+    if (PartnerConfigHelper.get(getContext())
+        .isPartnerConfigAvailable(PartnerConfig.CONFIG_LOADING_LAYOUT_PADDING_END)) {
+      float configPaddingRight =
+          PartnerConfigHelper.get(getContext())
+              .getDimension(getContext(), PartnerConfig.CONFIG_LOADING_LAYOUT_PADDING_END);
+      if (configPaddingRight >= 0) {
+        paddingRight = (int) configPaddingRight;
+      }
+    }
+
+    if (PartnerConfigHelper.get(getContext())
+        .isPartnerConfigAvailable(PartnerConfig.CONFIG_LOADING_LAYOUT_PADDING_BOTTOM)) {
+      float configPaddingBottom =
+          PartnerConfigHelper.get(getContext())
+              .getDimension(getContext(), PartnerConfig.CONFIG_LOADING_LAYOUT_PADDING_BOTTOM);
+      if (configPaddingBottom >= 0) {
+        FooterBarMixin footerBarMixin = getMixin(FooterBarMixin.class);
+        if (footerBarMixin == null || footerBarMixin.getButtonContainer() == null) {
+          paddingBottom = (int) configPaddingBottom;
+        } else {
+          paddingBottom =
+              (int) configPaddingBottom
+                  - (int)
+                      Math.min(
+                          configPaddingBottom,
+                          getButtonContainerHeight(footerBarMixin.getButtonContainer()));
+        }
+      }
+    }
+
+    linearLayout.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);
+  }
+
+  private static final int getButtonContainerHeight(View view) {
+    view.measure(
+        MeasureSpec.makeMeasureSpec(view.getMeasuredWidth(), MeasureSpec.EXACTLY),
+        MeasureSpec.makeMeasureSpec(view.getMeasuredHeight(), MeasureSpec.EXACTLY));
+    return view.getMeasuredHeight();
+  }
+
+  private void inflateLottieView() {
+    final View lottieLayout = peekLottieLayout();
+    if (lottieLayout == null) {
+      ViewStub viewStub = findManagedViewById(R.id.sud_loading_layout_lottie_stub);
+      if (viewStub != null) {
+        inflatedView = viewStub.inflate();
+        if (inflatedView instanceof LinearLayout) {
+          updateContentPadding((LinearLayout) inflatedView);
+        }
+        setLottieResource();
+      }
+    }
+  }
+
+  private void inflateIllustrationStub() {
+    final View progressLayout = peekProgressIllustrationLayout();
+    if (progressLayout == null) {
+      ViewStub viewStub = findManagedViewById(R.id.sud_loading_layout_illustration_stub);
+      if (viewStub != null) {
+        inflatedView = viewStub.inflate();
+        if (inflatedView instanceof LinearLayout) {
+          updateContentPadding((LinearLayout) inflatedView);
+        }
+        setIllustrationResource();
+      }
+    }
+  }
+
+  private void setLottieResource() {
+    LottieAnimationView lottieView = findViewById(R.id.sud_lottie_view);
+    if (lottieView == null) {
+      Log.w(TAG, "Lottie view not found, skip set resource. Wait for layout inflated.");
+      return;
+    }
+    if (customLottieResource != 0) {
+      InputStream inputRaw = getResources().openRawResource(customLottieResource);
+      lottieView.setAnimation(inputRaw, null);
+      lottieView.playAnimation();
+    } else {
+      PartnerConfigHelper partnerConfigHelper = PartnerConfigHelper.get(getContext());
+      ResourceEntry resourceEntry =
+          partnerConfigHelper.getIllustrationResourceEntry(
+              getContext(), animationConfig.getLottieConfig());
+
+      if (resourceEntry != null) {
+        InputStream inputRaw =
+            resourceEntry.getResources().openRawResource(resourceEntry.getResourceId());
+        lottieView.setAnimation(inputRaw, null);
+        lottieView.playAnimation();
+        setLottieLayoutVisibility(View.VISIBLE);
+        setIllustrationLayoutVisibility(View.GONE);
+        applyThemeCustomization();
+      } else {
+        setLottieLayoutVisibility(View.GONE);
+        setIllustrationLayoutVisibility(View.VISIBLE);
+        inflateIllustrationStub();
+      }
+    }
+  }
+
+  private void setIllustrationLayoutVisibility(int visibility) {
+    View illustrationLayout = findViewById(R.id.sud_layout_progress_illustration);
+    if (illustrationLayout != null) {
+      illustrationLayout.setVisibility(visibility);
+    }
+  }
+
+  private void setLottieLayoutVisibility(int visibility) {
+    View lottieLayout = findViewById(R.id.sud_layout_lottie_illustration);
+    if (lottieLayout != null) {
+      lottieLayout.setVisibility(visibility);
+    }
+  }
+
+  @VisibleForTesting
+  boolean isLottieLayoutVisible() {
+    View lottieLayout = findViewById(R.id.sud_layout_lottie_illustration);
+    return lottieLayout != null && lottieLayout.getVisibility() == View.VISIBLE;
+  }
+
+  private void setIllustrationResource() {
+    View illustrationLayout = findViewById(R.id.sud_layout_progress_illustration);
+    if (illustrationLayout == null) {
+      Log.i(TAG, "Illustration stub not inflated, skip set resource");
+      return;
+    }
+
+    IllustrationVideoView illustrationVideoView =
+        findManagedViewById(R.id.sud_progress_illustration);
+    ProgressBar progressBar = findManagedViewById(R.id.sud_progress_bar);
+
+    PartnerConfigHelper partnerConfigHelper = PartnerConfigHelper.get(getContext());
+    ResourceEntry resourceEntry =
+        partnerConfigHelper.getIllustrationResourceEntry(
+            getContext(), animationConfig.getIllustrationConfig());
+
+    if (resourceEntry != null) {
+      progressBar.setVisibility(GONE);
+      illustrationVideoView.setVisibility(VISIBLE);
+      illustrationVideoView.setVideoResourceEntry(resourceEntry);
+    } else {
+      progressBar.setVisibility(VISIBLE);
+      illustrationVideoView.setVisibility(GONE);
+    }
+  }
+
+  private LottieAnimationView findLottieAnimationView() {
+    return findViewById(R.id.sud_lottie_view);
+  }
+
+  private IllustrationVideoView findIllustrationVideoView() {
+    return findManagedViewById(R.id.sud_progress_illustration);
+  }
+
+  public void playAnimation() {
+    LottieAnimationView lottieAnimationView = findLottieAnimationView();
+    if (lottieAnimationView != null) {
+      lottieAnimationView.setRepeatCount(LottieDrawable.INFINITE);
+      lottieAnimationView.playAnimation();
+    }
+  }
+
+  /** Returns whether the layout is waiting for animation finish or not. */
+  public boolean isFinishing() {
+    LottieAnimationView lottieAnimationView = findLottieAnimationView();
+    if (lottieAnimationView != null) {
+      return !animationFinishListeners.isEmpty() && lottieAnimationView.getRepeatCount() == 0;
+    } else {
+      return false;
+    }
+  }
+
+  @AnimationType
+  public int getAnimationType() {
+    if (findLottieAnimationView() != null && isLottieLayoutVisible()) {
+      return AnimationType.LOTTIE;
+    } else if (findIllustrationVideoView() != null) {
+      return AnimationType.ILLUSTRATION;
+    } else {
+      return AnimationType.PROGRESS_BAR;
+    }
+  }
+
+  // TODO: Should add testcase with mocked LottieAnimationView.
+  /** Add an animator listener to {@link LottieAnimationView}. */
+  public void addAnimatorListener(Animator.AnimatorListener listener) {
+    LottieAnimationView animationView = findLottieAnimationView();
+    if (animationView != null) {
+      animationView.addAnimatorListener(listener);
+    }
+  }
+
+  /** Remove the listener from {@link LottieAnimationView}. */
+  public void removeAnimatorListener(AnimatorListener listener) {
+    LottieAnimationView animationView = findLottieAnimationView();
+    if (animationView != null) {
+      animationView.removeAnimatorListener(listener);
+    }
+  }
+
+  /** Remove all {@link AnimatorListener} from {@link LottieAnimationView}. */
+  public void removeAllAnimatorListener() {
+    LottieAnimationView animationView = findLottieAnimationView();
+    if (animationView != null) {
+      animationView.removeAllAnimatorListeners();
+    }
+  }
+
+  /** Add a value callback with property {@link LottieProperty.COLOR_FILTER}. */
+  public void addColorCallback(KeyPath keyPath, LottieValueCallback<ColorFilter> callback) {
+    LottieAnimationView animationView = findLottieAnimationView();
+    if (animationView != null) {
+      animationView.addValueCallback(keyPath, LottieProperty.COLOR_FILTER, callback);
+    }
+  }
+
+  /** Add a simple value callback with property {@link LottieProperty.COLOR_FILTER}. */
+  public void addColorCallback(KeyPath keyPath, SimpleLottieValueCallback<ColorFilter> callback) {
+    LottieAnimationView animationView = findLottieAnimationView();
+    if (animationView != null) {
+      animationView.addValueCallback(keyPath, LottieProperty.COLOR_FILTER, callback);
+    }
+  }
+
+  @VisibleForTesting
+  protected void loadCustomization() {
+    if (customizationMap.isEmpty()) {
+      PartnerConfigHelper helper = PartnerConfigHelper.get(getContext());
+      List<String> lists =
+          helper.getStringArray(
+              getContext(),
+              isNightMode(getResources().getConfiguration())
+                  ? animationConfig.getDarkThemeCustomization()
+                  : animationConfig.getLightThemeCustomization());
+      for (String item : lists) {
+        String[] splitItem = item.split(":");
+        if (splitItem.length == 2) {
+          customizationMap.put(
+              new KeyPath(splitItem[0]), new SimpleColorFilter(Color.parseColor(splitItem[1])));
+        } else {
+          Log.w(TAG, "incorrect format customization, value=" + item);
+        }
+      }
+    }
+  }
+
+  @VisibleForTesting
+  protected void applyThemeCustomization() {
+    LottieAnimationView animationView = findLottieAnimationView();
+    if (animationView != null) {
+      loadCustomization();
+      for (KeyPath keyPath : customizationMap.keySet()) {
+        animationView.addValueCallback(
+            keyPath,
+            LottieProperty.COLOR_FILTER,
+            new LottieValueCallback<>(customizationMap.get(keyPath)));
+      }
+    }
+  }
+
+  @Nullable
+  private View peekLottieLayout() {
+    return findViewById(R.id.sud_layout_lottie_illustration);
+  }
+
+  @Nullable
+  private View peekProgressIllustrationLayout() {
+    return findViewById(R.id.sud_layout_progress_illustration);
+  }
+
+  @Override
+  protected View onInflateTemplate(LayoutInflater inflater, int template) {
+    if (template == 0) {
+      template = R.layout.sud_glif_loading_template;
+    }
+    return inflateTemplate(inflater, R.style.SudThemeGlif_Light, template);
+  }
+
+  @Override
+  protected ViewGroup findContainer(int containerId) {
+    if (containerId == 0) {
+      containerId = R.id.sud_layout_content;
+    }
+    return super.findContainer(containerId);
+  }
+
+  /** The progress config used to maps to different animation */
+  public enum LottieAnimationConfig {
+    CONFIG_DEFAULT(
+        PartnerConfig.CONFIG_PROGRESS_ILLUSTRATION_DEFAULT,
+        PartnerConfig.CONFIG_LOADING_LOTTIE_DEFAULT,
+        PartnerConfig.CONFIG_LOTTIE_LIGHT_THEME_CUSTOMIZATION_DEFAULT,
+        PartnerConfig.CONFIG_LOTTIE_DARK_THEME_CUSTOMIZATION_DEFAULT),
+    CONFIG_ACCOUNT(
+        PartnerConfig.CONFIG_PROGRESS_ILLUSTRATION_ACCOUNT,
+        PartnerConfig.CONFIG_LOADING_LOTTIE_ACCOUNT,
+        PartnerConfig.CONFIG_LOTTIE_LIGHT_THEME_CUSTOMIZATION_ACCOUNT,
+        PartnerConfig.CONFIG_LOTTIE_DARK_THEME_CUSTOMIZATION_ACCOUNT),
+    CONFIG_CONNECTION(
+        PartnerConfig.CONFIG_PROGRESS_ILLUSTRATION_CONNECTION,
+        PartnerConfig.CONFIG_LOADING_LOTTIE_CONNECTION,
+        PartnerConfig.CONFIG_LOTTIE_LIGHT_THEME_CUSTOMIZATION_CONNECTION,
+        PartnerConfig.CONFIG_LOTTIE_DARK_THEME_CUSTOMIZATION_CONNECTION),
+    CONFIG_UPDATE(
+        PartnerConfig.CONFIG_PROGRESS_ILLUSTRATION_UPDATE,
+        PartnerConfig.CONFIG_LOADING_LOTTIE_UPDATE,
+        PartnerConfig.CONFIG_LOTTIE_LIGHT_THEME_CUSTOMIZATION_UPDATE,
+        PartnerConfig.CONFIG_LOTTIE_DARK_THEME_CUSTOMIZATION_UPDATE),
+    CONFIG_FINAL_HOLD(
+        PartnerConfig.CONFIG_PROGRESS_ILLUSTRATION_FINAL_HOLD,
+        PartnerConfig.CONFIG_LOADING_LOTTIE_FINAL_HOLD,
+        PartnerConfig.CONFIG_LOTTIE_LIGHT_THEME_CUSTOMIZATION_FINAL_HOLD,
+        PartnerConfig.CONFIG_LOTTIE_DARK_THEME_CUSTOMIZATION_FINAL_HOLD);
+
+    private final PartnerConfig illustrationConfig;
+    private final PartnerConfig lottieConfig;
+    private final PartnerConfig lightThemeCustomization;
+    private final PartnerConfig darkThemeCustomization;
+
+    LottieAnimationConfig(
+        PartnerConfig illustrationConfig,
+        PartnerConfig lottieConfig,
+        PartnerConfig lightThemeCustomization,
+        PartnerConfig darkThemeCustomization) {
+      if (illustrationConfig.getResourceType() != ResourceType.ILLUSTRATION
+          || lottieConfig.getResourceType() != ResourceType.ILLUSTRATION) {
+        throw new IllegalArgumentException(
+            "Illustration progress only allow illustration resource");
+      }
+      this.illustrationConfig = illustrationConfig;
+      this.lottieConfig = lottieConfig;
+      this.lightThemeCustomization = lightThemeCustomization;
+      this.darkThemeCustomization = darkThemeCustomization;
+    }
+
+    PartnerConfig getIllustrationConfig() {
+      return illustrationConfig;
+    }
+
+    PartnerConfig getLottieConfig() {
+      return lottieConfig;
+    }
+
+    PartnerConfig getLightThemeCustomization() {
+      return lightThemeCustomization;
+    }
+
+    PartnerConfig getDarkThemeCustomization() {
+      return darkThemeCustomization;
+    }
+  }
+
+  /**
+   * Register the {@link Runnable} as a callback class that will be perform when animation finished.
+   */
+  public void registerAnimationFinishRunnable(Runnable runnable) {
+    registerAnimationFinishRunnable(runnable, /* allowFinishWithMaximumDuration= */ false);
+  }
+
+  /**
+   * Register the {@link Runnable} as a callback class that will be perform when animation finished.
+   * {@code allowFinishWithMaximumDuration} to allow the animation finish advanced by {@link
+   * PartnerConfig#CONFIG_PROGRESS_ILLUSTRATION_DISPLAY_MINIMUM_MS} config. The {@code runnable}
+   * will be performed if the Lottie animation finish played and the duration of Lottie animation
+   * less than @link PartnerConfig#CONFIG_PROGRESS_ILLUSTRATION_DISPLAY_MINIMUM_MS} config.
+   */
+  public void registerAnimationFinishRunnable(
+      Runnable runnable, boolean allowFinishWithMaximumDuration) {
+    if (allowFinishWithMaximumDuration) {
+      int delayMs =
+          PartnerConfigHelper.get(getContext())
+              .getInteger(
+                  getContext(), PartnerConfig.CONFIG_PROGRESS_ILLUSTRATION_DISPLAY_MINIMUM_MS, 0);
+      animationFinishListeners.add(new LottieAnimationFinishListener(this, runnable, delayMs));
+    } else {
+      animationFinishListeners.add(
+          new LottieAnimationFinishListener(this, runnable, /* finishWithMinimumDuration= */ 0L));
+    }
+  }
+
+  /** The listener that to indicate the playing status for lottie animation. */
+  @VisibleForTesting
+  public static class LottieAnimationFinishListener {
+
+    private final Handler handler;
+    private final Runnable runnable;
+    private final GlifLoadingLayout glifLoadingLayout;
+    private final LottieAnimationView lottieAnimationView;
+
+    @VisibleForTesting
+    AnimatorListener animatorListener =
+        new AnimatorListener() {
+          @Override
+          public void onAnimationStart(Animator animation) {
+            // Do nothing.
+          }
+
+          @Override
+          public void onAnimationEnd(Animator animation) {
+            onAnimationFinished();
+          }
+
+          @Override
+          public void onAnimationCancel(Animator animation) {
+            // Do nothing.
+          }
+
+          @Override
+          public void onAnimationRepeat(Animator animation) {
+            // Do nothing.
+          }
+        };
+
+    @VisibleForTesting
+    LottieAnimationFinishListener(
+        GlifLoadingLayout glifLoadingLayout, Runnable runnable, long finishWithMinimumDuration) {
+      if (runnable == null) {
+        throw new NullPointerException("Runnable can not be null");
+      }
+      this.glifLoadingLayout = glifLoadingLayout;
+      this.runnable = runnable;
+      this.handler = new Handler(Looper.getMainLooper());
+      this.lottieAnimationView = glifLoadingLayout.findLottieAnimationView();
+
+      if (glifLoadingLayout.isLottieLayoutVisible() && !isZeroAnimatorDurationScale()) {
+        lottieAnimationView.setRepeatCount(0);
+        lottieAnimationView.addAnimatorListener(animatorListener);
+        if (finishWithMinimumDuration > 0) {
+          handler.postDelayed(this::onAnimationFinished, finishWithMinimumDuration);
+        }
+      } else {
+        onAnimationFinished();
+      }
+    }
+
+    @VisibleForTesting
+    boolean isZeroAnimatorDurationScale() {
+      try {
+        if (Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
+          return Global.getFloat(
+              glifLoadingLayout.getContext().getContentResolver(), Global.ANIMATOR_DURATION_SCALE)
+              == 0f;
+        } else {
+          return false;
+        }
+
+      } catch (SettingNotFoundException e) {
+        return false;
+      }
+    }
+
+    @VisibleForTesting
+    public void onAnimationFinished() {
+      handler.removeCallbacks(runnable);
+      runnable.run();
+      if (lottieAnimationView != null) {
+        lottieAnimationView.removeAnimatorListener(animatorListener);
+      }
+      glifLoadingLayout.animationFinishListeners.remove(this);
+    }
+  }
+
+  /** Annotates the state for the illustration. */
+  @Retention(RetentionPolicy.SOURCE)
+  @StringDef({
+    IllustrationType.ACCOUNT,
+    IllustrationType.CONNECTION,
+    IllustrationType.DEFAULT,
+    IllustrationType.UPDATE,
+    IllustrationType.FINAL_HOLD
+  })
+  public @interface IllustrationType {
+    String DEFAULT = "default";
+    String ACCOUNT = "account";
+    String CONNECTION = "connection";
+    String UPDATE = "update";
+    String FINAL_HOLD = "final_hold";
+  }
+
+  /** Annotates the type for the illustration. */
+  @Retention(RetentionPolicy.SOURCE)
+  @IntDef({AnimationType.LOTTIE, AnimationType.ILLUSTRATION, AnimationType.PROGRESS_BAR})
+  public @interface AnimationType {
+    int LOTTIE = 1;
+    int ILLUSTRATION = 2;
+    int PROGRESS_BAR = 3;
+  }
+}
diff --git a/lottie_loading_layout/src/com/google/android/setupdesign/LoadingFooterBarMixin.java b/lottie_loading_layout/src/com/google/android/setupdesign/LoadingFooterBarMixin.java
new file mode 100644
index 0000000..fcc2fff
--- /dev/null
+++ b/lottie_loading_layout/src/com/google/android/setupdesign/LoadingFooterBarMixin.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this 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.google.android.setupdesign;
+
+import android.util.AttributeSet;
+import android.widget.LinearLayout;
+import androidx.annotation.Nullable;
+import com.google.android.setupcompat.internal.TemplateLayout;
+import com.google.android.setupcompat.template.FooterBarMixin;
+
+/** A {@link Mixin} to get the container of footer bar for usage. */
+public class LoadingFooterBarMixin extends FooterBarMixin {
+
+  /**
+   * Creates a mixin for managing buttons on the footer.
+   *
+   * @param layout The {@link TemplateLayout} containing this mixin.
+   * @param attrs XML attributes given to the layout.
+   * @param defStyleAttr The default style attribute as given to the constructor of the layout.
+   */
+  public LoadingFooterBarMixin(
+      TemplateLayout layout, @Nullable AttributeSet attrs, int defStyleAttr) {
+    super(layout, attrs, defStyleAttr);
+  }
+
+  @Override
+  public LinearLayout getButtonContainer() {
+    return super.getButtonContainer();
+  }
+}
diff --git a/main/AndroidManifest.xml b/main/AndroidManifest.xml
index 245db97..984a73b 100644
--- a/main/AndroidManifest.xml
+++ b/main/AndroidManifest.xml
@@ -15,7 +15,15 @@
     limitations under the License.
 -->
 
-<manifest package="com.google.android.setupdesign">
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    package="com.google.android.setupdesign">
   <!-- Set in the BUILD or gradle file -->
   <uses-sdk />
+  <!-- after SDK 30, package need to declare its visible packages. -->
+  <queries tools:node="merge">
+    <intent>
+      <action android:name="com.android.setupwizard.action.PARTNER_CUSTOMIZATION" />
+    </intent>
+  </queries>
 </manifest>
diff --git a/main/res/anim-v31/sud_interpolator.xml b/main/res/anim-v31/sud_interpolator.xml
new file mode 100644
index 0000000..bec77f0
--- /dev/null
+++ b/main/res/anim-v31/sud_interpolator.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this 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.
+-->
+
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:pathData="M 0,0 C 0.05, 0, 0.133333, 0.06, 0.166666, 0.4 C 0.208333, 0.82, 0.25, 1, 1, 1" />
diff --git a/main/res/anim-v31/sud_slide_back_in.xml b/main/res/anim-v31/sud_slide_back_in.xml
new file mode 100644
index 0000000..77d4cd5
--- /dev/null
+++ b/main/res/anim-v31/sud_slide_back_in.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this 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.
+-->
+
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+    android:duration="@integer/sudTransitionDuration"
+    android:fromXDelta="-100%"
+    android:toXDelta="0%"
+    android:interpolator="@anim/sud_interpolator" />
diff --git a/main/res/anim-v31/sud_slide_back_out.xml b/main/res/anim-v31/sud_slide_back_out.xml
new file mode 100644
index 0000000..8ae16d4
--- /dev/null
+++ b/main/res/anim-v31/sud_slide_back_out.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this 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.
+-->
+
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+    android:duration="@integer/sudTransitionDuration"
+    android:fromXDelta="0%"
+    android:toXDelta="100%"
+    android:interpolator="@anim/sud_interpolator" />
diff --git a/main/res/anim-v31/sud_slide_next_in.xml b/main/res/anim-v31/sud_slide_next_in.xml
new file mode 100644
index 0000000..006d9b4
--- /dev/null
+++ b/main/res/anim-v31/sud_slide_next_in.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this 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.
+-->
+
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+    android:duration="@integer/sudTransitionDuration"
+    android:fromXDelta="100%"
+    android:toXDelta="0%"
+    android:interpolator="@anim/sud_interpolator" />
diff --git a/main/res/anim-v31/sud_slide_next_out.xml b/main/res/anim-v31/sud_slide_next_out.xml
new file mode 100644
index 0000000..341bbad
--- /dev/null
+++ b/main/res/anim-v31/sud_slide_next_out.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this 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.
+-->
+
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+    android:duration="@integer/sudTransitionDuration"
+    android:fromXDelta="0%"
+    android:toXDelta="-100%"
+    android:interpolator="@anim/sud_interpolator" />
diff --git a/main/res/anim/sud_pre_p_activity_close_enter.xml b/main/res/anim/sud_pre_p_activity_close_enter.xml
new file mode 100644
index 0000000..b8a7654
--- /dev/null
+++ b/main/res/anim/sud_pre_p_activity_close_enter.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2020 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this 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.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android" android:zAdjustment="normal">
+    <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+        android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
+        android:interpolator="@android:interpolator/linear_out_slow_in"
+        android:duration="250"/>
+</set>
diff --git a/main/res/anim/sud_pre_p_activity_close_exit.xml b/main/res/anim/sud_pre_p_activity_close_exit.xml
new file mode 100644
index 0000000..c813937
--- /dev/null
+++ b/main/res/anim/sud_pre_p_activity_close_exit.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2020 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this 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.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shareInterpolator="false" android:zAdjustment="top">
+    <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+        android:interpolator="@android:interpolator/linear"
+        android:fillEnabled="true"
+        android:fillBefore="false" android:fillAfter="true"
+        android:startOffset="100"
+        android:duration="150"/>
+    <translate android:fromYDelta="0%" android:toYDelta="8%"
+        android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
+        android:interpolator="@interpolator/sud_accelerate_quart"
+        android:duration="250"/>
+</set>
diff --git a/main/res/anim/sud_pre_p_activity_open_enter.xml b/main/res/anim/sud_pre_p_activity_open_enter.xml
new file mode 100644
index 0000000..63acc28
--- /dev/null
+++ b/main/res/anim/sud_pre_p_activity_open_enter.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2020 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this 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.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shareInterpolator="false"
+    android:zAdjustment="top">
+    <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
+        android:interpolator="@interpolator/sud_decelerate_quart"
+        android:fillEnabled="true"
+        android:fillBefore="false" android:fillAfter="true"
+        android:duration="200"/>
+    <translate android:fromYDelta="8%" android:toYDelta="0"
+        android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
+        android:interpolator="@android:interpolator/decelerate_quint"
+        android:duration="350"/>
+</set>
diff --git a/main/res/anim/sud_pre_p_activity_open_exit.xml b/main/res/anim/sud_pre_p_activity_open_exit.xml
new file mode 100644
index 0000000..950e32c
--- /dev/null
+++ b/main/res/anim/sud_pre_p_activity_open_exit.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2020 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this 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.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    android:zAdjustment="normal">
+    <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
+        android:fillEnabled="true" android:fillBefore="false" android:fillAfter="true"
+        android:interpolator="@android:interpolator/fast_out_slow_in"
+        android:duration="217"/>
+</set>
diff --git a/main/res/anim/sud_stay.xml b/main/res/anim/sud_stay.xml
new file mode 100644
index 0000000..285e7af
--- /dev/null
+++ b/main/res/anim/sud_stay.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Copyright (C) 2020 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<!-- This is needed to keep the exiting window shown while the entering window fades in -->
+<alpha xmlns:android="http://schemas.android.com/apk/res/android"
+    android:duration="@android:integer/config_longAnimTime"
+    android:fromAlpha="1.0"
+    android:interpolator="@android:interpolator/decelerate_quad"
+    android:toAlpha="1.0" />
diff --git a/main/res/color/sud_switch_track_on.xml b/main/res/color/sud_switch_track_on.xml
new file mode 100644
index 0000000..a95a1ee
--- /dev/null
+++ b/main/res/color/sud_switch_track_on.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools">
+  <item android:state_enabled="false"
+      android:color="?android:attr/colorForeground"
+      android:alpha="?android:attr/disabledAlpha" />
+  <item android:state_checked="true"
+      android:color="?android:attr/colorControlActivated" tools:ignore="NewApi"
+      android:alpha="?android:attr/disabledAlpha" />
+  <item android:color="?android:attr/colorForeground" />
+</selector>
diff --git a/main/res/drawable-v21/sud_switch_thumb_on.xml b/main/res/drawable-v21/sud_switch_thumb_on.xml
new file mode 100644
index 0000000..224f181
--- /dev/null
+++ b/main/res/drawable-v21/sud_switch_thumb_on.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this 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.
+  -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:top="@dimen/sud_switch_thumb_margin"
+        android:left="@dimen/sud_switch_thumb_margin"
+        android:right="@dimen/sud_switch_thumb_margin"
+        android:bottom="@dimen/sud_switch_thumb_margin">
+        <shape android:shape="oval">
+            <size
+                android:height="@dimen/sud_switch_thumb_size"
+                android:width="@dimen/sud_switch_thumb_size" />
+            <solid android:color="?android:attr/colorControlActivated" />
+        </shape>
+    </item>
+</layer-list>
\ No newline at end of file
diff --git a/main/res/drawable/sud_dialog_background_dark.xml b/main/res/drawable/sud_dialog_background_dark.xml
new file mode 100644
index 0000000..914cfec
--- /dev/null
+++ b/main/res/drawable/sud_dialog_background_dark.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this 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.
+-->
+
+<!-- The purpose of this file is to set the radius for datetime dialog which is
+     copy from abc_dialog_material_background.xml of
+     Base.V7.Theme.AppCompat.Light.Dialog. -->
+<inset xmlns:android="http://schemas.android.com/apk/res/android">
+    android:insetLeft="16dp"
+    android:insetTop="16dp"
+    android:insetRight="16dp"
+    android:insetBottom="16dp">
+    <shape android:shape="rectangle">
+        <corners android:radius="?attr/dialogCornerRadius" />
+        <solid android:color="@color/sud_glif_window_bg_dark_color" />
+    </shape>
+</inset>
diff --git a/main/res/drawable/sud_dialog_background_light.xml b/main/res/drawable/sud_dialog_background_light.xml
new file mode 100644
index 0000000..0302783
--- /dev/null
+++ b/main/res/drawable/sud_dialog_background_light.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this 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.
+-->
+
+<!-- The purpose of this file is to set the radius for datetime dialog which is
+     copy from abc_dialog_material_background.xml of
+     Base.V7.Theme.AppCompat.Light.Dialog. -->
+<inset xmlns:android="http://schemas.android.com/apk/res/android">
+    android:insetLeft="16dp"
+    android:insetTop="16dp"
+    android:insetRight="16dp"
+    android:insetBottom="16dp">
+    <shape android:shape="rectangle">
+        <corners android:radius="?attr/dialogCornerRadius" />
+        <solid android:color="@color/sud_glif_window_bg_light_color" />
+    </shape>
+</inset>
diff --git a/main/res/drawable/sud_scroll_bar_dark.xml b/main/res/drawable/sud_scroll_bar_dark.xml
new file mode 100644
index 0000000..fbfce55
--- /dev/null
+++ b/main/res/drawable/sud_scroll_bar_dark.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <solid android:color="#33FFFFFF" />
+    <corners android:radius="2dp" />
+</shape>
diff --git a/main/res/drawable/sud_scroll_bar_light.xml b/main/res/drawable/sud_scroll_bar_light.xml
new file mode 100644
index 0000000..ad32634
--- /dev/null
+++ b/main/res/drawable/sud_scroll_bar_light.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <solid android:color="#33000000" />
+    <corners android:radius="2dp" />
+</shape>
diff --git a/main/res/drawable/sud_switch_thumb_off.xml b/main/res/drawable/sud_switch_thumb_off.xml
new file mode 100644
index 0000000..6b4f115
--- /dev/null
+++ b/main/res/drawable/sud_switch_thumb_off.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this 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.
+  -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:top="@dimen/sud_switch_thumb_margin"
+        android:left="@dimen/sud_switch_thumb_margin"
+        android:right="@dimen/sud_switch_thumb_margin"
+        android:bottom="@dimen/sud_switch_thumb_margin">
+        <shape android:shape="oval">
+            <size
+                android:height="@dimen/sud_switch_thumb_size"
+                android:width="@dimen/sud_switch_thumb_size" />
+            <solid android:color="@color/sud_switch_thumb_off" />
+        </shape>
+    </item>
+</layer-list>
\ No newline at end of file
diff --git a/main/res/drawable/sud_switch_thumb_on.xml b/main/res/drawable/sud_switch_thumb_on.xml
new file mode 100644
index 0000000..cc1cf06
--- /dev/null
+++ b/main/res/drawable/sud_switch_thumb_on.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this 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.
+  -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools">
+    <item
+        android:top="@dimen/sud_switch_thumb_margin"
+        android:left="@dimen/sud_switch_thumb_margin"
+        android:right="@dimen/sud_switch_thumb_margin"
+        android:bottom="@dimen/sud_switch_thumb_margin">
+        <shape android:shape="oval">
+            <size
+                android:height="@dimen/sud_switch_thumb_size"
+                android:width="@dimen/sud_switch_thumb_size" />
+            <solid android:color="?android:attr/colorAccent" tools:ignore="NewApi" />
+        </shape>
+    </item>
+</layer-list>
diff --git a/main/res/drawable/sud_switch_thumb_selector.xml b/main/res/drawable/sud_switch_thumb_selector.xml
new file mode 100644
index 0000000..b30ffb0
--- /dev/null
+++ b/main/res/drawable/sud_switch_thumb_selector.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/sud_switch_thumb_on" android:state_checked="true" />
+    <item android:drawable="@drawable/sud_switch_thumb_off" android:state_checked="false" />
+</selector>
\ No newline at end of file
diff --git a/main/res/drawable/sud_switch_track_off_background.xml b/main/res/drawable/sud_switch_track_off_background.xml
new file mode 100644
index 0000000..d15cb9b
--- /dev/null
+++ b/main/res/drawable/sud_switch_track_off_background.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle"
+    android:width="@dimen/sud_switch_track_width"
+    android:height="@dimen/sud_switch_track_height">
+    <solid android:color="@color/sud_switch_track_off" />
+    <corners android:radius="@dimen/sud_switch_track_radius" />
+</shape>
\ No newline at end of file
diff --git a/main/res/drawable/sud_switch_track_on_background.xml b/main/res/drawable/sud_switch_track_on_background.xml
new file mode 100644
index 0000000..ff40595
--- /dev/null
+++ b/main/res/drawable/sud_switch_track_on_background.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle"
+    android:width="@dimen/sud_switch_track_width"
+    android:height="@dimen/sud_switch_track_height">
+    <solid android:color="@color/sud_switch_track_on" />
+    <corners android:radius="@dimen/sud_switch_track_radius" />
+</shape>
\ No newline at end of file
diff --git a/main/res/drawable/sud_switch_track_selector.xml b/main/res/drawable/sud_switch_track_selector.xml
new file mode 100644
index 0000000..11cee5c
--- /dev/null
+++ b/main/res/drawable/sud_switch_track_selector.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/sud_switch_track_on_background" android:state_checked="true" />
+    <item android:drawable="@drawable/sud_switch_track_off_background" android:state_checked="false" />
+</selector>
\ No newline at end of file
diff --git a/main/res/interpolator/sud_accelerate_quart.xml b/main/res/interpolator/sud_accelerate_quart.xml
new file mode 100644
index 0000000..b8a6ebf
--- /dev/null
+++ b/main/res/interpolator/sud_accelerate_quart.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Copyright (C) 2020 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this 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.
+-->
+
+<accelerateInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:factor="2.0" />
diff --git a/main/res/interpolator/sud_decelerate_quart.xml b/main/res/interpolator/sud_decelerate_quart.xml
new file mode 100644
index 0000000..06d7fe8
--- /dev/null
+++ b/main/res/interpolator/sud_decelerate_quart.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Copyright (C) 2020 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this 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.
+-->
+
+<decelerateInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:factor="2.0" />
diff --git a/main/res/layout-land-v31/sud_glif_blank_template_content.xml b/main/res/layout-land-v31/sud_glif_blank_template_content.xml
new file mode 100644
index 0000000..dd4d52d
--- /dev/null
+++ b/main/res/layout-land-v31/sud_glif_blank_template_content.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/sud_layout_template_content"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:orientation="horizontal">
+
+        <LinearLayout
+            android:id="@+id/sud_landscape_header_area"
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_weight="@dimen/sud_glif_land_header_area_weight"
+            android:orientation="vertical">
+
+            <ViewStub
+                android:id="@+id/sud_layout_sticky_header"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content" />
+
+        </LinearLayout>
+
+        <LinearLayout
+            android:id="@+id/sud_landscape_content_area"
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_weight="@dimen/sud_glif_land_content_area_weight"
+            android:orientation="vertical">
+
+            <FrameLayout
+                android:id="@+id/sud_layout_content"
+                android:layout_width="match_parent"
+                android:layout_height="0dp"
+                android:layout_weight="1" />
+
+        </LinearLayout>
+
+    </LinearLayout>
+
+    <ViewStub
+        android:id="@+id/suc_layout_footer"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+
+</LinearLayout>
diff --git a/main/res/layout-land-v31/sud_glif_list_template_content.xml b/main/res/layout-land-v31/sud_glif_list_template_content.xml
new file mode 100644
index 0000000..478ac5f
--- /dev/null
+++ b/main/res/layout-land-v31/sud_glif_list_template_content.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/sud_layout_template_content"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:orientation="horizontal">
+
+        <LinearLayout
+            android:id="@+id/sud_landscape_header_area"
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_weight="@dimen/sud_glif_land_header_area_weight"
+            android:orientation="vertical">
+
+            <ViewStub
+                android:id="@+id/sud_layout_sticky_header"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content" />
+
+            <com.google.android.setupdesign.view.BottomScrollView
+                android:id="@+id/sud_header_scroll_view"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:fillViewport="true"
+                android:scrollIndicators="?attr/sudScrollIndicators">
+
+                <include layout="@layout/sud_glif_header" />
+
+            </com.google.android.setupdesign.view.BottomScrollView>
+
+        </LinearLayout>
+
+        <LinearLayout
+            android:id="@+id/sud_landscape_content_area"
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_weight="@dimen/sud_glif_land_content_area_weight"
+            android:orientation="vertical">
+
+            <com.google.android.setupdesign.view.StickyHeaderListView
+                xmlns:app="http://schemas.android.com/apk/res-auto"
+                android:id="@android:id/list"
+                android:layout_width="match_parent"
+                android:layout_height="0dp"
+                android:layout_weight="1"
+                android:scrollIndicators="?attr/sudScrollIndicators" />
+
+        </LinearLayout>
+
+    </LinearLayout>
+
+    <ViewStub
+        android:id="@+id/suc_layout_footer"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+
+</LinearLayout>
diff --git a/main/res/layout-land-v31/sud_glif_preference_recycler_view.xml b/main/res/layout-land-v31/sud_glif_preference_recycler_view.xml
new file mode 100644
index 0000000..53fac8f
--- /dev/null
+++ b/main/res/layout-land-v31/sud_glif_preference_recycler_view.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this 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.
+-->
+
+<com.google.android.setupdesign.view.HeaderRecyclerView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/sud_recycler_view"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:clipChildren="false"
+    android:scrollbars="vertical" />
diff --git a/main/res/layout-land-v31/sud_glif_preference_template_content.xml b/main/res/layout-land-v31/sud_glif_preference_template_content.xml
new file mode 100644
index 0000000..c03d2fe
--- /dev/null
+++ b/main/res/layout-land-v31/sud_glif_preference_template_content.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/sud_layout_template_content"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:orientation="horizontal">
+
+        <LinearLayout
+            android:id="@+id/sud_landscape_header_area"
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_weight="@dimen/sud_glif_land_header_area_weight"
+            android:orientation="vertical">
+
+            <ViewStub
+                android:id="@+id/sud_layout_sticky_header"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content" />
+
+            <com.google.android.setupdesign.view.BottomScrollView
+                android:id="@+id/sud_header_scroll_view"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:fillViewport="true"
+                android:scrollIndicators="?attr/sudScrollIndicators">
+
+                <include layout="@layout/sud_glif_header" />
+
+            </com.google.android.setupdesign.view.BottomScrollView>
+
+        </LinearLayout>
+
+        <LinearLayout
+            android:id="@+id/sud_landscape_content_area"
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_weight="@dimen/sud_glif_land_content_area_weight"
+            android:orientation="vertical">
+
+            <FrameLayout
+                android:id="@+id/sud_layout_content"
+                android:layout_width="match_parent"
+                android:layout_height="0dp"
+                android:layout_weight="1" />
+
+        </LinearLayout>
+
+    </LinearLayout>
+
+    <ViewStub
+        android:id="@+id/suc_layout_footer"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+
+</LinearLayout>
diff --git a/main/res/layout-land-v31/sud_glif_recycler_template_content.xml b/main/res/layout-land-v31/sud_glif_recycler_template_content.xml
new file mode 100644
index 0000000..4840caf
--- /dev/null
+++ b/main/res/layout-land-v31/sud_glif_recycler_template_content.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/sud_layout_template_content"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:orientation="horizontal">
+
+        <LinearLayout
+            android:id="@+id/sud_landscape_header_area"
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_weight="@dimen/sud_glif_land_header_area_weight"
+            android:orientation="vertical">
+
+            <ViewStub
+                android:id="@+id/sud_layout_sticky_header"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content" />
+
+            <com.google.android.setupdesign.view.BottomScrollView
+                android:id="@+id/sud_header_scroll_view"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:fillViewport="true"
+                android:scrollIndicators="?attr/sudScrollIndicators">
+
+                <include layout="@layout/sud_glif_header" />
+
+            </com.google.android.setupdesign.view.BottomScrollView>
+
+        </LinearLayout>
+
+        <LinearLayout
+            android:id="@+id/sud_landscape_content_area"
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_weight="@dimen/sud_glif_land_content_area_weight"
+            android:orientation="vertical">
+
+            <com.google.android.setupdesign.view.HeaderRecyclerView
+                android:id="@+id/sud_recycler_view"
+                android:layout_width="match_parent"
+                android:layout_height="0dp"
+                android:layout_weight="1"
+                android:scrollbars="vertical"
+                android:scrollIndicators="?attr/sudScrollIndicators" />
+
+        </LinearLayout>
+
+    </LinearLayout>
+
+    <ViewStub
+        android:id="@+id/suc_layout_footer"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+
+</LinearLayout>
diff --git a/main/res/layout-land-v31/sud_glif_template_content.xml b/main/res/layout-land-v31/sud_glif_template_content.xml
new file mode 100644
index 0000000..d1b6b92
--- /dev/null
+++ b/main/res/layout-land-v31/sud_glif_template_content.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/sud_layout_template_content"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:orientation="horizontal">
+
+        <LinearLayout
+            android:id="@+id/sud_landscape_header_area"
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_weight="@dimen/sud_glif_land_header_area_weight"
+            android:orientation="vertical">
+
+            <ViewStub
+                android:id="@+id/sud_layout_sticky_header"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content" />
+
+            <com.google.android.setupdesign.view.BottomScrollView
+                android:id="@+id/sud_header_scroll_view"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:fillViewport="true"
+                android:scrollIndicators="?attr/sudScrollIndicators">
+
+                <include layout="@layout/sud_glif_header" />
+
+            </com.google.android.setupdesign.view.BottomScrollView>
+
+        </LinearLayout>
+
+        <LinearLayout
+            android:id="@+id/sud_landscape_content_area"
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:layout_weight="@dimen/sud_glif_land_content_area_weight"
+            android:orientation="vertical">
+
+            <com.google.android.setupdesign.view.BottomScrollView
+                android:id="@+id/sud_scroll_view"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:fillViewport="true"
+                android:scrollIndicators="?attr/sudScrollIndicators">
+
+                <LinearLayout
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:orientation="vertical">
+
+                    <FrameLayout
+                        android:id="@+id/sud_layout_content"
+                        android:layout_width="match_parent"
+                        android:layout_height="0dp"
+                        android:layout_weight="1" />
+
+                    <ViewStub
+                        android:id="@+id/sud_layout_illustration_progress_stub"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:inflatedId="@+id/sud_layout_progress_illustration"
+                        android:layout="@layout/sud_progress_illustration_layout" />
+                </LinearLayout>
+
+            </com.google.android.setupdesign.view.BottomScrollView>
+
+        </LinearLayout>
+
+    </LinearLayout>
+
+    <ViewStub
+        android:id="@+id/suc_layout_footer"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+
+</LinearLayout>
diff --git a/main/res/layout-v31/sud_glif_preference_template_compact.xml b/main/res/layout-v31/sud_glif_preference_template_compact.xml
new file mode 100644
index 0000000..a111e70
--- /dev/null
+++ b/main/res/layout-v31/sud_glif_preference_template_compact.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this 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.
+-->
+
+<com.google.android.setupcompat.view.StatusBarBackgroundLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/suc_layout_status"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <include layout="@layout/sud_glif_preference_template_content" />
+
+</com.google.android.setupcompat.view.StatusBarBackgroundLayout>
\ No newline at end of file
diff --git a/main/res/layout-v31/sud_glif_preference_template_content.xml b/main/res/layout-v31/sud_glif_preference_template_content.xml
new file mode 100644
index 0000000..c618139
--- /dev/null
+++ b/main/res/layout-v31/sud_glif_preference_template_content.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/sud_layout_template_content"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <include layout="@layout/sud_glif_blank_template_content" />
+
+</LinearLayout>
diff --git a/main/res/layout/sud_content_info.xml b/main/res/layout/sud_content_info.xml
new file mode 100644
index 0000000..288c3af
--- /dev/null
+++ b/main/res/layout/sud_content_info.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this 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"
+    android:id="@+id/sud_content_info_container"
+    style="@style/SudInfoContainer"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:baselineAligned="false"
+    android:orientation="horizontal">
+
+    <FrameLayout
+        android:id="@+id/sud_content_info_icon_container"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="top"
+        android:gravity="start">
+
+        <ImageView
+            android:id="@+id/sud_content_info_icon"
+            android:layout_width="@dimen/sud_content_info_icon_size"
+            android:layout_height="@dimen/sud_content_info_icon_size"
+            android:layout_marginEnd="@dimen/sud_content_info_icon_margin_end"
+            tools:ignore="ContentDescription" />
+
+    </FrameLayout>
+
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:gravity="center_vertical"
+        android:orientation="vertical">
+
+        <com.google.android.setupdesign.view.RichTextView
+            android:id="@+id/sud_content_info_description"
+            style="@style/SudInfoDescription"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="start"
+            android:textAlignment="viewStart"
+            tools:ignore="UnusedAttribute" />
+
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/main/res/layout/sud_glif_blank_template_content.xml b/main/res/layout/sud_glif_blank_template_content.xml
index 1eaae13..887655d 100644
--- a/main/res/layout/sud_glif_blank_template_content.xml
+++ b/main/res/layout/sud_glif_blank_template_content.xml
@@ -17,6 +17,7 @@
 
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/sud_layout_template_content"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical">
diff --git a/main/res/layout/sud_glif_header.xml b/main/res/layout/sud_glif_header.xml
index f5a1113..50feeb2 100644
--- a/main/res/layout/sud_glif_header.xml
+++ b/main/res/layout/sud_glif_header.xml
@@ -22,13 +22,25 @@
     android:layout_height="wrap_content"
     android:orientation="vertical">
 
-    <ImageView
-        android:id="@+id/sud_layout_icon"
-        style="?attr/sudGlifIconStyle"
-        android:layout_width="wrap_content"
+    <FrameLayout
+        android:id="@+id/sud_layout_icon_container"
+        style="@style/SudGlifIconContainer"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:contentDescription="@null"
-        android:visibility="gone" />
+        android:visibility="gone" >
+        <ImageView
+            android:id="@+id/sud_layout_icon"
+            style="?attr/sudGlifIconStyle"
+            android:layout_marginLeft="0dp"
+            android:layout_marginRight="0dp"
+            android:layout_marginTop="0dp"
+            android:layout_marginBottom="0dp"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:contentDescription="@null"
+            android:layout_gravity="?attr/sudGlifHeaderGravity"
+            android:visibility="gone" />
+    </FrameLayout>
 
     <TextView
         android:id="@+id/suc_layout_title"
@@ -41,10 +53,29 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_marginBottom="@dimen/sud_glif_progress_bar_margin_vertical"
-        android:layout_marginLeft="?attr/sudMarginSides"
-        android:layout_marginRight="?attr/sudMarginSides"
+        android:layout_marginLeft="?attr/sudMarginStart"
+        android:layout_marginRight="?attr/sudMarginStart"
         android:layout_marginTop="@dimen/sud_glif_progress_bar_margin_vertical"
         android:inflatedId="@+id/sud_layout_progress"
         android:layout="@layout/sud_progress_bar" />
 
+    <com.google.android.setupdesign.view.RichTextView
+        android:id="@+id/sud_layout_subtitle"
+        style="@style/SudGlifDescription"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:visibility="gone" />
+
+    <ProgressBar
+        android:id="@+id/sud_glif_progress_bar"
+        style="?android:attr/progressBarStyleHorizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="?attr/sudMarginStart"
+        android:layout_marginRight="?attr/sudMarginStart"
+        android:layout_marginBottom="@dimen/sud_progress_bar_margin_vertical"
+        android:layout_marginTop="@dimen/sud_progress_bar_margin_vertical"
+        android:visibility="gone"
+        android:indeterminate="true" />
+
 </LinearLayout>
diff --git a/main/res/layout/sud_glif_list_template_content.xml b/main/res/layout/sud_glif_list_template_content.xml
index 1a6b4cd..09c56d2 100644
--- a/main/res/layout/sud_glif_list_template_content.xml
+++ b/main/res/layout/sud_glif_list_template_content.xml
@@ -18,6 +18,7 @@
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/sud_layout_template_content"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical">
diff --git a/main/res/layout/sud_glif_recycler_template_content.xml b/main/res/layout/sud_glif_recycler_template_content.xml
index 9ca640b..c2cccf0 100644
--- a/main/res/layout/sud_glif_recycler_template_content.xml
+++ b/main/res/layout/sud_glif_recycler_template_content.xml
@@ -19,6 +19,7 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/sud_layout_template_content"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical">
diff --git a/main/res/layout/sud_glif_template_content.xml b/main/res/layout/sud_glif_template_content.xml
index a72861e..fa898eb 100644
--- a/main/res/layout/sud_glif_template_content.xml
+++ b/main/res/layout/sud_glif_template_content.xml
@@ -18,6 +18,7 @@
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:tools="http://schemas.android.com/tools"
+    android:id="@+id/sud_layout_template_content"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical">
@@ -45,6 +46,13 @@
 
             <include layout="@layout/sud_glif_header" />
 
+            <ViewStub
+                android:id="@+id/sud_layout_illustration_progress_stub"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:inflatedId="@+id/sud_layout_progress_illustration"
+                android:layout="@layout/sud_progress_illustration_layout" />
+
             <FrameLayout
                 android:id="@+id/sud_layout_content"
                 android:layout_width="match_parent"
diff --git a/main/res/layout/sud_progress_illustration_layout.xml b/main/res/layout/sud_progress_illustration_layout.xml
new file mode 100644
index 0000000..3b850f4
--- /dev/null
+++ b/main/res/layout/sud_progress_illustration_layout.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2020 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this 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/SudContentFrame"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
+
+    <TextView
+        android:id="@+id/sud_layout_description"
+        android:visibility="invisible"
+        style="@style/SudDescription.Glif"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+
+    <com.google.android.setupdesign.view.FillContentLayout
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <ProgressBar
+            android:id="@+id/sud_progress_bar"
+            android:visibility="gone"
+            style="@style/SudFourColorIndeterminateProgressBar"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent" />
+
+        <com.google.android.setupdesign.view.IllustrationVideoView
+            android:id="@+id/sud_progress_illustration"
+            android:visibility="gone"
+            style="@style/SudContentIllustration"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" />
+
+    </com.google.android.setupdesign.view.FillContentLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/main/res/values-land-v31/dimens.xml b/main/res/values-land-v31/dimens.xml
new file mode 100644
index 0000000..8b8d10d
--- /dev/null
+++ b/main/res/values-land-v31/dimens.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this 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>
+
+    <!-- General -->
+    <dimen name="sud_glif_footer_padding_start">16dp</dimen>
+    <!-- Calculated by (sud_glif_margin_end - 4dp internal padding of button) -->
+    <dimen name="sud_glif_footer_padding_end">28dp</dimen>
+    <dimen name="sud_glif_margin_start">32dp</dimen>
+    <dimen name="sud_glif_margin_end">32dp</dimen>
+
+</resources>
diff --git a/main/res/values-land/dimens.xml b/main/res/values-land/dimens.xml
index 88a97be..49ed20d 100644
--- a/main/res/values-land/dimens.xml
+++ b/main/res/values-land/dimens.xml
@@ -17,6 +17,11 @@
 
 <resources>
 
+    <!-- General -->
+    <dimen name="sud_glif_button_min_height">36dp</dimen>
+    <dimen name="sud_glif_footer_min_height">52dp</dimen>
+    <dimen name="sud_glif_footer_padding_vertical">0dp</dimen>
+
     <!-- Card layout (for tablets) -->
     <dimen name="sud_card_title_padding_end">32dp</dimen>
     <dimen name="sud_card_title_padding_start">56dp</dimen>
@@ -33,4 +38,11 @@
     <!-- Illustration -->
     <item name="sud_illustration_aspect_ratio" format="float" type="dimen">0</item>
 
+    <!-- TODO: Add testcase for testing padding value in landscape mode -->
+    <!-- Loading content styles -->
+    <dimen name="sud_content_loading_frame_padding_top">0dp</dimen>
+    <dimen name="sud_content_loading_frame_padding_start">0dp</dimen>
+    <dimen name="sud_content_loading_frame_padding_end">0dp</dimen>
+    <dimen name="sud_content_loading_frame_padding_bottom">24dp</dimen>
+
 </resources>
diff --git a/main/res/values-night-v31/colors.xml b/main/res/values-night-v31/colors.xml
new file mode 100644
index 0000000..f929595
--- /dev/null
+++ b/main/res/values-night-v31/colors.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this 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>
+  <!-- Accent color -->
+  <color name="sud_dynamic_color_accent_glif_v3">@color/sud_dynamic_color_accent_glif_v3_dark</color>
+
+
+  <color name="sud_system_primary_text">@color/sud_system_neutral1_50</color>
+  <color name="sud_system_secondary_text">@color/sud_system_neutral2_200</color>
+  <color name="sud_system_tertiary_text_inactive">@color/sud_system_neutral2_400</color>
+  <color name="sud_system_error_warning">@color/sud_error_warning_default_dark</color>
+  <color name="sud_system_background_surface">@color/sud_system_neutral1_900</color>
+  <color name="sud_system_accent_icon_text_button">@color/sud_system_accent1_100</color>
+  <color name="sud_system_button_surface">@color/sud_system_accent1_100</color>
+  <color name="sud_system_button_text">@color/sud_system_neutral1_900</color>
+  <color name="sud_system_dividing_line">@color/sud_system_neutral2_300</color>
+
+</resources>
\ No newline at end of file
diff --git a/main/res/values-night/colors.xml b/main/res/values-night/colors.xml
new file mode 100644
index 0000000..1062ea7
--- /dev/null
+++ b/main/res/values-night/colors.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this 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>
+
+  <color name="sud_system_primary_text">@color/sud_primary_default_text_dark</color>
+  <color name="sud_system_secondary_text">@color/sud_secondary_default_text_dark</color>
+  <color name="sud_system_tertiary_text_inactive">@color/sud_inactive_default_dark</color>
+  <color name="sud_system_error_warning">@color/sud_error_warning_default_dark</color>
+  <color name="sud_system_success_done">@color/success_color_device_default_dark</color>
+  <color name="sud_system_fallback_accent">@color/fallback_color_device_default_dark</color>
+
+  <color name="sud_portal_pending_progress">@color/sud_portal_pending_progress_dark</color>
+
+
+  <color name="sud_uniformity_backdrop_color">#2A2B2E</color>
+</resources>
\ No newline at end of file
diff --git a/main/res/values-night/styles.xml b/main/res/values-night/styles.xml
index ca94c12..d609b4c 100644
--- a/main/res/values-night/styles.xml
+++ b/main/res/values-night/styles.xml
@@ -22,5 +22,7 @@
     <style name="SudThemeGlif.DayNight" parent="SudThemeGlif" />
     <style name="SudThemeGlifV2.DayNight" parent="SudThemeGlifV2" />
     <style name="SudThemeGlifV3.DayNight" parent="SudThemeGlifV3" />
+    <style name="SudDynamicColorThemeGlifV3.DayNight" parent="SudDynamicColorThemeGlifV3" />
+    <style name="SudFullDynamicColorThemeGlifV3.DayNight" parent="SudFullDynamicColorThemeGlifV3" />
 
 </resources>
diff --git a/main/res/values-sw600dp/dimens.xml b/main/res/values-sw600dp/dimens.xml
index bf9e1e9..b7802b1 100644
--- a/main/res/values-sw600dp/dimens.xml
+++ b/main/res/values-sw600dp/dimens.xml
@@ -18,11 +18,12 @@
 <resources>
 
     <!-- General -->
-    <!-- Calculated by (sud_glif_margin_sides - 4dp internal padding of button) -->
+    <!-- Calculated by (sud_glif_margin_start - 4dp internal padding of button) -->
     <dimen name="sud_glif_button_margin_end">36dp</dimen>
-    <!-- Calculated by (sud_glif_margin_sides - sud_glif_button_padding) -->
+    <!-- Calculated by (sud_glif_margin_start - sud_glif_button_padding) -->
     <dimen name="sud_glif_button_margin_start">24dp</dimen>
-    <dimen name="sud_glif_margin_sides">40dp</dimen>
+    <dimen name="sud_glif_margin_start">40dp</dimen>
+    <dimen name="sud_glif_margin_end">40dp</dimen>
 
     <!-- Decor view (illustration or the header without illustration) -->
     <dimen name="sud_decor_padding_top">256dp</dimen>
diff --git a/main/res/values-v21/styles.xml b/main/res/values-v21/styles.xml
index 6a4df1b..c961745 100644
--- a/main/res/values-v21/styles.xml
+++ b/main/res/values-v21/styles.xml
@@ -54,10 +54,10 @@
 
     <style name="SudItemContainer">
         <item name="android:minHeight">?android:attr/listPreferredItemHeight</item>
-        <item name="android:paddingBottom">@dimen/sud_items_padding_vertical</item>
+        <item name="android:paddingBottom">@dimen/sud_items_padding_bottom</item>
         <item name="android:paddingEnd">?android:attr/listPreferredItemPaddingEnd</item>
         <item name="android:paddingStart">?android:attr/listPreferredItemPaddingStart</item>
-        <item name="android:paddingTop">@dimen/sud_items_padding_vertical</item>
+        <item name="android:paddingTop">@dimen/sud_items_padding_top</item>
     </style>
 
     <style name="SudItemTitle">
diff --git a/main/res/values-v27/styles.xml b/main/res/values-v27/styles.xml
index d3906b8..c24f538 100644
--- a/main/res/values-v27/styles.xml
+++ b/main/res/values-v27/styles.xml
@@ -27,5 +27,14 @@
         <!-- Ignore NewApi: For some reason lint seems to think this API is new in v28 (b/73514594) -->
         <item name="android:windowLightNavigationBar" tools:ignore="NewApi">true</item>
         <item name="sucLightSystemNavBar" tools:ignore="NewApi">?android:attr/windowLightNavigationBar</item>
+        <item name="sucSystemNavBarDividerColor" tools:ignore="NewApi">?android:attr/navigationBarDividerColor</item>
+    </style>
+
+    <style name="SudThemeGlifV3" parent="SudBaseThemeGlifV3">
+        <item name="android:navigationBarColor">@color/sud_glif_v3_nav_bar_color_dark</item>
+        <item name="android:navigationBarDividerColor" tools:ignore="NewApi">@color/sud_glif_v3_nav_bar_divider_color_dark</item>
+        <item name="android:windowLightNavigationBar" tools:ignore="NewApi">false</item>
+        <item name="sucLightSystemNavBar" tools:ignore="NewApi">?android:attr/windowLightNavigationBar</item>
+        <item name="sucSystemNavBarDividerColor" tools:ignore="NewApi">?android:attr/navigationBarDividerColor</item>
     </style>
 </resources>
diff --git a/main/res/values-v31/colors.xml b/main/res/values-v31/colors.xml
new file mode 100644
index 0000000..91587ee
--- /dev/null
+++ b/main/res/values-v31/colors.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this 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>
+  <!-- Default color for BC -->
+
+  <color name="sud_color_accent_glif_v3_dark">#ff669df6</color>
+
+  <color name="sud_color_accent_glif_v3_light">#ff1a73e8</color>
+
+  <!-- Accent color -->
+  <color name="sud_dynamic_color_accent_glif_v3">@color/sud_dynamic_color_accent_glif_v3_light</color>
+  <color name="sud_dynamic_color_accent_glif_v3_dark">@color/sud_system_accent1_100</color>
+  <color name="sud_dynamic_color_accent_glif_v3_light">@color/sud_system_accent1_600</color>
+
+
+  <color name="sud_error_warning_default_dark">#F28B82</color>
+
+  <color name="sud_error_warning_default_light">#EA4335</color>
+
+
+
+  <color name="sud_system_accent1_100">@android:color/system_accent1_100</color>
+
+  <color name="sud_system_accent1_200">@android:color/system_accent1_200</color>
+
+  <color name="sud_system_accent1_600">@android:color/system_accent1_600</color>
+
+
+
+  <color name="sud_system_neutral1_0">@android:color/system_neutral1_0</color>
+
+  <color name="sud_system_neutral1_50">@android:color/system_neutral1_50</color>
+
+  <color name="sud_system_neutral1_800">@android:color/system_neutral1_800</color>
+
+  <color name="sud_system_neutral1_900">@android:color/system_neutral1_900</color>
+
+
+
+  <color name="sud_system_neutral2_50">@android:color/system_neutral1_50</color>
+
+  <color name="sud_system_neutral2_100">@android:color/system_neutral1_100</color>
+
+  <color name="sud_system_neutral2_200">@android:color/system_neutral2_200</color>
+
+  <color name="sud_system_neutral2_300">@android:color/system_accent2_300</color>
+
+  <color name="sud_system_neutral2_400">@android:color/system_neutral2_400</color>
+
+  <color name="sud_system_neutral2_500">@android:color/system_neutral2_500</color>
+
+  <color name="sud_system_neutral2_700">@android:color/system_neutral2_700</color>
+
+
+  <color name="sud_system_primary_text">@color/sud_system_neutral1_900</color>
+  <color name="sud_system_secondary_text">@color/sud_system_neutral2_700</color>
+  <color name="sud_system_tertiary_text_inactive">@color/sud_system_neutral2_500</color>
+  <color name="sud_system_error_warning">@color/sud_error_warning_default_light</color>
+  <color name="sud_system_background_surface">@color/sud_system_neutral1_50</color>
+  <color name="sud_system_accent_icon_text_button">@color/sud_system_accent1_600</color>
+  <color name="sud_system_button_surface">@color/sud_system_accent1_100</color>
+  <color name="sud_system_button_text">@color/sud_system_neutral1_900</color>
+  <color name="sud_system_dividing_line">@color/sud_system_neutral2_300</color>
+
+</resources>
\ No newline at end of file
diff --git a/main/res/values-v31/config.xml b/main/res/values-v31/config.xml
new file mode 100644
index 0000000..7367e33
--- /dev/null
+++ b/main/res/values-v31/config.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<resources>
+  <!-- The duration (in milliseconds) of activity transitions -->
+  <integer name="sudTransitionDuration">450</integer>
+
+</resources>
\ No newline at end of file
diff --git a/main/res/values-v31/layouts.xml b/main/res/values-v31/layouts.xml
new file mode 100644
index 0000000..e6198e8
--- /dev/null
+++ b/main/res/values-v31/layouts.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this 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">
+
+    <item name="sud_glif_preference_template" type="layout">@layout/sud_glif_preference_template_compact</item>
+
+</resources>
diff --git a/main/res/values-v31/styles.xml b/main/res/values-v31/styles.xml
new file mode 100644
index 0000000..e1240e4
--- /dev/null
+++ b/main/res/values-v31/styles.xml
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Copyright (C) 2021 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this 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>
+  <!-- Main theme for dynamic color -->
+  <style name="SudDynamicColorThemeGlifV3" parent="SudThemeGlifV3">
+    <item name="colorAccent">@color/sud_dynamic_color_accent_glif_v3_dark</item>
+    <item name="android:textColorLink">@color/sud_dynamic_color_accent_glif_v3_dark</item>
+    <item name="alertDialogTheme">@style/SudDynamicColorAlertDialogThemeCompat</item>
+    <item name="android:datePickerDialogTheme">@style/SudDynamicColorDateTimePickerDialogTheme</item>
+    <item name="android:timePickerDialogTheme">@style/SudDynamicColorDateTimePickerDialogTheme</item>
+    <item name="sucFullDynamicColor">false</item>
+  </style>
+
+  <style name="SudDynamicColorThemeGlifV3.Light" parent="SudThemeGlifV3.Light">
+    <item name="colorAccent">@color/sud_dynamic_color_accent_glif_v3_light</item>
+    <item name="android:textColorLink">@color/sud_dynamic_color_accent_glif_v3_light</item>
+    <item name="alertDialogTheme">@style/SudDynamicColorAlertDialogThemeCompat.Light</item>
+    <item name="android:datePickerDialogTheme">@style/SudDynamicColorDateTimePickerDialogTheme.Light</item>
+    <item name="android:timePickerDialogTheme">@style/SudDynamicColorDateTimePickerDialogTheme.Light</item>
+    <item name="sucFullDynamicColor">false</item>
+  </style>
+
+  <style name="SudFullDynamicColorThemeGlifV3" parent="SudDynamicColorThemeGlifV3">
+    <item name="android:windowBackground">@android:color/system_neutral1_900</item>
+    <item name="android:colorForeground">@android:color/system_neutral1_50</item>
+    <item name="android:colorForegroundInverse">@android:color/system_neutral1_900</item>
+    <item name="android:colorBackground">@android:color/system_neutral1_900</item>
+    <item name="android:colorBackgroundCacheHint">@android:color/system_neutral1_900</item>
+    <item name="colorBackgroundFloating">@android:color/system_neutral1_900</item>
+    <item name="android:navigationBarColor">@android:color/system_neutral1_900</item>
+
+    <item name="android:textColorPrimary">@color/sud_system_primary_text</item>
+    <item name="android:textColorSecondary">@color/sud_system_secondary_text</item>
+    <item name="android:textColorTertiary">@color/sud_system_tertiary_text_inactive</item>
+    <item name="android:textColorPrimaryDisableOnly">@color/sud_system_tertiary_text_inactive</item>
+    <item name="android:textColorPrimaryInverseDisableOnly">@color/sud_system_tertiary_text_inactive</item>
+
+    <item name="colorControlNormal">?android:attr/textColorSecondary</item>
+    <item name="colorControlHighlight">@color/ripple_material_dark</item>
+    <item name="colorButtonNormal">@color/button_material_dark</item>
+    <item name="colorSwitchThumbNormal">@color/switch_thumb_material_dark</item>
+
+    <item name="alertDialogTheme">@style/SudFullDynamicColorAlertDialogThemeCompat</item>
+
+    <item name="sucFullDynamicColor">true</item>
+  </style>
+
+  <style name="SudFullDynamicColorThemeGlifV3.Light" parent="SudDynamicColorThemeGlifV3.Light">
+    <item name="android:windowBackground">@android:color/system_neutral1_50</item>
+    <item name="android:colorForeground">@android:color/system_neutral1_900</item>
+    <item name="android:colorForegroundInverse">@android:color/system_neutral1_50</item>
+    <item name="android:colorBackground">@android:color/system_neutral1_50</item>
+    <item name="android:colorBackgroundCacheHint">@@android:color/system_neutral1_50</item>
+    <item name="colorBackgroundFloating">@android:color/system_neutral1_50</item>
+    <item name="android:navigationBarColor">@android:color/system_neutral1_50</item>
+
+    <item name="android:textColorPrimary">@color/sud_system_primary_text</item>
+    <item name="android:textColorSecondary">@color/sud_system_secondary_text</item>
+    <item name="android:textColorTertiary">@color/sud_system_tertiary_text_inactive</item>
+    <item name="android:textColorPrimaryDisableOnly">@color/sud_system_tertiary_text_inactive</item>
+
+    <item name="colorControlNormal">?android:attr/textColorSecondary</item>
+    <item name="colorControlHighlight">@color/ripple_material_light</item>
+    <item name="colorButtonNormal">@color/button_material_light</item>
+    <item name="colorSwitchThumbNormal">@color/switch_thumb_material_light</item>
+
+    <item name="alertDialogTheme">@style/SudFullDynamicColorAlertDialogThemeCompat.Light</item>
+
+    <item name="sucFullDynamicColor">true</item>
+  </style>
+
+  <!-- Dynamic color theme for alert dialog -->
+  <style name="SudDynamicColorAlertDialogThemeCompat" parent="Theme.AppCompat.Dialog.Alert">
+    <item name="android:textAllCaps">false</item>
+    <item name="android:colorBackground">@color/sud_glif_v3_dialog_background_color_dark</item>
+    <item name="colorAccent">@color/sud_dynamic_color_accent_glif_v3_dark</item>
+    <item name="dialogCornerRadius">@dimen/sud_glif_device_default_dialog_corner_radius</item>
+  </style>
+
+  <style name="SudDynamicColorAlertDialogThemeCompat.Light" parent="Theme.AppCompat.Light.Dialog.Alert">
+    <item name="android:textAllCaps">false</item>
+    <item name="android:colorBackground">@color/sud_glif_v3_dialog_background_color_light</item>
+    <item name="colorAccent">@color/sud_dynamic_color_accent_glif_v3_light</item>
+    <item name="dialogCornerRadius">@dimen/sud_glif_device_default_dialog_corner_radius</item>
+  </style>
+
+  <style name="SudFullDynamicColorAlertDialogThemeCompat" parent="SudDynamicColorAlertDialogThemeCompat">
+    <item name="android:colorBackground">@color/sud_system_neutral1_900</item>
+  </style>
+  <style name="SudFullDynamicColorAlertDialogThemeCompat.Light" parent="SudDynamicColorAlertDialogThemeCompat.Light">
+    <item name="android:colorBackground">@color/sud_system_neutral1_50</item>
+  </style>
+
+  <!-- Dynamic color theme for date time dialog -->
+  <style name="SudDynamicColorDateTimePickerDialogTheme" parent="SudDateTimePickerDialogTheme">
+    <item name="colorAccent">@color/sud_dynamic_color_accent_glif_v3_dark</item>
+    <item name="dialogCornerRadius">@dimen/sud_glif_device_default_dialog_corner_radius</item>
+  </style>
+
+  <style name="SudDynamicColorDateTimePickerDialogTheme.Light" parent="SudDateTimePickerDialogTheme.Light">
+    <item name="colorAccent">@color/sud_dynamic_color_accent_glif_v3_light</item>
+    <item name="dialogCornerRadius">@dimen/sud_glif_device_default_dialog_corner_radius</item>
+  </style>
+
+</resources>
diff --git a/main/res/values/attrs.xml b/main/res/values/attrs.xml
index 95c8bb5..c946912 100644
--- a/main/res/values/attrs.xml
+++ b/main/res/values/attrs.xml
@@ -19,7 +19,8 @@
 
     <!-- Theme attributes -->
     <attr name="sudLayoutTheme" format="reference" />
-    <attr name="sudMarginSides" format="dimension|reference" />
+    <attr name="sudMarginStart" format="dimension|reference" />
+    <attr name="sudMarginEnd" format="dimension|reference" />
     <attr name="sudEditTextBackgroundColor" format="color" />
     <attr name="sudButtonHighlightAlpha" format="float" />
 
@@ -39,7 +40,24 @@
         <!-- Push object to the end of its container, not changing its size. -->
         <flag name="end" value="0x00800005" />
     </attr>
+
+    <attr name="sudGlifSubtitleGravity">
+        <!-- Push object to the left of its container, not changing its size. -->
+        <flag name="left" value="0x03" />
+        <!-- Push object to the right of its container, not changing its size. -->
+        <flag name="right" value="0x05" />
+        <!-- Place object in the horizontal center of its container, not changing its size. -->
+        <flag name="center_horizontal" value="0x01" />
+        <!-- Grow the horizontal size of the object if needed so it completely fills its container. -->
+        <flag name="fill_horizontal" value="0x07" />
+        <!-- Push object to the beginning of its container, not changing its size. -->
+        <flag name="start" value="0x00800003" />
+        <!-- Push object to the end of its container, not changing its size. -->
+        <flag name="end" value="0x00800005" />
+    </attr>
+
     <attr name="sudGlifIconStyle" format="reference" />
+    <attr name="sudGlifIconSize" format="dimension" />
 
     <attr name="sudButtonAllCaps" format="boolean" />
     <attr name="sudButtonCornerRadius" format="dimension" />
@@ -83,6 +101,9 @@
         <flag name="end" value="0x20" />
     </attr>
 
+    <!-- Custom the scroll bar indicator  -->
+    <attr name="sudScrollBarThumb" format="reference" />
+
     <!-- Custom view attributes -->
     <attr name="sudColorPrimary" format="color" />
     <attr name="sudHeader" format="reference" />
@@ -93,6 +114,8 @@
     <attr name="sudDividerInsetStartNoIcon" format="dimension|reference" />
     <attr name="sudItemDescriptionStyle" format="reference" />
     <attr name="sudItemDescriptionTitleStyle" format="reference" />
+    <attr name="sudContentFramePaddingTop" format="dimension|reference" />
+    <attr name="sudContentFramePaddingBottom" format="dimension|reference" />
 
     <attr name="sudHasStableIds" format="boolean|reference" />
 
@@ -110,6 +133,7 @@
 
     <declare-styleable name="SudIllustrationVideoView">
         <attr name="sudVideo" format="reference" />
+        <attr name="sudPauseVideoWhenFinished" format="boolean" />
     </declare-styleable>
 
     <declare-styleable name="SudGlifLayout">
@@ -128,6 +152,10 @@
         <attr name="android:width" />
     </declare-styleable>
 
+    <attr name="sudContentIllustrationMaxWidth" format="dimension" />
+    <attr name="sudContentIllustrationMaxHeight" format="dimension" />
+    <attr name="sudContentIllustrationPaddingTop" format="dimension" />
+    <attr name="sudContentIllustrationPaddingBottom" format="dimension" />
     <declare-styleable name="SudFillContentLayout">
         <attr name="android:maxHeight" />
         <attr name="android:maxWidth" />
@@ -211,4 +239,24 @@
         <attr name="sudCollapsedSummary" format="string" localization="suggested" />
         <attr name="sudExpandedSummary" format="string" localization="suggested" />
     </declare-styleable>
+
+    <declare-styleable name="SudDescriptionMixin">
+        <attr name="sudDescriptionText" format="string" localization="suggested" />
+        <attr name="sudDescriptionTextColor" format="reference|color" />
+        <attr name="sudGlifDescriptionMarginTop" format="dimension" />
+        <attr name="sudGlifDescriptionMarginBottom" format="dimension" />
+    </declare-styleable>
+
+    <declare-styleable name="SudProgressBarMixin">
+        <attr name="sudUseBottomProgressBar" format="boolean" />
+    </declare-styleable>
+
+    <declare-styleable name="SudGlifLoadingFramePadding">
+        <attr name="sudLoadingContentFramePaddingTop" format="dimension|reference" />
+        <attr name="sudLoadingContentFramePaddingStart" format="dimension|reference" />
+        <attr name="sudLoadingContentFramePaddingEnd" format="dimension|reference" />
+        <attr name="sudLoadingContentFramePaddingBottom" format="dimension|reference" />
+    </declare-styleable>
+
+    <attr name="sudLoadingHeaderHeight" format="dimension|reference" />
 </resources>
diff --git a/main/res/values/colors.xml b/main/res/values/colors.xml
index a386eb5..798abc9 100644
--- a/main/res/values/colors.xml
+++ b/main/res/values/colors.xml
@@ -18,7 +18,6 @@
 <resources>
 
     <!-- General colors -->
-
     <color name="sud_color_accent_dark">#ff448aff</color>
     <color name="sud_color_accent_light">#ff3367d6</color>
     <color name="sud_color_background_dark">#ff303030</color>
@@ -35,25 +34,116 @@
     <color name="sud_flat_button_highlight">#1f000000</color>
 
     <!-- Navigation bar colors -->
-
     <color name="sud_navbar_bg_dark">#ff21272b</color>
     <color name="sud_navbar_bg_light">#ffe4e7e9</color>
 
     <!-- GLIF colors -->
     <color name="sud_color_accent_glif_dark">#ff4285f4</color>
     <color name="sud_color_accent_glif_light">#ff4285f4</color>
-    <color name="sud_color_accent_glif_v3_light">#ff1a73e8</color>
     <color name="sud_color_accent_glif_v3_dark">#ff8ab4f8</color>
+    <color name="sud_color_accent_glif_v3_light">#ff1a73e8</color>
     <color name="sud_glif_background_color_dark">#ff000000</color>
     <color name="sud_glif_background_color_light">#ffffffff</color>
     <color name="sud_glif_edit_text_bg_dark_color">#ff202124</color>
     <color name="sud_glif_edit_text_bg_light_color">#0a000000</color>
     <color name="sud_glif_v3_dialog_background_color_dark">#ff3c4043</color>
+    <color name="sud_glif_v3_dialog_background_color_light">#fff1f3f4</color>
+    <color name="sud_glif_v3_nav_bar_color_dark">#ff000000</color>
     <color name="sud_glif_v3_nav_bar_color_light">#ffffffff</color>
+    <color name="sud_glif_v3_nav_bar_divider_color_dark">#00000000</color>
     <color name="sud_glif_v3_nav_bar_divider_color_light">#1f000000</color>
+    <color name="sud_glif_v3_text_color_dark">#ffffffff</color>
+    <color name="sud_glif_v3_text_color_light">#de000000</color>
+    <color name="sud_glif_window_bg_dark_color">#ff202124</color>
+    <color name="sud_glif_window_bg_light_color">#ffffffff</color>
 
     <!-- Color for error text  -->
-    <color name="sud_color_error_text_light">#ffd93025</color> <!-- Google red 600 -->
-    <color name="sud_color_error_text_dark">#fff28b82</color> <!-- Google red 300 -->
+
+    <color name="sud_color_error_text_dark">#fff28b82</color>
+
+    <color name="sud_color_error_text_light">#ffd93025</color>
+
+
+    <color name="sud_dynamic_color_accent_glif_v3_dark">@color/sud_color_accent_glif_v3_dark</color>
+    <color name="sud_dynamic_color_accent_glif_v3_light">@color/sud_color_accent_glif_v3_light</color>
+
+
+
+    <color name="sud_system_accent1_100">#8DF5E3</color>
+
+    <color name="sud_system_accent1_200">#71D8C7</color>
+
+    <color name="sud_system_accent1_600">#006C5F</color>
+
+
+
+    <color name="sud_system_neutral1_0">#ffffff</color>
+
+    <color name="sud_system_neutral1_50">#f0f0f0</color>
+
+    <color name="sud_system_neutral1_800">#303030</color>
+
+    <color name="sud_system_neutral1_900">#1b1b1b</color>
+
+
+
+    <color name="sud_system_neutral2_50">#f0f0f0</color>
+
+    <color name="sud_system_neutral2_100">#e2e2e2</color>
+
+    <color name="sud_system_neutral2_200">#c6c6c6</color>
+
+    <color name="sud_system_neutral2_300">#ababab</color>
+
+    <color name="sud_system_neutral2_400">#909090</color>
+
+    <color name="sud_system_neutral2_500">#757575</color>
+
+    <color name="sud_system_neutral2_700">#464646</color>
+
+
+    <color name="success_color_device_default_dark">#5BB974</color>
+
+    <color name="success_color_device_default_light">#1E8E3E</color>
+
+    <color name="fallback_color_device_default_dark">#669DF6</color>
+
+    <color name="fallback_color_device_default_light">#1A73E8</color>
+
+    <color name="sud_portal_pending_progress_light">#fdc69c</color>
+
+    <color name="sud_portal_pending_progress_dark">#b06000</color>
+
+    <color name="sud_primary_default_text_dark">#FFFFFF</color>
+
+    <color name="sud_primary_default_text_light">#202124</color>
+
+    <color name="sud_secondary_default_text_dark">#9AA0A6</color>
+
+    <color name="sud_secondary_default_text_light">#5F6368</color>
+
+    <color name="sud_inactive_default_dark">#5F6368</color>
+
+    <color name="sud_inactive_default_light">#9AA0A6</color>
+
+    <color name="sud_error_warning_default_dark">#EE675C</color>
+
+    <color name="sud_error_warning_default_light">#D93025</color>
+
+
+    <color name="sud_system_primary_text">@color/sud_primary_default_text_light</color>
+    <color name="sud_system_secondary_text">@color/sud_secondary_default_text_light</color>
+    <color name="sud_system_tertiary_text_inactive">@color/sud_inactive_default_light</color>
+    <color name="sud_system_error_warning">@color/sud_error_warning_default_light</color>
+    <color name="sud_system_success_done">@color/success_color_device_default_light</color>
+    <color name="sud_system_fallback_accent">@color/fallback_color_device_default_light</color>
+
+    <color name="sud_portal_pending_progress">@color/sud_portal_pending_progress_light</color>
+
+    <color name="sud_switch_track_off">#FF757575</color>
+    <color name="sud_switch_thumb_off">#BFFFFFFF</color>
+
+
+    <color name="sud_uniformity_backdrop_color">@android:color/white</color>
 
 </resources>
diff --git a/main/res/values/dimens.xml b/main/res/values/dimens.xml
index 7508f25..64970c8 100644
--- a/main/res/values/dimens.xml
+++ b/main/res/values/dimens.xml
@@ -21,20 +21,29 @@
     <dimen name="sud_layout_margin_sides">40dp</dimen>
 
     <dimen name="sud_glif_button_corner_radius">2dp</dimen>
-    <!-- Calculated by (sud_glif_margin_sides - 4dp internal padding of button) -->
+    <!-- Calculated by (sud_glif_margin_start - 4dp internal padding of button) -->
     <dimen name="sud_glif_button_margin_end">20dp</dimen>
-    <!-- Calculated by (sud_glif_margin_sides - sud_glif_button_padding) -->
+    <!-- Calculated by (sud_glif_margin_start - sud_glif_button_padding) -->
     <dimen name="sud_glif_button_margin_start">8dp</dimen>
     <dimen name="sud_glif_button_padding">16dp</dimen>
+    <dimen name="sud_glif_button_min_height">48dp</dimen>
     <!-- Negative of sud_glif_button_padding -->
     <dimen name="sud_glif_negative_button_padding">-16dp</dimen>
     <dimen name="sud_glif_footer_padding_vertical">8dp</dimen>
+    <dimen name="sud_glif_footer_padding_start">8dp</dimen>
+    <!-- Calculated by (sud_glif_margin_end - 4dp internal padding of button) -->
+    <dimen name="sud_glif_footer_padding_end">20dp</dimen>
     <dimen name="sud_glif_footer_min_height">72dp</dimen>
-    <dimen name="sud_glif_margin_sides">24dp</dimen>
-    <dimen name="sud_glif_margin_top">56dp</dimen>
+    <dimen name="sud_glif_margin_start">24dp</dimen>
+    <dimen name="sud_glif_margin_end">24dp</dimen>
+    <dimen name="sud_glif_icon_margin_top">56dp</dimen>
 
     <dimen name="sud_glif_alert_dialog_corner_radius">8dp</dimen>
     <dimen name="sud_glif_v3_button_corner_radius">4dp</dimen>
+    <dimen name="sud_glif_device_default_dialog_corner_radius">28dp</dimen>
+    <dimen name="sud_glif_land_header_area_weight">1</dimen>
+    <dimen name="sud_glif_land_content_area_weight">1</dimen>
+    <dimen name="sud_glif_land_middle_horizontal_spacing">48dp</dimen>
 
     <!-- Content styles -->
     <dimen name="sud_check_box_line_spacing_extra">4sp</dimen>
@@ -51,7 +60,11 @@
     <dimen name="sud_description_margin_bottom_lists">24dp</dimen>
     <dimen name="sud_description_line_spacing_extra">4sp</dimen>
     <dimen name="sud_description_text_size">16sp</dimen>
+    <!-- This value is the margin bottom difference between DescriptionItem and DescriptionMixin. -->
+    <dimen name="sud_description_margin_top_extra">1dp</dimen>
+    <dimen name="sud_description_margin_bottom_extra">12dp</dimen>
 
+    <!-- TODO: Remove sud_description_glif_margin_xxx once all apps migrate to sud_glif_description_margin_xxx -->
     <dimen name="sud_description_glif_margin_top">3dp</dimen>
     <dimen name="sud_description_glif_margin_bottom_lists">24dp</dimen>
 
@@ -64,6 +77,22 @@
     <dimen name="sud_content_illustration_min_width">172dp</dimen>
     <dimen name="sud_content_illustration_padding_vertical">24dp</dimen>
 
+    <!-- Loading content styles -->
+    <dimen name="sud_content_loading_frame_padding_top">0dp</dimen>
+    <dimen name="sud_content_loading_frame_padding_start">40dp</dimen>
+    <dimen name="sud_content_loading_frame_padding_end">40dp</dimen>
+    <dimen name="sud_content_loading_frame_padding_bottom">144dp</dimen>
+
+    <dimen name="sud_loading_header_height">274dp</dimen>
+
+    <!--  Glif Content info text -->
+    <dimen name="sud_content_info_text_size">16sp</dimen>
+    <dimen name="sud_content_info_line_spacing_extra">3sp</dimen>
+    <dimen name="sud_content_info_icon_size">18dp</dimen>
+    <dimen name="sud_content_info_icon_margin_end">16dp</dimen>
+    <dimen name="sud_content_info_padding_top">0dp</dimen>
+    <dimen name="sud_content_info_padding_bottom">0dp</dimen>
+
     <!-- Margin on the start to offset for margin in the drawable -->
     <dimen name="sud_radio_button_margin_start">-6dp</dimen>
     <dimen name="sud_radio_button_margin_top">0dp</dimen>
@@ -104,16 +133,26 @@
 
     <dimen name="sud_glif_header_title_margin_top">16dp</dimen>
     <dimen name="sud_glif_header_title_margin_bottom">2dp</dimen>
+    <dimen name="sud_header_container_margin_bottom">0dp</dimen>
+
+    <!-- This value leverages sud_description_glif_margin_top -->
+    <dimen name="sud_glif_description_margin_top">3dp</dimen>
+    <dimen name="sud_glif_description_margin_bottom">12dp</dimen>
 
     <dimen name="sud_glif_icon_max_height">32dp</dimen>
-
     <!-- Illustration -->
     <item name="sud_illustration_aspect_ratio" format="float" type="dimen">2.22</item>
 
     <!-- Items -->
     <dimen name="sud_items_icon_container_width">48dp</dimen>
+    <!-- TODO: Remove it when all attributes being used migrated into new one. -->
     <dimen name="sud_items_padding_vertical">15dp</dimen>
     <dimen name="sud_items_verbose_padding_vertical">20dp</dimen>
+    <dimen name="sud_items_title_text_size">16sp</dimen>
+    <dimen name="sud_items_summary_text_size">14sp</dimen>
+    <dimen name="sud_items_summary_margin_top">4dp</dimen>
+    <dimen name="sud_items_padding_top">15dp</dimen>
+    <dimen name="sud_items_padding_bottom">15dp</dimen>
 
     <!-- Ignore UnusedResources: can be used by clients -->
     <dimen name="sud_items_icon_divider_inset" tools:ignore="UnusedResources">88dp</dimen>
@@ -160,4 +199,22 @@
     <!-- ExpandableSwithItem -->
     <dimen name="sud_expand_arrow_drawable_padding">4dp</dimen>
 
+    <!-- SwitchBar -->
+    <!-- Minimum width of switch -->
+    <dimen name="sud_switch_min_width">52dp</dimen>
+    <!-- Margin of switch thumb -->
+    <dimen name="sud_switch_thumb_margin">4dp</dimen>
+    <!-- Size of switch thumb -->
+    <dimen name="sud_switch_thumb_size">20dp</dimen>
+    <!-- Width of switch track -->
+    <dimen name="sud_switch_track_width">52dp</dimen>
+    <!-- Height of switch track -->
+    <dimen name="sud_switch_track_height">28dp</dimen>
+    <!-- Radius of switch track -->
+    <dimen name="sud_switch_track_radius">35dp</dimen>
+
+    <!-- IconUniformityAppImageView -->
+    <!-- Set 0dp since we don't want shadow. -->
+    <dimen name="sud_icon_uniformity_elevation">0dp</dimen>
+
 </resources>
diff --git a/main/res/values/layouts.xml b/main/res/values/layouts.xml
index cf37c37..910965f 100644
--- a/main/res/values/layouts.xml
+++ b/main/res/values/layouts.xml
@@ -53,4 +53,3 @@
     <item name="sud_glif_preference_template" type="layout">@layout/sud_glif_blank_template_compact</item>
     <item name="sud_glif_recycler_template" type="layout">@layout/sud_glif_recycler_template_compact</item>
 </resources>
-
diff --git a/main/res/values/styles.xml b/main/res/values/styles.xml
index d3d6018..4725735 100644
--- a/main/res/values/styles.xml
+++ b/main/res/values/styles.xml
@@ -25,8 +25,8 @@
         <!-- Specify the indeterminateTintMode to work around a bug in Lollipop -->
         <item name="android:indeterminateTintMode" tools:ignore="NewApi">src_in</item>
         <item name="android:listPreferredItemHeight">@dimen/sud_items_preferred_height</item>
-        <item name="android:listPreferredItemPaddingEnd" tools:ignore="NewApi">?attr/sudMarginSides</item>
-        <item name="android:listPreferredItemPaddingStart" tools:ignore="NewApi">?attr/sudMarginSides</item>
+        <item name="android:listPreferredItemPaddingEnd" tools:ignore="NewApi">?attr/sudMarginEnd</item>
+        <item name="android:listPreferredItemPaddingStart" tools:ignore="NewApi">?attr/sudMarginStart</item>
         <item name="android:navigationBarColor" tools:ignore="NewApi">@android:color/black</item>
         <item name="android:statusBarColor" tools:ignore="NewApi">@android:color/black</item>
         <item name="android:textAppearanceListItemSmall" tools:ignore="NewApi">?attr/textAppearanceListItemSmall</item>
@@ -37,21 +37,33 @@
 
         <item name="colorAccent">@color/sud_color_accent_dark</item>
         <item name="sudFillContentLayoutStyle">@style/SudFillContentLayout</item>
-        <item name="listPreferredItemPaddingLeft">?attr/sudMarginSides</item>
-        <item name="listPreferredItemPaddingRight">?attr/sudMarginSides</item>
+        <item name="listPreferredItemPaddingLeft">?attr/sudMarginStart</item>
+        <item name="listPreferredItemPaddingRight">?attr/sudMarginEnd</item>
         <item name="sudButtonAllCaps">true</item>
         <item name="sudButtonFontFamily">sans-serif</item>
         <item name="sudButtonHighlightAlpha">0.24</item>
         <item name="sudCardBackground">@drawable/sud_card_bg_dark</item>
+        <item name="sudContentFramePaddingTop">@dimen/sud_content_frame_padding_top</item>
+        <item name="sudContentFramePaddingBottom">@dimen/sud_content_frame_padding_bottom</item>
+        <item name="sudLoadingContentFramePaddingTop">@dimen/sud_content_loading_frame_padding_top</item>
+        <item name="sudLoadingContentFramePaddingStart">@dimen/sud_content_loading_frame_padding_start</item>
+        <item name="sudLoadingContentFramePaddingEnd">@dimen/sud_content_loading_frame_padding_end</item>
+        <item name="sudLoadingContentFramePaddingBottom">@dimen/sud_content_loading_frame_padding_bottom</item>
         <item name="sudDividerInsetEnd">0dp</item>
         <item name="sudDividerInsetStart">@dimen/sud_items_icon_divider_inset</item>
         <item name="sudDividerInsetStartNoIcon">@dimen/sud_items_text_divider_inset</item>
         <item name="sudItemDescriptionStyle">@style/SudItemContainer.Description</item>
         <item name="sudItemDescriptionTitleStyle">@style/SudItemTitle</item>
         <item name="sudListItemIconColor">@color/sud_list_item_icon_color_dark</item>
-        <item name="sudMarginSides">@dimen/sud_layout_margin_sides</item>
+        <item name="sudMarginStart">@dimen/sud_layout_margin_sides</item>
+        <item name="sudMarginEnd">@dimen/sud_layout_margin_sides</item>
         <item name="sudNavBarTheme">@style/SudNavBarThemeDark</item>
         <item name="textAppearanceListItemSmall">@style/TextAppearance.SudItemSummary</item>
+        <item name="sudContentIllustrationMaxWidth">@dimen/sud_content_illustration_max_width</item>
+        <item name="sudContentIllustrationMaxHeight">@dimen/sud_content_illustration_max_height</item>
+        <item name="sudContentIllustrationPaddingTop">@dimen/sud_content_illustration_padding_vertical</item>
+        <item name="sudContentIllustrationPaddingBottom">@dimen/sud_content_illustration_padding_vertical</item>
+        <item name="sudLoadingHeaderHeight">@dimen/sud_loading_header_height</item>
     </style>
 
     <style name="SudThemeMaterial.Light" parent="Theme.AppCompat.Light.NoActionBar">
@@ -60,8 +72,8 @@
         <!-- Specify the indeterminateTintMode to work around a bug in Lollipop -->
         <item name="android:indeterminateTintMode" tools:ignore="NewApi">src_in</item>
         <item name="android:listPreferredItemHeight">@dimen/sud_items_preferred_height</item>
-        <item name="android:listPreferredItemPaddingEnd" tools:ignore="NewApi">?attr/sudMarginSides</item>
-        <item name="android:listPreferredItemPaddingStart" tools:ignore="NewApi">?attr/sudMarginSides</item>
+        <item name="android:listPreferredItemPaddingEnd" tools:ignore="NewApi">?attr/sudMarginEnd</item>
+        <item name="android:listPreferredItemPaddingStart" tools:ignore="NewApi">?attr/sudMarginStart</item>
         <item name="android:navigationBarColor" tools:ignore="NewApi">@android:color/black</item>
         <item name="android:statusBarColor" tools:ignore="NewApi">@android:color/black</item>
         <item name="android:textAppearanceListItemSmall" tools:ignore="NewApi">?attr/textAppearanceListItemSmall</item>
@@ -72,21 +84,33 @@
 
         <item name="colorAccent">@color/sud_color_accent_light</item>
         <item name="sudFillContentLayoutStyle">@style/SudFillContentLayout</item>
-        <item name="listPreferredItemPaddingLeft">?attr/sudMarginSides</item>
-        <item name="listPreferredItemPaddingRight">?attr/sudMarginSides</item>
+        <item name="listPreferredItemPaddingLeft">?attr/sudMarginStart</item>
+        <item name="listPreferredItemPaddingRight">?attr/sudMarginEnd</item>
         <item name="sudButtonAllCaps">true</item>
         <item name="sudButtonFontFamily">sans-serif</item>
         <item name="sudButtonHighlightAlpha">0.24</item>
         <item name="sudCardBackground">@drawable/sud_card_bg_light</item>
+        <item name="sudContentFramePaddingTop">@dimen/sud_content_frame_padding_top</item>
+        <item name="sudContentFramePaddingBottom">@dimen/sud_content_frame_padding_bottom</item>
+        <item name="sudLoadingContentFramePaddingTop">@dimen/sud_content_loading_frame_padding_top</item>
+        <item name="sudLoadingContentFramePaddingStart">@dimen/sud_content_loading_frame_padding_start</item>
+        <item name="sudLoadingContentFramePaddingEnd">@dimen/sud_content_loading_frame_padding_end</item>
+        <item name="sudLoadingContentFramePaddingBottom">@dimen/sud_content_loading_frame_padding_bottom</item>
         <item name="sudDividerInsetEnd">0dp</item>
         <item name="sudDividerInsetStart">@dimen/sud_items_icon_divider_inset</item>
         <item name="sudDividerInsetStartNoIcon">@dimen/sud_items_text_divider_inset</item>
         <item name="sudItemDescriptionStyle">@style/SudItemContainer.Description</item>
         <item name="sudItemDescriptionTitleStyle">@style/SudItemTitle</item>
         <item name="sudListItemIconColor">@color/sud_list_item_icon_color_light</item>
-        <item name="sudMarginSides">@dimen/sud_layout_margin_sides</item>
+        <item name="sudMarginStart">@dimen/sud_layout_margin_sides</item>
+        <item name="sudMarginEnd">@dimen/sud_layout_margin_sides</item>
         <item name="sudNavBarTheme">@style/SudNavBarThemeLight</item>
         <item name="textAppearanceListItemSmall">@style/TextAppearance.SudItemSummary</item>
+        <item name="sudContentIllustrationMaxWidth">@dimen/sud_content_illustration_max_width</item>
+        <item name="sudContentIllustrationMaxHeight">@dimen/sud_content_illustration_max_height</item>
+        <item name="sudContentIllustrationPaddingTop">@dimen/sud_content_illustration_padding_vertical</item>
+        <item name="sudContentIllustrationPaddingBottom">@dimen/sud_content_illustration_padding_vertical</item>
+        <item name="sudLoadingHeaderHeight">@dimen/sud_loading_header_height</item>
     </style>
 
     <style name="SudBaseThemeGlif" parent="Theme.AppCompat.NoActionBar">
@@ -95,8 +119,8 @@
         <!-- Specify the indeterminateTintMode to work around a bug in Lollipop -->
         <item name="android:indeterminateTintMode" tools:ignore="NewApi">src_in</item>
         <item name="android:listPreferredItemHeight">@dimen/sud_items_preferred_height</item>
-        <item name="android:listPreferredItemPaddingEnd" tools:ignore="NewApi">?attr/sudMarginSides</item>
-        <item name="android:listPreferredItemPaddingStart" tools:ignore="NewApi">?attr/sudMarginSides</item>
+        <item name="android:listPreferredItemPaddingEnd" tools:ignore="NewApi">?attr/sudMarginEnd</item>
+        <item name="android:listPreferredItemPaddingStart" tools:ignore="NewApi">?attr/sudMarginStart</item>
         <item name="android:navigationBarColor" tools:ignore="NewApi">@android:color/black</item>
         <item name="android:statusBarColor" tools:ignore="NewApi">@android:color/transparent</item>
         <item name="android:textAppearanceListItemSmall" tools:ignore="NewApi">?attr/textAppearanceListItemSmall</item>
@@ -105,27 +129,44 @@
         <item name="android:windowDisablePreview">true</item>
         <item name="android:windowSoftInputMode">adjustResize</item>
         <item name="android:colorError" tools:targetApi="26">@color/sud_color_error_text_dark</item>
+        <item name="android:scrollbarThumbVertical">?attr/sudScrollBarThumb</item>
 
         <item name="colorAccent">@color/sud_color_accent_glif_dark</item>
         <item name="colorPrimary">?attr/colorAccent</item>
-        <item name="listPreferredItemPaddingLeft">?attr/sudMarginSides</item>
-        <item name="listPreferredItemPaddingRight">?attr/sudMarginSides</item>
+        <item name="listPreferredItemPaddingLeft">?attr/sudMarginStart</item>
+        <item name="listPreferredItemPaddingRight">?attr/sudMarginEnd</item>
         <item name="sudButtonAllCaps">true</item>
         <item name="sudButtonCornerRadius">@dimen/sud_glif_button_corner_radius</item>
         <item name="sudButtonFontFamily">sans-serif-medium</item>
         <item name="sudButtonHighlightAlpha">0.24</item>
         <item name="sudColorPrimary">?attr/colorPrimary</item>
+        <item name="sudContentFramePaddingTop">@dimen/sud_content_frame_padding_top</item>
+        <item name="sudContentFramePaddingBottom">@dimen/sud_content_frame_padding_bottom</item>
+        <item name="sudLoadingContentFramePaddingTop">@dimen/sud_content_loading_frame_padding_top</item>
+        <item name="sudLoadingContentFramePaddingStart">@dimen/sud_content_loading_frame_padding_start</item>
+        <item name="sudLoadingContentFramePaddingEnd">@dimen/sud_content_loading_frame_padding_end</item>
+        <item name="sudLoadingContentFramePaddingBottom">@dimen/sud_content_loading_frame_padding_bottom</item>
         <item name="sudFillContentLayoutStyle">@style/SudFillContentLayout</item>
         <item name="sudDividerInsetEnd">0dp</item>
         <item name="sudDividerInsetStart">@dimen/sud_items_glif_icon_divider_inset</item>
         <item name="sudDividerInsetStartNoIcon">@dimen/sud_items_glif_text_divider_inset</item>
         <item name="sudGlifHeaderGravity">start</item>
+        <item name="sudGlifSubtitleGravity">start</item>
+        <item name="sucGlifHeaderMarginTop">@dimen/sud_glif_header_title_margin_top</item>
+        <item name="sudGlifDescriptionMarginTop">@dimen/sud_glif_description_margin_top</item>
+        <item name="sucGlifHeaderMarginBottom">@dimen/sud_glif_header_title_margin_bottom</item>
+        <item name="sudGlifDescriptionMarginBottom">@dimen/sud_glif_description_margin_bottom</item>
+        <item name="sucHeaderContainerMarginBottom">@dimen/sud_header_container_margin_bottom</item>
         <item name="sudGlifIconStyle">@style/SudGlifIcon</item>
+        <item name="sucGlifIconMarginTop">@dimen/sud_glif_icon_margin_top</item>
+        <item name="sudGlifIconSize">@dimen/sud_glif_icon_max_height</item>
         <item name="sudItemDescriptionStyle">@style/SudItemContainer.Description.Glif</item>
         <item name="sudItemDescriptionTitleStyle">@style/SudItemTitle.GlifDescription</item>
         <item name="sudListItemIconColor">@color/sud_list_item_icon_color_dark</item>
-        <item name="sudMarginSides">@dimen/sud_glif_margin_sides</item>
+        <item name="sudMarginStart">@dimen/sud_glif_margin_start</item>
+        <item name="sudMarginEnd">@dimen/sud_glif_margin_end</item>
         <item name="sudScrollIndicators">bottom</item>
+        <item name="sudScrollBarThumb">@drawable/sud_scroll_bar_dark</item>
         <item name="textAppearanceListItem">@style/TextAppearance.SudGlifItemTitle</item>
         <item name="textAppearanceListItemSmall">@style/TextAppearance.SudGlifItemSummary</item>
         <item name="sucFooterBarButtonFontFamily">?attr/sudButtonFontFamily</item>
@@ -136,6 +177,16 @@
         <item name="sucStatusBarBackground">?android:attr/colorBackground</item>
         <item name="sucSystemNavBarBackgroundColor">@android:color/black</item>
         <item name="sucFooterBarPaddingVertical">@dimen/sud_glif_footer_padding_vertical</item>
+        <item name="sucFooterBarPaddingStart">@dimen/sud_glif_footer_padding_start</item>
+        <item name="sucFooterBarPaddingEnd">@dimen/sud_glif_footer_padding_end</item>
+        <item name="sucFooterBarMinHeight">@dimen/sud_glif_footer_min_height</item>
+        <item name="sucFooterButtonPaddingStart">@dimen/sud_glif_button_padding</item>
+        <item name="sucFooterButtonPaddingEnd">@dimen/sud_glif_button_padding</item>
+        <item name="sudContentIllustrationMaxWidth">@dimen/sud_content_illustration_max_width</item>
+        <item name="sudContentIllustrationMaxHeight">@dimen/sud_content_illustration_max_height</item>
+        <item name="sudContentIllustrationPaddingTop">@dimen/sud_content_illustration_padding_vertical</item>
+        <item name="sudContentIllustrationPaddingBottom">@dimen/sud_content_illustration_padding_vertical</item>
+        <item name="sudLoadingHeaderHeight">@dimen/sud_loading_header_height</item>
     </style>
     <style name="SudThemeGlif" parent="SudBaseThemeGlif"/>
 
@@ -145,8 +196,8 @@
         <!-- Specify the indeterminateTintMode to work around a bug in Lollipop -->
         <item name="android:indeterminateTintMode" tools:ignore="NewApi">src_in</item>
         <item name="android:listPreferredItemHeight">@dimen/sud_items_preferred_height</item>
-        <item name="android:listPreferredItemPaddingEnd" tools:ignore="NewApi">?attr/sudMarginSides</item>
-        <item name="android:listPreferredItemPaddingStart" tools:ignore="NewApi">?attr/sudMarginSides</item>
+        <item name="android:listPreferredItemPaddingEnd" tools:ignore="NewApi">?attr/sudMarginEnd</item>
+        <item name="android:listPreferredItemPaddingStart" tools:ignore="NewApi">?attr/sudMarginStart</item>
         <item name="android:navigationBarColor" tools:ignore="NewApi">@android:color/black</item>
         <item name="android:statusBarColor" tools:ignore="NewApi">@android:color/transparent</item>
         <item name="android:textAppearanceListItemSmall" tools:ignore="NewApi">?attr/textAppearanceListItemSmall</item>
@@ -155,27 +206,44 @@
         <item name="android:windowDisablePreview">true</item>
         <item name="android:windowSoftInputMode">adjustResize</item>
         <item name="android:colorError" tools:targetApi="26">@color/sud_color_error_text_light</item>
+        <item name="android:scrollbarThumbVertical">?attr/sudScrollBarThumb</item>
 
         <item name="colorAccent">@color/sud_color_accent_glif_light</item>
         <item name="colorPrimary">?attr/colorAccent</item>
-        <item name="listPreferredItemPaddingLeft">?attr/sudMarginSides</item>
-        <item name="listPreferredItemPaddingRight">?attr/sudMarginSides</item>
+        <item name="listPreferredItemPaddingLeft">?attr/sudMarginStart</item>
+        <item name="listPreferredItemPaddingRight">?attr/sudMarginEnd</item>
         <item name="sudButtonAllCaps">true</item>
         <item name="sudButtonCornerRadius">@dimen/sud_glif_button_corner_radius</item>
         <item name="sudButtonFontFamily">sans-serif-medium</item>
         <item name="sudButtonHighlightAlpha">0.12</item>
         <item name="sudColorPrimary">?attr/colorPrimary</item>
+        <item name="sudContentFramePaddingTop">@dimen/sud_content_frame_padding_top</item>
+        <item name="sudContentFramePaddingBottom">@dimen/sud_content_frame_padding_bottom</item>
+        <item name="sudLoadingContentFramePaddingTop">@dimen/sud_content_loading_frame_padding_top</item>
+        <item name="sudLoadingContentFramePaddingStart">@dimen/sud_content_loading_frame_padding_start</item>
+        <item name="sudLoadingContentFramePaddingEnd">@dimen/sud_content_loading_frame_padding_end</item>
+        <item name="sudLoadingContentFramePaddingBottom">@dimen/sud_content_loading_frame_padding_bottom</item>
         <item name="sudFillContentLayoutStyle">@style/SudFillContentLayout</item>
         <item name="sudDividerInsetEnd">0dp</item>
         <item name="sudDividerInsetStart">@dimen/sud_items_glif_icon_divider_inset</item>
         <item name="sudDividerInsetStartNoIcon">@dimen/sud_items_glif_text_divider_inset</item>
         <item name="sudGlifHeaderGravity">start</item>
+        <item name="sudGlifSubtitleGravity">start</item>
+        <item name="sucGlifHeaderMarginTop">@dimen/sud_glif_header_title_margin_top</item>
+        <item name="sudGlifDescriptionMarginTop">@dimen/sud_glif_description_margin_top</item>
+        <item name="sucGlifHeaderMarginBottom">@dimen/sud_glif_header_title_margin_bottom</item>
+        <item name="sudGlifDescriptionMarginBottom">@dimen/sud_glif_description_margin_bottom</item>
+        <item name="sucHeaderContainerMarginBottom">@dimen/sud_header_container_margin_bottom</item>
         <item name="sudGlifIconStyle">@style/SudGlifIcon</item>
+        <item name="sucGlifIconMarginTop">@dimen/sud_glif_icon_margin_top</item>
+        <item name="sudGlifIconSize">@dimen/sud_glif_icon_max_height</item>
         <item name="sudItemDescriptionStyle">@style/SudItemContainer.Description.Glif</item>
         <item name="sudItemDescriptionTitleStyle">@style/SudItemTitle.GlifDescription</item>
         <item name="sudListItemIconColor">@color/sud_list_item_icon_color_light</item>
-        <item name="sudMarginSides">@dimen/sud_glif_margin_sides</item>
+        <item name="sudMarginStart">@dimen/sud_glif_margin_start</item>
+        <item name="sudMarginEnd">@dimen/sud_glif_margin_end</item>
         <item name="sudScrollIndicators">bottom</item>
+        <item name="sudScrollBarThumb">@drawable/sud_scroll_bar_light</item>
         <item name="textAppearanceListItem">@style/TextAppearance.SudGlifItemTitle</item>
         <item name="textAppearanceListItemSmall">@style/TextAppearance.SudGlifItemSummary</item>
         <item name="sucFooterBarButtonFontFamily">?attr/sudButtonFontFamily</item>
@@ -186,6 +254,16 @@
         <item name="sucStatusBarBackground">?android:attr/colorBackground</item>
         <item name="sucSystemNavBarBackgroundColor">@android:color/black</item>
         <item name="sucFooterBarPaddingVertical">@dimen/sud_glif_footer_padding_vertical</item>
+        <item name="sucFooterBarPaddingStart">@dimen/sud_glif_footer_padding_start</item>
+        <item name="sucFooterBarPaddingEnd">@dimen/sud_glif_footer_padding_end</item>
+        <item name="sucFooterBarMinHeight">@dimen/sud_glif_footer_min_height</item>
+        <item name="sucFooterButtonPaddingStart">@dimen/sud_glif_button_padding</item>
+        <item name="sucFooterButtonPaddingEnd">@dimen/sud_glif_button_padding</item>
+        <item name="sudContentIllustrationMaxWidth">@dimen/sud_content_illustration_max_width</item>
+        <item name="sudContentIllustrationMaxHeight">@dimen/sud_content_illustration_max_height</item>
+        <item name="sudContentIllustrationPaddingTop">@dimen/sud_content_illustration_padding_vertical</item>
+        <item name="sudContentIllustrationPaddingBottom">@dimen/sud_content_illustration_padding_vertical</item>
+        <item name="sudLoadingHeaderHeight">@dimen/sud_loading_header_height</item>
     </style>
     <style name="SudThemeGlif.Light" parent="SudBaseThemeGlif.Light"/>
 
@@ -195,13 +273,15 @@
 
         <item name="sudBackgroundBaseColor">?android:attr/colorBackground</item>
         <item name="sudBackgroundPatterned">false</item>
-        <item name="sudDividerInsetEnd">?attr/sudMarginSides</item>
-        <item name="sudDividerInsetStart">?attr/sudMarginSides</item>
-        <item name="sudDividerInsetStartNoIcon">?attr/sudMarginSides</item>
+        <item name="sudDividerInsetEnd">?attr/sudMarginEnd</item>
+        <item name="sudDividerInsetStart">?attr/sudMarginStart</item>
+        <item name="sudDividerInsetStartNoIcon">?attr/sudMarginStart</item>
         <item name="sudGlifHeaderGravity">center_horizontal</item>
+        <item name="sudGlifSubtitleGravity">center_horizontal</item>
         <item name="sudScrollIndicators">top|bottom</item>
         <item name="sudEditTextBackgroundColor">@color/sud_glif_edit_text_bg_dark_color</item>
         <item name="android:editTextStyle">@style/SudEditText</item>
+        <item name="alertDialogTheme">@style/SudAlertDialogThemeCompat</item>
         <item name="android:alertDialogTheme" tools:targetApi="honeycomb">@style/SudAlertDialogTheme</item>
         <item name="sucLightStatusBar" tools:targetApi="m">?android:attr/windowLightStatusBar</item>
     </style>
@@ -212,18 +292,20 @@
 
         <item name="sudBackgroundBaseColor">?android:attr/colorBackground</item>
         <item name="sudBackgroundPatterned">false</item>
-        <item name="sudDividerInsetEnd">?attr/sudMarginSides</item>
-        <item name="sudDividerInsetStart">?attr/sudMarginSides</item>
-        <item name="sudDividerInsetStartNoIcon">?attr/sudMarginSides</item>
+        <item name="sudDividerInsetEnd">?attr/sudMarginEnd</item>
+        <item name="sudDividerInsetStart">?attr/sudMarginStart</item>
+        <item name="sudDividerInsetStartNoIcon">?attr/sudMarginStart</item>
         <item name="sudGlifHeaderGravity">center_horizontal</item>
+        <item name="sudGlifSubtitleGravity">center_horizontal</item>
         <item name="sudScrollIndicators">top|bottom</item>
         <item name="sudEditTextBackgroundColor">@color/sud_glif_edit_text_bg_light_color</item>
         <item name="android:editTextStyle">@style/SudEditText</item>
+        <item name="alertDialogTheme">@style/SudAlertDialogThemeCompat.Light</item>
         <item name="android:alertDialogTheme" tools:targetApi="honeycomb">@style/SudAlertDialogTheme.Light</item>
         <item name="sucLightStatusBar" tools:targetApi="m">?android:attr/windowLightStatusBar</item>
     </style>
 
-    <style name="SudThemeGlifV3" parent="SudThemeGlifV2">
+    <style name="SudBaseThemeGlifV3" parent="SudThemeGlifV2">
         <item name="colorAccent">@color/sud_color_accent_glif_v3_dark</item>
         <item name="colorBackgroundFloating">@color/sud_glif_v3_dialog_background_color_dark</item>
         <item name="android:datePickerDialogTheme">@style/SudDateTimePickerDialogTheme</item>
@@ -233,6 +315,9 @@
         <item name="sudButtonCornerRadius">@dimen/sud_glif_v3_button_corner_radius</item>
         <item name="sudButtonFontFamily">@string/sudFontSecondaryMedium</item>
     </style>
+    <style name="SudThemeGlifV3" parent="SudBaseThemeGlifV3" />
+    <style name="SudDynamicColorThemeGlifV3" parent="SudThemeGlifV3" />
+    <style name="SudFullDynamicColorThemeGlifV3" parent="SudDynamicColorThemeGlifV3" />
 
     <style name="SudBaseThemeGlifV3.Light" parent="SudThemeGlifV2.Light">
         <item name="colorAccent">@color/sud_color_accent_glif_v3_light</item>
@@ -244,6 +329,8 @@
         <item name="sudButtonFontFamily">@string/sudFontSecondaryMedium</item>
     </style>
     <style name="SudThemeGlifV3.Light" parent="SudBaseThemeGlifV3.Light" />
+    <style name="SudDynamicColorThemeGlifV3.Light" parent="SudThemeGlifV3.Light" />
+    <style name="SudFullDynamicColorThemeGlifV3.Light" parent="SudDynamicColorThemeGlifV3.Light" />
 
     <style name="Animation.SudWindowAnimation" parent="@android:style/Animation.Activity">
         <item name="android:activityOpenEnterAnimation">@anim/sud_slide_next_in</item>
@@ -257,16 +344,38 @@
     <style name="SudThemeGlif.DayNight" parent="SudThemeGlif.Light" />
     <style name="SudThemeGlifV2.DayNight" parent="SudThemeGlifV2.Light" />
     <style name="SudThemeGlifV3.DayNight" parent="SudThemeGlifV3.Light" />
+    <style name="SudDynamicColorThemeGlifV3.DayNight" parent="SudDynamicColorThemeGlifV3.Light" />
+    <style name="SudFullDynamicColorThemeGlifV3.DayNight" parent="SudFullDynamicColorThemeGlifV3.Light" />
 
     <!-- Content styles -->
 
     <!-- Ignore UnusedResources: Used by clients -->
     <style name="SudContentFrame" tools:ignore="UnusedResources">
         <item name="android:clipToPadding">false</item>
-        <item name="android:paddingTop">@dimen/sud_content_frame_padding_top</item>
-        <item name="android:paddingLeft">?attr/sudMarginSides</item>
-        <item name="android:paddingRight">?attr/sudMarginSides</item>
-        <item name="android:paddingBottom">@dimen/sud_content_frame_padding_bottom</item>
+        <item name="android:paddingTop">?attr/sudContentFramePaddingTop</item>
+        <item name="android:paddingLeft">?attr/sudMarginStart</item>
+        <item name="android:paddingRight">?attr/sudMarginEnd</item>
+        <item name="android:paddingBottom">?attr/sudContentFramePaddingBottom</item>
+    </style>
+
+    <style name="SudLoadingContentFrame" tools:ignore="UnusedResources">
+        <item name="android:clipToPadding">false</item>
+        <item name="android:paddingTop">?attr/sudLoadingContentFramePaddingTop</item>
+        <item name="android:paddingLeft">?attr/sudLoadingContentFramePaddingStart</item>
+        <item name="android:paddingRight">?attr/sudLoadingContentFramePaddingEnd</item>
+        <item name="android:paddingBottom">?attr/sudLoadingContentFramePaddingBottom</item>
+    </style>
+
+    <!-- Content info -->
+
+    <style name="SudInfoContainer">
+        <item name="android:paddingTop">@dimen/sud_content_info_padding_top</item>
+        <item name="android:paddingBottom">@dimen/sud_content_info_padding_bottom</item>
+    </style>
+
+    <style name="SudInfoDescription">
+        <item name="android:textSize">@dimen/sud_content_info_text_size</item>
+        <item name="android:lineSpacingExtra">@dimen/sud_content_info_line_spacing_extra</item>
     </style>
 
     <!-- Ignore UnusedResources: Used by clients -->
@@ -338,10 +447,10 @@
     <style name="SudFillContentLayout">
         <item name="android:minWidth">@dimen/sud_content_illustration_min_width</item>
         <item name="android:minHeight">@dimen/sud_content_illustration_min_height</item>
-        <item name="android:maxWidth">@dimen/sud_content_illustration_max_width</item>
-        <item name="android:maxHeight">@dimen/sud_content_illustration_max_height</item>
-        <item name="android:paddingTop">@dimen/sud_content_illustration_padding_vertical</item>
-        <item name="android:paddingBottom">@dimen/sud_content_illustration_padding_vertical</item>
+        <item name="android:maxWidth">?attr/sudContentIllustrationMaxWidth</item>
+        <item name="android:maxHeight">?attr/sudContentIllustrationMaxHeight</item>
+        <item name="android:paddingTop">?attr/sudContentIllustrationPaddingTop</item>
+        <item name="android:paddingBottom">?attr/sudContentIllustrationPaddingBottom</item>
     </style>
 
     <!-- Ignore UnusedResources: used by clients -->
@@ -404,8 +513,8 @@
 
     <style name="SudHeaderTitle" parent="SudBaseHeaderTitle">
         <item name="android:layout_marginBottom">@dimen/sud_header_title_margin_bottom</item>
-        <item name="android:layout_marginLeft">?attr/sudMarginSides</item>
-        <item name="android:layout_marginRight">?attr/sudMarginSides</item>
+        <item name="android:layout_marginLeft">?attr/sudMarginStart</item>
+        <item name="android:layout_marginRight">?attr/sudMarginEnd</item>
         <item name="android:lineSpacingExtra">@dimen/sud_header_title_line_spacing_extra</item>
         <item name="android:paddingBottom">@dimen/sud_header_title_padding_bottom</item>
         <item name="android:paddingTop">@dimen/sud_header_title_padding_top</item>
@@ -510,12 +619,12 @@
 
     <style name="SudItemContainer">
         <item name="android:minHeight">?android:attr/listPreferredItemHeight</item>
-        <item name="android:paddingBottom">@dimen/sud_items_padding_vertical</item>
+        <item name="android:paddingBottom">@dimen/sud_items_padding_bottom</item>
         <item name="android:paddingEnd" tools:ignore="NewApi">?attr/listPreferredItemPaddingRight</item>
         <item name="android:paddingLeft">?attr/listPreferredItemPaddingLeft</item>
         <item name="android:paddingRight">?attr/listPreferredItemPaddingRight</item>
         <item name="android:paddingStart" tools:ignore="NewApi">?attr/listPreferredItemPaddingLeft</item>
-        <item name="android:paddingTop">@dimen/sud_items_padding_vertical</item>
+        <item name="android:paddingTop">@dimen/sud_items_padding_top</item>
     </style>
 
     <style name="SudItemContainer.Description" parent="SudItemContainer">
@@ -577,24 +686,44 @@
 
     <style name="SudGlifHeaderTitle" parent="SudBaseHeaderTitle">
         <item name="android:gravity">?attr/sudGlifHeaderGravity</item>
-        <item name="android:layout_marginBottom">@dimen/sud_glif_header_title_margin_bottom</item>
-        <item name="android:layout_marginLeft">?attr/sudMarginSides</item>
-        <item name="android:layout_marginRight">?attr/sudMarginSides</item>
-        <item name="android:layout_marginTop">@dimen/sud_glif_header_title_margin_top</item>
+        <item name="android:layout_marginBottom">?attr/sucGlifHeaderMarginBottom</item>
+        <item name="android:layout_marginLeft">?attr/sudMarginStart</item>
+        <item name="android:layout_marginRight">?attr/sudMarginEnd</item>
+        <item name="android:layout_marginTop">?attr/sucGlifHeaderMarginTop</item>
         <item name="android:fontFamily" tools:targetApi="jelly_bean">@string/sudFontSecondary</item>
         <item name="android:textAlignment" tools:targetApi="jelly_bean_mr1">gravity</item>
         <item name="android:textColor">?android:attr/textColorPrimary</item>
     </style>
+
+    <style name="SudGlifDescription" parent="SudDescription.Glif">
+        <item name="android:layout_marginTop">?attr/sudGlifDescriptionMarginTop</item>
+        <item name="android:layout_marginBottom">?attr/sudGlifDescriptionMarginBottom</item>
+        <item name="android:layout_marginLeft">?attr/sudMarginStart</item>
+        <item name="android:layout_marginStart">?attr/sudMarginStart</item>
+        <item name="android:layout_marginRight">?attr/sudMarginEnd</item>
+        <item name="android:layout_marginEnd">?attr/sudMarginEnd</item>
+        <item name="android:fontFamily" tools:targetApi="jelly_bean">@string/sudFontSecondary</item>
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+    </style>
+
     <style name="SudGlifHeaderContainer">
         <item name="android:gravity">?attr/sudGlifHeaderGravity</item>
+        <item name="android:layout_marginBottom">?attr/sucHeaderContainerMarginBottom</item>
+    </style>
+
+    <style name="SudGlifIconContainer">
+        <item name="android:layout_marginLeft">?attr/sudMarginStart</item>
+        <item name="android:layout_marginRight">?attr/sudMarginEnd</item>
+        <item name="android:layout_marginTop">?attr/sucGlifIconMarginTop</item>
+        <item name="android:maxHeight">?attr/sudGlifIconSize</item>
     </style>
 
     <style name="SudGlifIcon">
-        <item name="android:layout_marginLeft">?attr/sudMarginSides</item>
-        <item name="android:layout_marginRight">?attr/sudMarginSides</item>
-        <item name="android:layout_marginTop">@dimen/sud_glif_margin_top</item>
+        <item name="android:layout_marginLeft">?attr/sudMarginStart</item>
+        <item name="android:layout_marginRight">?attr/sudMarginEnd</item>
+        <item name="android:layout_marginTop">?attr/sucGlifIconMarginTop</item>
         <item name="android:adjustViewBounds">true</item>
-        <item name="android:maxHeight">@dimen/sud_glif_icon_max_height</item>
+        <item name="android:maxHeight">?attr/sudGlifIconSize</item>
         <item name="android:scaleType">centerInside</item>
     </style>
 
@@ -604,12 +733,12 @@
     </style>
 
     <style name="TextAppearance.SudGlifItemTitle" parent="android:TextAppearance">
-        <item name="android:textSize">16sp</item>
+        <item name="android:textSize">@dimen/sud_items_title_text_size</item>
         <item name="android:textColor">?android:attr/textColorPrimary</item>
     </style>
 
     <style name="TextAppearance.SudGlifItemSummary" parent="android:TextAppearance">
-        <item name="android:textSize">14sp</item>
+        <item name="android:textSize">@dimen/sud_items_summary_text_size</item>
         <item name="android:textColor">?android:attr/textColorSecondary</item>
     </style>
 
@@ -651,29 +780,42 @@
         <item name="android:minHeight">@dimen/sud_edit_text_min_height</item>
     </style>
 
-    <style name="SudAlertDialogTheme" parent="Theme.AppCompat.Dialog.Alert">
+    <style name="SudAlertDialogThemeCompat" parent="Theme.AppCompat.Dialog.Alert">
         <item name="android:textAllCaps">false</item>
         <item name="android:colorBackground">@color/sud_glif_v3_dialog_background_color_dark</item>
         <item name="colorAccent">@color/sud_color_accent_glif_v3_dark</item>
         <item name="dialogCornerRadius">@dimen/sud_glif_alert_dialog_corner_radius</item>
     </style>
 
-    <style name="SudAlertDialogTheme.Light" parent="Theme.AppCompat.Light.Dialog.Alert">
+    <style name="SudAlertDialogThemeCompat.Light" parent="Theme.AppCompat.Light.Dialog.Alert">
         <item name="android:textAllCaps">false</item>
         <item name="colorAccent">@color/sud_color_accent_glif_v3_light</item>
         <item name="dialogCornerRadius">@dimen/sud_glif_alert_dialog_corner_radius</item>
     </style>
 
+    <style name="SudAlertDialogTheme" parent="SudAlertDialogThemeCompat"/>
+    <style name="SudAlertDialogTheme.Light" parent="SudAlertDialogThemeCompat.Light"/>
+
     <style name="SudDateTimePickerDialogTheme" parent="Theme.AppCompat.Dialog">
         <item name="android:textAllCaps">false</item>
         <item name="colorAccent">@color/sud_color_accent_glif_v3_dark</item>
         <item name="dialogCornerRadius">@dimen/sud_glif_alert_dialog_corner_radius</item>
+        <item name="android:windowBackground">@drawable/sud_dialog_background_dark</item>
     </style>
 
     <style name="SudDateTimePickerDialogTheme.Light" parent="Theme.AppCompat.Light.Dialog">
         <item name="android:textAllCaps">false</item>
         <item name="colorAccent">@color/sud_color_accent_glif_v3_light</item>
         <item name="dialogCornerRadius">@dimen/sud_glif_alert_dialog_corner_radius</item>
+        <item name="android:windowBackground">@drawable/sud_dialog_background_light</item>
+    </style>
+
+    <style name="SudSwitchBarStyle">
+        <item name="android:layout_gravity">center_vertical</item>
+        <item name="track">@drawable/sud_switch_track_selector</item>
+        <item name="android:track">@drawable/sud_switch_track_selector</item>
+        <item name="android:thumb">@drawable/sud_switch_thumb_selector</item>
+        <item name="android:switchMinWidth">@dimen/sud_switch_min_width</item>
     </style>
 
 </resources>
diff --git a/main/src/com/google/android/setupdesign/DividerItemDecoration.java b/main/src/com/google/android/setupdesign/DividerItemDecoration.java
index fa0166f..df86b23 100644
--- a/main/src/com/google/android/setupdesign/DividerItemDecoration.java
+++ b/main/src/com/google/android/setupdesign/DividerItemDecoration.java
@@ -21,10 +21,10 @@
 import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
-import androidx.annotation.IntDef;
 import androidx.core.view.ViewCompat;
 import androidx.recyclerview.widget.RecyclerView;
 import android.view.View;
+import androidx.annotation.IntDef;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
@@ -125,7 +125,7 @@
     }
   }
 
-  private boolean shouldDrawDividerBelow(View view, RecyclerView parent) {
+  protected 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;
diff --git a/main/src/com/google/android/setupdesign/GlifLayout.java b/main/src/com/google/android/setupdesign/GlifLayout.java
index de4f8c1..829e3d7 100644
--- a/main/src/com/google/android/setupdesign/GlifLayout.java
+++ b/main/src/com/google/android/setupdesign/GlifLayout.java
@@ -22,11 +22,8 @@
 import android.content.res.TypedArray;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.os.Build.VERSION_CODES;
-import androidx.annotation.ColorInt;
-import androidx.annotation.LayoutRes;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -35,16 +32,25 @@
 import android.widget.ProgressBar;
 import android.widget.ScrollView;
 import android.widget.TextView;
+import androidx.annotation.ColorInt;
+import androidx.annotation.LayoutRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
 import com.google.android.setupcompat.PartnerCustomizationLayout;
 import com.google.android.setupcompat.partnerconfig.PartnerConfig;
 import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
 import com.google.android.setupcompat.template.StatusBarMixin;
+import com.google.android.setupdesign.template.DescriptionMixin;
 import com.google.android.setupdesign.template.HeaderMixin;
 import com.google.android.setupdesign.template.IconMixin;
+import com.google.android.setupdesign.template.IllustrationProgressMixin;
 import com.google.android.setupdesign.template.ProgressBarMixin;
 import com.google.android.setupdesign.template.RequireScrollMixin;
 import com.google.android.setupdesign.template.ScrollViewScrollHandlingDelegate;
 import com.google.android.setupdesign.util.DescriptionStyler;
+import com.google.android.setupdesign.util.LayoutStyler;
+import com.google.android.setupdesign.util.PartnerStyleHelper;
 
 /**
  * Layout for the GLIF theme used in Setup Wizard for N.
@@ -67,8 +73,6 @@
  */
 public class GlifLayout extends PartnerCustomizationLayout {
 
-  private static final String TAG = "GlifLayout";
-
   private ColorStateList primaryColor;
 
   private boolean backgroundPatterned = true;
@@ -105,6 +109,9 @@
   // 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) {
+    if (isInEditMode()) {
+      return;
+    }
 
     TypedArray a =
         getContext().obtainStyledAttributes(attrs, R.styleable.SudGlifLayout, defStyleAttr, 0);
@@ -113,8 +120,10 @@
     applyPartnerHeavyThemeResource = shouldApplyPartnerResource() && usePartnerHeavyTheme;
 
     registerMixin(HeaderMixin.class, new HeaderMixin(this, attrs, defStyleAttr));
+    registerMixin(DescriptionMixin.class, new DescriptionMixin(this, attrs, defStyleAttr));
     registerMixin(IconMixin.class, new IconMixin(this, attrs, defStyleAttr));
-    registerMixin(ProgressBarMixin.class, new ProgressBarMixin(this));
+    registerMixin(ProgressBarMixin.class, new ProgressBarMixin(this, attrs, defStyleAttr));
+    registerMixin(IllustrationProgressMixin.class, new IllustrationProgressMixin(this));
     final RequireScrollMixin requireScrollMixin = new RequireScrollMixin(this);
     registerMixin(RequireScrollMixin.class, requireScrollMixin);
 
@@ -131,7 +140,21 @@
 
     if (applyPartnerHeavyThemeResource) {
       updateContentBackgroundColorWithPartnerConfig();
+
+      View view = findManagedViewById(R.id.sud_layout_content);
+      if (view != null) {
+        // The margin of content is defined by @style/SudContentFrame. The Setupdesign library
+        // cannot obtain the content resource ID of the client, so the value of the content margin
+        // cannot be adjusted through GlifLayout. If the margin sides are changed through the
+        // partner config, it can only be based on the increased or decreased value to adjust the
+        // value of pading. In this way, the value of content margin plus padding will be equal to
+        // the value of partner config.
+        LayoutStyler.applyPartnerCustomizationExtraPaddingStyle(view);
+
+        applyPartnerCustomizationContentPaddingTopStyle(view);
+      }
     }
+    updateLandscapeMiddleHorizontalSpacing();
 
     ColorStateList backgroundColor =
         a.getColorStateList(R.styleable.SudGlifLayout_sudBackgroundBaseColor);
@@ -153,15 +176,79 @@
     super.onFinishInflate();
     getMixin(IconMixin.class).tryApplyPartnerCustomizationStyle();
     getMixin(HeaderMixin.class).tryApplyPartnerCustomizationStyle();
+    getMixin(DescriptionMixin.class).tryApplyPartnerCustomizationStyle();
     tryApplyPartnerCustomizationStyleToShortDescription();
   }
 
+  // TODO: remove when all sud_layout_description has migrated to
+  // DescriptionMixin(sud_layout_subtitle)
   private void tryApplyPartnerCustomizationStyleToShortDescription() {
-    if (applyPartnerHeavyThemeResource) {
-      TextView description =
-          this.findManagedViewById(com.google.android.setupdesign.R.id.sud_layout_description);
-      if (description != null) {
-        DescriptionStyler.applyPartnerCustomizationStyle(description);
+    TextView description = this.findManagedViewById(R.id.sud_layout_description);
+    if (description != null) {
+      if (applyPartnerHeavyThemeResource) {
+        DescriptionStyler.applyPartnerCustomizationHeavyStyle(description);
+      } else if (shouldApplyPartnerResource()) {
+        DescriptionStyler.applyPartnerCustomizationLightStyle(description);
+      }
+    }
+  }
+
+  protected void updateLandscapeMiddleHorizontalSpacing() {
+    int horizontalSpacing =
+        getResources().getDimensionPixelSize(R.dimen.sud_glif_land_middle_horizontal_spacing);
+
+    View headerView = this.findManagedViewById(R.id.sud_landscape_header_area);
+    if (headerView != null) {
+      if (PartnerConfigHelper.get(getContext())
+          .isPartnerConfigAvailable(PartnerConfig.CONFIG_LAYOUT_MARGIN_END)) {
+        int layoutMarginEnd =
+            (int)
+                PartnerConfigHelper.get(getContext())
+                    .getDimension(getContext(), PartnerConfig.CONFIG_LAYOUT_MARGIN_END);
+
+        int paddingEnd = (horizontalSpacing / 2) - layoutMarginEnd;
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+          headerView.setPadding(
+              headerView.getPaddingStart(),
+              headerView.getPaddingTop(),
+              paddingEnd,
+              headerView.getPaddingBottom());
+        } else {
+          headerView.setPadding(
+              headerView.getPaddingLeft(),
+              headerView.getPaddingTop(),
+              paddingEnd,
+              headerView.getPaddingBottom());
+        }
+      }
+    }
+
+    View contentView = this.findManagedViewById(R.id.sud_landscape_content_area);
+    if (contentView != null) {
+      if (PartnerConfigHelper.get(getContext())
+          .isPartnerConfigAvailable(PartnerConfig.CONFIG_LAYOUT_MARGIN_START)) {
+        int layoutMarginStart =
+            (int)
+                PartnerConfigHelper.get(getContext())
+                    .getDimension(getContext(), PartnerConfig.CONFIG_LAYOUT_MARGIN_START);
+
+        int paddingStart = 0;
+        if (headerView != null) {
+          paddingStart = (horizontalSpacing / 2) - layoutMarginStart;
+        }
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+          contentView.setPadding(
+              paddingStart,
+              contentView.getPaddingTop(),
+              contentView.getPaddingEnd(),
+              contentView.getPaddingBottom());
+        } else {
+          contentView.setPadding(
+              paddingStart,
+              contentView.getPaddingTop(),
+              contentView.getPaddingRight(),
+              contentView.getPaddingBottom());
+        }
       }
     }
   }
@@ -187,8 +274,8 @@
    * 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.
+   * @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.sud_layout_sticky_header);
@@ -217,6 +304,35 @@
     return getMixin(HeaderMixin.class).getText();
   }
 
+  public TextView getDescriptionTextView() {
+    return getMixin(DescriptionMixin.class).getTextView();
+  }
+
+  /**
+   * Sets the description text and also sets the text visibility to visible. This can also be set
+   * via the XML attribute {@code app:sudDescriptionText}.
+   *
+   * @param title The resource ID of the text to be set as description
+   */
+  public void setDescriptionText(@StringRes int title) {
+    getMixin(DescriptionMixin.class).setText(title);
+  }
+
+  /**
+   * Sets the description text and also sets the text visibility to visible. This can also be set
+   * via the XML attribute {@code app:sudDescriptionText}.
+   *
+   * @param title The text to be set as description
+   */
+  public void setDescriptionText(CharSequence title) {
+    getMixin(DescriptionMixin.class).setText(title);
+  }
+
+  /** Returns the current description text. */
+  public CharSequence getDescriptionText() {
+    return getMixin(DescriptionMixin.class).getText();
+  }
+
   public void setHeaderColor(ColorStateList color) {
     getMixin(HeaderMixin.class).setTextColor(color);
   }
@@ -234,6 +350,24 @@
   }
 
   /**
+   * Sets the visibility of header area in landscape mode. These views inlcudes icon, header title
+   * and subtitle. It can make the content view become full screen when set false.
+   */
+  @TargetApi(Build.VERSION_CODES.S)
+  public void setLandscapeHeaderAreaVisible(boolean visible) {
+    View view = this.findManagedViewById(R.id.sud_landscape_header_area);
+    if (view == null) {
+      return;
+    }
+    if (visible) {
+      view.setVisibility(View.VISIBLE);
+    } else {
+      view.setVisibility(View.GONE);
+    }
+    updateLandscapeMiddleHorizontalSpacing();
+  }
+
+  /**
    * Sets the primary color of this layout, which will be used to determine the color of the
    * progress bar and the background pattern.
    */
@@ -253,7 +387,7 @@
    * 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.
+   *     #getPrimaryColor()} will be used
    */
   public void setBackgroundBaseColor(@Nullable ColorStateList color) {
     backgroundBaseColor = color;
@@ -316,15 +450,45 @@
    * Returns if the current layout/activity applies heavy partner customized configurations or not.
    */
   public boolean shouldApplyPartnerHeavyThemeResource() {
-    return applyPartnerHeavyThemeResource;
+
+    return applyPartnerHeavyThemeResource
+        || (shouldApplyPartnerResource()
+            && PartnerConfigHelper.shouldApplyExtendedPartnerConfig(getContext()));
   }
 
   /** Updates the background color of this layout with the partner-customizable background color. */
   private void updateContentBackgroundColorWithPartnerConfig() {
+    // If full dynamic color enabled which means this activity is running outside of setup
+    // flow, the colors should refer to R.style.SudFullDynamicColorThemeGlifV3.
+    if (useFullDynamicColor()) {
+      return;
+    }
+
     @ColorInt
     int color =
         PartnerConfigHelper.get(getContext())
             .getColor(getContext(), PartnerConfig.CONFIG_LAYOUT_BACKGROUND_COLOR);
     this.getRootView().setBackgroundColor(color);
   }
+
+  @TargetApi(VERSION_CODES.JELLY_BEAN_MR1)
+  protected static void applyPartnerCustomizationContentPaddingTopStyle(View view) {
+    Context context = view.getContext();
+    boolean partnerPaddingTopAvailable =
+        PartnerConfigHelper.get(context)
+            .isPartnerConfigAvailable(PartnerConfig.CONFIG_CONTENT_PADDING_TOP);
+
+    if (PartnerStyleHelper.shouldApplyPartnerHeavyThemeResource(view)
+        && partnerPaddingTopAvailable) {
+      int paddingTop =
+          (int)
+              PartnerConfigHelper.get(context)
+                  .getDimension(context, PartnerConfig.CONFIG_CONTENT_PADDING_TOP);
+
+      if (paddingTop != view.getPaddingTop()) {
+        view.setPadding(
+            view.getPaddingStart(), paddingTop, view.getPaddingEnd(), view.getPaddingBottom());
+      }
+    }
+  }
 }
diff --git a/main/src/com/google/android/setupdesign/GlifListLayout.java b/main/src/com/google/android/setupdesign/GlifListLayout.java
index 31335cc..60f0343 100644
--- a/main/src/com/google/android/setupdesign/GlifListLayout.java
+++ b/main/src/com/google/android/setupdesign/GlifListLayout.java
@@ -63,12 +63,22 @@
   }
 
   private void init(AttributeSet attrs, int defStyleAttr) {
+    if (isInEditMode()) {
+      return;
+    }
+
     listMixin = new ListMixin(this, attrs, defStyleAttr);
     registerMixin(ListMixin.class, listMixin);
 
     final RequireScrollMixin requireScrollMixin = getMixin(RequireScrollMixin.class);
     requireScrollMixin.setScrollHandlingDelegate(
         new ListViewScrollHandlingDelegate(requireScrollMixin, getListView()));
+
+    View view = this.findManagedViewById(R.id.sud_landscape_content_area);
+    if (view != null) {
+      applyPartnerCustomizationContentPaddingTopStyle(view);
+    }
+    updateLandscapeMiddleHorizontalSpacing();
   }
 
   @Override
diff --git a/main/src/com/google/android/setupdesign/GlifRecyclerLayout.java b/main/src/com/google/android/setupdesign/GlifRecyclerLayout.java
index c74cff0..5e75436 100644
--- a/main/src/com/google/android/setupdesign/GlifRecyclerLayout.java
+++ b/main/src/com/google/android/setupdesign/GlifRecyclerLayout.java
@@ -64,12 +64,22 @@
   }
 
   private void init(AttributeSet attrs, int defStyleAttr) {
+    if (isInEditMode()) {
+      return;
+    }
+
     recyclerMixin.parseAttributes(attrs, defStyleAttr);
     registerMixin(RecyclerMixin.class, recyclerMixin);
 
     final RequireScrollMixin requireScrollMixin = getMixin(RequireScrollMixin.class);
     requireScrollMixin.setScrollHandlingDelegate(
         new RecyclerViewScrollHandlingDelegate(requireScrollMixin, getRecyclerView()));
+
+    View view = this.findManagedViewById(R.id.sud_landscape_content_area);
+    if (view != null) {
+      applyPartnerCustomizationContentPaddingTopStyle(view);
+    }
+    updateLandscapeMiddleHorizontalSpacing();
   }
 
   @Override
diff --git a/main/src/com/google/android/setupdesign/SetupWizardItemsLayout.java b/main/src/com/google/android/setupdesign/SetupWizardItemsLayout.java
index 2f3dd86..28f95fb 100644
--- a/main/src/com/google/android/setupdesign/SetupWizardItemsLayout.java
+++ b/main/src/com/google/android/setupdesign/SetupWizardItemsLayout.java
@@ -17,9 +17,9 @@
 package com.google.android.setupdesign;
 
 import android.content.Context;
-import androidx.annotation.Nullable;
 import android.util.AttributeSet;
 import android.widget.ListAdapter;
+import androidx.annotation.Nullable;
 import com.google.android.setupdesign.items.ItemAdapter;
 
 /** @deprecated Use {@link SetupWizardListLayout} instead. */
diff --git a/main/src/com/google/android/setupdesign/SetupWizardLayout.java b/main/src/com/google/android/setupdesign/SetupWizardLayout.java
index 5797aa7..b91cec7 100644
--- a/main/src/com/google/android/setupdesign/SetupWizardLayout.java
+++ b/main/src/com/google/android/setupdesign/SetupWizardLayout.java
@@ -40,6 +40,7 @@
 import android.widget.TextView;
 import com.google.android.setupcompat.internal.TemplateLayout;
 import com.google.android.setupcompat.template.SystemNavBarMixin;
+import com.google.android.setupdesign.template.DescriptionMixin;
 import com.google.android.setupdesign.template.HeaderMixin;
 import com.google.android.setupdesign.template.NavigationBarMixin;
 import com.google.android.setupdesign.template.ProgressBarMixin;
@@ -80,10 +81,15 @@
   // 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) {
+    if (isInEditMode()) {
+      return;
+    }
+
     registerMixin(SystemNavBarMixin.class, new SystemNavBarMixin(this, /* window= */ null));
     registerMixin(
         HeaderMixin.class,
         new HeaderMixin(this, attrs, defStyleAttr));
+    registerMixin(DescriptionMixin.class, new DescriptionMixin(this, attrs, defStyleAttr));
     registerMixin(ProgressBarMixin.class, new ProgressBarMixin(this));
     registerMixin(NavigationBarMixin.class, new NavigationBarMixin(this));
     final RequireScrollMixin requireScrollMixin = new RequireScrollMixin(this);
diff --git a/main/src/com/google/android/setupdesign/SetupWizardListLayout.java b/main/src/com/google/android/setupdesign/SetupWizardListLayout.java
index 760602f..dcb35d2 100644
--- a/main/src/com/google/android/setupdesign/SetupWizardListLayout.java
+++ b/main/src/com/google/android/setupdesign/SetupWizardListLayout.java
@@ -59,6 +59,10 @@
   }
 
   private void init(AttributeSet attrs, int defStyleAttr) {
+    if (isInEditMode()) {
+      return;
+    }
+
     listMixin = new ListMixin(this, attrs, defStyleAttr);
     registerMixin(ListMixin.class, listMixin);
 
diff --git a/main/src/com/google/android/setupdesign/SetupWizardRecyclerLayout.java b/main/src/com/google/android/setupdesign/SetupWizardRecyclerLayout.java
index f0dc138..f2a7e3d 100644
--- a/main/src/com/google/android/setupdesign/SetupWizardRecyclerLayout.java
+++ b/main/src/com/google/android/setupdesign/SetupWizardRecyclerLayout.java
@@ -60,6 +60,10 @@
   }
 
   private void init(AttributeSet attrs, int defStyleAttr) {
+    if (isInEditMode()) {
+      return;
+    }
+
     recyclerMixin.parseAttributes(attrs, defStyleAttr);
     registerMixin(RecyclerMixin.class, recyclerMixin);
 
diff --git a/main/src/com/google/android/setupdesign/accessibility/LinkAccessibilityHelper.java b/main/src/com/google/android/setupdesign/accessibility/LinkAccessibilityHelper.java
index ed9daf3..1a55b25 100644
--- a/main/src/com/google/android/setupdesign/accessibility/LinkAccessibilityHelper.java
+++ b/main/src/com/google/android/setupdesign/accessibility/LinkAccessibilityHelper.java
@@ -19,12 +19,9 @@
 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;
@@ -34,6 +31,9 @@
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+import androidx.customview.widget.ExploreByTouchHelper;
 import java.util.List;
 
 /**
diff --git a/main/src/com/google/android/setupdesign/items/AbstractItemHierarchy.java b/main/src/com/google/android/setupdesign/items/AbstractItemHierarchy.java
index 267e4ab..45c8e9e 100644
--- a/main/src/com/google/android/setupdesign/items/AbstractItemHierarchy.java
+++ b/main/src/com/google/android/setupdesign/items/AbstractItemHierarchy.java
@@ -20,6 +20,7 @@
 import android.content.res.TypedArray;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.view.View;
 import com.google.android.setupdesign.R;
 import java.util.ArrayList;
 
@@ -33,13 +34,13 @@
   /* non-static section */
 
   private final ArrayList<Observer> observers = new ArrayList<>();
-  private int id = 0;
+  private int id = View.NO_ID;
 
   public AbstractItemHierarchy() {}
 
   public AbstractItemHierarchy(Context context, AttributeSet attrs) {
     TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SudAbstractItem);
-    id = a.getResourceId(R.styleable.SudAbstractItem_android_id, 0);
+    id = a.getResourceId(R.styleable.SudAbstractItem_android_id, View.NO_ID);
     a.recycle();
   }
 
diff --git a/main/src/com/google/android/setupdesign/items/DescriptionItem.java b/main/src/com/google/android/setupdesign/items/DescriptionItem.java
index 24fbf5f..36ccfb9 100644
--- a/main/src/com/google/android/setupdesign/items/DescriptionItem.java
+++ b/main/src/com/google/android/setupdesign/items/DescriptionItem.java
@@ -19,17 +19,24 @@
 import android.content.Context;
 import android.util.AttributeSet;
 import android.view.View;
+import android.widget.FrameLayout;
 import android.widget.TextView;
+import com.google.android.setupcompat.PartnerCustomizationLayout;
+import com.google.android.setupdesign.GlifLayout;
 import com.google.android.setupdesign.R;
 import com.google.android.setupdesign.util.DescriptionStyler;
 
 /**
  * Definition of an item in an {@link ItemHierarchy}. An item is usually defined in XML and inflated
  * using {@link ItemInflater}.
+ *
+ * @deprecated Use {@link com.google.android.setupdesign.template.DescriptionMixin} instead.
  */
+@Deprecated
 public class DescriptionItem extends Item {
 
-  private boolean applyPartnerDescriptionStyle = false;
+  private boolean partnerDescriptionHeavyStyle = false;
+  private boolean partnerDescriptionLightStyle = false;
 
   public DescriptionItem() {
     super();
@@ -39,16 +46,40 @@
     super(context, attrs);
   }
 
-  public boolean shouldApplyPartnerDescriptionStyle() {
-    return applyPartnerDescriptionStyle;
+  /**
+   * Returns true if the description of partner's layout should apply heavy style, this depends on
+   * if the layout fulfill conditions in {@code applyPartnerHeavyThemeResource} of {@link
+   * com.google.android.setupdesign.GlifLayout}
+   */
+  public boolean shouldApplyPartnerDescriptionHeavyStyle() {
+    return partnerDescriptionHeavyStyle;
+  }
+
+  /**
+   * Returns true if the description of partner's layout should apply light style, this depends on
+   * if the layout fulfill conditions in {@code shouldApplyPartnerResource} of {@link
+   * com.google.android.setupcompat.PartnerCustomizationLayout}
+   */
+  public boolean shouldApplyPartnerDescriptionLightStyle() {
+    return partnerDescriptionLightStyle;
   }
 
   /**
    * Applies partner description style on the title of the item, i.e. the TextView with {@code
    * R.id.sud_items_title}.
+   *
+   * @param layout A layout indicates if the description of partner's layout should apply heavy or
+   *     light style
    */
-  public void setApplyPartnerDescriptionStyle(boolean applyPartnerDescriptionStyle) {
-    this.applyPartnerDescriptionStyle = applyPartnerDescriptionStyle;
+  public void setPartnerDescriptionStyle(FrameLayout layout) {
+    if (layout instanceof GlifLayout) {
+      this.partnerDescriptionHeavyStyle =
+          ((GlifLayout) layout).shouldApplyPartnerHeavyThemeResource();
+    }
+    if (layout instanceof PartnerCustomizationLayout) {
+      this.partnerDescriptionLightStyle =
+          ((PartnerCustomizationLayout) layout).shouldApplyPartnerResource();
+    }
     notifyItemChanged();
   }
 
@@ -56,8 +87,10 @@
   public void onBindView(View view) {
     super.onBindView(view);
     TextView label = (TextView) view.findViewById(R.id.sud_items_title);
-    if (shouldApplyPartnerDescriptionStyle()) {
-      DescriptionStyler.applyPartnerCustomizationStyle(label);
+    if (shouldApplyPartnerDescriptionHeavyStyle()) {
+      DescriptionStyler.applyPartnerCustomizationHeavyStyle(label);
+    } else if (shouldApplyPartnerDescriptionLightStyle()) {
+      DescriptionStyler.applyPartnerCustomizationLightStyle(label);
     }
   }
 }
diff --git a/main/src/com/google/android/setupdesign/items/ExpandableSwitchItem.java b/main/src/com/google/android/setupdesign/items/ExpandableSwitchItem.java
index 42438d6..29eaf23 100644
--- a/main/src/com/google/android/setupdesign/items/ExpandableSwitchItem.java
+++ b/main/src/com/google/android/setupdesign/items/ExpandableSwitchItem.java
@@ -35,6 +35,7 @@
 import android.widget.CompoundButton.OnCheckedChangeListener;
 import android.widget.TextView;
 import com.google.android.setupdesign.R;
+import com.google.android.setupdesign.util.LayoutStyler;
 import com.google.android.setupdesign.view.CheckableLinearLayout;
 
 /**
@@ -179,6 +180,8 @@
     // 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);
+
+    LayoutStyler.applyPartnerCustomizationLayoutPaddingStyle(content);
   }
 
   @Override
diff --git a/main/src/com/google/android/setupdesign/items/Item.java b/main/src/com/google/android/setupdesign/items/Item.java
index e5d173f..fd3f2eb 100644
--- a/main/src/com/google/android/setupdesign/items/Item.java
+++ b/main/src/com/google/android/setupdesign/items/Item.java
@@ -20,8 +20,6 @@
 import android.content.res.TypedArray;
 import android.graphics.Color;
 import android.graphics.drawable.Drawable;
-import androidx.annotation.ColorInt;
-import androidx.annotation.Nullable;
 import android.util.AttributeSet;
 import android.view.Gravity;
 import android.view.View;
@@ -29,7 +27,11 @@
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
+import androidx.annotation.ColorInt;
+import androidx.annotation.Nullable;
 import com.google.android.setupdesign.R;
+import com.google.android.setupdesign.util.ItemStyler;
+import com.google.android.setupdesign.util.LayoutStyler;
 
 /**
  * Definition of an item in an {@link ItemHierarchy}. An item is usually defined in XML and inflated
@@ -200,6 +202,16 @@
     }
 
     view.setId(getViewId());
+
+    // ExpandableSwitchItem uses its child view to apply the style SudItemContainer. It is not
+    // possible to directly adjust the padding start/end of the item's layout here. It needs to
+    // get its child view to adjust it first, so skip the Layout padding adjustment.
+    // If the item view is a header layout, it doesn't need to adjust the layout padding start/end
+    // here. It will be adjusted by HeaderMixin.
+    if (!(this instanceof ExpandableSwitchItem) && view.getId() != R.id.sud_layout_header) {
+      LayoutStyler.applyPartnerCustomizationLayoutPaddingStyle(view);
+    }
+    ItemStyler.applyPartnerCustomizationItemStyle(view);
   }
 
   /**
diff --git a/main/src/com/google/android/setupdesign/items/ItemHierarchy.java b/main/src/com/google/android/setupdesign/items/ItemHierarchy.java
index b22e9ef..6a5b51f 100644
--- a/main/src/com/google/android/setupdesign/items/ItemHierarchy.java
+++ b/main/src/com/google/android/setupdesign/items/ItemHierarchy.java
@@ -71,7 +71,7 @@
   /**
    * Get the item at position.
    *
-   * @param position An integer from 0 to {@link #getCount()}}, which indicates the position in this
+   * @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}.
    */
diff --git a/main/src/com/google/android/setupdesign/items/ItemViewHolder.java b/main/src/com/google/android/setupdesign/items/ItemViewHolder.java
index f79b2b6..3cffddd 100644
--- a/main/src/com/google/android/setupdesign/items/ItemViewHolder.java
+++ b/main/src/com/google/android/setupdesign/items/ItemViewHolder.java
@@ -20,7 +20,11 @@
 import android.view.View;
 import com.google.android.setupdesign.DividerItemDecoration;
 
-class ItemViewHolder extends RecyclerView.ViewHolder
+/**
+ * ViewHolder for the RecyclerItemAdapter that describes an item view and metadata about its place
+ * within the RecyclerView.
+ */
+public class ItemViewHolder extends RecyclerView.ViewHolder
     implements DividerItemDecoration.DividedViewHolder {
 
   private boolean isEnabled;
diff --git a/main/src/com/google/android/setupdesign/items/RecyclerItemAdapter.java b/main/src/com/google/android/setupdesign/items/RecyclerItemAdapter.java
index 3526bf5..dbbda29 100644
--- a/main/src/com/google/android/setupdesign/items/RecyclerItemAdapter.java
+++ b/main/src/com/google/android/setupdesign/items/RecyclerItemAdapter.java
@@ -21,12 +21,12 @@
 import android.graphics.drawable.ColorDrawable;
 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 com.google.android.setupcompat.partnerconfig.PartnerConfig;
 import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
 import com.google.android.setupdesign.R;
@@ -61,6 +61,7 @@
 
   private final ItemHierarchy itemHierarchy;
   @VisibleForTesting public final boolean applyPartnerHeavyThemeResource;
+  @VisibleForTesting public final boolean useFullDynamicColor;
   private OnItemSelectedListener listener;
 
   public RecyclerItemAdapter(ItemHierarchy hierarchy) {
@@ -68,7 +69,15 @@
   }
 
   public RecyclerItemAdapter(ItemHierarchy hierarchy, boolean applyPartnerHeavyThemeResource) {
+    this(hierarchy, applyPartnerHeavyThemeResource, /* useFullDynamicColor= */ false);
+  }
+
+  public RecyclerItemAdapter(
+      ItemHierarchy hierarchy,
+      boolean applyPartnerHeavyThemeResource,
+      boolean useFullDynamicColor) {
     this.applyPartnerHeavyThemeResource = applyPartnerHeavyThemeResource;
+    this.useFullDynamicColor = useFullDynamicColor;
     itemHierarchy = hierarchy;
     itemHierarchy.registerObserver(this);
   }
@@ -118,7 +127,9 @@
       } else {
         background = view.getBackground();
         if (background == null) {
-          if (applyPartnerHeavyThemeResource) {
+          // If full dynamic color enabled which means this activity is running outside of setup
+          // flow, the colors should refer to R.style.SudFullDynamicColorThemeGlifV3.
+          if (applyPartnerHeavyThemeResource && !useFullDynamicColor) {
             int color =
                 PartnerConfigHelper.get(view.getContext())
                     .getColor(view.getContext(), PartnerConfig.CONFIG_LAYOUT_BACKGROUND_COLOR);
diff --git a/main/src/com/google/android/setupdesign/items/ReflectionInflater.java b/main/src/com/google/android/setupdesign/items/ReflectionInflater.java
index 329d240..8123c18 100644
--- a/main/src/com/google/android/setupdesign/items/ReflectionInflater.java
+++ b/main/src/com/google/android/setupdesign/items/ReflectionInflater.java
@@ -17,10 +17,10 @@
 package com.google.android.setupdesign.items;
 
 import android.content.Context;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import android.util.AttributeSet;
 import android.view.InflateException;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import java.lang.reflect.Constructor;
 import java.util.HashMap;
 
diff --git a/main/src/com/google/android/setupdesign/items/SimpleInflater.java b/main/src/com/google/android/setupdesign/items/SimpleInflater.java
index c7e370a..0c7d1d9 100644
--- a/main/src/com/google/android/setupdesign/items/SimpleInflater.java
+++ b/main/src/com/google/android/setupdesign/items/SimpleInflater.java
@@ -18,11 +18,11 @@
 
 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;
diff --git a/main/src/com/google/android/setupdesign/span/LinkSpan.java b/main/src/com/google/android/setupdesign/span/LinkSpan.java
index 7f1f02b..7d91012 100644
--- a/main/src/com/google/android/setupdesign/span/LinkSpan.java
+++ b/main/src/com/google/android/setupdesign/span/LinkSpan.java
@@ -19,7 +19,6 @@
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.os.Build;
-import androidx.annotation.Nullable;
 import android.text.Selection;
 import android.text.Spannable;
 import android.text.TextPaint;
@@ -27,6 +26,7 @@
 import android.util.Log;
 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
diff --git a/main/src/com/google/android/setupdesign/template/DescriptionMixin.java b/main/src/com/google/android/setupdesign/template/DescriptionMixin.java
new file mode 100644
index 0000000..bbccf82
--- /dev/null
+++ b/main/src/com/google/android/setupdesign/template/DescriptionMixin.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this 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.google.android.setupdesign.template;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+import androidx.annotation.AttrRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
+import com.google.android.setupcompat.PartnerCustomizationLayout;
+import com.google.android.setupcompat.internal.TemplateLayout;
+import com.google.android.setupcompat.template.Mixin;
+import com.google.android.setupdesign.R;
+import com.google.android.setupdesign.util.HeaderAreaStyler;
+import com.google.android.setupdesign.util.PartnerStyleHelper;
+
+/**
+ * A {@link com.google.android.setupcompat.template.Mixin} for setting and getting the description
+ * text.
+ */
+public class DescriptionMixin implements Mixin {
+
+  private static final String TAG = "DescriptionMixin";
+  private final TemplateLayout templateLayout;
+
+  /**
+   * A {@link com.google.android.setupcompat.template.Mixin} for setting and getting the
+   * description.
+   *
+   * @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 DescriptionMixin(
+      @NonNull TemplateLayout layout, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
+    templateLayout = layout;
+
+    final TypedArray a =
+        layout
+            .getContext()
+            .obtainStyledAttributes(attrs, R.styleable.SudDescriptionMixin, defStyleAttr, 0);
+
+    // Set the description text
+    final CharSequence descriptionText =
+        a.getText(R.styleable.SudDescriptionMixin_sudDescriptionText);
+    if (descriptionText != null) {
+      setText(descriptionText);
+    }
+    // Set the description text color
+    final ColorStateList descriptionTextColor =
+        a.getColorStateList(R.styleable.SudDescriptionMixin_sudDescriptionTextColor);
+    if (descriptionTextColor != null) {
+      setTextColor(descriptionTextColor);
+    }
+
+    a.recycle();
+  }
+
+  /**
+   * Applies the partner customizations to the description text (contains text alignment) and
+   * background, if apply heavy theme resource, it will apply all partner customizations, otherwise,
+   * only apply alignment style.
+   */
+  public void tryApplyPartnerCustomizationStyle() {
+    TextView description = templateLayout.findManagedViewById(R.id.sud_layout_subtitle);
+    boolean partnerHeavyThemeLayout = PartnerStyleHelper.isPartnerHeavyThemeLayout(templateLayout);
+    if (partnerHeavyThemeLayout) {
+      if (description != null) {
+        HeaderAreaStyler.applyPartnerCustomizationDescriptionHeavyStyle(description);
+      }
+    } else if (templateLayout instanceof PartnerCustomizationLayout
+        && ((PartnerCustomizationLayout) templateLayout).shouldApplyPartnerResource()) {
+      if (description != null) {
+        HeaderAreaStyler.applyPartnerCustomizationDescriptionLightStyle(description);
+      }
+    }
+  }
+
+  /** Returns the TextView displaying the description. */
+  public TextView getTextView() {
+    return (TextView) templateLayout.findManagedViewById(R.id.sud_layout_subtitle);
+  }
+
+  /**
+   * Sets the description text and also sets the text visibility to visible. This can also be set
+   * via the XML attribute {@code app:sudDescriptionText}.
+   *
+   * @param title The resource ID of the text to be set as description
+   */
+  public void setText(@StringRes int title) {
+    final TextView titleView = getTextView();
+    if (titleView != null && title != 0) {
+      titleView.setText(title);
+      setVisibility(View.VISIBLE);
+    } else {
+      Log.w(TAG, "Fail to set text due to either invalid resource id or text view not found.");
+    }
+  }
+
+  /**
+   * Sets the description text and also sets the text visibility to visible. This can also be set
+   * via the XML attribute {@code app:sudDescriptionText}.
+   *
+   * @param title The text to be set as description
+   */
+  public void setText(CharSequence title) {
+    final TextView titleView = getTextView();
+    if (titleView != null) {
+      titleView.setText(title);
+      setVisibility(View.VISIBLE);
+    }
+  }
+
+  /** Returns the current description text. */
+  public CharSequence getText() {
+    final TextView titleView = getTextView();
+    return titleView != null ? titleView.getText() : null;
+  }
+
+  /** Sets the visibility of description text */
+  public void setVisibility(int visibility) {
+    final TextView titleView = getTextView();
+    if (titleView != null) {
+      titleView.setVisibility(visibility);
+    }
+  }
+
+  /**
+   * Sets the color of the description text. This can also be set via XML using {@code
+   * app:sudDescriptionTextColor}.
+   *
+   * @param color The text color of the description
+   */
+  public void setTextColor(ColorStateList color) {
+    final TextView titleView = getTextView();
+    if (titleView != null) {
+      titleView.setTextColor(color);
+    }
+  }
+
+  /** Returns the current text color of the description. */
+  public ColorStateList getTextColor() {
+    final TextView titleView = getTextView();
+    return titleView != null ? titleView.getTextColors() : null;
+  }
+
+  /**
+   * Call this method ONLY when a layout migrates from {@link
+   * com.google.android.setupdesign.items.DescriptionItem} to {@link DescriptionMixin}.
+   *
+   * <p>If a screen is migrated from {@link com.google.android.setupdesign.items.DescriptionItem} it
+   * will looks slightly different from the original UI. This method helps keeping the UI consistent
+   * with the original UI.
+   */
+  public void adjustLegacyDescriptionItem() {
+    final TextView titleView = getTextView();
+    final Context context = titleView.getContext();
+
+    final ViewGroup.LayoutParams lp = titleView.getLayoutParams();
+    if (lp instanceof ViewGroup.MarginLayoutParams) {
+      final ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams) lp;
+      int extraBottomMargin =
+          (int) context.getResources().getDimension(R.dimen.sud_description_margin_bottom_extra);
+      int extraTopMargin =
+          (int) context.getResources().getDimension(R.dimen.sud_description_margin_top_extra);
+      mlp.setMargins(
+          mlp.leftMargin,
+          mlp.topMargin + extraTopMargin,
+          mlp.rightMargin,
+          mlp.bottomMargin + extraBottomMargin);
+    }
+  }
+}
diff --git a/main/src/com/google/android/setupdesign/template/HeaderMixin.java b/main/src/com/google/android/setupdesign/template/HeaderMixin.java
index 0a6eb9e..d040c94 100644
--- a/main/src/com/google/android/setupdesign/template/HeaderMixin.java
+++ b/main/src/com/google/android/setupdesign/template/HeaderMixin.java
@@ -16,19 +16,30 @@
 
 package com.google.android.setupdesign.template;
 
+import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.view.ViewTreeObserver;
+import android.widget.LinearLayout;
+import android.widget.TextView;
 import androidx.annotation.AttrRes;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import android.util.AttributeSet;
-import android.view.ViewParent;
-import android.widget.LinearLayout;
-import android.widget.TextView;
+import androidx.annotation.VisibleForTesting;
 import com.google.android.setupcompat.internal.TemplateLayout;
+import com.google.android.setupcompat.partnerconfig.PartnerConfig;
+import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
 import com.google.android.setupcompat.template.Mixin;
 import com.google.android.setupdesign.R;
 import com.google.android.setupdesign.util.HeaderAreaStyler;
+import com.google.android.setupdesign.util.LayoutStyler;
 import com.google.android.setupdesign.util.PartnerStyleHelper;
 
 /**
@@ -37,11 +48,19 @@
 public class HeaderMixin implements Mixin {
 
   private final TemplateLayout templateLayout;
+  @VisibleForTesting boolean autoTextSizeEnabled = false;
+  private float headerAutoSizeMaxTextSizeInPx;
+  private float headerAutoSizeMinTextSizeInPx;
+  private float headerAutoSizeLineExtraSpacingInPx;
+  private int headerAutoSizeMaxLineOfMaxSize;
+  private static final int AUTO_SIZE_DEFAULT_MAX_LINES = 6;
 
   /**
-   * @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.
+   * A {@link com.google.android.setupcompat.template.Mixin} for setting and getting the Header.
+   *
+   * @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) {
@@ -52,41 +71,103 @@
             .getContext()
             .obtainStyledAttributes(attrs, R.styleable.SucHeaderMixin, defStyleAttr, 0);
 
-    // Set the header text
     final CharSequence headerText = a.getText(R.styleable.SucHeaderMixin_sucHeaderText);
+    final ColorStateList headerTextColor =
+        a.getColorStateList(R.styleable.SucHeaderMixin_sucHeaderTextColor);
+
+    a.recycle();
+
+    // overlay the Auto size config settings
+    updateAutoTextSizeWithPartnerConfig();
+
+    // Set the header text
     if (headerText != null) {
       setText(headerText);
     }
     // Set the header text color
-    final ColorStateList headerTextColor =
-        a.getColorStateList(R.styleable.SucHeaderMixin_sucHeaderTextColor);
     if (headerTextColor != null) {
       setTextColor(headerTextColor);
     }
-
-    a.recycle();
   }
 
-  /**
-   * Tries to apply the partner customizations to the header text and background if the layout of
-   * this {@link HeaderMixin} is set to apply partner heavy theme resource.
-   */
-  public void tryApplyPartnerCustomizationStyle() {
-    if (!PartnerStyleHelper.isPartnerHeavyThemeLayout(templateLayout)) {
+  private void updateAutoTextSizeWithPartnerConfig() {
+    Context context = templateLayout.getContext();
+    if (!PartnerStyleHelper.isPartnerHeavyThemeLayout(templateLayout)
+        || !PartnerConfigHelper.shouldApplyExtendedPartnerConfig(context)) {
+      autoTextSizeEnabled = false;
+      return;
+    }
+    // overridden by partner resource
+    if (PartnerConfigHelper.get(context)
+        .isPartnerConfigAvailable(PartnerConfig.CONFIG_HEADER_AUTO_SIZE_ENABLED)) {
+      autoTextSizeEnabled =
+          PartnerConfigHelper.get(context)
+              .getBoolean(
+                  context, PartnerConfig.CONFIG_HEADER_AUTO_SIZE_ENABLED, autoTextSizeEnabled);
+    }
+    if (!autoTextSizeEnabled) {
       return;
     }
 
-    TextView header = templateLayout.findManagedViewById(R.id.suc_layout_title);
-    if (header != null) {
-      HeaderAreaStyler.applyPartnerCustomizationHeaderStyle(header);
+    if (PartnerConfigHelper.get(context)
+        .isPartnerConfigAvailable(PartnerConfig.CONFIG_HEADER_AUTO_SIZE_MAX_TEXT_SIZE)) {
+      headerAutoSizeMaxTextSizeInPx =
+          PartnerConfigHelper.get(context)
+              .getDimension(context, PartnerConfig.CONFIG_HEADER_AUTO_SIZE_MAX_TEXT_SIZE);
     }
-    LinearLayout headerLayout = templateLayout.findManagedViewById(R.id.sud_layout_header);
-    if (headerLayout != null) {
-      HeaderAreaStyler.applyPartnerCustomizationHeaderAreaStyle(headerLayout);
+    if (PartnerConfigHelper.get(context)
+        .isPartnerConfigAvailable(PartnerConfig.CONFIG_HEADER_AUTO_SIZE_MIN_TEXT_SIZE)) {
+      headerAutoSizeMinTextSizeInPx =
+          PartnerConfigHelper.get(context)
+              .getDimension(context, PartnerConfig.CONFIG_HEADER_AUTO_SIZE_MIN_TEXT_SIZE);
+    }
+    if (PartnerConfigHelper.get(context)
+        .isPartnerConfigAvailable(PartnerConfig.CONFIG_HEADER_AUTO_SIZE_LINE_SPACING_EXTRA)) {
+      headerAutoSizeLineExtraSpacingInPx =
+          PartnerConfigHelper.get(context)
+              .getDimension(context, PartnerConfig.CONFIG_HEADER_AUTO_SIZE_LINE_SPACING_EXTRA);
+    }
+    if (PartnerConfigHelper.get(context)
+        .isPartnerConfigAvailable(PartnerConfig.CONFIG_HEADER_AUTO_SIZE_MAX_LINE_OF_MAX_SIZE)) {
+      headerAutoSizeMaxLineOfMaxSize =
+          PartnerConfigHelper.get(context)
+              .getInteger(context, PartnerConfig.CONFIG_HEADER_AUTO_SIZE_MAX_LINE_OF_MAX_SIZE, 0);
+    }
+    if ((headerAutoSizeMaxLineOfMaxSize < 1)
+        || (headerAutoSizeMinTextSizeInPx <= 0)
+        || (headerAutoSizeMaxTextSizeInPx < headerAutoSizeMinTextSizeInPx)) {
+      Log.w("HeaderMixin", "Invalid configs, disable auto text size.");
+      autoTextSizeEnabled = false;
     }
   }
 
-  /** @return The TextView displaying the header. */
+  /**
+   * Applies the partner customizations to the header text (contains text alignment), background,
+   * and margin. If apply heavy theme resource, it will apply all partner customizations, otherwise,
+   * only apply alignment style. In addition, if only enable extended customized flag, the margin
+   * style will be applied.
+   */
+  public void tryApplyPartnerCustomizationStyle() {
+    TextView header = templateLayout.findManagedViewById(R.id.suc_layout_title);
+    boolean partnerLightThemeLayout = PartnerStyleHelper.isPartnerLightThemeLayout(templateLayout);
+    boolean partnerHeavyThemeLayout = PartnerStyleHelper.isPartnerHeavyThemeLayout(templateLayout);
+    if (partnerHeavyThemeLayout) {
+      View headerAreaView = templateLayout.findManagedViewById(R.id.sud_layout_header);
+      HeaderAreaStyler.applyPartnerCustomizationHeaderHeavyStyle(header);
+      HeaderAreaStyler.applyPartnerCustomizationHeaderAreaStyle((ViewGroup) headerAreaView);
+      LayoutStyler.applyPartnerCustomizationExtraPaddingStyle(headerAreaView);
+      // overlay the Auto size config settings
+      updateAutoTextSizeWithPartnerConfig();
+    } else if (partnerLightThemeLayout) {
+      HeaderAreaStyler.applyPartnerCustomizationHeaderLightStyle(header);
+    }
+    if (autoTextSizeEnabled) {
+      // Override the text size setting of the header
+      autoAdjustTextSize(header);
+    }
+  }
+
+  /** Returns the TextView displaying the header. */
   public TextView getTextView() {
     return (TextView) templateLayout.findManagedViewById(R.id.suc_layout_title);
   }
@@ -94,11 +175,15 @@
   /**
    * Sets the header text. This can also be set via the XML attribute {@code app:sucHeaderText}.
    *
-   * @param title The resource ID of the text to be set as header.
+   * @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) {
+      if (autoTextSizeEnabled) {
+        // Override the text size setting of the header
+        autoAdjustTextSize(titleView);
+      }
       titleView.setText(title);
     }
   }
@@ -106,16 +191,58 @@
   /**
    * Sets the header text. This can also be set via the XML attribute {@code app:sucHeaderText}.
    *
-   * @param title The text to be set as header.
+   * @param title The text to be set as header
    */
   public void setText(CharSequence title) {
     final TextView titleView = getTextView();
     if (titleView != null) {
+      if (autoTextSizeEnabled) {
+        // Override the text size setting of the header
+        autoAdjustTextSize(titleView);
+      }
       titleView.setText(title);
     }
   }
 
-  /** @return The current header text. */
+  private void autoAdjustTextSize(TextView titleView) {
+    if (titleView == null) {
+      return;
+    }
+    // preset as the max size
+    titleView.setTextSize(TypedValue.COMPLEX_UNIT_PX, headerAutoSizeMaxTextSizeInPx);
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+      titleView.setLineHeight(
+          Math.round(headerAutoSizeLineExtraSpacingInPx + headerAutoSizeMaxTextSizeInPx));
+    }
+    titleView.setMaxLines(AUTO_SIZE_DEFAULT_MAX_LINES);
+
+    // reset text size if the line count for max text size > headerAutoSizeMaxLineOfMaxTextSize
+    titleView
+        .getViewTreeObserver()
+        .addOnPreDrawListener(
+            new ViewTreeObserver.OnPreDrawListener() {
+              @Override
+              public boolean onPreDraw() {
+                // Remove listener to avoid this called every frame
+                titleView.getViewTreeObserver().removeOnPreDrawListener(this);
+                int lineCount = titleView.getLineCount();
+                if (lineCount > headerAutoSizeMaxLineOfMaxSize) {
+                  // reset text size
+                  titleView.setTextSize(TypedValue.COMPLEX_UNIT_PX, headerAutoSizeMinTextSizeInPx);
+                  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+                    titleView.setLineHeight(
+                        Math.round(
+                            headerAutoSizeLineExtraSpacingInPx + headerAutoSizeMinTextSizeInPx));
+                  }
+                  titleView.invalidate();
+                  return false; // false to skip this frame
+                }
+                return true;
+              }
+            });
+  }
+
+  /** Returns the current header text. */
   public CharSequence getText() {
     final TextView titleView = getTextView();
     return titleView != null ? titleView.getText() : null;
@@ -133,7 +260,7 @@
    * Sets the color of the header text. This can also be set via XML using {@code
    * app:sucHeaderTextColor}.
    *
-   * @param color The text color of the header.
+   * @param color The text color of the header
    */
   public void setTextColor(ColorStateList color) {
     final TextView titleView = getTextView();
@@ -142,7 +269,11 @@
     }
   }
 
-  /** Sets the background color of the header's parent LinearLayout */
+  /**
+   * Sets the background color of the header's parent LinearLayout.
+   *
+   * @param color The background color of the header's parent LinearLayout
+   */
   public void setBackgroundColor(int color) {
     final TextView titleView = getTextView();
     if (titleView != null) {
diff --git a/main/src/com/google/android/setupdesign/template/IconMixin.java b/main/src/com/google/android/setupdesign/template/IconMixin.java
index dca934f..75e624a 100644
--- a/main/src/com/google/android/setupdesign/template/IconMixin.java
+++ b/main/src/com/google/android/setupdesign/template/IconMixin.java
@@ -22,12 +22,13 @@
 import android.graphics.drawable.Drawable;
 import android.os.Build.VERSION;
 import android.os.Build.VERSION_CODES;
-import androidx.annotation.ColorInt;
-import androidx.annotation.DrawableRes;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewGroup.LayoutParams;
+import android.widget.FrameLayout;
 import android.widget.ImageView;
+import androidx.annotation.ColorInt;
+import androidx.annotation.DrawableRes;
 import com.google.android.setupcompat.internal.TemplateLayout;
 import com.google.android.setupcompat.template.Mixin;
 import com.google.android.setupdesign.R;
@@ -44,15 +45,17 @@
 
   private final int originalHeight;
   private final ImageView.ScaleType originalScaleType;
-
+  private final Context context;
   /**
-   * @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.
+   * A {@link com.google.android.setupcompat.template.Mixin} for setting and getting the Icon.
+   *
+   * @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();
+    context = layout.getContext();
 
     ImageView iconView = getView();
     if (iconView != null) {
@@ -65,14 +68,17 @@
     }
 
     final TypedArray a =
-        context.obtainStyledAttributes(attrs, R.styleable.SudIconMixin, defStyleAttr, 0);
+        context.obtainStyledAttributes(
+            attrs, R.styleable.SudIconMixin, defStyleAttr, /* defStyleRes= */ 0);
 
-    final @DrawableRes int icon = a.getResourceId(R.styleable.SudIconMixin_android_icon, 0);
+    final @DrawableRes int icon =
+        a.getResourceId(R.styleable.SudIconMixin_android_icon, /* defValue= */ 0);
     if (icon != 0) {
       setIcon(icon);
     }
 
-    final boolean upscaleIcon = a.getBoolean(R.styleable.SudIconMixin_sudUpscaleIcon, false);
+    final boolean upscaleIcon =
+        a.getBoolean(R.styleable.SudIconMixin_sudUpscaleIcon, /* defValue= */ false);
     setUpscaleIcon(upscaleIcon);
 
     final @ColorInt int iconTint =
@@ -84,38 +90,40 @@
     a.recycle();
   }
 
-  /**
-   * Tries to apply the partner customization to the header icon if the layout of this {@link
-   * IconMixin} is set to apply partner heavy theme resource.
-   */
+  /** Tries to apply the partner customization to the header icon. */
   public void tryApplyPartnerCustomizationStyle() {
-    if (!PartnerStyleHelper.isPartnerHeavyThemeLayout(templateLayout)) {
-      return;
-    }
-
-    ImageView iconImage = templateLayout.findManagedViewById(R.id.sud_layout_icon);
-    if (iconImage != null) {
-      HeaderAreaStyler.applyPartnerCustomizationIconStyle(iconImage);
+    if (PartnerStyleHelper.isPartnerHeavyThemeLayout(templateLayout)) {
+      // apply partner heavy configs
+      HeaderAreaStyler.applyPartnerCustomizationIconStyle(getView(), getContainerView());
+    } else if (PartnerStyleHelper.isPartnerLightThemeLayout(templateLayout)) {
+      // apply partner light configs
+      HeaderAreaStyler.applyPartnerCustomizationIconStyle(getView());
     }
   }
 
   /**
    * Sets the icon on this layout. The icon can also be set in XML using {@code android:icon}.
    *
-   * @param icon A drawable icon.
+   * @param icon A drawable icon
    */
   public void setIcon(Drawable icon) {
     final ImageView iconView = getView();
     if (iconView != null) {
+      if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+        if (icon != null) {
+          icon.applyTheme(context.getTheme());
+        }
+      }
       iconView.setImageDrawable(icon);
       iconView.setVisibility(icon != null ? View.VISIBLE : View.GONE);
+      setIconContainerVisibility(iconView.getVisibility());
     }
   }
 
   /**
    * Sets the icon on this layout. The icon can also be set in XML using {@code android:icon}.
    *
-   * @param icon A drawable icon resource.
+   * @param icon A drawable icon resource
    */
   public void setIcon(@DrawableRes int icon) {
     final ImageView iconView = getView();
@@ -124,6 +132,7 @@
       // 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);
+      setIconContainerVisibility(iconView.getVisibility());
     }
   }
 
@@ -133,7 +142,11 @@
     return iconView != null ? iconView.getDrawable() : null;
   }
 
-  /** Forces the icon view to be as big as desired in the style. */
+  /**
+   * Forces the icon view to be as big as desired in the style.
+   *
+   * @param shouldUpscaleIcon If scale icon view as desired
+   */
   public void setUpscaleIcon(boolean shouldUpscaleIcon) {
     final int maxHeight;
     final ImageView iconView = getView();
@@ -150,7 +163,11 @@
     }
   }
 
-  /** Tints the icon on this layout to the given color. */
+  /**
+   * Tints the icon on this layout to the given color.
+   *
+   * @param tint The color to tint the icon
+   */
   public void setIconTint(@ColorInt int tint) {
     final ImageView iconView = getView();
     if (iconView != null) {
@@ -158,7 +175,11 @@
     }
   }
 
-  /** Sets the content description of the icon view */
+  /**
+   * Sets the content description of the icon view.
+   *
+   * @param description The description char
+   */
   public void setContentDescription(CharSequence description) {
     final ImageView iconView = getView();
     if (iconView != null) {
@@ -172,16 +193,32 @@
     return iconView != null ? iconView.getContentDescription() : null;
   }
 
-  /** Sets the visibiltiy of the icon view */
+  /**
+   * Sets the visibility of the icon view.
+   *
+   * @param visibility Set it visible or not
+   */
   public void setVisibility(int visibility) {
     final ImageView iconView = getView();
     if (iconView != null) {
       iconView.setVisibility(visibility);
+      setIconContainerVisibility(visibility);
     }
   }
 
-  /** @return The ImageView responsible for displaying the icon. */
+  /** Returns the ImageView responsible for displaying the icon. */
   protected ImageView getView() {
     return (ImageView) templateLayout.findManagedViewById(R.id.sud_layout_icon);
   }
+
+  /** Returns the container of the ImageView responsible for displaying the icon. */
+  protected FrameLayout getContainerView() {
+    return (FrameLayout) templateLayout.findManagedViewById(R.id.sud_layout_icon_container);
+  }
+
+  private void setIconContainerVisibility(int visibility) {
+    if (getContainerView() != null) {
+      getContainerView().setVisibility(visibility);
+    }
+  }
 }
diff --git a/main/src/com/google/android/setupdesign/template/IllustrationProgressMixin.java b/main/src/com/google/android/setupdesign/template/IllustrationProgressMixin.java
new file mode 100644
index 0000000..23b9fa0
--- /dev/null
+++ b/main/src/com/google/android/setupdesign/template/IllustrationProgressMixin.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this 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.google.android.setupdesign.template;
+
+import static android.view.View.GONE;
+import static android.view.View.INVISIBLE;
+import static android.view.View.VISIBLE;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.os.Build.VERSION_CODES;
+import android.view.View;
+import android.view.ViewStub;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import androidx.annotation.Nullable;
+import com.google.android.setupcompat.partnerconfig.PartnerConfig;
+import com.google.android.setupcompat.partnerconfig.PartnerConfig.ResourceType;
+import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
+import com.google.android.setupcompat.partnerconfig.ResourceEntry;
+import com.google.android.setupcompat.template.Mixin;
+import com.google.android.setupdesign.GlifLayout;
+import com.google.android.setupdesign.R;
+import com.google.android.setupdesign.view.IllustrationVideoView;
+
+// TODO: remove this mixin after migrate to new GlifLoadingLayout
+/**
+ * A {@link Mixin} for showing a progress illustration.
+ *
+ * @deprecated Will be replaced by GlifLoadingLayout.
+ */
+@TargetApi(VERSION_CODES.ICE_CREAM_SANDWICH)
+@Deprecated
+public class IllustrationProgressMixin implements Mixin {
+
+  private final GlifLayout glifLayout;
+  private final Context context;
+
+  private ProgressConfig progressConfig = ProgressConfig.CONFIG_DEFAULT;
+  private String progressDescription;
+
+  public IllustrationProgressMixin(GlifLayout layout) {
+    this.glifLayout = layout;
+    context = layout.getContext();
+  }
+
+  /**
+   * Sets whether the progress layout is shown. If the progress layout has not been inflated from
+   * the stub, this method will inflate the progress layout.
+   *
+   * @param shown True to show the progress layout, false to set the layout visibiltiy to {@code
+   *     GONE}
+   */
+  public void setShown(boolean shown) {
+    if (!shown) {
+      View view = peekProgressIllustrationLayout();
+      if (view != null) {
+        view.setVisibility(GONE);
+      }
+    } else {
+      View view = getProgressIllustrationLayout();
+      if (view != null) {
+        view.setVisibility(VISIBLE);
+
+        if (progressDescription != null) {
+          TextView descriptionView = view.findViewById(R.id.sud_layout_description);
+          if (descriptionView != null) {
+            descriptionView.setVisibility(VISIBLE);
+            descriptionView.setText(progressDescription);
+          }
+        }
+      }
+    }
+  }
+
+  /** Returns true if the progress layout is currently shown. */
+  public boolean isShown() {
+    View view = peekProgressIllustrationLayout();
+    return view != null && view.getVisibility() == VISIBLE;
+  }
+
+  /**
+   * Sets the type of progress illustration.
+   *
+   * @param config {@link ProgressConfig}
+   */
+  public void setProgressConfig(ProgressConfig config) {
+    this.progressConfig = config;
+
+    // When ViewStub not inflated, do nothing. It will set illustration resource when inflate
+    // layout.
+    if (peekProgressIllustrationLayout() != null) {
+      setIllustrationResource();
+    }
+  }
+
+  /**
+   * Sets the description text of progress
+   * @param description the description text.
+   */
+  public void setProgressIllustrationDescription(String description) {
+    progressDescription = description;
+
+    if (isShown()) {
+      final View progressLayout = getProgressIllustrationLayout();
+      if (progressLayout != null) {
+        TextView descriptionView = progressLayout.findViewById(R.id.sud_layout_description);
+        if (description != null) {
+          descriptionView.setVisibility(VISIBLE);
+          descriptionView.setText(description);
+        } else {
+          descriptionView.setVisibility(INVISIBLE);
+          descriptionView.setText(description);
+        }
+      }
+    }
+  }
+
+  @Nullable private View getProgressIllustrationLayout() {
+    final View progressLayout = peekProgressIllustrationLayout();
+    if (progressLayout == null) {
+      final ViewStub viewStub =
+          glifLayout.findManagedViewById(R.id.sud_layout_illustration_progress_stub);
+
+      if (viewStub != null) {
+        viewStub.inflate();
+        setIllustrationResource();
+      }
+    }
+
+    return peekProgressIllustrationLayout();
+  }
+
+  private void setIllustrationResource() {
+    IllustrationVideoView illustrationVideoView =
+        glifLayout.findManagedViewById(R.id.sud_progress_illustration);
+    ProgressBar progressBar = glifLayout.findManagedViewById(R.id.sud_progress_bar);
+
+    PartnerConfigHelper partnerConfigHelper = PartnerConfigHelper.get(context);
+    ResourceEntry resourceEntry =
+        partnerConfigHelper.getIllustrationResourceEntry(
+            context, progressConfig.getPartnerConfig());
+
+    if (resourceEntry != null) {
+      progressBar.setVisibility(GONE);
+      illustrationVideoView.setVisibility(VISIBLE);
+      illustrationVideoView.setVideoResourceEntry(resourceEntry);
+    } else {
+      progressBar.setVisibility(VISIBLE);
+      illustrationVideoView.setVisibility(GONE);
+    }
+  }
+
+  @Nullable private View peekProgressIllustrationLayout() {
+    return glifLayout.findViewById(R.id.sud_layout_progress_illustration);
+  }
+
+  /** The progress config used to maps to different animation */
+  public enum ProgressConfig {
+    CONFIG_DEFAULT(PartnerConfig.CONFIG_PROGRESS_ILLUSTRATION_DEFAULT),
+    CONFIG_ACCOUNT(PartnerConfig.CONFIG_PROGRESS_ILLUSTRATION_ACCOUNT),
+    CONFIG_CONNECTION(PartnerConfig.CONFIG_PROGRESS_ILLUSTRATION_CONNECTION),
+    CONFIG_UPDATE(PartnerConfig.CONFIG_PROGRESS_ILLUSTRATION_UPDATE);
+
+    private final PartnerConfig config;
+
+    ProgressConfig(PartnerConfig config) {
+      if (config.getResourceType() != ResourceType.ILLUSTRATION) {
+        throw new IllegalArgumentException(
+            "Illustration progress only allow illustration resource");
+      }
+      this.config = config;
+    }
+
+    PartnerConfig getPartnerConfig() {
+      return config;
+    }
+  }
+}
diff --git a/main/src/com/google/android/setupdesign/template/ListMixin.java b/main/src/com/google/android/setupdesign/template/ListMixin.java
index 38928aa..5963a79 100644
--- a/main/src/com/google/android/setupdesign/template/ListMixin.java
+++ b/main/src/com/google/android/setupdesign/template/ListMixin.java
@@ -21,21 +21,24 @@
 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.google.android.setupcompat.internal.TemplateLayout;
+import com.google.android.setupcompat.partnerconfig.PartnerConfig;
+import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
 import com.google.android.setupcompat.template.Mixin;
 import com.google.android.setupdesign.R;
 import com.google.android.setupdesign.items.ItemAdapter;
 import com.google.android.setupdesign.items.ItemGroup;
 import com.google.android.setupdesign.items.ItemInflater;
 import com.google.android.setupdesign.util.DrawableLayoutDirectionHelper;
+import com.google.android.setupdesign.util.PartnerStyleHelper;
 
 /** A {@link Mixin} for interacting with ListViews. */
 public class ListMixin implements Mixin {
@@ -64,18 +67,56 @@
       final ItemGroup inflated = (ItemGroup) new ItemInflater(context).inflate(entries);
       setAdapter(new ItemAdapter(inflated));
     }
-    int dividerInset = a.getDimensionPixelSize(R.styleable.SudListMixin_sudDividerInset, -1);
-    if (dividerInset != -1) {
-      setDividerInset(dividerInset);
-    } else {
-      int dividerInsetStart =
-          a.getDimensionPixelSize(R.styleable.SudListMixin_sudDividerInsetStart, 0);
-      int dividerInsetEnd = a.getDimensionPixelSize(R.styleable.SudListMixin_sudDividerInsetEnd, 0);
-      setDividerInsets(dividerInsetStart, dividerInsetEnd);
+
+    boolean isDividerDisplay = isDividerShown(context);
+    if (isDividerDisplay) {
+      int dividerInset = a.getDimensionPixelSize(R.styleable.SudListMixin_sudDividerInset, -1);
+      if (dividerInset != -1) {
+        setDividerInset(dividerInset);
+      } else {
+        int dividerInsetStart =
+            a.getDimensionPixelSize(R.styleable.SudListMixin_sudDividerInsetStart, 0);
+        int dividerInsetEnd =
+            a.getDimensionPixelSize(R.styleable.SudListMixin_sudDividerInsetEnd, 0);
+
+        if (PartnerStyleHelper.shouldApplyPartnerHeavyThemeResource(templateLayout)) {
+          if (PartnerConfigHelper.get(context)
+              .isPartnerConfigAvailable(PartnerConfig.CONFIG_LAYOUT_MARGIN_START)) {
+            dividerInsetStart =
+                (int)
+                    PartnerConfigHelper.get(context)
+                        .getDimension(context, PartnerConfig.CONFIG_LAYOUT_MARGIN_START);
+          }
+          if (PartnerConfigHelper.get(context)
+              .isPartnerConfigAvailable(PartnerConfig.CONFIG_LAYOUT_MARGIN_END)) {
+            dividerInsetEnd =
+                (int)
+                    PartnerConfigHelper.get(context)
+                        .getDimension(context, PartnerConfig.CONFIG_LAYOUT_MARGIN_END);
+          }
+        }
+        setDividerInsets(dividerInsetStart, dividerInsetEnd);
+      }
     }
     a.recycle();
   }
 
+  private boolean isDividerShown(Context context) {
+    if (PartnerStyleHelper.shouldApplyPartnerResource(templateLayout)) {
+      if (PartnerConfigHelper.get(context)
+          .isPartnerConfigAvailable(PartnerConfig.CONFIG_ITEMS_DIVIDER_SHOWN)) {
+        boolean isDividerDisplayed =
+            PartnerConfigHelper.get(context)
+                .getBoolean(context, PartnerConfig.CONFIG_ITEMS_DIVIDER_SHOWN, true);
+        if (!isDividerDisplayed) {
+          getListView().setDivider(null);
+          return isDividerDisplayed;
+        }
+      }
+    }
+    return true;
+  }
+
   /**
    * @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.
diff --git a/main/src/com/google/android/setupdesign/template/ListViewScrollHandlingDelegate.java b/main/src/com/google/android/setupdesign/template/ListViewScrollHandlingDelegate.java
index 2040c6b..3c7fdfd 100644
--- a/main/src/com/google/android/setupdesign/template/ListViewScrollHandlingDelegate.java
+++ b/main/src/com/google/android/setupdesign/template/ListViewScrollHandlingDelegate.java
@@ -16,12 +16,12 @@
 
 package com.google.android.setupdesign.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.google.android.setupdesign.template.RequireScrollMixin.ScrollHandlingDelegate;
 
 /**
diff --git a/main/src/com/google/android/setupdesign/template/ProgressBarMixin.java b/main/src/com/google/android/setupdesign/template/ProgressBarMixin.java
index 7e55d51..da1b997 100644
--- a/main/src/com/google/android/setupdesign/template/ProgressBarMixin.java
+++ b/main/src/com/google/android/setupdesign/template/ProgressBarMixin.java
@@ -17,12 +17,16 @@
 package com.google.android.setupdesign.template;
 
 import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
 import android.os.Build;
 import android.os.Build.VERSION_CODES;
-import androidx.annotation.Nullable;
+import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewStub;
 import android.widget.ProgressBar;
+import androidx.annotation.AttrRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import com.google.android.setupcompat.internal.TemplateLayout;
 import com.google.android.setupcompat.template.Mixin;
 import com.google.android.setupdesign.R;
@@ -31,17 +35,64 @@
 public class ProgressBarMixin implements Mixin {
 
   private final TemplateLayout templateLayout;
+  private final boolean useBottomProgressBar;
 
   @Nullable private ColorStateList color;
 
   /** @param layout The layout this mixin belongs to. */
-  public ProgressBarMixin(TemplateLayout layout) {
+  public ProgressBarMixin(@NonNull TemplateLayout layout) {
+    this(layout, null, 0);
+  }
+
+  /**
+   * Constructor that allow using the bottom progress bar.
+   *
+   * @param layout The layout this mixin belongs to.
+   * @param useBottomProgressBar Whether to use bottom progress
+   */
+  public ProgressBarMixin(@NonNull TemplateLayout layout, boolean useBottomProgressBar) {
     templateLayout = layout;
+    this.useBottomProgressBar = useBottomProgressBar;
+  }
+
+  /**
+   * Constructor that provide styled attribute information in this Context's theme.
+   *
+   * @param layout The {@link TemplateLayout} containing this mixin.
+   * @param attrs XML attributes given to the layout.
+   * @param defStyleAttr The default style attribute as given to the constructor of the layout.
+   */
+  public ProgressBarMixin(
+      @NonNull TemplateLayout layout, AttributeSet attrs, @AttrRes int defStyleAttr) {
+    templateLayout = layout;
+
+    boolean useBottomProgressBar = false;
+    if (attrs != null) {
+      final TypedArray a =
+          layout
+              .getContext()
+              .obtainStyledAttributes(attrs, R.styleable.SudProgressBarMixin, defStyleAttr, 0);
+
+      if (a.hasValue(R.styleable.SudProgressBarMixin_sudUseBottomProgressBar)) {
+        // Set whether we use bottom progress bar or not
+        useBottomProgressBar =
+            a.getBoolean(R.styleable.SudProgressBarMixin_sudUseBottomProgressBar, false);
+      }
+
+      a.recycle();
+
+      // To avoid bottom progressbar bouncing, change the view state from GONE to INVISIBLE
+      setShown(false);
+    }
+
+    this.useBottomProgressBar = useBottomProgressBar;
   }
 
   /** @return True if the progress bar is currently shown. */
   public boolean isShown() {
-    final View progressBar = templateLayout.findManagedViewById(R.id.sud_layout_progress);
+    final View progressBar =
+        templateLayout.findManagedViewById(
+            useBottomProgressBar ? R.id.sud_glif_progress_bar : R.id.sud_layout_progress);
     return progressBar != null && progressBar.getVisibility() == View.VISIBLE;
   }
 
@@ -60,7 +111,7 @@
     } else {
       View progressBar = peekProgressBar();
       if (progressBar != null) {
-        progressBar.setVisibility(View.GONE);
+        progressBar.setVisibility(useBottomProgressBar ? View.INVISIBLE : View.GONE);
       }
     }
   }
@@ -73,8 +124,8 @@
    *     progress bar built-in.
    */
   private ProgressBar getProgressBar() {
-    final View progressBar = peekProgressBar();
-    if (progressBar == null) {
+    final View progressBarView = peekProgressBar();
+    if (progressBarView == null && !useBottomProgressBar) {
       final ViewStub progressBarStub =
           (ViewStub) templateLayout.findManagedViewById(R.id.sud_layout_progress_stub);
       if (progressBarStub != null) {
@@ -94,7 +145,9 @@
    *     or if the template does not contain a progress bar.
    */
   public ProgressBar peekProgressBar() {
-    return (ProgressBar) templateLayout.findManagedViewById(R.id.sud_layout_progress);
+    return (ProgressBar)
+        templateLayout.findManagedViewById(
+            useBottomProgressBar ? R.id.sud_glif_progress_bar : R.id.sud_layout_progress);
   }
 
   /** Sets the color of the indeterminate progress bar. This method is a no-op on SDK < 21. */
diff --git a/main/src/com/google/android/setupdesign/template/RecyclerMixin.java b/main/src/com/google/android/setupdesign/template/RecyclerMixin.java
index 11e9144..b327060 100644
--- a/main/src/com/google/android/setupdesign/template/RecyclerMixin.java
+++ b/main/src/com/google/android/setupdesign/template/RecyclerMixin.java
@@ -21,15 +21,17 @@
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.os.Build.VERSION_CODES;
-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 androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import com.google.android.setupcompat.internal.TemplateLayout;
+import com.google.android.setupcompat.partnerconfig.PartnerConfig;
+import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
 import com.google.android.setupcompat.template.Mixin;
 import com.google.android.setupdesign.DividerItemDecoration;
 import com.google.android.setupdesign.GlifLayout;
@@ -38,6 +40,7 @@
 import com.google.android.setupdesign.items.ItemInflater;
 import com.google.android.setupdesign.items.RecyclerItemAdapter;
 import com.google.android.setupdesign.util.DrawableLayoutDirectionHelper;
+import com.google.android.setupdesign.util.PartnerStyleHelper;
 import com.google.android.setupdesign.view.HeaderRecyclerView;
 import com.google.android.setupdesign.view.HeaderRecyclerView.HeaderAdapter;
 
@@ -64,6 +67,7 @@
 
   private int dividerInsetStart;
   private int dividerInsetEnd;
+  private boolean isDividerDisplay = true;
 
   /**
    * Creates the RecyclerMixin. Unlike typical mixins which are created in the constructor, this
@@ -86,7 +90,22 @@
       header = ((HeaderRecyclerView) recyclerView).getHeader();
     }
 
-    this.recyclerView.addItemDecoration(dividerDecoration);
+    isDividerDisplay = isShowItemsDivider();
+    if (isDividerDisplay) {
+      this.recyclerView.addItemDecoration(dividerDecoration);
+    }
+  }
+
+  private boolean isShowItemsDivider() {
+    // Skips to add item decoration if config flag is false.
+    if (PartnerStyleHelper.shouldApplyPartnerResource(templateLayout)) {
+      if (PartnerConfigHelper.get(recyclerView.getContext())
+          .isPartnerConfigAvailable(PartnerConfig.CONFIG_ITEMS_DIVIDER_SHOWN)) {
+        return PartnerConfigHelper.get(recyclerView.getContext())
+            .getBoolean(recyclerView.getContext(), PartnerConfig.CONFIG_ITEMS_DIVIDER_SHOWN, true);
+      }
+    }
+    return true;
   }
 
   /**
@@ -108,16 +127,24 @@
       final ItemHierarchy inflated = new ItemInflater(context).inflate(entries);
 
       boolean applyPartnerHeavyThemeResource = false;
+      boolean useFullDynamicColor = false;
       if (templateLayout instanceof GlifLayout) {
         applyPartnerHeavyThemeResource =
             ((GlifLayout) templateLayout).shouldApplyPartnerHeavyThemeResource();
+        useFullDynamicColor = ((GlifLayout) templateLayout).useFullDynamicColor();
       }
 
       final RecyclerItemAdapter adapter =
-          new RecyclerItemAdapter(inflated, applyPartnerHeavyThemeResource);
+          new RecyclerItemAdapter(inflated, applyPartnerHeavyThemeResource, useFullDynamicColor);
       adapter.setHasStableIds(a.getBoolean(R.styleable.SudRecyclerMixin_sudHasStableIds, false));
       setAdapter(adapter);
     }
+
+    if (!isDividerDisplay) {
+      a.recycle();
+      return;
+    }
+
     int dividerInset = a.getDimensionPixelSize(R.styleable.SudRecyclerMixin_sudDividerInset, -1);
     if (dividerInset != -1) {
       setDividerInset(dividerInset);
@@ -126,6 +153,23 @@
           a.getDimensionPixelSize(R.styleable.SudRecyclerMixin_sudDividerInsetStart, 0);
       int dividerInsetEnd =
           a.getDimensionPixelSize(R.styleable.SudRecyclerMixin_sudDividerInsetEnd, 0);
+
+      if (PartnerStyleHelper.shouldApplyPartnerHeavyThemeResource(templateLayout)) {
+        if (PartnerConfigHelper.get(context)
+            .isPartnerConfigAvailable(PartnerConfig.CONFIG_LAYOUT_MARGIN_START)) {
+          dividerInsetStart =
+              (int)
+                  PartnerConfigHelper.get(context)
+                      .getDimension(context, PartnerConfig.CONFIG_LAYOUT_MARGIN_START);
+        }
+        if (PartnerConfigHelper.get(context)
+            .isPartnerConfigAvailable(PartnerConfig.CONFIG_LAYOUT_MARGIN_END)) {
+          dividerInsetEnd =
+              (int)
+                  PartnerConfigHelper.get(context)
+                      .getDimension(context, PartnerConfig.CONFIG_LAYOUT_MARGIN_END);
+        }
+      }
       setDividerInsets(dividerInsetStart, dividerInsetEnd);
     }
 
@@ -172,7 +216,7 @@
    * @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 :(
+    // RecyclerView.getAdapter returns raw type :(
     final RecyclerView.Adapter<? extends ViewHolder> adapter = recyclerView.getAdapter();
     if (adapter instanceof HeaderAdapter) {
       return ((HeaderAdapter<? extends ViewHolder>) adapter).getWrappedAdapter();
diff --git a/main/src/com/google/android/setupdesign/template/RecyclerViewScrollHandlingDelegate.java b/main/src/com/google/android/setupdesign/template/RecyclerViewScrollHandlingDelegate.java
index 71094cf..b7a8d4a 100644
--- a/main/src/com/google/android/setupdesign/template/RecyclerViewScrollHandlingDelegate.java
+++ b/main/src/com/google/android/setupdesign/template/RecyclerViewScrollHandlingDelegate.java
@@ -16,10 +16,10 @@
 
 package com.google.android.setupdesign.template;
 
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import androidx.recyclerview.widget.RecyclerView;
 import android.util.Log;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import com.google.android.setupdesign.template.RequireScrollMixin.ScrollHandlingDelegate;
 
 /**
diff --git a/main/src/com/google/android/setupdesign/template/RequireScrollMixin.java b/main/src/com/google/android/setupdesign/template/RequireScrollMixin.java
index f1c7cbe..62c503c 100644
--- a/main/src/com/google/android/setupdesign/template/RequireScrollMixin.java
+++ b/main/src/com/google/android/setupdesign/template/RequireScrollMixin.java
@@ -19,12 +19,12 @@
 import android.content.Context;
 import android.os.Handler;
 import android.os.Looper;
-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 androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
 import com.google.android.setupcompat.internal.TemplateLayout;
 import com.google.android.setupcompat.template.FooterButton;
 import com.google.android.setupcompat.template.Mixin;
diff --git a/main/src/com/google/android/setupdesign/template/ScrollViewScrollHandlingDelegate.java b/main/src/com/google/android/setupdesign/template/ScrollViewScrollHandlingDelegate.java
index 0fbe5ce..852656f 100644
--- a/main/src/com/google/android/setupdesign/template/ScrollViewScrollHandlingDelegate.java
+++ b/main/src/com/google/android/setupdesign/template/ScrollViewScrollHandlingDelegate.java
@@ -16,10 +16,10 @@
 
 package com.google.android.setupdesign.template;
 
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 import android.util.Log;
 import android.widget.ScrollView;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import com.google.android.setupdesign.template.RequireScrollMixin.ScrollHandlingDelegate;
 import com.google.android.setupdesign.view.BottomScrollView;
 import com.google.android.setupdesign.view.BottomScrollView.BottomScrollListener;
diff --git a/main/src/com/google/android/setupdesign/transition/TransitionHelper.java b/main/src/com/google/android/setupdesign/transition/TransitionHelper.java
new file mode 100644
index 0000000..ec2c480
--- /dev/null
+++ b/main/src/com/google/android/setupdesign/transition/TransitionHelper.java
@@ -0,0 +1,669 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this 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.google.android.setupdesign.transition;
+
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.app.ActivityOptions;
+import android.app.Fragment;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.TypedArray;
+import android.os.Build;
+import android.os.Build.VERSION_CODES;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.util.Log;
+import android.view.Window;
+import androidx.annotation.IntDef;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import com.google.android.material.transition.platform.MaterialSharedAxis;
+import com.google.android.setupcompat.partnerconfig.PartnerConfig;
+import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
+import com.google.android.setupcompat.util.BuildCompatUtils;
+import com.google.android.setupdesign.R;
+import com.google.android.setupdesign.util.ThemeHelper;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** Helper class for apply the transition to the pages which uses platform version. */
+public class TransitionHelper {
+
+  private static final String TAG = "TransitionHelper";
+
+  /*
+   * In Setup Wizard, all Just-a-sec style screens (i.e. screens that has an indeterminate
+   * progress bar and automatically finishes itself), should do a cross-fade when entering or
+   * exiting the screen. For all other screens, the transition should be a slide-in-from-right
+   * or customized.
+   *
+   * We use two different ways to override the transitions. The first is calling
+   * overridePendingTransition in code, and the second is using windowAnimationStyle in the theme.
+   * They have the following priority when framework is figuring out what transition to use:
+   * 1. overridePendingTransition, entering activity (highest priority)
+   * 2. overridePendingTransition, exiting activity
+   * 3. windowAnimationStyle, entering activity
+   * 4. windowAnimationStyle, exiting activity
+   *
+   * This is why, in general, overridePendingTransition is used to specify the fade animation,
+   * while windowAnimationStyle is used to specify the slide transition. This way fade animation
+   * will take priority over the slide animation.
+   *
+   * Below are types of animation when switching activities. These are return values for
+   * {@link #getTransition()}. Each of these values represents 4 animations: (backward exit,
+   * backward enter, forward exit, forward enter).
+   *
+   * We override the transition in the following flow
+   * +--------------+-------------------------+--------------------------+
+   * |              | going forward           | going backward           |
+   * +--------------+-------------------------+--------------------------+
+   * | old activity | startActivity(OnResult) | onActivityResult         |
+   * +--------------+-------------------------+--------------------------+
+   * | new activity | onStart                 | finish (RESULT_CANCELED) |
+   * +--------------+-------------------------+--------------------------+
+   */
+
+  /** The constant of transition type. */
+  @Retention(RetentionPolicy.SOURCE)
+  @IntDef({
+    TRANSITION_NONE,
+    TRANSITION_NO_OVERRIDE,
+    TRANSITION_FRAMEWORK_DEFAULT,
+    TRANSITION_SLIDE,
+    TRANSITION_FADE,
+    TRANSITION_FRAMEWORK_DEFAULT_PRE_P,
+    TRANSITION_CAPTIVE,
+  })
+  public @interface TransitionType {}
+
+  /** No transition, as in overridePendingTransition(0, 0). */
+  public static final int TRANSITION_NONE = -1;
+
+  /**
+   * No override. If this is specified as the transition, overridePendingTransition will not be
+   * called.
+   */
+  public static final int TRANSITION_NO_OVERRIDE = 0;
+
+  /**
+   * Override the transition to the framework default. This values are read from {@link
+   * android.R.style#Animation_Activity}.
+   */
+  public static final int TRANSITION_FRAMEWORK_DEFAULT = 1;
+
+  /** Override the transition to a slide-in-from-right (or from-left for RTL locales). */
+  public static final int TRANSITION_SLIDE = 2;
+
+  /**
+   * Override the transition to fade in the new activity, while keeping the old activity. Setup
+   * wizard does not use cross fade to avoid the bright-dim-bright effect when transitioning between
+   * two screens that look similar.
+   */
+  public static final int TRANSITION_FADE = 3;
+
+  /** Override the transition to the old framework default pre P. */
+  public static final int TRANSITION_FRAMEWORK_DEFAULT_PRE_P = 4;
+
+  /**
+   * Override the transition to the specific transition and the transition type will depends on the
+   * partner resource.
+   */
+  // TODO: Add new partner resource to determine which transition type would be apply.
+  public static final int TRANSITION_CAPTIVE = 5;
+
+  /**
+   * No override. If this is specified as the transition, the enter/exit transition of the window
+   * will not be set and keep original behavior.
+   */
+  public static final int CONFIG_TRANSITION_NONE = 0;
+
+  /** Override the transition to the specific type that will depend on the partner resource. */
+  public static final int CONFIG_TRANSITION_SHARED_X_AXIS = 1;
+
+  /**
+   * Passed in an intent as EXTRA_ACTIVITY_OPTIONS. This is the {@link ActivityOptions} of the
+   * transition used in {@link Activity#startActivity} or {@link Activity#startActivityForResult}.
+   */
+  public static final String EXTRA_ACTIVITY_OPTIONS = "sud:activity_options";
+
+  /** A flag to avoid the {@link Activity#finish} been called more than once. */
+  @VisibleForTesting static boolean isFinishCalled = false;
+
+  /** A flag to avoid the {@link Activity#startActivity} called more than once. */
+  @VisibleForTesting static boolean isStartActivity = false;
+
+  /** A flag to avoid the {@link Activity#startActivityForResult} called more than once. */
+  @VisibleForTesting static boolean isStartActivityForResult = false;
+
+  private TransitionHelper() {}
+
+  /**
+   * Apply the transition for going forward which is decided by partner resource {@link
+   * PartnerConfig#CONFIG_TRANSITION_TYPE} and system property {@code setupwizard.transition_type}.
+   * The default transition that will be applied is {@link #TRANSITION_SLIDE}. The timing to apply
+   * the transition is going forward from the previous activity to this, or going forward from this
+   * activity to the next.
+   *
+   * <p>For example, in the flow below, the forward transitions will be applied to all arrows
+   * pointing to the right. Previous screen --> This screen --> Next screen
+   */
+  @TargetApi(VERSION_CODES.LOLLIPOP)
+  public static void applyForwardTransition(Activity activity) {
+    applyForwardTransition(activity, TRANSITION_CAPTIVE);
+  }
+
+  /**
+   * Apply the transition for going forward which is decided by partner resource {@link
+   * PartnerConfig#CONFIG_TRANSITION_TYPE} and system property {@code setupwizard.transition_type}.
+   * The default transition that will be applied is {@link #CONFIG_TRANSITION_NONE}. The timing to
+   * apply the transition is going forward from the previous {@link Fragment} to this, or going
+   * forward from this {@link Fragment} to the next.
+   */
+  @TargetApi(VERSION_CODES.M)
+  public static void applyForwardTransition(Fragment fragment) {
+    if (Build.VERSION.SDK_INT >= VERSION_CODES.M) {
+      if (getConfigTransitionType(fragment.getContext()) == CONFIG_TRANSITION_SHARED_X_AXIS) {
+        MaterialSharedAxis exitTransition =
+            new MaterialSharedAxis(MaterialSharedAxis.X, /* forward= */ true);
+        fragment.setExitTransition(exitTransition);
+
+        MaterialSharedAxis enterTransition =
+            new MaterialSharedAxis(MaterialSharedAxis.X, /* forward= */ true);
+        fragment.setEnterTransition(enterTransition);
+      } else {
+        Log.w(TAG, "Not apply the forward transition for platform fragment.");
+      }
+    } else {
+      Log.w(
+          TAG,
+          "Not apply the forward transition for platform fragment. The API is supported from"
+              + " Android Sdk "
+              + VERSION_CODES.M);
+    }
+  }
+
+  /**
+   * Apply the transition for going forward. This is applied when going forward from the previous
+   * activity to this, or going forward from this activity to the next.
+   *
+   * <p>For example, in the flow below, the forward transitions will be applied to all arrows
+   * pointing to the right. Previous screen --> This screen --> Next screen
+   */
+  @TargetApi(VERSION_CODES.LOLLIPOP)
+  public static void applyForwardTransition(Activity activity, @TransitionType int transitionId) {
+    if (transitionId == TRANSITION_SLIDE) {
+      activity.overridePendingTransition(R.anim.sud_slide_next_in, R.anim.sud_slide_next_out);
+    } else if (transitionId == TRANSITION_FADE) {
+      activity.overridePendingTransition(android.R.anim.fade_in, R.anim.sud_stay);
+    } else if (transitionId == TRANSITION_FRAMEWORK_DEFAULT) {
+      TypedArray typedArray =
+          activity.obtainStyledAttributes(
+              android.R.style.Animation_Activity,
+              new int[] {
+                android.R.attr.activityOpenEnterAnimation, android.R.attr.activityOpenExitAnimation
+              });
+      activity.overridePendingTransition(
+          typedArray.getResourceId(/* index= */ 0, /* defValue= */ 0),
+          typedArray.getResourceId(/* index= */ 1, /* defValue= */ 0));
+      typedArray.recycle();
+    } else if (transitionId == TRANSITION_FRAMEWORK_DEFAULT_PRE_P) {
+      activity.overridePendingTransition(
+          R.anim.sud_pre_p_activity_open_enter, R.anim.sud_pre_p_activity_open_exit);
+    } else if (transitionId == TRANSITION_NONE) {
+      // For TRANSITION_NONE, turn off the transition
+      activity.overridePendingTransition(/* enterAnim= */ 0, /* exitAnim= */ 0);
+    } else if (transitionId == TRANSITION_CAPTIVE) {
+      if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+        // 1. Do not change the transition behavior by default
+        // 2. If the flag present, apply the transition from transition type
+        if (getConfigTransitionType(activity) == CONFIG_TRANSITION_SHARED_X_AXIS) {
+          Window window = activity.getWindow();
+          if (window != null) {
+            MaterialSharedAxis exitTransition =
+                new MaterialSharedAxis(MaterialSharedAxis.X, /* forward= */ true);
+            window.setExitTransition(exitTransition);
+
+            window.setAllowEnterTransitionOverlap(true);
+
+            MaterialSharedAxis enterTransition =
+                new MaterialSharedAxis(MaterialSharedAxis.X, /* forward= */ true);
+            window.setEnterTransition(enterTransition);
+          } else {
+            Log.w(TAG, "applyForwardTransition: Invalid window=" + window);
+          }
+        }
+      } else {
+        Log.w(TAG, "This API is supported from Android Sdk " + VERSION_CODES.LOLLIPOP);
+      }
+    }
+    // For TRANSITION_NO_OVERRIDE or other values, do not override the transition
+  }
+
+  /**
+   * Apply the transition for going backward which is decided by partner resource {@link
+   * PartnerConfig#CONFIG_TRANSITION_TYPE} and system property {@code setupwizard.transition_type}.
+   * The default transition that will be applied is {@link #TRANSITION_SLIDE}. The timing to apply
+   * the transition is going backward from the next activity to this, or going backward from this
+   * activity to the previous.
+   *
+   * <p>For example, in the flow below, the backward transitions will be applied to all arrows
+   * pointing to the left. Previous screen <-- This screen <-- Next screen
+   */
+  @TargetApi(VERSION_CODES.LOLLIPOP)
+  public static void applyBackwardTransition(Activity activity) {
+    applyBackwardTransition(activity, TRANSITION_CAPTIVE);
+  }
+
+  /**
+   * Apply the transition for going backward which is decided by partner resource {@link
+   * PartnerConfig#CONFIG_TRANSITION_TYPE} and system property {@code setupwizard.transition_type}.
+   * The default transition that will be applied is {@link #CONFIG_TRANSITION_NONE}. The timing to
+   * apply the transition is going backward from the next {@link Fragment} to this, or going
+   * backward from this {@link Fragment} to the previous.
+   */
+  @TargetApi(VERSION_CODES.M)
+  public static void applyBackwardTransition(Fragment fragment) {
+    if (Build.VERSION.SDK_INT >= VERSION_CODES.M) {
+      if (getConfigTransitionType(fragment.getContext()) == CONFIG_TRANSITION_SHARED_X_AXIS) {
+        MaterialSharedAxis returnTransition =
+            new MaterialSharedAxis(MaterialSharedAxis.X, /* forward= */ false);
+        fragment.setReturnTransition(returnTransition);
+
+        MaterialSharedAxis reenterTransition =
+            new MaterialSharedAxis(MaterialSharedAxis.X, /* forward= */ false);
+        fragment.setReenterTransition(reenterTransition);
+      } else {
+        Log.w(TAG, "Not apply the backward transition for platform fragment.");
+      }
+    } else {
+      Log.w(
+          TAG,
+          "Not apply the backward transition for platform fragment. The API is supported from"
+              + " Android Sdk "
+              + VERSION_CODES.M);
+    }
+  }
+
+  /**
+   * Apply the transition for going backward. This is applied when going backward from the next
+   * activity to this, or going backward from this activity to the previous.
+   *
+   * <p>For example, in the flow below, the backward transitions will be applied to all arrows
+   * pointing to the left. Previous screen <-- This screen <-- Next screen
+   */
+  @TargetApi(VERSION_CODES.LOLLIPOP)
+  public static void applyBackwardTransition(Activity activity, @TransitionType int transitionId) {
+    if (transitionId == TRANSITION_SLIDE) {
+      activity.overridePendingTransition(R.anim.sud_slide_back_in, R.anim.sud_slide_back_out);
+    } else if (transitionId == TRANSITION_FADE) {
+      activity.overridePendingTransition(R.anim.sud_stay, android.R.anim.fade_out);
+    } else if (transitionId == TRANSITION_FRAMEWORK_DEFAULT) {
+      TypedArray typedArray =
+          activity.obtainStyledAttributes(
+              android.R.style.Animation_Activity,
+              new int[] {
+                android.R.attr.activityCloseEnterAnimation,
+                android.R.attr.activityCloseExitAnimation
+              });
+      activity.overridePendingTransition(
+          typedArray.getResourceId(/* index= */ 0, /* defValue= */ 0),
+          typedArray.getResourceId(/* index= */ 1, /* defValue= */ 0));
+      typedArray.recycle();
+    } else if (transitionId == TRANSITION_FRAMEWORK_DEFAULT_PRE_P) {
+      activity.overridePendingTransition(
+          R.anim.sud_pre_p_activity_close_enter, R.anim.sud_pre_p_activity_close_exit);
+    } else if (transitionId == TRANSITION_NONE) {
+      // For TRANSITION_NONE, turn off the transition
+      activity.overridePendingTransition(/* enterAnim= */ 0, /* exitAnim= */ 0);
+    } else if (transitionId == TRANSITION_CAPTIVE) {
+      if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+        // 1. Do not change the transition behavior by default
+        // 2. If the flag present, apply the transition from transition type
+        if (getConfigTransitionType(activity) == CONFIG_TRANSITION_SHARED_X_AXIS) {
+          Window window = activity.getWindow();
+          if (window != null) {
+            MaterialSharedAxis reenterTransition =
+                new MaterialSharedAxis(MaterialSharedAxis.X, /* forward= */ false);
+            window.setReenterTransition(reenterTransition);
+
+            MaterialSharedAxis returnTransition =
+                new MaterialSharedAxis(MaterialSharedAxis.X, /* forward= */ false);
+            window.setReturnTransition(returnTransition);
+          } else {
+            Log.w(TAG, "applyBackwardTransition: Invalid window=" + window);
+          }
+        }
+      } else {
+        Log.w(TAG, "This API is supported from Android Sdk " + VERSION_CODES.LOLLIPOP);
+      }
+    }
+    // For TRANSITION_NO_OVERRIDE or other values, do not override the transition
+  }
+
+  /**
+   * A wrapper method, create an {@link android.app.ActivityOptions} to transition between
+   * activities as the {@link ActivityOptions} parameter of {@link Activity#startActivity}.
+   *
+   * @throws IllegalArgumentException is thrown when {@code activity} or {@code intent} is null.
+   * @throws android.content.ActivityNotFoundException if there was no {@link Activity} found to run
+   *     the given Intent.
+   */
+  public static void startActivityWithTransition(Activity activity, Intent intent) {
+    startActivityWithTransition(activity, intent, /* overrideActivityOptions= */ null);
+  }
+
+  /**
+   * A wrapper method, create an {@link android.app.ActivityOptions} to transition between
+   * activities as the {@link ActivityOptions} parameter of {@link Activity#startActivity}.
+   *
+   * @throws IllegalArgumentException is thrown when {@code activity} or {@code intent} is null.
+   * @throws android.content.ActivityNotFoundException if there was no {@link Activity} found to run
+   *     the given Intent.
+   */
+  public static void startActivityWithTransition(
+      Activity activity, Intent intent, Bundle overrideActivityOptions) {
+    if (activity == null) {
+      throw new IllegalArgumentException("Invalid activity=" + activity);
+    }
+
+    if (intent == null) {
+      throw new IllegalArgumentException("Invalid intent=" + intent);
+    }
+
+    if ((intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) == Intent.FLAG_ACTIVITY_NEW_TASK) {
+      Log.e(
+          TAG,
+          "The transition won't take effect since the WindowManager does not allow override new"
+              + " task transitions");
+    }
+
+    if (!isStartActivity) {
+      isStartActivity = true;
+      if (getConfigTransitionType(activity) == CONFIG_TRANSITION_SHARED_X_AXIS) {
+        if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+          if (activity.getWindow() != null
+              && !activity.getWindow().hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)) {
+            Log.w(
+                TAG,
+                "The transition won't take effect due to NO FEATURE_ACTIVITY_TRANSITIONS feature");
+          }
+
+          Bundle bundleActivityOptions;
+          if (overrideActivityOptions != null) {
+            bundleActivityOptions = overrideActivityOptions;
+          } else {
+            bundleActivityOptions = makeActivityOptions(activity, intent);
+          }
+          intent.putExtra(EXTRA_ACTIVITY_OPTIONS, (Parcelable) bundleActivityOptions);
+          activity.startActivity(intent, bundleActivityOptions);
+        } else {
+          Log.w(
+              TAG,
+              "Fallback to using startActivity due to the"
+                  + " ActivityOptions#makeSceneTransitionAnimation is supported from Android Sdk "
+                  + VERSION_CODES.LOLLIPOP);
+          startActivityWithTransitionInternal(activity, intent, overrideActivityOptions);
+        }
+      } else {
+        startActivityWithTransitionInternal(activity, intent, overrideActivityOptions);
+      }
+    }
+    isStartActivity = false;
+  }
+
+  private static void startActivityWithTransitionInternal(
+      Activity activity, Intent intent, Bundle overrideActivityOptions) {
+    try {
+      if (Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
+        if (getConfigTransitionType(activity) == CONFIG_TRANSITION_SHARED_X_AXIS
+            && overrideActivityOptions != null) {
+          activity.startActivity(intent, overrideActivityOptions);
+        } else {
+          activity.startActivity(intent);
+        }
+      } else {
+        Log.w(
+            TAG,
+            "Fallback to using startActivity(Intent) due to the startActivity(Intent, Bundle) is"
+                + " supported from Android Sdk "
+                + VERSION_CODES.JELLY_BEAN);
+        activity.startActivity(intent);
+      }
+    } catch (ActivityNotFoundException e) {
+      Log.w(TAG, "Activity not found when startActivity with transition.");
+      isStartActivity = false;
+      throw e;
+    }
+  }
+
+  public static void startActivityForResultWithTransition(
+      Activity activity, Intent intent, int requestCode) {
+    startActivityForResultWithTransition(
+        activity, intent, requestCode, /* overrideActivityOptions= */ null);
+  }
+
+  /**
+   * A wrapper method, create an {@link android.app.ActivityOptions} to transition between
+   * activities as the {@code activityOptions} parameter of {@link Activity#startActivityForResult}.
+   *
+   * @throws IllegalArgumentException is thrown when {@code activity} or {@code intent} is null.
+   * @throws android.content.ActivityNotFoundException if there was no {@link Activity} found to run
+   *     the given Intent.
+   */
+  public static void startActivityForResultWithTransition(
+      Activity activity, Intent intent, int requestCode, Bundle overrideActivityOptions) {
+    if (activity == null) {
+      throw new IllegalArgumentException("Invalid activity=" + activity);
+    }
+
+    if (intent == null) {
+      throw new IllegalArgumentException("Invalid intent=" + intent);
+    }
+
+    if ((intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) == Intent.FLAG_ACTIVITY_NEW_TASK) {
+      Log.e(
+          TAG,
+          "The transition won't take effect since the WindowManager does not allow override new"
+              + " task transitions");
+    }
+
+    if (!isStartActivityForResult) {
+      isStartActivityForResult = true;
+      if (getConfigTransitionType(activity) == CONFIG_TRANSITION_SHARED_X_AXIS) {
+        if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+          if (activity.getWindow() != null
+              && !activity.getWindow().hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)) {
+            Log.w(
+                TAG,
+                "The transition won't take effect due to NO FEATURE_ACTIVITY_TRANSITIONS feature");
+          }
+
+          Bundle bundleActivityOptions;
+          if (overrideActivityOptions != null) {
+            bundleActivityOptions = overrideActivityOptions;
+          } else {
+            bundleActivityOptions = makeActivityOptions(activity, intent);
+          }
+          intent.putExtra(EXTRA_ACTIVITY_OPTIONS, (Parcelable) bundleActivityOptions);
+          activity.startActivityForResult(intent, requestCode, bundleActivityOptions);
+        } else {
+          Log.w(
+              TAG,
+              "Fallback to using startActivityForResult API due to the"
+                  + " ActivityOptions#makeSceneTransitionAnimation is supported from Android Sdk "
+                  + VERSION_CODES.LOLLIPOP);
+          startActivityForResultWithTransitionInternal(
+              activity, intent, requestCode, overrideActivityOptions);
+        }
+      } else {
+        startActivityForResultWithTransitionInternal(
+            activity, intent, requestCode, overrideActivityOptions);
+      }
+      isStartActivityForResult = false;
+    }
+  }
+
+  private static void startActivityForResultWithTransitionInternal(
+      Activity activity, Intent intent, int requestCode, Bundle overrideActivityOptions) {
+    try {
+      if (Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN) {
+        if (getConfigTransitionType(activity) == CONFIG_TRANSITION_SHARED_X_AXIS
+            && overrideActivityOptions != null) {
+          activity.startActivityForResult(intent, requestCode, overrideActivityOptions);
+        } else {
+          activity.startActivityForResult(intent, requestCode);
+        }
+      } else {
+        Log.w(
+            TAG,
+            "Fallback to using startActivityForResult(Intent) due to the"
+                + " startActivityForResult(Intent,int) is supported from Android Sdk "
+                + VERSION_CODES.JELLY_BEAN);
+        activity.startActivityForResult(intent, requestCode);
+      }
+    } catch (ActivityNotFoundException e) {
+      Log.w(TAG, "Activity not found when startActivityForResult with transition.");
+      isStartActivityForResult = false;
+      throw e;
+    }
+  }
+
+  /**
+   * A wrapper method, calling {@link Activity#finishAfterTransition()} to trigger exit transition
+   * when running in Android S and the transition type {link #CONFIG_TRANSITION_SHARED_X_AXIS}.
+   *
+   * @throws IllegalArgumentException is thrown when {@code activity} is null.
+   */
+  public static void finishActivity(Activity activity) {
+    if (activity == null) {
+      throw new IllegalArgumentException("Invalid activity=" + activity);
+    }
+
+    // Avoids finish been called more than once.
+    if (!isFinishCalled) {
+      isFinishCalled = true;
+      if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP
+          && getConfigTransitionType(activity) == CONFIG_TRANSITION_SHARED_X_AXIS) {
+        activity.finishAfterTransition();
+      } else {
+        Log.w(
+            TAG,
+            "Fallback to using Activity#finish() due to the"
+                + " Activity#finishAfterTransition() is supported from Android Sdk "
+                + VERSION_CODES.LOLLIPOP);
+        activity.finish();
+      }
+    }
+      isFinishCalled = false;
+  }
+
+  /**
+   * Returns the transition type from the {@link PartnerConfig#CONFIG_TRANSITION_TYPE} partner
+   * resource on Android S, otherwise returns {@link #CONFIG_TRANSITION_NONE}.
+   */
+  public static int getConfigTransitionType(Context context) {
+    return BuildCompatUtils.isAtLeastS() && ThemeHelper.shouldApplyExtendedPartnerConfig(context)
+        ? PartnerConfigHelper.get(context)
+            .getInteger(context, PartnerConfig.CONFIG_TRANSITION_TYPE, CONFIG_TRANSITION_NONE)
+        : CONFIG_TRANSITION_NONE;
+  }
+
+  /**
+   * A wrapper method, create a {@link Bundle} from {@link ActivityOptions} to transition between
+   * Activities using cross-Activity scene animations. This {@link Bundle} that can be used with
+   * {@link Context#startActivity(Intent, Bundle)} and related methods.
+   *
+   * <p>Example usage:
+   *
+   * <pre>{@code
+   * Intent intent = new Intent("com.example.NEXT_ACTIVITY");
+   * activity.startActivity(intent, TransitionHelper.makeActivityOptions(activity, intent, null);
+   * }</pre>
+   *
+   * <p>Unexpected usage:
+   *
+   * <pre>{@code
+   * Intent intent = new Intent("com.example.NEXT_ACTIVITY");
+   * Intent intent2 = new Intent("com.example.NEXT_ACTIVITY");
+   * activity.startActivity(intent, TransitionHelper.makeActivityOptions(activity, intent2, null);
+   * }</pre>
+   */
+  @Nullable
+  public static Bundle makeActivityOptions(Activity activity, Intent intent) {
+    return makeActivityOptions(activity, intent, false);
+  }
+
+  /**
+   * A wrapper method, create a {@link Bundle} from {@link ActivityOptions} to transition between
+   * Activities using cross-Activity scene animations. This {@link Bundle} that can be used with
+   * {@link Context#startActivity(Intent, Bundle)} and related methods. When this {@code activity}
+   * is a no UI activity(the activity doesn't inflate any layouts), you will need to pass the bundle
+   * coming from previous UI activity as the {@link ActivityOptions}, otherwise, the transition
+   * won't be take effect. The {@code overrideActivityOptionsFromIntent} is supporting this purpose
+   * to return the {@link ActivityOptions} instead of creating from this no UI activity while the
+   * transition is apply {@link #CONFIG_TRANSITION_SHARED_X_AXIS} config. Moreover, the
+   * startActivity*WithTransition relative methods and {@link #makeActivityOptions} will put {@link
+   * ActivityOptions} to the {@code intent} by default, you can get the {@link ActivityOptions}
+   * which makes from previous activity by accessing {@link #EXTRA_ACTIVITY_OPTIONS} extra from
+   * {@link Activity#getIntent()}.
+   *
+   * <p>Example usage of a no UI activity:
+   *
+   * <pre>{@code
+   * Intent intent = new Intent("com.example.NEXT_ACTIVITY");
+   * activity.startActivity(intent, TransitionHelper.makeActivityOptions(activity, intent, true);
+   * }</pre>
+   */
+  @Nullable
+  public static Bundle makeActivityOptions(
+      Activity activity, Intent intent, boolean overrideActivityOptionsFromIntent) {
+    Bundle resultBundle = null;
+    if (activity == null || intent == null) {
+      return resultBundle;
+    }
+
+    if ((intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) == Intent.FLAG_ACTIVITY_NEW_TASK) {
+      Log.e(
+          TAG,
+          "The transition won't take effect since the WindowManager does not allow override new"
+              + " task transitions");
+    }
+
+    if (getConfigTransitionType(activity) == CONFIG_TRANSITION_SHARED_X_AXIS) {
+      if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+        if (activity.getWindow() != null
+            && !activity.getWindow().hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)) {
+          Log.w(
+              TAG,
+              "The transition won't take effect due to NO FEATURE_ACTIVITY_TRANSITIONS feature");
+        }
+
+        if (overrideActivityOptionsFromIntent && activity.getIntent() != null) {
+          resultBundle = activity.getIntent().getBundleExtra(EXTRA_ACTIVITY_OPTIONS);
+        } else {
+          resultBundle = ActivityOptions.makeSceneTransitionAnimation(activity).toBundle();
+        }
+        intent.putExtra(EXTRA_ACTIVITY_OPTIONS, (Parcelable) resultBundle);
+        return resultBundle;
+      }
+    }
+
+    return resultBundle;
+  }
+}
diff --git a/main/src/com/google/android/setupdesign/transition/support/TransitionHelper.java b/main/src/com/google/android/setupdesign/transition/support/TransitionHelper.java
new file mode 100644
index 0000000..8fd9d6e
--- /dev/null
+++ b/main/src/com/google/android/setupdesign/transition/support/TransitionHelper.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this 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.google.android.setupdesign.transition.support;
+
+import static com.google.android.setupdesign.transition.TransitionHelper.CONFIG_TRANSITION_SHARED_X_AXIS;
+import static com.google.android.setupdesign.transition.TransitionHelper.getConfigTransitionType;
+
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.os.Build;
+import android.os.Build.VERSION_CODES;
+import androidx.fragment.app.Fragment;
+import android.util.Log;
+import android.view.Window;
+import androidx.annotation.Nullable;
+import androidx.core.app.ActivityOptionsCompat;
+import com.google.android.material.transition.platform.MaterialSharedAxis;
+import com.google.android.setupcompat.partnerconfig.PartnerConfig;
+
+/** Helper class for apply the transition to the pages which uses support library. */
+public class TransitionHelper {
+
+  private static final String TAG = "TransitionHelper";
+
+  private TransitionHelper() {}
+
+  /**
+   * Apply the transition for going forward which is decided by partner resource {@link
+   * PartnerConfig#CONFIG_TRANSITION_TYPE} and system property {@code setupwizard.transition_type}.
+   * The default transition that will be applied is {@link
+   * com.google.android.setupdesign.transition.TransitionHelper#CONFIG_TRANSITION_NONE}. The timing
+   * to apply the transition is going forward from the previous {@link Fragment} to this, or going
+   * forward from this {@link Fragment} to the next.
+   */
+  @TargetApi(VERSION_CODES.M)
+  public static void applyForwardTransition(Fragment fragment) {
+    if (Build.VERSION.SDK_INT >= VERSION_CODES.M) {
+      if (CONFIG_TRANSITION_SHARED_X_AXIS == getConfigTransitionType(fragment.getContext())) {
+        MaterialSharedAxis exitTransition =
+            new MaterialSharedAxis(MaterialSharedAxis.X, /* forward= */ true);
+        fragment.setExitTransition(exitTransition);
+
+        MaterialSharedAxis enterTransition =
+            new MaterialSharedAxis(MaterialSharedAxis.X, /* forward= */ true);
+        fragment.setEnterTransition(enterTransition);
+      } else {
+        Log.w(TAG, "Not apply the forward transition for support lib's fragment.");
+      }
+    } else {
+      Log.w(
+          TAG,
+          "Not apply the forward transition for support lib's fragment. The API is supported from"
+              + " Android Sdk "
+              + VERSION_CODES.M);
+    }
+  }
+
+  /**
+   * Apply the transition for going backward which is decided by partner resource {@link
+   * PartnerConfig#CONFIG_TRANSITION_TYPE} and system property {@code setupwizard.transition_type}.
+   * The default transition that will be applied is {@link
+   * com.google.android.setupdesign.transition.TransitionHelper#CONFIG_TRANSITION_NONE}. The timing
+   * to apply the transition is going backward from the next {@link Fragment} to this, or going
+   * backward from this {@link Fragment} to the previous.
+   */
+  @TargetApi(VERSION_CODES.M)
+  public static void applyBackwardTransition(Fragment fragment) {
+    if (Build.VERSION.SDK_INT >= VERSION_CODES.M) {
+      if (CONFIG_TRANSITION_SHARED_X_AXIS == getConfigTransitionType(fragment.getContext())) {
+        MaterialSharedAxis returnTransition =
+            new MaterialSharedAxis(MaterialSharedAxis.X, /* forward= */ false);
+        fragment.setReturnTransition(returnTransition);
+
+        MaterialSharedAxis reenterTransition =
+            new MaterialSharedAxis(MaterialSharedAxis.X, /* forward= */ false);
+        fragment.setReenterTransition(reenterTransition);
+      } else {
+        Log.w(TAG, "Not apply the backward transition for support lib's fragment.");
+      }
+    } else {
+      Log.w(
+          TAG,
+          "Not apply the backward transition for support lib's fragment. The API is supported from"
+              + " Android Sdk "
+              + VERSION_CODES.M);
+    }
+  }
+
+  /**
+   * A wrapper method, create an {@link ActivityOptionsCompat} to transition between activities as
+   * the {@link ActivityOptionsCompat} parameter of {@link
+   * androidx.activity.result.ActivityResultLauncher#launch(I, ActivityOptionsCompat)} method.
+   */
+  @Nullable
+  public static ActivityOptionsCompat makeActivityOptionsCompat(Activity activity) {
+    ActivityOptionsCompat activityOptionsCompat = null;
+    if (activity == null) {
+      return activityOptionsCompat;
+    }
+
+    if (getConfigTransitionType(activity) == CONFIG_TRANSITION_SHARED_X_AXIS) {
+      if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+        if (activity.getWindow() != null
+            && !activity.getWindow().hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)) {
+          Log.w(
+              TAG,
+              "The transition won't take effect due to NO FEATURE_ACTIVITY_TRANSITIONS feature");
+        }
+
+        activityOptionsCompat = ActivityOptionsCompat.makeSceneTransitionAnimation(activity);
+      }
+    }
+
+    return activityOptionsCompat;
+  }
+}
diff --git a/main/src/com/google/android/setupdesign/util/ButtonStyler.java b/main/src/com/google/android/setupdesign/util/ButtonStyler.java
new file mode 100644
index 0000000..8eb92af
--- /dev/null
+++ b/main/src/com/google/android/setupdesign/util/ButtonStyler.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this 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.google.android.setupdesign.util;
+
+import android.content.Context;
+import android.widget.Button;
+import com.google.android.setupcompat.template.FooterButtonStyleUtils;
+
+/** Utils for buttons in GlifLayout content area to get the style as footer buttons. */
+public class ButtonStyler {
+
+  /** Apply the partner primary button style to given {@code button}. */
+  public static void applyPartnerCustomizationPrimaryButtonStyle(Context context, Button button) {
+    if (button == null || context == null) {
+      return;
+    }
+    FooterButtonStyleUtils.applyPrimaryButtonPartnerResource(
+        context, button, ThemeHelper.shouldApplyDynamicColor(context));
+  }
+
+  /** Apply the partner secondary button style to given {@code button}. */
+  public static void applyPartnerCustomizationSecondaryButtonStyle(Context context, Button button) {
+    if (button == null || context == null) {
+      return;
+    }
+    FooterButtonStyleUtils.applySecondaryButtonPartnerResource(
+        context, button, ThemeHelper.shouldApplyDynamicColor(context));
+  }
+
+  private ButtonStyler() {}
+}
diff --git a/main/src/com/google/android/setupdesign/util/ContentStyler.java b/main/src/com/google/android/setupdesign/util/ContentStyler.java
index 0ab49b8..d390780 100644
--- a/main/src/com/google/android/setupdesign/util/ContentStyler.java
+++ b/main/src/com/google/android/setupdesign/util/ContentStyler.java
@@ -17,19 +17,33 @@
 package com.google.android.setupdesign.util;
 
 import android.content.Context;
+import android.os.Build;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
 import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.ImageView.ScaleType;
 import android.widget.TextView;
+import androidx.annotation.Nullable;
 import com.google.android.setupcompat.partnerconfig.PartnerConfig;
 import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
+import com.google.android.setupdesign.R;
 import com.google.android.setupdesign.util.TextViewPartnerStyler.TextPartnerConfigs;
 import java.util.Locale;
 
 /**
- * Applies the partner style of content to the given TextView {@code contentText}. The user needs to
- * check if the {@code contentText} should apply partner heavy theme before calling this method.
+ * Applies the partner style of content to the given TextView {@code contentText}. The theme should
+ * set partner heavy theme first, and then the partner style of content would be applied. The user
+ * can also check if the {@code contentText} should apply partner heavy theme before calling this
+ * method.
  */
 public final class ContentStyler {
-  public static void applyPartnerCustomizationStyle(TextView contentText) {
+  public static void applyBodyPartnerCustomizationStyle(TextView contentText) {
+    if (!PartnerStyleHelper.shouldApplyPartnerHeavyThemeResource(contentText)) {
+      return;
+    }
 
     TextViewPartnerStyler.applyPartnerCustomizationStyle(
         contentText,
@@ -38,10 +52,149 @@
             PartnerConfig.CONFIG_CONTENT_LINK_TEXT_COLOR,
             PartnerConfig.CONFIG_CONTENT_TEXT_SIZE,
             PartnerConfig.CONFIG_CONTENT_FONT_FAMILY,
+            null,
+            null,
             ContentStyler.getPartnerContentTextGravity(contentText.getContext())));
   }
 
-  public static int getPartnerContentTextGravity(Context context) {
+  /**
+   * Applies the partner heavy style of content info to the given views including content info
+   * container, content info icon and content info text, the given views should be included in a
+   * layout which the same view hierarchy with {@link R.layout#sud_content_info}.
+   *
+   * @param infoContainer A view the container resource of content info
+   * @param infoIcon A image view the icon resource of content info
+   * @param infoText A text view content info resource
+   */
+  public static void applyInfoPartnerCustomizationStyle(
+      @Nullable View infoContainer, @Nullable ImageView infoIcon, TextView infoText) {
+    if (!PartnerStyleHelper.shouldApplyPartnerHeavyThemeResource(infoText)) {
+      return;
+    }
+
+    Context context = infoText.getContext();
+
+    boolean textSizeConfigAvailable =
+        PartnerConfigHelper.get(context)
+            .isPartnerConfigAvailable(PartnerConfig.CONFIG_CONTENT_INFO_TEXT_SIZE);
+    boolean fontFamilyConfigAvailable =
+        PartnerConfigHelper.get(context)
+            .isPartnerConfigAvailable(PartnerConfig.CONFIG_CONTENT_INFO_FONT_FAMILY);
+
+    TextViewPartnerStyler.applyPartnerCustomizationStyle(
+        infoText,
+        new TextPartnerConfigs(
+            null,
+            null,
+            textSizeConfigAvailable ? PartnerConfig.CONFIG_CONTENT_INFO_TEXT_SIZE : null,
+            fontFamilyConfigAvailable ? PartnerConfig.CONFIG_CONTENT_INFO_FONT_FAMILY : null,
+            null,
+            null,
+            0));
+
+    // TODO: Move CONFIG_CONTENT_INFO_LINE_SPACING_EXTRA to TextPartnerConfigs for
+    // customize
+    boolean isAtLeastP = VERSION.SDK_INT >= VERSION_CODES.P;
+    if (isAtLeastP
+        && PartnerConfigHelper.get(context)
+            .isPartnerConfigAvailable(PartnerConfig.CONFIG_CONTENT_INFO_LINE_SPACING_EXTRA)) {
+      int textLineSpacingExtraInPx =
+          (int)
+              PartnerConfigHelper.get(context)
+                  .getDimension(context, PartnerConfig.CONFIG_CONTENT_INFO_LINE_SPACING_EXTRA);
+
+      float infoTextSizeInPx = infoText.getTextSize();
+      if (textSizeConfigAvailable) {
+        float textSizeInPx =
+            PartnerConfigHelper.get(context)
+                .getDimension(context, PartnerConfig.CONFIG_CONTENT_INFO_TEXT_SIZE, 0);
+        if (textSizeInPx > 0) {
+          infoTextSizeInPx = textSizeInPx;
+        }
+      }
+
+      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+        infoText.setLineHeight(Math.round(textLineSpacingExtraInPx + infoTextSizeInPx));
+      }
+    }
+
+    if (infoIcon != null) {
+      ViewGroup.LayoutParams lp = infoIcon.getLayoutParams();
+
+      if (PartnerConfigHelper.get(context)
+          .isPartnerConfigAvailable(PartnerConfig.CONFIG_CONTENT_INFO_ICON_SIZE)) {
+        int oldHeight = lp.height;
+        lp.height =
+            (int)
+                PartnerConfigHelper.get(context)
+                    .getDimension(context, PartnerConfig.CONFIG_CONTENT_INFO_ICON_SIZE);
+        lp.width = lp.width * (lp.height / oldHeight);
+        infoIcon.setScaleType(ScaleType.FIT_CENTER);
+      }
+
+      boolean partnerConfigAvailable =
+          PartnerConfigHelper.get(context)
+              .isPartnerConfigAvailable(PartnerConfig.CONFIG_CONTENT_INFO_ICON_MARGIN_END);
+      if (partnerConfigAvailable && lp instanceof ViewGroup.MarginLayoutParams) {
+        final ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams) lp;
+        int endMargin =
+            (int)
+                PartnerConfigHelper.get(context)
+                    .getDimension(context, PartnerConfig.CONFIG_CONTENT_INFO_ICON_MARGIN_END);
+        mlp.setMargins(mlp.leftMargin, mlp.topMargin, endMargin, mlp.bottomMargin);
+      }
+    }
+
+    if (infoContainer != null) {
+      float paddingTop;
+      if (PartnerConfigHelper.get(context)
+          .isPartnerConfigAvailable(PartnerConfig.CONFIG_CONTENT_INFO_PADDING_TOP)) {
+        paddingTop =
+            PartnerConfigHelper.get(context)
+                .getDimension(context, PartnerConfig.CONFIG_CONTENT_INFO_PADDING_TOP);
+      } else {
+        paddingTop = infoContainer.getPaddingTop();
+      }
+
+      float paddingBottom;
+      if (PartnerConfigHelper.get(context)
+          .isPartnerConfigAvailable(PartnerConfig.CONFIG_CONTENT_INFO_PADDING_BOTTOM)) {
+        paddingBottom =
+            PartnerConfigHelper.get(context)
+                .getDimension(context, PartnerConfig.CONFIG_CONTENT_INFO_PADDING_BOTTOM);
+      } else {
+        paddingBottom = infoContainer.getPaddingBottom();
+      }
+
+      if (paddingTop != infoContainer.getPaddingTop()
+          || paddingBottom != infoContainer.getPaddingBottom()) {
+        infoContainer.setPadding(0, (int) paddingTop, 0, (int) paddingBottom);
+      }
+    }
+  }
+
+  /**
+   * Returns the layout margin start from partner config. If the activity of given {@code context}
+   * does not enable the partner heavy theme, then returns the default value from GlifTheme.
+   *
+   * @param context The context of a GlifLayout activity.
+   */
+  public static float getPartnerContentMarginStart(Context context) {
+    // default value is GlifTheme layout margin start.
+    // That is the attr sudMarginStart, and the value is sud_layout_margin_sides.
+    float result = context.getResources().getDimension(R.dimen.sud_layout_margin_sides);
+    if (PartnerStyleHelper.shouldApplyPartnerHeavyThemeResource(context)) {
+      if (PartnerConfigHelper.get(context)
+          .isPartnerConfigAvailable(PartnerConfig.CONFIG_LAYOUT_MARGIN_START)) {
+        result =
+            PartnerConfigHelper.get(context)
+                .getDimension(context, PartnerConfig.CONFIG_LAYOUT_MARGIN_START, result);
+      }
+    }
+    return result;
+  }
+
+  private static int getPartnerContentTextGravity(Context context) {
     String gravity =
         PartnerConfigHelper.get(context)
             .getString(context, PartnerConfig.CONFIG_CONTENT_LAYOUT_GRAVITY);
diff --git a/main/src/com/google/android/setupdesign/util/DescriptionStyler.java b/main/src/com/google/android/setupdesign/util/DescriptionStyler.java
index 0a05f9d..0a786c6 100644
--- a/main/src/com/google/android/setupdesign/util/DescriptionStyler.java
+++ b/main/src/com/google/android/setupdesign/util/DescriptionStyler.java
@@ -22,13 +22,18 @@
 
 /**
  * Applies the partner style of description to the given TextView {@code description}. The user
- * needs to check if the {@code description} should apply partner heavy theme before calling this
- * method.
+ * needs to check if the {@code description} should apply partner heavy theme or light theme before
+ * calling this method, only heavy theme can apply for all configs.
  */
 public final class DescriptionStyler {
 
-  public static void applyPartnerCustomizationStyle(TextView description) {
-
+  /**
+   * Applies the partner heavy style of description to the given text view. Must check the current
+   * text view applies partner customized configurations to heavy theme before applying.
+   *
+   * @param description A text view description resource
+   */
+  public static void applyPartnerCustomizationHeavyStyle(TextView description) {
     TextViewPartnerStyler.applyPartnerCustomizationStyle(
         description,
         new TextPartnerConfigs(
@@ -36,6 +41,27 @@
             PartnerConfig.CONFIG_DESCRIPTION_LINK_TEXT_COLOR,
             PartnerConfig.CONFIG_DESCRIPTION_TEXT_SIZE,
             PartnerConfig.CONFIG_DESCRIPTION_FONT_FAMILY,
+            null,
+            null,
+            PartnerStyleHelper.getLayoutGravity(description.getContext())));
+  }
+
+  /**
+   * Applies the partner light style of description to the given text view. Must check the current
+   * text view applies partner customized configurations to light theme before applying.
+   *
+   * @param description A text view description resource
+   */
+  public static void applyPartnerCustomizationLightStyle(TextView description) {
+    TextViewPartnerStyler.applyPartnerCustomizationLightStyle(
+        description,
+        new TextPartnerConfigs(
+            null,
+            null,
+            null,
+            null,
+            null,
+            null,
             PartnerStyleHelper.getLayoutGravity(description.getContext())));
   }
 
diff --git a/main/src/com/google/android/setupdesign/util/DynamicColorPalette.java b/main/src/com/google/android/setupdesign/util/DynamicColorPalette.java
new file mode 100644
index 0000000..2db8c75
--- /dev/null
+++ b/main/src/com/google/android/setupdesign/util/DynamicColorPalette.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this 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.google.android.setupdesign.util;
+
+import android.content.Context;
+import androidx.annotation.ColorInt;
+import androidx.annotation.IntDef;
+import androidx.annotation.VisibleForTesting;
+import com.google.android.setupdesign.R;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** The class to get dynamic colors. */
+public final class DynamicColorPalette {
+
+  @VisibleForTesting static int colorRes = 0;
+
+  private DynamicColorPalette() {}
+
+  /** Dynamic color category. */
+  @Retention(RetentionPolicy.SOURCE)
+  @IntDef({
+    ColorType.ACCENT,
+    ColorType.PRIMARY_TEXT,
+    ColorType.SECONDARY_TEXT,
+    ColorType.DISABLED_OPTION,
+    ColorType.ERROR_WARNING,
+    ColorType.SUCCESS_DONE,
+    ColorType.FALLBACK_ACCENT,
+    ColorType.BACKGROUND_SURFACE,
+  })
+  public @interface ColorType {
+    int ACCENT = 0;
+    int PRIMARY_TEXT = 1;
+    int SECONDARY_TEXT = 2;
+    int DISABLED_OPTION = 3;
+    int ERROR_WARNING = 4;
+    int SUCCESS_DONE = 5;
+    int FALLBACK_ACCENT = 6;
+    int BACKGROUND_SURFACE = 7;
+  }
+
+  @ColorInt
+  public static int getColor(Context context, @ColorType int dynamicColorCategory) {
+    switch (dynamicColorCategory) {
+      case ColorType.ACCENT:
+        colorRes = R.color.sud_dynamic_color_accent_glif_v3;
+        break;
+      case ColorType.PRIMARY_TEXT:
+        colorRes = R.color.sud_system_primary_text;
+        break;
+      case ColorType.SECONDARY_TEXT:
+        colorRes = R.color.sud_system_secondary_text;
+        break;
+      case ColorType.DISABLED_OPTION:
+        colorRes = R.color.sud_system_tertiary_text_inactive;
+        break;
+      case ColorType.ERROR_WARNING:
+        colorRes = R.color.sud_system_error_warning;
+        break;
+      case ColorType.SUCCESS_DONE:
+        colorRes = R.color.sud_system_success_done;
+        break;
+      case ColorType.FALLBACK_ACCENT:
+        colorRes = R.color.sud_system_fallback_accent;
+        break;
+      case ColorType.BACKGROUND_SURFACE:
+        colorRes = R.color.sud_system_background_surface;
+        break;
+        // fall out
+    }
+
+    return context.getResources().getColor(colorRes);
+  }
+}
diff --git a/main/src/com/google/android/setupdesign/util/HeaderAreaStyler.java b/main/src/com/google/android/setupdesign/util/HeaderAreaStyler.java
index cd5135d..1b3daac 100644
--- a/main/src/com/google/android/setupdesign/util/HeaderAreaStyler.java
+++ b/main/src/com/google/android/setupdesign/util/HeaderAreaStyler.java
@@ -16,24 +16,46 @@
 
 package com.google.android.setupdesign.util;
 
+import static com.google.android.setupcompat.util.BuildCompatUtils.isAtLeastS;
+
 import android.content.Context;
-import androidx.annotation.Nullable;
+import android.graphics.drawable.VectorDrawable;
+import android.os.Build;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.util.Log;
 import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+import android.view.ViewTreeObserver;
+import android.widget.FrameLayout;
 import android.widget.ImageView;
-import android.widget.LinearLayout;
+import android.widget.ImageView.ScaleType;
 import android.widget.TextView;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat;
 import com.google.android.setupcompat.partnerconfig.PartnerConfig;
 import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
 import com.google.android.setupdesign.util.TextViewPartnerStyler.TextPartnerConfigs;
 
 /**
- * Helper class to apply the partner customization for the header area widgets. The user needs to
- * check if the header area widgets should apply partner heavy theme before calling these methods.
+ * Applies the partner customization for the header area widgets. The user needs to check if the
+ * header area widgets should apply partner heavy theme or light theme before calling these methods.
  */
 public final class HeaderAreaStyler {
 
-  /** Applies the partner style of header text to the given textView {@code header}. */
-  public static void applyPartnerCustomizationHeaderStyle(@Nullable TextView header) {
+  private static final String TAG = "HeaderAreaStyler";
+
+  @VisibleForTesting
+  static final String WARN_TO_USE_DRAWABLE =
+      "To achieve scaling icon in SetupDesign lib, should use vector drawable icon from ";
+
+  /**
+   * Applies the partner heavy style of header text to the given textView {@code header}.
+   *
+   * @param header A header text would apply partner heavy style
+   */
+  public static void applyPartnerCustomizationHeaderHeavyStyle(@Nullable TextView header) {
 
     if (header == null) {
       return;
@@ -45,38 +67,199 @@
             null,
             PartnerConfig.CONFIG_HEADER_TEXT_SIZE,
             PartnerConfig.CONFIG_HEADER_FONT_FAMILY,
+            PartnerConfig.CONFIG_HEADER_TEXT_MARGIN_TOP,
+            PartnerConfig.CONFIG_HEADER_TEXT_MARGIN_BOTTOM,
             PartnerStyleHelper.getLayoutGravity(header.getContext())));
   }
 
-  /** Applies the partner style of header background to the given layout {@code headerArea}. */
-  public static void applyPartnerCustomizationHeaderAreaStyle(ViewGroup headerArea) {
+  /**
+   * Applies the partner heavy style of description text to the given textView {@code description}.
+   *
+   * @param description A description text would apply partner heavy style
+   */
+  public static void applyPartnerCustomizationDescriptionHeavyStyle(
+      @Nullable TextView description) {
 
+    if (description == null) {
+      return;
+    }
+    TextViewPartnerStyler.applyPartnerCustomizationStyle(
+        description,
+        new TextPartnerConfigs(
+            PartnerConfig.CONFIG_DESCRIPTION_TEXT_COLOR,
+            PartnerConfig.CONFIG_DESCRIPTION_LINK_TEXT_COLOR,
+            PartnerConfig.CONFIG_DESCRIPTION_TEXT_SIZE,
+            PartnerConfig.CONFIG_DESCRIPTION_FONT_FAMILY,
+            PartnerConfig.CONFIG_DESCRIPTION_TEXT_MARGIN_TOP,
+            PartnerConfig.CONFIG_DESCRIPTION_TEXT_MARGIN_BOTTOM,
+            PartnerStyleHelper.getLayoutGravity(description.getContext())));
+  }
+
+  /**
+   * Applies the partner light style of header text to the given textView {@code header}.
+   *
+   * @param header A header text would apply partner light style
+   */
+  public static void applyPartnerCustomizationHeaderLightStyle(@Nullable TextView header) {
+
+    if (header == null) {
+      return;
+    }
+
+    TextViewPartnerStyler.applyPartnerCustomizationLightStyle(
+        header,
+        new TextPartnerConfigs(
+            null,
+            null,
+            null,
+            null,
+            null,
+            null,
+            PartnerStyleHelper.getLayoutGravity(header.getContext())));
+  }
+
+  /**
+   * Applies the partner light style of description text to the given textView {@code description}.
+   *
+   * @param description A description text would apply partner light style
+   */
+  public static void applyPartnerCustomizationDescriptionLightStyle(
+      @Nullable TextView description) {
+
+    if (description == null) {
+      return;
+    }
+
+    TextViewPartnerStyler.applyPartnerCustomizationLightStyle(
+        description,
+        new TextPartnerConfigs(
+            null,
+            null,
+            null,
+            null,
+            null,
+            null,
+            PartnerStyleHelper.getLayoutGravity(description.getContext())));
+  }
+
+  /**
+   * Applies the partner style of header area to the given layout {@code headerArea}. The theme
+   * should set partner heavy theme first, and then the partner style of header would be applied. As
+   * for the margin bottom of header, it would also be appied when heavy theme parter config is
+   * enabled.
+   *
+   * @param headerArea A ViewGroup would apply the partner style of header area
+   */
+  public static void applyPartnerCustomizationHeaderAreaStyle(ViewGroup headerArea) {
     if (headerArea == null) {
       return;
     }
-    Context context = headerArea.getContext();
-    int color =
-        PartnerConfigHelper.get(context)
-            .getColor(context, PartnerConfig.CONFIG_HEADER_AREA_BACKGROUND_COLOR);
-    headerArea.setBackgroundColor(color);
+    if (PartnerStyleHelper.shouldApplyPartnerHeavyThemeResource(headerArea)) {
+      Context context = headerArea.getContext();
+
+      int color =
+          PartnerConfigHelper.get(context)
+              .getColor(context, PartnerConfig.CONFIG_HEADER_AREA_BACKGROUND_COLOR);
+      headerArea.setBackgroundColor(color);
+
+      if (PartnerConfigHelper.get(context)
+          .isPartnerConfigAvailable(PartnerConfig.CONFIG_HEADER_CONTAINER_MARGIN_BOTTOM)) {
+        final ViewGroup.LayoutParams lp = headerArea.getLayoutParams();
+        if (lp instanceof ViewGroup.MarginLayoutParams) {
+          final ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams) lp;
+
+          int bottomMargin =
+              (int)
+                  PartnerConfigHelper.get(context)
+                      .getDimension(context, PartnerConfig.CONFIG_HEADER_CONTAINER_MARGIN_BOTTOM);
+          mlp.setMargins(mlp.leftMargin, mlp.topMargin, mlp.rightMargin, bottomMargin);
+          headerArea.setLayoutParams(lp);
+        }
+      }
+    }
   }
 
-  /** Applies the partner style of header icon to the given {@code iconImage}. */
-  public static void applyPartnerCustomizationIconStyle(@Nullable ImageView iconImage) {
-
-    if (iconImage == null) {
+  /**
+   * Applies the partner heavy style of header icon to the given {@code iconImage}. The theme should
+   * check partner heavy theme first, and then the partner icon size would be applied.
+   *
+   * @param iconImage A ImageView would apply the partner style of header icon
+   * @param iconContainer The container of the header icon
+   */
+  public static void applyPartnerCustomizationIconStyle(
+      @Nullable ImageView iconImage, FrameLayout iconContainer) {
+    if (iconImage == null || iconContainer == null) {
       return;
     }
 
+    Context context = iconImage.getContext();
+    int gravity = PartnerStyleHelper.getLayoutGravity(context);
+    if (gravity != 0) {
+      setGravity(iconImage, gravity);
+    }
+
+    final ViewGroup.LayoutParams lp = iconContainer.getLayoutParams();
+    boolean partnerConfigAvailable =
+        PartnerConfigHelper.get(context)
+            .isPartnerConfigAvailable(PartnerConfig.CONFIG_ICON_MARGIN_TOP);
+    if (partnerConfigAvailable && lp instanceof ViewGroup.MarginLayoutParams) {
+      final ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams) lp;
+      int topMargin =
+          (int)
+              PartnerConfigHelper.get(context)
+                  .getDimension(context, PartnerConfig.CONFIG_ICON_MARGIN_TOP);
+      mlp.setMargins(mlp.leftMargin, topMargin, mlp.rightMargin, mlp.bottomMargin);
+    }
+
+    if (PartnerConfigHelper.get(context).isPartnerConfigAvailable(PartnerConfig.CONFIG_ICON_SIZE)) {
+
+      checkImageType(iconImage);
+
+      final ViewGroup.LayoutParams lpIcon = iconImage.getLayoutParams();
+      lpIcon.height =
+          (int)
+              PartnerConfigHelper.get(context)
+                  .getDimension(context, PartnerConfig.CONFIG_ICON_SIZE);
+      lpIcon.width = LayoutParams.WRAP_CONTENT;
+      iconImage.setScaleType(ScaleType.FIT_CENTER);
+    }
+  }
+
+  /** Applies the partner light style of header icon to the given {@code iconImage}. */
+  public static void applyPartnerCustomizationIconStyle(@Nullable ImageView iconImage) {
+    if (iconImage == null) {
+      return;
+    }
     int gravity = PartnerStyleHelper.getLayoutGravity(iconImage.getContext());
     if (gravity != 0) {
       setGravity(iconImage, gravity);
     }
   }
 
+  private static void checkImageType(ImageView imageView) {
+    ViewTreeObserver vto = imageView.getViewTreeObserver();
+    vto.addOnPreDrawListener(
+        new ViewTreeObserver.OnPreDrawListener() {
+          @Override
+          public boolean onPreDraw() {
+            imageView.getViewTreeObserver().removeOnPreDrawListener(this);
+            // TODO: Remove when Partners all used Drawable icon image and never use
+            if (isAtLeastS()
+                && !(imageView.getDrawable() == null
+                    || (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP
+                        && imageView.getDrawable() instanceof VectorDrawable)
+                    || imageView.getDrawable() instanceof VectorDrawableCompat)
+                && (Build.TYPE.equals("userdebug") || Build.TYPE.equals("eng"))) {
+              Log.w(TAG, WARN_TO_USE_DRAWABLE + imageView.getContext().getPackageName());
+            }
+            return true;
+          }
+        });
+  }
+
   private static void setGravity(ImageView icon, int gravity) {
-    if (icon.getLayoutParams() instanceof LinearLayout.LayoutParams) {
-      LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) icon.getLayoutParams();
+    if (icon.getLayoutParams() instanceof FrameLayout.LayoutParams) {
+      FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) icon.getLayoutParams();
       layoutParams.gravity = gravity;
       icon.setLayoutParams(layoutParams);
     }
diff --git a/main/src/com/google/android/setupdesign/util/ItemStyler.java b/main/src/com/google/android/setupdesign/util/ItemStyler.java
new file mode 100644
index 0000000..ecddfd4
--- /dev/null
+++ b/main/src/com/google/android/setupdesign/util/ItemStyler.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this 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.google.android.setupdesign.util;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.os.Build;
+import android.os.Build.VERSION_CODES;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import androidx.annotation.Nullable;
+import com.google.android.setupcompat.partnerconfig.PartnerConfig;
+import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
+import com.google.android.setupdesign.R;
+import com.google.android.setupdesign.util.TextViewPartnerStyler.TextPartnerConfigs;
+
+/**
+ * Applies the partner style of layout to the given View {@code view}. The user needs to check if
+ * the {@code view} should apply partner heavy theme before calling this method.
+ */
+public final class ItemStyler {
+
+  /**
+   * Applies the heavy theme partner configs to the given listItemView {@code listItemView}. The
+   * user needs to check before calling this method:
+   *
+   * <p>1) If the {@code listItemView} should apply heavy theme resource by calling {@link
+   * PartnerStyleHelper#shouldApplyPartnerHeavyThemeResource}.
+   *
+   * <p>2) If the layout of the {@code listItemView} contains fixed resource IDs which attempts to
+   * apply heavy theme resources (The resource ID of the title is "sud_items_title" and the resource
+   * ID of the summary is "sud_items_summary"), refer to {@link R.layout#sud_items_default}.
+   *
+   * @param listItemView A view would be applied heavy theme styles
+   */
+  @TargetApi(VERSION_CODES.JELLY_BEAN_MR1)
+  public static void applyPartnerCustomizationItemStyle(@Nullable View listItemView) {
+    if (listItemView == null) {
+      return;
+    }
+    if (!PartnerStyleHelper.shouldApplyPartnerHeavyThemeResource(listItemView)) {
+      return;
+    }
+
+    final TextView titleTextView = listItemView.findViewById(R.id.sud_items_title);
+    // apply title text style
+    applyPartnerCustomizationItemTitleStyle(titleTextView);
+
+    // adjust list item view gravity
+    TextView summaryTextView = listItemView.findViewById(R.id.sud_items_summary);
+    if (summaryTextView.getVisibility() == View.GONE && listItemView instanceof LinearLayout) {
+      // Set list items to vertical center when there is no summary.
+      ((LinearLayout) listItemView).setGravity(Gravity.CENTER_VERTICAL);
+    }
+
+    // apply summary text style
+    applyPartnerCustomizationItemSummaryStyle(summaryTextView);
+
+    // apply list item view style
+    applyPartnerCustomizationItemViewLayoutStyle(listItemView);
+  }
+
+  /**
+   * Applies the partner heavy style to the given list item title text view. Will check the current
+   * text view enabled the partner customized heavy theme configurations before applying.
+   *
+   * @param titleTextView A textView of a list item title text.
+   */
+  public static void applyPartnerCustomizationItemTitleStyle(TextView titleTextView) {
+    if (!PartnerStyleHelper.shouldApplyPartnerHeavyThemeResource(titleTextView)) {
+      return;
+    }
+    TextViewPartnerStyler.applyPartnerCustomizationStyle(
+        titleTextView,
+        new TextPartnerConfigs(
+            null,
+            null,
+            PartnerConfig.CONFIG_ITEMS_TITLE_TEXT_SIZE,
+            PartnerConfig.CONFIG_ITEMS_TITLE_FONT_FAMILY,
+            null,
+            null,
+            PartnerStyleHelper.getLayoutGravity(titleTextView.getContext())));
+  }
+
+  /**
+   * Applies the partner heavy style to the given summary text view. Will check the current text
+   * view enabled the partner customized heavy theme configurations before applying.
+   *
+   * @param summaryTextView A textView of a list item summary text.
+   */
+  public static void applyPartnerCustomizationItemSummaryStyle(TextView summaryTextView) {
+    if (!PartnerStyleHelper.shouldApplyPartnerHeavyThemeResource(summaryTextView)) {
+      return;
+    }
+
+    TextViewPartnerStyler.applyPartnerCustomizationStyle(
+        summaryTextView,
+        new TextPartnerConfigs(
+            null,
+            null,
+            PartnerConfig.CONFIG_ITEMS_SUMMARY_TEXT_SIZE,
+            PartnerConfig.CONFIG_ITEMS_SUMMARY_FONT_FAMILY,
+            PartnerConfig.CONFIG_ITEMS_SUMMARY_MARGIN_TOP,
+            null,
+            PartnerStyleHelper.getLayoutGravity(summaryTextView.getContext())));
+  }
+
+  private static void applyPartnerCustomizationItemViewLayoutStyle(@Nullable View listItemView) {
+    Context context = listItemView.getContext();
+    float paddingTop;
+    if (PartnerConfigHelper.get(context)
+        .isPartnerConfigAvailable(PartnerConfig.CONFIG_ITEMS_PADDING_TOP)) {
+      paddingTop =
+          PartnerConfigHelper.get(context)
+              .getDimension(context, PartnerConfig.CONFIG_ITEMS_PADDING_TOP);
+    } else {
+      paddingTop = listItemView.getPaddingTop();
+    }
+
+    float paddingBottom;
+    if (PartnerConfigHelper.get(context)
+        .isPartnerConfigAvailable(PartnerConfig.CONFIG_ITEMS_PADDING_BOTTOM)) {
+      paddingBottom =
+          PartnerConfigHelper.get(context)
+              .getDimension(context, PartnerConfig.CONFIG_ITEMS_PADDING_BOTTOM);
+    } else {
+      paddingBottom = listItemView.getPaddingBottom();
+    }
+
+    if (paddingTop != listItemView.getPaddingTop()
+        || paddingBottom != listItemView.getPaddingBottom()) {
+      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+        listItemView.setPadding(
+            listItemView.getPaddingStart(),
+            (int) paddingTop,
+            listItemView.getPaddingEnd(),
+            (int) paddingBottom);
+      } else {
+        listItemView.setPadding(
+            listItemView.getPaddingLeft(),
+            (int) paddingTop,
+            listItemView.getPaddingRight(),
+            (int) paddingBottom);
+      }
+    }
+
+    if (PartnerConfigHelper.get(context)
+        .isPartnerConfigAvailable(PartnerConfig.CONFIG_ITEMS_MIN_HEIGHT)) {
+      float minHeight =
+          PartnerConfigHelper.get(context)
+              .getDimension(context, PartnerConfig.CONFIG_ITEMS_MIN_HEIGHT);
+      listItemView.setMinimumHeight((int) minHeight);
+    }
+  }
+
+  private ItemStyler() {}
+}
diff --git a/main/src/com/google/android/setupdesign/util/LayoutStyler.java b/main/src/com/google/android/setupdesign/util/LayoutStyler.java
new file mode 100644
index 0000000..b707521
--- /dev/null
+++ b/main/src/com/google/android/setupdesign/util/LayoutStyler.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this 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.google.android.setupdesign.util;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.os.Build.VERSION_CODES;
+import android.view.View;
+import androidx.annotation.Nullable;
+import com.google.android.setupcompat.partnerconfig.PartnerConfig;
+import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
+import com.google.android.setupdesign.R;
+
+/**
+ * Applies the partner style of layout to the given View {@code view}. The user needs to check if
+ * the {@code view} should apply partner heavy theme before calling this method.
+ */
+public final class LayoutStyler {
+
+  /**
+   * Applies the partner layout padding style to the given view {@code view}. The theme should set
+   * partner heavy theme config first, and then the partner layout style would be applied.
+   *
+   * @param view A view would be applied partner layout padding style
+   */
+  @TargetApi(VERSION_CODES.JELLY_BEAN_MR1)
+  public static void applyPartnerCustomizationLayoutPaddingStyle(@Nullable View view) {
+    if (view == null) {
+      return;
+    }
+
+    Context context = view.getContext();
+    boolean partnerMarginStartAvailable =
+        PartnerConfigHelper.get(context)
+            .isPartnerConfigAvailable(PartnerConfig.CONFIG_LAYOUT_MARGIN_START);
+    boolean partnerMarginEndAvailable =
+        PartnerConfigHelper.get(context)
+            .isPartnerConfigAvailable(PartnerConfig.CONFIG_LAYOUT_MARGIN_END);
+
+    if (PartnerStyleHelper.shouldApplyPartnerHeavyThemeResource(view)
+        && (partnerMarginStartAvailable || partnerMarginEndAvailable)) {
+      int paddingStart;
+      int paddingEnd;
+      if (partnerMarginStartAvailable) {
+        paddingStart =
+            (int)
+                PartnerConfigHelper.get(context)
+                    .getDimension(context, PartnerConfig.CONFIG_LAYOUT_MARGIN_START);
+      } else {
+        paddingStart = view.getPaddingStart();
+      }
+      if (partnerMarginEndAvailable) {
+        paddingEnd =
+            (int)
+                PartnerConfigHelper.get(context)
+                    .getDimension(context, PartnerConfig.CONFIG_LAYOUT_MARGIN_END);
+      } else {
+        paddingEnd = view.getPaddingEnd();
+      }
+
+      if (paddingStart != view.getPaddingStart() || paddingEnd != view.getPaddingEnd()) {
+        view.setPadding(paddingStart, view.getPaddingTop(), paddingEnd, view.getPaddingBottom());
+      }
+    }
+  }
+
+  /**
+   * Applies the extra padding style to the given view {@code view}. This method is used when {@code
+   * view} already sets its margin, and like to extra padding make view.margin + view.pendding =
+   * global page margin.
+   *
+   * @param view A view would be applied extra padding style based on the layout margin of partner
+   *     config.
+   */
+  @TargetApi(VERSION_CODES.JELLY_BEAN_MR1)
+  public static void applyPartnerCustomizationExtraPaddingStyle(@Nullable View view) {
+    if (view == null) {
+      return;
+    }
+
+    Context context = view.getContext();
+    boolean partnerMarginStartAvailable =
+        PartnerConfigHelper.get(context)
+            .isPartnerConfigAvailable(PartnerConfig.CONFIG_LAYOUT_MARGIN_START);
+    boolean partnerMarginEndAvailable =
+        PartnerConfigHelper.get(context)
+            .isPartnerConfigAvailable(PartnerConfig.CONFIG_LAYOUT_MARGIN_END);
+
+    if (PartnerStyleHelper.shouldApplyPartnerHeavyThemeResource(view)
+        && (partnerMarginStartAvailable || partnerMarginEndAvailable)) {
+      int extraPaddingStart;
+      int extraPaddingEnd;
+
+      TypedArray a =
+          context.obtainStyledAttributes(new int[] {R.attr.sudMarginStart, R.attr.sudMarginEnd});
+      int layoutMarginStart = a.getDimensionPixelSize(0, 0);
+      int layoutMarginEnd = a.getDimensionPixelSize(1, 0);
+      a.recycle();
+
+      if (partnerMarginStartAvailable) {
+        extraPaddingStart =
+            ((int)
+                    PartnerConfigHelper.get(context)
+                        .getDimension(context, PartnerConfig.CONFIG_LAYOUT_MARGIN_START))
+                - layoutMarginStart;
+      } else {
+        extraPaddingStart = view.getPaddingStart();
+      }
+
+      if (partnerMarginEndAvailable) {
+        extraPaddingEnd =
+            ((int)
+                    PartnerConfigHelper.get(context)
+                        .getDimension(context, PartnerConfig.CONFIG_LAYOUT_MARGIN_END))
+                - layoutMarginEnd;
+      } else {
+        extraPaddingEnd = view.getPaddingEnd();
+      }
+
+      if (extraPaddingStart != view.getPaddingStart() || extraPaddingEnd != view.getPaddingEnd()) {
+        // If the view is a content view, padding start and padding end will be the same.
+        view.setPadding(
+            extraPaddingStart,
+            view.getPaddingTop(),
+            view.getId() == R.id.sud_layout_content ? extraPaddingStart : extraPaddingEnd,
+            view.getPaddingBottom());
+      }
+    }
+  }
+
+  private LayoutStyler() {}
+}
diff --git a/main/src/com/google/android/setupdesign/util/Partner.java b/main/src/com/google/android/setupdesign/util/Partner.java
index aee5070..cf26f16 100644
--- a/main/src/com/google/android/setupdesign/util/Partner.java
+++ b/main/src/com/google/android/setupdesign/util/Partner.java
@@ -24,16 +24,20 @@
 import android.content.pm.ResolveInfo;
 import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
 import android.os.Build.VERSION;
 import android.os.Build.VERSION_CODES;
+import android.util.Log;
+import android.util.TypedValue;
 import androidx.annotation.AnyRes;
 import androidx.annotation.ArrayRes;
+import androidx.annotation.BoolRes;
 import androidx.annotation.ColorRes;
 import androidx.annotation.DrawableRes;
 import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
 import androidx.annotation.StringRes;
 import androidx.annotation.VisibleForTesting;
-import android.util.Log;
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
@@ -69,6 +73,17 @@
   }
 
   /**
+   * Gets a boolean value from partner overlay, or if not available, gets the value from the
+   * original context instead.
+   *
+   * @see #getResourceEntry(Context, int)
+   */
+  public static boolean getBoolean(Context context, @BoolRes int id) {
+    final ResourceEntry entry = getResourceEntry(context, id);
+    return entry.resources.getBoolean(entry.id);
+  }
+
+  /**
    * Gets a drawable from partner overlay, or if not available, the drawable from the original
    * context.
    *
@@ -107,6 +122,21 @@
   }
 
   /**
+   * Gets an {@link Icon} from partner overlay, or if not available, the drawable from the original
+   * context. In some cases, icon can be set {@code null} to remove default icon.
+   *
+   * @see #getResourceEntry(Context, int)
+   */
+  @Nullable
+  @RequiresApi(VERSION_CODES.M)
+  public static Icon getIcon(Context context, @DrawableRes int id) {
+    Partner.ResourceEntry entry = Partner.getResourceEntry(context, id);
+    return (getTypedValue(entry).data == 0)
+        ? null
+        : Icon.createWithResource(entry.packageName, 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.
@@ -214,4 +244,10 @@
   public int getIdentifier(String name, String defType) {
     return resources.getIdentifier(name, defType, packageName);
   }
+
+  private static TypedValue getTypedValue(ResourceEntry resourceEntry) {
+    TypedValue typedValue = new TypedValue();
+    resourceEntry.resources.getValue(resourceEntry.id, typedValue, true);
+    return typedValue;
+  }
 }
diff --git a/main/src/com/google/android/setupdesign/util/PartnerStyleHelper.java b/main/src/com/google/android/setupdesign/util/PartnerStyleHelper.java
index d1d4a26..78900b1 100644
--- a/main/src/com/google/android/setupdesign/util/PartnerStyleHelper.java
+++ b/main/src/com/google/android/setupdesign/util/PartnerStyleHelper.java
@@ -16,19 +16,27 @@
 
 package com.google.android.setupdesign.util;
 
+import android.app.Activity;
 import android.content.Context;
+import android.content.res.TypedArray;
+import android.os.Build;
 import android.view.Gravity;
-import android.widget.FrameLayout;
+import android.view.View;
+import com.google.android.setupcompat.PartnerCustomizationLayout;
+import com.google.android.setupcompat.internal.TemplateLayout;
 import com.google.android.setupcompat.partnerconfig.PartnerConfig;
 import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
+import com.google.android.setupcompat.util.WizardManagerHelper;
 import com.google.android.setupdesign.GlifLayout;
+import com.google.android.setupdesign.R;
 import java.util.Locale;
 
 /** The helper reads styles from the partner configurations. */
 public final class PartnerStyleHelper {
 
+  private static final String TAG = "PartnerStyleHelper";
   /**
-   * Returns the partner configuration of layout gravity, usually apply to wigets in header area.
+   * Returns the partner configuration of layout gravity, usually apply to widgets in header area.
    */
   public static int getLayoutGravity(Context context) {
     String gravity =
@@ -47,14 +55,152 @@
   }
 
   /** Returns the given layout if apply partner heavy theme. */
-  public static boolean isPartnerHeavyThemeLayout(FrameLayout layout) {
+  public static boolean isPartnerHeavyThemeLayout(TemplateLayout layout) {
     if (!(layout instanceof GlifLayout)) {
       return false;
     }
-    if (!((GlifLayout) layout).shouldApplyPartnerHeavyThemeResource()) {
+    return ((GlifLayout) layout).shouldApplyPartnerHeavyThemeResource();
+  }
+
+  /** Returns the given layout if apply partner light theme. */
+  public static boolean isPartnerLightThemeLayout(TemplateLayout layout) {
+    if (!(layout instanceof PartnerCustomizationLayout)) {
       return false;
     }
-    return true;
+    return ((PartnerCustomizationLayout) layout).shouldApplyPartnerResource();
+  }
+
+  /**
+   * Returns if the current layout/activity of the given {@code view} applies partner customized
+   * configurations or not.
+   *
+   * @param view A PartnerCustomizationLayout view, would be used to get the activity and context.
+   */
+  public static boolean shouldApplyPartnerResource(View view) {
+    if (view == null) {
+      return false;
+    }
+    if (view instanceof PartnerCustomizationLayout) {
+      return isPartnerLightThemeLayout((PartnerCustomizationLayout) view);
+    }
+    return shouldApplyPartnerResource(view.getContext());
+  }
+
+  private static boolean shouldApplyPartnerResource(Context context) {
+    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
+      return false;
+    }
+
+    if (!PartnerConfigHelper.get(context).isAvailable()) {
+      return false;
+    }
+
+    Activity activity = null;
+    try {
+      activity = PartnerCustomizationLayout.lookupActivityFromContext(context);
+      if (activity != null) {
+        TemplateLayout layout = findLayoutFromActivity(activity);
+        if (layout instanceof PartnerCustomizationLayout) {
+          return ((PartnerCustomizationLayout) layout).shouldApplyPartnerResource();
+        }
+      }
+    } catch (IllegalArgumentException | ClassCastException ex) {
+      // fall through
+    }
+
+    // try best to get partner resource settings from attrs
+    boolean isSetupFlow = false;
+    if (activity != null) {
+      isSetupFlow = WizardManagerHelper.isAnySetupWizard(activity.getIntent());
+    }
+    TypedArray a = context.obtainStyledAttributes(new int[] {R.attr.sucUsePartnerResource});
+    boolean usePartnerResource = a.getBoolean(0, true);
+    a.recycle();
+
+    return isSetupFlow || usePartnerResource;
+  }
+
+  /**
+   * Returns if the current layout/activity applies heavy partner customized configurations or not.
+   *
+   * @param view A view would be used to get the activity and context.
+   */
+  public static boolean shouldApplyPartnerHeavyThemeResource(View view) {
+    if (view == null) {
+      return false;
+    }
+    if (view instanceof GlifLayout) {
+      return isPartnerHeavyThemeLayout((GlifLayout) view);
+    }
+    return shouldApplyPartnerHeavyThemeResource(view.getContext());
+  }
+
+  static boolean shouldApplyPartnerHeavyThemeResource(Context context) {
+    try {
+      Activity activity = PartnerCustomizationLayout.lookupActivityFromContext(context);
+      TemplateLayout layout = findLayoutFromActivity(activity);
+      if (layout instanceof GlifLayout) {
+        return ((GlifLayout) layout).shouldApplyPartnerHeavyThemeResource();
+      }
+    } catch (IllegalArgumentException | ClassCastException ex) {
+      // fall through
+    }
+
+    // try best to get partner resource settings from attr
+    TypedArray a = context.obtainStyledAttributes(new int[] {R.attr.sudUsePartnerHeavyTheme});
+    boolean usePartnerHeavyTheme = a.getBoolean(0, false);
+    a.recycle();
+    usePartnerHeavyTheme =
+        usePartnerHeavyTheme || PartnerConfigHelper.shouldApplyExtendedPartnerConfig(context);
+
+    return shouldApplyPartnerResource(context) && usePartnerHeavyTheme;
+  }
+
+  /**
+   * Returns if the current layout/activity applies dynamic color configurations or not.
+   *
+   * @param view A GlifLayout view would be used to get the activity and context.
+   */
+  public static boolean useDynamicColor(View view) {
+    if (view == null) {
+      return false;
+    }
+    return getDynamicColorAttributeFromTheme(view.getContext());
+  }
+
+  static boolean getDynamicColorAttributeFromTheme(Context context) {
+    try {
+      Activity activity = PartnerCustomizationLayout.lookupActivityFromContext(context);
+      TemplateLayout layout = findLayoutFromActivity(activity);
+      if (layout instanceof GlifLayout) {
+        return ((GlifLayout) layout).shouldApplyDynamicColor();
+      }
+    } catch (IllegalArgumentException | ClassCastException ex) {
+      // fall through
+    }
+
+    // try best to get dynamic color settings from attr
+    TypedArray a = context.obtainStyledAttributes(new int[] {R.attr.sucFullDynamicColor});
+    boolean useDynamicColorTheme =
+        a.hasValue(
+            com.google
+                .android
+                .setupcompat
+                .R
+                .styleable
+                .SucPartnerCustomizationLayout_sucFullDynamicColor);
+    a.recycle();
+
+    return useDynamicColorTheme;
+  }
+
+  private static TemplateLayout findLayoutFromActivity(Activity activity) {
+    if (activity == null) {
+      return null;
+    }
+    // This only worked after activity setContentView, otherwise it will return null
+    View rootView = activity.findViewById(R.id.suc_layout_status);
+    return rootView != null ? (TemplateLayout) rootView.getParent() : null;
   }
 
   private PartnerStyleHelper() {}
diff --git a/main/src/com/google/android/setupdesign/util/TextViewPartnerStyler.java b/main/src/com/google/android/setupdesign/util/TextViewPartnerStyler.java
index 552102e..7b4acea 100644
--- a/main/src/com/google/android/setupdesign/util/TextViewPartnerStyler.java
+++ b/main/src/com/google/android/setupdesign/util/TextViewPartnerStyler.java
@@ -18,10 +18,12 @@
 
 import android.content.Context;
 import android.graphics.Typeface;
+import android.util.TypedValue;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.TextView;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import android.util.TypedValue;
-import android.widget.TextView;
 import com.google.android.setupcompat.partnerconfig.PartnerConfig;
 import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
 
@@ -37,7 +39,9 @@
     }
 
     Context context = textView.getContext();
-    if (textPartnerConfigs.getTextColorConfig() != null) {
+    if (textPartnerConfigs.getTextColorConfig() != null
+        && PartnerConfigHelper.get(context)
+            .isPartnerConfigAvailable(textPartnerConfigs.getTextColorConfig())) {
       int textColor =
           PartnerConfigHelper.get(context)
               .getColor(context, textPartnerConfigs.getTextColorConfig());
@@ -46,7 +50,10 @@
       }
     }
 
-    if (textPartnerConfigs.getTextLinkedColorConfig() != null) {
+    if (textPartnerConfigs.getTextLinkedColorConfig() != null
+        && PartnerConfigHelper.get(context)
+            .isPartnerConfigAvailable(textPartnerConfigs.getTextLinkedColorConfig())
+        && !PartnerStyleHelper.useDynamicColor(textView)) {
       int linkTextColor =
           PartnerConfigHelper.get(context)
               .getColor(context, textPartnerConfigs.getTextLinkedColorConfig());
@@ -55,7 +62,9 @@
       }
     }
 
-    if (textPartnerConfigs.getTextSizeConfig() != null) {
+    if (textPartnerConfigs.getTextSizeConfig() != null
+        && PartnerConfigHelper.get(context)
+            .isPartnerConfigAvailable(textPartnerConfigs.getTextSizeConfig())) {
       float textSize =
           PartnerConfigHelper.get(context)
               .getDimension(context, textPartnerConfigs.getTextSizeConfig(), 0);
@@ -64,7 +73,9 @@
       }
     }
 
-    if (textPartnerConfigs.getTextFontFamilyConfig() != null) {
+    if (textPartnerConfigs.getTextFontFamilyConfig() != null
+        && PartnerConfigHelper.get(context)
+            .isPartnerConfigAvailable(textPartnerConfigs.getTextFontFamilyConfig())) {
       String fontFamilyName =
           PartnerConfigHelper.get(context)
               .getString(context, textPartnerConfigs.getTextFontFamilyConfig());
@@ -74,6 +85,54 @@
       }
     }
 
+    if (textPartnerConfigs.getTextMarginTop() != null
+        || textPartnerConfigs.getTextMarginBottom() != null) {
+      int topMargin;
+      int bottomMargin;
+      final ViewGroup.LayoutParams lp = textView.getLayoutParams();
+      if (lp instanceof LinearLayout.LayoutParams) {
+        final LinearLayout.LayoutParams mlp = (LinearLayout.LayoutParams) lp;
+        if (textPartnerConfigs.getTextMarginTop() != null
+            && PartnerConfigHelper.get(context)
+                .isPartnerConfigAvailable(textPartnerConfigs.getTextMarginTop())) {
+          topMargin =
+              (int)
+                  PartnerConfigHelper.get(context)
+                      .getDimension(context, textPartnerConfigs.getTextMarginTop());
+        } else {
+          topMargin = mlp.topMargin;
+        }
+
+        if (textPartnerConfigs.getTextMarginBottom() != null
+            && PartnerConfigHelper.get(context)
+                .isPartnerConfigAvailable(textPartnerConfigs.getTextMarginBottom())) {
+          bottomMargin =
+              (int)
+                  PartnerConfigHelper.get(context)
+                      .getDimension(context, textPartnerConfigs.getTextMarginBottom());
+        } else {
+          bottomMargin = mlp.bottomMargin;
+        }
+        mlp.setMargins(mlp.leftMargin, topMargin, mlp.rightMargin, bottomMargin);
+        textView.setLayoutParams(lp);
+      }
+    }
+    textView.setGravity(textPartnerConfigs.getTextGravity());
+  }
+
+  /**
+   * Applies given partner configurations {@code textPartnerConfigs} to the {@code textView}.
+   *
+   * @param textView A text view would apply the gravity
+   * @param textPartnerConfigs A partner conflagrations contains text gravity would be set
+   */
+  public static void applyPartnerCustomizationLightStyle(
+      @NonNull TextView textView, @NonNull TextPartnerConfigs textPartnerConfigs) {
+
+    if (textView == null || textPartnerConfigs == null) {
+      return;
+    }
+
     textView.setGravity(textPartnerConfigs.getTextGravity());
   }
 
@@ -83,6 +142,8 @@
     private final PartnerConfig textLinkedColorConfig;
     private final PartnerConfig textSizeConfig;
     private final PartnerConfig textFontFamilyConfig;
+    private final PartnerConfig textMarginTopConfig;
+    private final PartnerConfig textMarginBottomConfig;
     private final int textGravity;
 
     public TextPartnerConfigs(
@@ -90,11 +151,15 @@
         @Nullable PartnerConfig textLinkedColorConfig,
         @Nullable PartnerConfig textSizeConfig,
         @Nullable PartnerConfig textFontFamilyConfig,
+        @Nullable PartnerConfig textMarginTopConfig,
+        @Nullable PartnerConfig textMarginBottomConfig,
         int textGravity) {
       this.textColorConfig = textColorConfig;
       this.textLinkedColorConfig = textLinkedColorConfig;
       this.textSizeConfig = textSizeConfig;
       this.textFontFamilyConfig = textFontFamilyConfig;
+      this.textMarginTopConfig = textMarginTopConfig;
+      this.textMarginBottomConfig = textMarginBottomConfig;
       this.textGravity = textGravity;
     }
 
@@ -114,6 +179,14 @@
       return textFontFamilyConfig;
     }
 
+    public PartnerConfig getTextMarginTop() {
+      return textMarginTopConfig;
+    }
+
+    public PartnerConfig getTextMarginBottom() {
+      return textMarginBottomConfig;
+    }
+
     public int getTextGravity() {
       return textGravity;
     }
diff --git a/main/src/com/google/android/setupdesign/util/ThemeHelper.java b/main/src/com/google/android/setupdesign/util/ThemeHelper.java
index 4247d99..0b750c9 100644
--- a/main/src/com/google/android/setupdesign/util/ThemeHelper.java
+++ b/main/src/com/google/android/setupdesign/util/ThemeHelper.java
@@ -17,11 +17,22 @@
 package com.google.android.setupdesign.util;
 
 import android.app.Activity;
+import android.content.Context;
 import android.content.Intent;
+import androidx.annotation.NonNull;
+import androidx.annotation.StyleRes;
+import com.google.android.setupcompat.PartnerCustomizationLayout;
+import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
+import com.google.android.setupcompat.util.BuildCompatUtils;
+import com.google.android.setupcompat.util.Logger;
 import com.google.android.setupcompat.util.WizardManagerHelper;
+import com.google.android.setupdesign.R;
+import java.util.Objects;
 
 /** The helper class holds the constant names of themes and util functions */
-public class ThemeHelper {
+public final class ThemeHelper {
+
+  private static final Logger LOG = new Logger("ThemeHelper");
 
   /**
    * Passed in a setup wizard intent as {@link WizardManagerHelper#EXTRA_THEME}. This is the dark
@@ -59,6 +70,16 @@
    */
   public static final String THEME_GLIF_V3_LIGHT = "glif_v3_light";
 
+  /**
+   * Placeholder, not avirailed yet.
+   */
+  public static final String THEME_GLIF_V4 = "glif_v4";
+
+  /**
+   * Placeholder, not avirailed yet.
+   */
+  public static final String THEME_GLIF_V4_LIGHT = "glif_v4_light";
+
   public static final String THEME_HOLO = "holo";
   public static final String THEME_HOLO_LIGHT = "holo_light";
   public static final String THEME_MATERIAL = "material";
@@ -92,13 +113,15 @@
         || THEME_MATERIAL_LIGHT.equals(theme)
         || THEME_GLIF_LIGHT.equals(theme)
         || THEME_GLIF_V2_LIGHT.equals(theme)
-        || THEME_GLIF_V3_LIGHT.equals(theme)) {
+        || THEME_GLIF_V3_LIGHT.equals(theme)
+        || THEME_GLIF_V4_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)) {
+        || THEME_GLIF_V3.equals(theme)
+        || THEME_GLIF_V4.equals(theme)) {
       return false;
     } else {
       return def;
@@ -125,4 +148,127 @@
   public static void applyTheme(Activity activity) {
     ThemeResolver.getDefault().applyTheme(activity);
   }
+
+  /**
+   * Checks whether SetupWizard supports the DayNight theme during setup flow; if it returns false,
+   * setup flow is always light theme.
+   *
+   * @return true if the SetupWizard is listening to system DayNight theme setting.
+   */
+  public static boolean isSetupWizardDayNightEnabled(@NonNull Context context) {
+    return PartnerConfigHelper.isSetupWizardDayNightEnabled(context);
+  }
+
+  /**
+   * Returns true if the partner provider of SetupWizard is ready to support more partner configs.
+   */
+  public static boolean shouldApplyExtendedPartnerConfig(@NonNull Context context) {
+    return PartnerConfigHelper.shouldApplyExtendedPartnerConfig(context);
+  }
+
+  /**
+   * Returns {@code true} if the partner provider of SetupWizard is ready to support dynamic color.
+   */
+  public static boolean isSetupWizardDynamicColorEnabled(@NonNull Context context) {
+    return PartnerConfigHelper.isSetupWizardDynamicColorEnabled(context);
+  }
+
+  /** Returns {@code true} if this {@code context} should apply dynamic color. */
+  public static boolean shouldApplyDynamicColor(@NonNull Context context) {
+    return shouldApplyExtendedPartnerConfig(context) && isSetupWizardDynamicColorEnabled(context);
+  }
+
+  /**
+   * Returns a theme resource id if the {@link com.google.android.setupdesign.GlifLayout} should
+   * apply dynamic color.
+   *
+   * <p>Otherwise returns {@code 0}.
+   */
+  @StyleRes
+  public static int getDynamicColorTheme(@NonNull Context context) {
+    @StyleRes int resId = 0;
+
+    Activity activity;
+    try {
+      activity = PartnerCustomizationLayout.lookupActivityFromContext(context);
+    } catch (IllegalArgumentException ex) {
+      LOG.e(Objects.requireNonNull(ex.getMessage()));
+      return resId;
+    }
+
+    boolean isSetupFlow = WizardManagerHelper.isAnySetupWizard(activity.getIntent());
+    boolean isDayNightEnabled = isSetupWizardDayNightEnabled(context);
+
+    if (isSetupFlow) {
+      // return theme for inside setup flow
+      resId =
+          isDayNightEnabled
+              ? R.style.SudDynamicColorThemeGlifV3_DayNight
+              : R.style.SudDynamicColorThemeGlifV3_Light;
+    } else {
+      // return theme for outside setup flow
+      resId =
+          isDayNightEnabled
+              ? R.style.SudFullDynamicColorThemeGlifV3_DayNight
+              : R.style.SudFullDynamicColorThemeGlifV3_Light;
+      LOG.atInfo(
+          "Return "
+              + (isDayNightEnabled
+                  ? "SudFullDynamicColorThemeGlifV3_DayNight"
+                  : "SudFullDynamicColorThemeGlifV3_Light"));
+    }
+
+    LOG.atDebug(
+        "Gets the dynamic accentColor: [Light] "
+            + colorIntToHex(context, R.color.sud_dynamic_color_accent_glif_v3_light)
+            + ", "
+            + (BuildCompatUtils.isAtLeastS()
+                ? colorIntToHex(context, android.R.color.system_accent1_600)
+                : "n/a")
+            + ", [Dark] "
+            + colorIntToHex(context, R.color.sud_dynamic_color_accent_glif_v3_dark)
+            + ", "
+            + (BuildCompatUtils.isAtLeastS()
+                ? colorIntToHex(context, android.R.color.system_accent1_100)
+                : "n/a"));
+
+    return resId;
+  }
+
+  /** Returns {@code true} if the dynamic color is set. */
+  public static boolean trySetDynamicColor(@NonNull Context context) {
+    if (!shouldApplyExtendedPartnerConfig(context)) {
+      LOG.w("SetupWizard does not supports the extended partner configs.");
+      return false;
+    }
+
+    if (!isSetupWizardDynamicColorEnabled(context)) {
+      LOG.w("SetupWizard does not support the dynamic color or supporting status unknown.");
+      return false;
+    }
+
+    Activity activity;
+    try {
+      activity = PartnerCustomizationLayout.lookupActivityFromContext(context);
+    } catch (IllegalArgumentException ex) {
+      LOG.e(Objects.requireNonNull(ex.getMessage()));
+      return false;
+    }
+
+    @StyleRes int resId = getDynamicColorTheme(context);
+    if (resId != 0) {
+      activity.setTheme(resId);
+    } else {
+      LOG.w("Error occurred on getting dynamic color theme.");
+      return false;
+    }
+
+    return true;
+  }
+
+  private static String colorIntToHex(Context context, int colorInt) {
+    return String.format("#%06X", (0xFFFFFF & context.getResources().getColor(colorInt)));
+  }
+
+  private ThemeHelper() {}
 }
diff --git a/main/src/com/google/android/setupdesign/util/ThemeResolver.java b/main/src/com/google/android/setupdesign/util/ThemeResolver.java
index 98b8883..c7a28b1 100644
--- a/main/src/com/google/android/setupdesign/util/ThemeResolver.java
+++ b/main/src/com/google/android/setupdesign/util/ThemeResolver.java
@@ -86,6 +86,17 @@
   }
 
   /**
+   * Returns the style for the given SetupWizard intent. If the specified intent does not include
+   * the intent extra {@link WizardManagerHelper#EXTRA_THEME}, the default theme 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, boolean suppressDayNight) {
+    return resolve(intent.getStringExtra(WizardManagerHelper.EXTRA_THEME), suppressDayNight);
+  }
+
+  /**
    * 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
@@ -128,7 +139,11 @@
 
   /** Reads the theme from the intent, and applies the resolved theme to the activity. */
   public void applyTheme(Activity activity) {
-    activity.setTheme(resolve(activity.getIntent()));
+    activity.setTheme(
+        resolve(
+            activity.getIntent(),
+            /* suppressDayNight= */ WizardManagerHelper.isAnySetupWizard(activity.getIntent())
+                && !ThemeHelper.isSetupWizardDayNightEnabled(activity)));
   }
 
   /**
diff --git a/main/src/com/google/android/setupdesign/view/BottomScrollView.java b/main/src/com/google/android/setupdesign/view/BottomScrollView.java
index 83527b0..a3b250d 100644
--- a/main/src/com/google/android/setupdesign/view/BottomScrollView.java
+++ b/main/src/com/google/android/setupdesign/view/BottomScrollView.java
@@ -17,10 +17,10 @@
 package com.google.android.setupdesign.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
@@ -68,7 +68,6 @@
     return listener;
   }
 
-  @VisibleForTesting
   public int getScrollThreshold() {
     return scrollThreshold;
   }
diff --git a/main/src/com/google/android/setupdesign/view/CheckableLinearLayout.java b/main/src/com/google/android/setupdesign/view/CheckableLinearLayout.java
index b12a20f..34d6ae3 100644
--- a/main/src/com/google/android/setupdesign/view/CheckableLinearLayout.java
+++ b/main/src/com/google/android/setupdesign/view/CheckableLinearLayout.java
@@ -19,10 +19,10 @@
 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
diff --git a/main/src/com/google/android/setupdesign/view/FillContentLayout.java b/main/src/com/google/android/setupdesign/view/FillContentLayout.java
index 49e195f..af49fbb 100644
--- a/main/src/com/google/android/setupdesign/view/FillContentLayout.java
+++ b/main/src/com/google/android/setupdesign/view/FillContentLayout.java
@@ -58,6 +58,10 @@
   }
 
   private void init(Context context, AttributeSet attrs, int defStyleAttr) {
+    if (isInEditMode()) {
+      return;
+    }
+
     TypedArray a =
         context.obtainStyledAttributes(attrs, R.styleable.SudFillContentLayout, defStyleAttr, 0);
 
diff --git a/main/src/com/google/android/setupdesign/view/HeaderRecyclerView.java b/main/src/com/google/android/setupdesign/view/HeaderRecyclerView.java
index b3161fd..57fc35d 100644
--- a/main/src/com/google/android/setupdesign/view/HeaderRecyclerView.java
+++ b/main/src/com/google/android/setupdesign/view/HeaderRecyclerView.java
@@ -218,6 +218,10 @@
   }
 
   private void init(AttributeSet attrs, int defStyleAttr) {
+    if (isInEditMode()) {
+      return;
+    }
+
     final TypedArray a =
         getContext()
             .obtainStyledAttributes(attrs, R.styleable.SudHeaderRecyclerView, defStyleAttr, 0);
diff --git a/main/src/com/google/android/setupdesign/view/IconUniformityAppImageView.java b/main/src/com/google/android/setupdesign/view/IconUniformityAppImageView.java
new file mode 100644
index 0000000..9f0e21d
--- /dev/null
+++ b/main/src/com/google/android/setupdesign/view/IconUniformityAppImageView.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this 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.google.android.setupdesign.view;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Matrix.ScaleToFit;
+import android.graphics.Outline;
+import android.graphics.RectF;
+import android.graphics.drawable.GradientDrawable;
+import android.os.Build;
+import androidx.annotation.ColorRes;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewOutlineProvider;
+import android.widget.ImageView;
+import androidx.annotation.Nullable;
+import androidx.core.content.ContextCompat;
+import com.google.android.setupdesign.R;
+import com.google.android.setupdesign.widget.CardBackgroundDrawable;
+
+/** An ImageView that displays an app icon according to the icon uniformity spec. */
+public class IconUniformityAppImageView extends ImageView
+    implements IconUniformityAppImageViewBindable {
+  // Scaling factor for inset on each side of legacy icon.
+  private static final Float LEGACY_SIZE_SCALE_FACTOR = 0.75f;
+
+  private static final Float LEGACY_SIZE_SCALE_MARGIN_FACTOR = (1f - LEGACY_SIZE_SCALE_FACTOR) / 2f;
+
+  // Apps & games radius is 20% of icon height.
+  private static final Float APPS_ICON_RADIUS_MULTIPLIER = 0.20f;
+
+  @ColorRes private int backdropColorResId = 0;
+
+  private static final boolean ON_L_PLUS = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
+
+  private CardBackgroundDrawable cardBackgroundDrawable;
+  /** Drawable used as background after the actual image data is visible. */
+  private final GradientDrawable backdropDrawable = new GradientDrawable();
+
+  public IconUniformityAppImageView(Context context) {
+    super(context);
+  }
+
+  public IconUniformityAppImageView(Context context, @Nullable AttributeSet attrs) {
+    super(context, attrs);
+  }
+
+  public IconUniformityAppImageView(
+      Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+    super(context, attrs, defStyleAttr);
+  }
+
+  @TargetApi(23)
+  public IconUniformityAppImageView(
+      Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+    super(context, attrs, defStyleAttr, defStyleRes);
+  }
+
+  @Override
+  protected void onFinishInflate() {
+    super.onFinishInflate();
+    backdropColorResId = R.color.sud_uniformity_backdrop_color;
+    backdropDrawable.setColor(ContextCompat.getColor(getContext(), backdropColorResId));
+  }
+
+  @Override
+  public void bindView(IconUniformityAppImageViewData viewData) {
+    if (Build.VERSION.SDK_INT <= 17) {
+      // clipPath is not supported on hardware accelerated canvas so won't take effect unless we
+      // manually set to software.
+      setLayerType(LAYER_TYPE_SOFTWARE, /* paint= */ null);
+    }
+
+    setLegacyTransformationMatrix(
+        viewData.icon.getMinimumWidth(),
+        viewData.icon.getMinimumHeight(),
+        getLayoutParams().width,
+        getLayoutParams().height);
+
+    float radius = getLayoutParams().height * APPS_ICON_RADIUS_MULTIPLIER;
+
+    if (ON_L_PLUS) {
+      setBackgroundColor(ContextCompat.getColor(getContext(), backdropColorResId));
+      backdropDrawable.setCornerRadius(radius);
+      setElevation(getContext().getResources().getDimension(R.dimen.sud_icon_uniformity_elevation));
+      setClipToOutline(true);
+      setOutlineProvider(
+          new ViewOutlineProvider() {
+            @Override
+            public void getOutline(View view, Outline outline) {
+              outline.setRoundRect(
+                  /* left= */ 0,
+                  /* top= */ 0,
+                  /* right= */ getLayoutParams().width,
+                  /* bottom= */ getLayoutParams().height,
+                  /* radius= */ radius);
+            }
+          });
+    } else {
+      cardBackgroundDrawable =
+          new CardBackgroundDrawable(
+              ContextCompat.getColor(getContext(), backdropColorResId),
+              /* radius= */ radius,
+              /* inset= */ 0f);
+      cardBackgroundDrawable.setBounds(
+          /* left= */ 0,
+          /* top= */ 0,
+          /* right= */ getLayoutParams().width,
+          /* bottom= */ getLayoutParams().height);
+    }
+
+    setImageDrawable(viewData.icon);
+  }
+
+  @Override
+  public void onRecycle() {
+    setImageDrawable(null);
+  }
+
+  @Override
+  protected void onDraw(Canvas canvas) {
+    super.onDraw(canvas);
+    if (!ON_L_PLUS && cardBackgroundDrawable != null) {
+      cardBackgroundDrawable.draw(canvas);
+    }
+    super.onDraw(canvas);
+  }
+
+  private void setLegacyTransformationMatrix(
+      float drawableWidth, float drawableHeight, float imageViewWidth, float imageViewHeight) {
+    Matrix scaleMatrix = new Matrix();
+    float verticalMargin = imageViewHeight * LEGACY_SIZE_SCALE_MARGIN_FACTOR;
+    float horizontalMargin = imageViewWidth * LEGACY_SIZE_SCALE_MARGIN_FACTOR;
+    RectF scrRectF = new RectF(0f, 0f, drawableWidth, drawableHeight);
+    RectF destRectF =
+        new RectF(
+            horizontalMargin,
+            verticalMargin,
+            imageViewWidth - horizontalMargin,
+            imageViewHeight - verticalMargin);
+
+    scaleMatrix.setRectToRect(scrRectF, destRectF, ScaleToFit.FILL);
+
+    setScaleType(ScaleType.MATRIX);
+    setImageMatrix(scaleMatrix);
+  }
+}
diff --git a/main/src/com/google/android/setupdesign/view/IconUniformityAppImageViewBindable.java b/main/src/com/google/android/setupdesign/view/IconUniformityAppImageViewBindable.java
new file mode 100644
index 0000000..1eab81b
--- /dev/null
+++ b/main/src/com/google/android/setupdesign/view/IconUniformityAppImageViewBindable.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this 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.google.android.setupdesign.view;
+
+import android.graphics.drawable.Drawable;
+
+/** ViewBindable for [IconUniformityAppImageView] */
+public interface IconUniformityAppImageViewBindable {
+
+  /** Data for [IconUniformityAppImageView] */
+  class IconUniformityAppImageViewData {
+    public Drawable icon;
+
+    public IconUniformityAppImageViewData(Drawable icon) {
+      this.icon = icon;
+    }
+  }
+
+  void bindView(IconUniformityAppImageViewData viewData);
+
+  void onRecycle();
+}
diff --git a/main/src/com/google/android/setupdesign/view/Illustration.java b/main/src/com/google/android/setupdesign/view/Illustration.java
index 14072db..e890307 100644
--- a/main/src/com/google/android/setupdesign/view/Illustration.java
+++ b/main/src/com/google/android/setupdesign/view/Illustration.java
@@ -70,6 +70,10 @@
   // 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 (isInEditMode()) {
+      return;
+    }
+
     if (attrs != null) {
       TypedArray a =
           getContext().obtainStyledAttributes(attrs, R.styleable.SudIllustration, defStyleAttr, 0);
diff --git a/main/src/com/google/android/setupdesign/view/IllustrationVideoView.java b/main/src/com/google/android/setupdesign/view/IllustrationVideoView.java
index 2e4fd71..de71f7c 100644
--- a/main/src/com/google/android/setupdesign/view/IllustrationVideoView.java
+++ b/main/src/com/google/android/setupdesign/view/IllustrationVideoView.java
@@ -28,15 +28,16 @@
 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.google.android.setupcompat.util.BuildCompatUtils;
 import com.google.android.setupdesign.R;
 import java.io.IOException;
 
@@ -76,6 +77,8 @@
 
   private boolean prepared;
 
+  private boolean shouldPauseVideoWhenFinished = true;
+
   /**
    * The visibility of this view as set by the user. This view combines this with {@link
    * #isMediaPlayerLoading} to determine the final visibility.
@@ -92,9 +95,24 @@
 
   public IllustrationVideoView(Context context, AttributeSet attrs) {
     super(context, attrs);
+    if (!isInEditMode()) {
+      init(context, attrs);
+    }
+  }
+
+  private void init(Context context, AttributeSet attrs) {
     final TypedArray a =
         context.obtainStyledAttributes(attrs, R.styleable.SudIllustrationVideoView);
     final int videoResId = a.getResourceId(R.styleable.SudIllustrationVideoView_sudVideo, 0);
+
+    // TODO: remove the usage of BuildCompatUtils#isAtLeatestS if VERSION_CODE.S is
+    // support by system.
+    if (BuildCompatUtils.isAtLeastS()) {
+      boolean shouldPauseVideo =
+          a.getBoolean(R.styleable.SudIllustrationVideoView_sudPauseVideoWhenFinished, true);
+      setPauseVideoWhenFinished(shouldPauseVideo);
+    }
+
     a.recycle();
     setVideoResource(videoResId);
 
@@ -143,12 +161,43 @@
   /**
    * Set the video to be played by this view.
    *
+   * @param resourceEntry the {@link com.google.android.setupdesign.util.Partner.ResourceEntry} of
+   *     the video, typically an MP4 under res/raw.
+   */
+  public void setVideoResourceEntry(
+      com.google.android.setupdesign.util.Partner.ResourceEntry resourceEntry) {
+    setVideoResource(resourceEntry.id, resourceEntry.packageName);
+  }
+
+  /**
+   * Set the video to be played by this view.
+   *
+   * @param resourceEntry the {@link com.google.android.setupcompat.partnerconfig.ResourceEntry} of
+   *     the video, typically an MP4 under res/raw.
+   */
+  public void setVideoResourceEntry(
+      com.google.android.setupcompat.partnerconfig.ResourceEntry resourceEntry) {
+    setVideoResource(resourceEntry.getResourceId(), resourceEntry.getPackageName());
+  }
+
+  /**
+   * 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());
   }
 
+  /**
+   * Sets whether the video pauses during the screen transition.
+   *
+   * @param paused Whether the video pauses.
+   */
+  public void setPauseVideoWhenFinished(boolean paused) {
+    shouldPauseVideoWhenFinished = paused;
+  }
+
   @Override
   public void onWindowFocusChanged(boolean hasWindowFocus) {
     super.onWindowFocusChanged(hasWindowFocus);
@@ -304,8 +353,12 @@
 
   @Override
   public void stop() {
-    if (prepared && mediaPlayer != null) {
-      mediaPlayer.pause();
+    if (shouldPauseVideoWhenFinished) {
+      if (prepared && mediaPlayer != null) {
+        mediaPlayer.pause();
+      }
+    } else {
+      // do not pause the media player.
     }
   }
 
diff --git a/main/src/com/google/android/setupdesign/view/IntrinsicSizeFrameLayout.java b/main/src/com/google/android/setupdesign/view/IntrinsicSizeFrameLayout.java
index f714205..d02839b 100644
--- a/main/src/com/google/android/setupdesign/view/IntrinsicSizeFrameLayout.java
+++ b/main/src/com/google/android/setupdesign/view/IntrinsicSizeFrameLayout.java
@@ -21,7 +21,11 @@
 import android.content.res.TypedArray;
 import android.os.Build.VERSION_CODES;
 import android.util.AttributeSet;
+import android.view.ViewGroup;
 import android.widget.FrameLayout;
+import com.google.android.setupcompat.partnerconfig.PartnerConfig;
+import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
+import com.google.android.setupcompat.util.BuildCompatUtils;
 import com.google.android.setupdesign.R;
 
 /**
@@ -54,6 +58,10 @@
   }
 
   private void init(Context context, AttributeSet attrs, int defStyleAttr) {
+    if (isInEditMode()) {
+      return;
+    }
+
     final TypedArray a =
         context.obtainStyledAttributes(
             attrs, R.styleable.SudIntrinsicSizeFrameLayout, defStyleAttr, 0);
@@ -62,6 +70,37 @@
     intrinsicWidth =
         a.getDimensionPixelSize(R.styleable.SudIntrinsicSizeFrameLayout_android_width, 0);
     a.recycle();
+
+    if (BuildCompatUtils.isAtLeastS()) {
+      if (PartnerConfigHelper.get(context)
+          .isPartnerConfigAvailable(PartnerConfig.CONFIG_CARD_VIEW_INTRINSIC_HEIGHT)) {
+        intrinsicHeight =
+            (int)
+                PartnerConfigHelper.get(context)
+                    .getDimension(context, PartnerConfig.CONFIG_CARD_VIEW_INTRINSIC_HEIGHT);
+      }
+      if (PartnerConfigHelper.get(context)
+          .isPartnerConfigAvailable(PartnerConfig.CONFIG_CARD_VIEW_INTRINSIC_WIDTH)) {
+        intrinsicWidth =
+            (int)
+                PartnerConfigHelper.get(context)
+                    .getDimension(context, PartnerConfig.CONFIG_CARD_VIEW_INTRINSIC_WIDTH);
+      }
+    }
+  }
+
+  @Override
+  public void setLayoutParams(ViewGroup.LayoutParams params) {
+    if (BuildCompatUtils.isAtLeastS()) {
+      // When both intrinsic height and width are 0, the card view style would be removed from
+      // foldable/tablet layout. It must set the layout width and height to MATCH_PARENT and then it
+      // can ignore the IntrinsicSizeFrameLayout from the foldable/tablet layout.
+      if (intrinsicHeight == 0 && intrinsicWidth == 0) {
+        params.width = ViewGroup.LayoutParams.MATCH_PARENT;
+        params.height = ViewGroup.LayoutParams.MATCH_PARENT;
+      }
+    }
+    super.setLayoutParams(params);
   }
 
   @Override
diff --git a/main/src/com/google/android/setupdesign/view/NavigationBar.java b/main/src/com/google/android/setupdesign/view/NavigationBar.java
index 9d978f0..df3bee4 100644
--- a/main/src/com/google/android/setupdesign/view/NavigationBar.java
+++ b/main/src/com/google/android/setupdesign/view/NavigationBar.java
@@ -21,12 +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.google.android.setupdesign.R;
 
 /**
@@ -103,6 +103,10 @@
   // 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() {
+    if (isInEditMode()) {
+      return;
+    }
+
     View.inflate(getContext(), R.layout.sud_navbar_view, this);
     nextButton = (Button) findViewById(R.id.sud_navbar_next);
     backButton = (Button) findViewById(R.id.sud_navbar_back);
diff --git a/main/src/com/google/android/setupdesign/view/NavigationBarButton.java b/main/src/com/google/android/setupdesign/view/NavigationBarButton.java
index 44a5b85..bb1e506 100644
--- a/main/src/com/google/android/setupdesign/view/NavigationBarButton.java
+++ b/main/src/com/google/android/setupdesign/view/NavigationBarButton.java
@@ -23,9 +23,9 @@
 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
@@ -45,6 +45,10 @@
   }
 
   private void init() {
+    if (isInEditMode()) {
+      return;
+    }
+
     // 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) {
diff --git a/main/src/com/google/android/setupdesign/view/RichTextView.java b/main/src/com/google/android/setupdesign/view/RichTextView.java
index 338b856..f3348b4 100644
--- a/main/src/com/google/android/setupdesign/view/RichTextView.java
+++ b/main/src/com/google/android/setupdesign/view/RichTextView.java
@@ -106,6 +106,10 @@
   }
 
   private void init() {
+    if (isInEditMode()) {
+      return;
+    }
+
     accessibilityHelper = new LinkAccessibilityHelper(this);
     ViewCompat.setAccessibilityDelegate(this, accessibilityHelper);
   }
diff --git a/main/src/com/google/android/setupdesign/view/StickyHeaderListView.java b/main/src/com/google/android/setupdesign/view/StickyHeaderListView.java
index 3efb85c..07d1781 100644
--- a/main/src/com/google/android/setupdesign/view/StickyHeaderListView.java
+++ b/main/src/com/google/android/setupdesign/view/StickyHeaderListView.java
@@ -73,6 +73,10 @@
   }
 
   private void init(AttributeSet attrs, int defStyleAttr) {
+    if (isInEditMode()) {
+      return;
+    }
+
     final TypedArray a =
         getContext()
             .obtainStyledAttributes(attrs, R.styleable.SudStickyHeaderListView, defStyleAttr, 0);
diff --git a/main/src/com/google/android/setupdesign/widget/CardBackgroundDrawable.java b/main/src/com/google/android/setupdesign/widget/CardBackgroundDrawable.java
new file mode 100644
index 0000000..b354921
--- /dev/null
+++ b/main/src/com/google/android/setupdesign/widget/CardBackgroundDrawable.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this 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.google.android.setupdesign.widget;
+
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Path.Direction;
+import android.graphics.Path.FillType;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+import androidx.annotation.ColorInt;
+import androidx.annotation.Nullable;
+
+/** A rounded rectangle drawable. */
+public class CardBackgroundDrawable extends Drawable {
+  private final float inset;
+
+  private final Paint paint;
+  private final RectF cardBounds = new RectF();
+  private final Path clipPath = new Path();
+
+  private float cornerRadius;
+  private boolean dirty = false;
+
+  /**
+   * @param color Background color of the card to be rendered
+   * @param radius Corner rounding radius
+   * @param inset Inset from the edge of the canvas to the card
+   */
+  public CardBackgroundDrawable(@ColorInt int color, float radius, float inset) {
+    cornerRadius = radius;
+    paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
+    paint.setColor(color);
+    this.inset = inset;
+  }
+
+  @Override
+  public void onBoundsChange(Rect bounds) {
+    super.onBoundsChange(bounds);
+    dirty = true;
+  }
+
+  @Override
+  public void setColorFilter(@Nullable ColorFilter cf) {
+    paint.setColorFilter(cf);
+  }
+
+  @Override
+  public int getOpacity() {
+    return PixelFormat.OPAQUE;
+  }
+
+  public void setCornerRadius(float radius) {
+    if (cornerRadius == radius) {
+      return;
+    }
+
+    cornerRadius = radius;
+    dirty = true;
+    invalidateSelf();
+  }
+
+  @Override
+  public void draw(Canvas canvas) {
+    if (dirty) {
+      buildComponents(getBounds());
+      dirty = false;
+    }
+
+    if (cornerRadius > 0) {
+      canvas.clipPath(clipPath);
+    }
+  }
+
+  @Override
+  public void setAlpha(int alpha) {}
+
+  private void buildComponents(Rect bounds) {
+    cardBounds.set(bounds);
+    cardBounds.inset(inset, inset);
+
+    clipPath.reset();
+    clipPath.setFillType(FillType.EVEN_ODD);
+    clipPath.addRoundRect(cardBounds, cornerRadius, cornerRadius, Direction.CW);
+  }
+}