Merging from ub-launcher3-master @ build 6519151

Bug:154410862
Test: manual, presubmit on the source branch
x20/teams/android-launcher/merge/ub-launcher3-master_rvc-d1-dev_6519151.html

Change-Id: I3b0891cd7ab31f39844a843818dade3709cd4437
diff --git a/res/drawable/option_border_custom.xml b/res/drawable/option_border_custom.xml
index 0fe8d12..648bc4f 100644
--- a/res/drawable/option_border_custom.xml
+++ b/res/drawable/option_border_custom.xml
@@ -18,7 +18,7 @@
     <item android:id="@android:id/mask">
         <shape android:shape="rectangle">
             <solid android:color="@android:color/white"/>
-            <corners android:radius="4dp" />
+            <corners android:radius="?android:dialogCornerRadius" />
         </shape>
     </item>
     <item android:drawable="@drawable/option_border_edge_custom" />
diff --git a/res/drawable/option_border_edge_custom.xml b/res/drawable/option_border_edge_custom.xml
index 1fba26b..1863c5d 100644
--- a/res/drawable/option_border_edge_custom.xml
+++ b/res/drawable/option_border_edge_custom.xml
@@ -20,7 +20,7 @@
             <stroke
                 android:color="@color/option_border_color"
                 android:width="@dimen/option_selected_border_width" />
-            <corners android:radius="4dp" />
+            <corners android:radius="?android:dialogCornerRadius" />
         </shape>
     </item>
     <item android:state_activated="false">
@@ -29,7 +29,7 @@
                 android:color="@color/black_24_alpha"
                 android:alpha="0.24"
                 android:width="@dimen/option_border_width" />
-            <corners android:radius="4dp" />
+            <corners android:radius="?android:dialogCornerRadius" />
         </shape>
     </item>
 </selector>
diff --git a/res/layout-land/fragment_grid_picker.xml b/res/layout-land/fragment_grid_picker.xml
index 6c2d8c5..dab9308 100644
--- a/res/layout-land/fragment_grid_picker.xml
+++ b/res/layout-land/fragment_grid_picker.xml
@@ -31,13 +31,16 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:orientation="horizontal">
-        <com.android.wallpaper.widget.PreviewPager
-            android:id="@+id/grid_preview_pager"
+        <FrameLayout
             android:layout_width="0dp"
             android:layout_height="match_parent"
             android:layout_weight="1"
-            android:background="@color/secondary_color"
-            app:card_style="screen_aspect_ratio"/>
+            android:clipToPadding="false"
+            android:paddingTop="@dimen/preview_content_padding_top"
+            android:paddingBottom="@dimen/preview_content_padding_bottom"
+            android:background="@color/fullscreen_preview_background">
+            <include layout="@layout/grid_preview_card"/>
+        </FrameLayout>
 
         <FrameLayout
             android:id="@+id/options_section"
diff --git a/res/layout-land/fragment_theme_picker.xml b/res/layout-land/fragment_theme_picker.xml
index dc30232..915fb47 100644
--- a/res/layout-land/fragment_theme_picker.xml
+++ b/res/layout-land/fragment_theme_picker.xml
@@ -31,39 +31,28 @@
             android:layout_height="match_parent"
             android:orientation="horizontal">
 
-            <com.android.wallpaper.widget.PreviewPager
-                android:id="@+id/theme_preview_pager"
+            <FrameLayout
                 android:layout_width="0dp"
                 android:layout_height="match_parent"
-                android:layout_weight="1"
-                android:background="@color/secondary_color"/>
-            <View
-                android:layout_width="1dp"
-                android:layout_height="match_parent"
-                android:background="@color/divider_color"/>
-            <LinearLayout
-                android:id="@+id/options_section"
-                android:layout_width="0dp"
-                android:layout_height="match_parent"
-                android:layout_weight="1"
-                android:paddingVertical="10dp"
-                android:orientation="vertical">
-
-                <androidx.recyclerview.widget.RecyclerView
-                    android:id="@+id/options_container"
+                android:layout_weight="1">
+                <FrameLayout
+                    android:id="@+id/preview_card_container"
                     android:layout_width="match_parent"
-                    android:layout_height="0dp"
-                    android:layout_weight="1"/>
+                    android:layout_height="@dimen/preview_content_height"
+                    android:paddingTop="@dimen/preview_content_padding_top"
+                    android:paddingBottom="@dimen/preview_content_padding_bottom"
+                    android:clipToPadding="false"
+                    android:background="@color/fullscreen_preview_background">
+                    <include layout="@layout/theme_preview_card_v2"/>
+                </FrameLayout>
+            </FrameLayout>
 
-                <Button
-                    android:id="@+id/apply_button"
-                    style="@style/ActionPrimaryButton"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:layout_gravity="end"
-                    android:paddingHorizontal="10dp"
-                    android:text="@string/apply_theme_btn"/>
-            </LinearLayout>
+            <androidx.recyclerview.widget.RecyclerView
+                android:id="@+id/options_container"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"
+                android:paddingVertical="10dp" />
         </LinearLayout>
 
         <androidx.core.widget.ContentLoadingProgressBar
diff --git a/res/layout/activity_custom_theme.xml b/res/layout/activity_custom_theme.xml
index b2aaa73..24d58b7 100644
--- a/res/layout/activity_custom_theme.xml
+++ b/res/layout/activity_custom_theme.xml
@@ -33,7 +33,8 @@
         android:id="@+id/custom_theme_nav"
         android:layout_width="match_parent"
         android:layout_height="@dimen/custom_theme_nav_height"
-        android:layout_marginHorizontal="12dp">
+        android:paddingHorizontal="12dp"
+        android:background="?android:colorPrimary">
         <Button
             android:id="@+id/previous_button"
             style="@style/ActionSecondaryButton"
diff --git a/res/layout/custom_theme_option.xml b/res/layout/custom_theme_option.xml
index c91cbf1..2805185 100644
--- a/res/layout/custom_theme_option.xml
+++ b/res/layout/custom_theme_option.xml
@@ -16,15 +16,22 @@
 -->
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="wrap_content"
-    android:layout_height="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingHorizontal="@dimen/option_padding_horizontal"
+    android:paddingBottom="@dimen/option_bottom_margin"
+    android:clipChildren="false"
+    android:clipToPadding="false"
     android:orientation="vertical">
 
     <TextView
         android:id="@+id/option_label"
-        android:layout_width="wrap_content"
+        android:layout_width="@dimen/option_tile_width"
         android:layout_height="wrap_content"
         android:layout_gravity="center_horizontal"
         android:layout_marginBottom="@dimen/theme_option_label_margin"
+        android:ellipsize="end"
+        android:gravity="center_horizontal"
+        android:maxLines="1"
         android:textAppearance="@style/OptionTitleTextAppearance"/>
     <FrameLayout
         android:id="@+id/option_tile"
@@ -35,8 +42,8 @@
         android:paddingVertical="@dimen/option_tile_padding_vertical"
         android:background="@drawable/option_border_custom">
         <ImageView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
+            android:layout_width="@dimen/option_icon_size"
+            android:layout_height="@dimen/option_icon_size"
             android:layout_gravity="center"
             android:src="@drawable/ic_add_24px"
             android:tint="?android:attr/colorAccent" />
diff --git a/res/layout/fragment_grid_full_preview.xml b/res/layout/fragment_grid_full_preview.xml
index 1c01743..d39be36 100644
--- a/res/layout/fragment_grid_full_preview.xml
+++ b/res/layout/fragment_grid_full_preview.xml
@@ -26,33 +26,11 @@
         android:layout_width="match_parent"
         android:layout_height="0dp"
         android:layout_weight="1"
+        android:paddingTop="@dimen/full_preview_page_default_padding_top"
+        android:paddingBottom="@dimen/full_preview_page_default_padding_bottom"
+        android:clipToPadding="false"
         android:background="@color/fullscreen_preview_background">
 
-        <androidx.cardview.widget.CardView
-            style="@style/FullContentPreviewCard"
-            android:id="@+id/grid_full_preview_card"
-            android:layout_marginTop="24dp"
-            android:layout_marginBottom="32dp"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:layout_gravity="center">
-
-            <ImageView
-                android:id="@+id/grid_full_preview_image"
-                android:layout_width="match_parent"
-                android:layout_height="match_parent"
-                android:background="@color/primary_color"/>
-
-            <SurfaceView
-                android:id="@+id/grid_full_preview_option_surface"
-                android:layout_width="match_parent"
-                android:layout_height="match_parent"/>
-
-            <SurfaceView
-                android:id="@+id/grid_full_preview_wallpaper_surface"
-                android:layout_width="match_parent"
-                android:layout_height="match_parent" />
-
-        </androidx.cardview.widget.CardView>
+        <include layout="@layout/grid_preview_card"/>
     </FrameLayout>
 </LinearLayout>
diff --git a/res/layout/fragment_grid_picker.xml b/res/layout/fragment_grid_picker.xml
index 5bf96e1..af70e0c 100644
--- a/res/layout/fragment_grid_picker.xml
+++ b/res/layout/fragment_grid_picker.xml
@@ -16,7 +16,6 @@
 -->
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical"
@@ -27,26 +26,21 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent">
 
-        <androidx.constraintlayout.widget.ConstraintLayout
+        <LinearLayout
             android:id="@+id/content_section"
             android:layout_width="match_parent"
-            android:layout_height="match_parent">
+            android:layout_height="match_parent"
+            android:orientation="vertical">
 
-            <com.android.wallpaper.widget.PreviewPager
-                android:id="@+id/grid_preview_pager"
+            <FrameLayout
                 android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:background="@color/preview_pager_background"
-                app:card_style="screen_aspect_ratio"
-                app:layout_constrainedHeight="true"
-                app:layout_constraintTop_toTopOf="parent"
-                app:layout_constraintBottom_toTopOf="@id/options_title"
-                app:layout_constraintStart_toStartOf="parent"
-                app:layout_constraintEnd_toEndOf="parent"
-                app:layout_constraintHeight_max="@dimen/preview_pager_max_height"
-                app:layout_constraintHeight_min="@dimen/preview_pager_min_height"
-                app:layout_constraintVertical_bias="0.0"
-                app:layout_constraintVertical_chainStyle="spread_inside"/>
+                android:layout_height="@dimen/preview_content_height"
+                android:paddingTop="@dimen/preview_content_padding_top"
+                android:paddingBottom="@dimen/preview_content_padding_bottom"
+                android:clipToPadding="false"
+                android:background="@color/fullscreen_preview_background">
+                <include layout="@layout/grid_preview_card"/>
+            </FrameLayout>
 
             <TextView
                 android:id="@+id/options_title"
@@ -57,22 +51,14 @@
                 android:lineHeight="24dp"
                 android:singleLine="true"
                 android:text="@string/grid_options_title"
-                android:textAppearance="@style/TitleTextAppearance"
-                app:layout_constraintTop_toBottomOf="@id/grid_preview_pager"
-                app:layout_constraintBottom_toTopOf="@id/options_container"
-                app:layout_constraintStart_toStartOf="parent"
-                app:layout_constraintEnd_toEndOf="parent"/>
+                android:textAppearance="@style/TitleTextAppearance"/>
 
             <androidx.recyclerview.widget.RecyclerView
                 android:id="@+id/options_container"
                 android:layout_width="match_parent"
-                android:layout_height="@dimen/options_container_height"
-                android:layout_gravity="center_horizontal"
-                app:layout_constraintTop_toBottomOf="@+id/options_title"
-                app:layout_constraintStart_toStartOf="parent"
-                app:layout_constraintEnd_toEndOf="parent"
-                app:layout_constraintVertical_bias="1.0"/>
-        </androidx.constraintlayout.widget.ConstraintLayout>
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_horizontal"/>
+        </LinearLayout>
 
         <androidx.core.widget.ContentLoadingProgressBar
             android:id="@+id/loading_indicator"
diff --git a/res/layout/fragment_theme_full_preview.xml b/res/layout/fragment_theme_full_preview.xml
new file mode 100644
index 0000000..7573c5b
--- /dev/null
+++ b/res/layout/fragment_theme_full_preview.xml
@@ -0,0 +1,36 @@
+<?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"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <include layout="@layout/section_header"/>
+
+    <FrameLayout
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"
+        android:paddingTop="@dimen/full_preview_page_default_padding_top"
+        android:paddingBottom="@dimen/full_preview_page_default_padding_bottom"
+        android:clipToPadding="false"
+        android:background="@color/fullscreen_preview_background">
+
+        <include layout="@layout/theme_preview_card_v2"/>
+    </FrameLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/fragment_theme_picker.xml b/res/layout/fragment_theme_picker.xml
index d15d93f..704114f 100644
--- a/res/layout/fragment_theme_picker.xml
+++ b/res/layout/fragment_theme_picker.xml
@@ -16,7 +16,6 @@
 -->
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical"
@@ -27,49 +26,31 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent">
 
-        <androidx.constraintlayout.widget.ConstraintLayout
+        <LinearLayout
             android:id="@+id/content_section"
             android:layout_width="match_parent"
-            android:layout_height="match_parent">
+            android:layout_height="match_parent"
+            android:orientation="vertical">
 
-            <com.android.wallpaper.widget.PreviewPager
-                android:id="@+id/theme_preview_pager"
+            <FrameLayout
+                android:id="@+id/preview_card_container"
                 android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:background="@color/preview_pager_background"
-                app:layout_constrainedHeight="true"
-                app:layout_constraintBottom_toTopOf="@id/options_container"
-                app:layout_constraintEnd_toEndOf="parent"
-                app:layout_constraintHeight_max="@dimen/preview_pager_max_height"
-                app:layout_constraintStart_toStartOf="parent"
-                app:layout_constraintTop_toTopOf="parent"
-                app:layout_constraintVertical_bias="0.0"
-                app:layout_constraintVertical_chainStyle="spread_inside"/>
+                android:layout_height="@dimen/preview_content_height"
+                android:paddingTop="@dimen/preview_content_padding_top"
+                android:paddingBottom="@dimen/preview_content_padding_bottom"
+                android:clipToPadding="false"
+                android:background="@color/fullscreen_preview_background">
+                <include layout="@layout/theme_preview_card_v2"/>
+            </FrameLayout>
 
             <androidx.recyclerview.widget.RecyclerView
                 android:id="@+id/options_container"
                 android:layout_width="match_parent"
-                android:layout_height="@dimen/options_container_height"
+                android:layout_height="0dp"
                 android:layout_gravity="bottom|center_horizontal"
                 android:layout_marginTop="10dp"
-                android:layout_weight="1"
-                app:layout_constraintEnd_toEndOf="parent"
-                app:layout_constraintStart_toStartOf="parent"
-                app:layout_constraintTop_toBottomOf="@+id/theme_preview_pager"
-                app:layout_constraintVertical_bias="1.0"/>
-
-            <Button
-                android:id="@+id/apply_button"
-                style="@style/ActionPrimaryButton"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_gravity="end"
-                android:layout_marginEnd="10dp"
-                android:layout_marginBottom="10dp"
-                android:text="@string/apply_theme_btn"
-                app:layout_constraintBottom_toBottomOf="parent"
-                app:layout_constraintEnd_toEndOf="parent"/>
-        </androidx.constraintlayout.widget.ConstraintLayout>
+                android:layout_weight="1"/>
+        </LinearLayout>
 
         <androidx.core.widget.ContentLoadingProgressBar
             android:id="@+id/loading_indicator"
diff --git a/res/layout/grid_preview_card.xml b/res/layout/grid_preview_card.xml
index 90b5578..2939f80 100644
--- a/res/layout/grid_preview_card.xml
+++ b/res/layout/grid_preview_card.xml
@@ -18,18 +18,22 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     style="@style/FullContentPreviewCard"
     android:layout_width="match_parent"
-    android:layout_height="match_parent">
+    android:layout_height="match_parent"
+    android:layout_gravity="center">
 
     <ImageView
-        android:id="@+id/grid_preview_image"
+        android:id="@+id/wallpaper_preview_image"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:background="@color/primary_color" />
 
     <SurfaceView
-        android:id="@+id/grid_preview_surface"
+        android:id="@+id/wallpaper_preview_surface"
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:background="@color/primary_color" />
+        android:layout_height="match_parent" />
 
+    <FrameLayout
+        android:id="@+id/grid_preview_container"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
 </androidx.cardview.widget.CardView>
\ No newline at end of file
diff --git a/res/layout/theme_info_view.xml b/res/layout/theme_info_view.xml
new file mode 100644
index 0000000..2bb8701
--- /dev/null
+++ b/res/layout/theme_info_view.xml
@@ -0,0 +1,77 @@
+<?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.
+-->
+<com.android.customization.widget.ThemeInfoView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:gravity="center_horizontal"
+    android:orientation="vertical"
+    android:paddingHorizontal="@dimen/wallpaper_info_pane_horizontal_padding"
+    android:paddingTop="@dimen/wallpaper_info_pane_top_padding"
+    android:paddingBottom="@dimen/wallpaper_info_pane_bottom_padding"
+    android:theme="@style/WallpaperPicker.BottomPaneStyle">
+
+    <TextView
+        android:id="@+id/style_info_title"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="@dimen/theme_info_margin"
+        android:gravity="center"
+        android:lineHeight="24dp"
+        android:textAppearance="@style/SubtitleTextAppearance"
+        android:textColor="@color/action_bar_bottom_sheet_text_color"
+        android:textSize="16sp"
+        android:text="@string/style_info_description"/>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:gravity="center">
+
+        <TextView
+            android:id="@+id/font_preview"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:layout_marginHorizontal="@dimen/theme_info_margin"
+            android:textSize="@dimen/theme_info_text_size"
+            android:textColor="?android:attr/colorForeground"
+            android:text="@string/font_component_option_thumbnail"/>
+
+        <ImageView
+            android:id="@+id/qs_preview_icon"
+            android:layout_width="@dimen/theme_info_icon_size"
+            android:layout_height="@dimen/theme_info_icon_size"
+            android:layout_marginHorizontal="@dimen/theme_info_margin"
+            android:tint="@color/theme_preview_icon_color"/>
+
+        <ImageView
+            android:id="@+id/app_preview_icon"
+            android:layout_width="@dimen/theme_info_icon_size"
+            android:layout_height="@dimen/theme_info_icon_size"
+            android:layout_marginHorizontal="@dimen/theme_info_margin"
+            android:layout_marginVertical="@dimen/theme_info_app_preview_icon_margin"
+            android:elevation="@dimen/theme_info_app_preview_icon_elevation"/>
+
+        <ImageView
+            android:id="@+id/shape_preview_icon"
+            android:layout_width="@dimen/theme_info_icon_size"
+            android:layout_height="@dimen/theme_info_icon_size"
+            android:layout_marginHorizontal="@dimen/theme_info_margin"/>
+    </LinearLayout>
+</com.android.customization.widget.ThemeInfoView>
\ No newline at end of file
diff --git a/res/layout/theme_option.xml b/res/layout/theme_option.xml
index 98fae71..557d532 100644
--- a/res/layout/theme_option.xml
+++ b/res/layout/theme_option.xml
@@ -17,7 +17,7 @@
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:paddingRight="2dp"
+    android:paddingHorizontal="@dimen/option_padding_horizontal"
     android:paddingBottom="@dimen/option_bottom_margin"
     android:clipChildren="false"
     android:clipToPadding="false"
@@ -47,21 +47,21 @@
             android:layout_height="@dimen/theme_option_icon_sample_height"
             android:layout_alignParentTop="true"
             android:layout_alignParentLeft="true"
-            android:layout_margin="@dimen/theme_option_sample_padding"
             android:tint="?android:colorForeground"/>
         <ImageView
             android:id="@+id/theme_option_shape"
             android:layout_width="@dimen/theme_option_shape_sample_width"
             android:layout_height="@dimen/theme_option_shape_sample_height"
-            android:layout_alignParentRight="true"
             android:layout_alignBottom="@+id/theme_option_icon"
-            android:layout_marginHorizontal="@dimen/theme_option_sample_padding"/>
+            android:layout_toEndOf="@id/theme_option_icon"
+            android:layout_marginLeft="@dimen/theme_option_sample_margin"/>
         <TextView
             android:id="@+id/theme_option_font"
             android:layout_width="@dimen/theme_option_font_sample_width"
             android:layout_height="@dimen/theme_option_font_sample_height"
-            android:layout_alignParentBottom="true"
-            android:layout_gravity="center_horizontal"
+            android:layout_gravity="center"
+            android:layout_below="@id/theme_option_icon"
+            android:layout_marginTop="@dimen/option_bottom_margin"
             android:autoSizeMaxTextSize="@dimen/theme_option_font_text_size"
             android:autoSizeMinTextSize="@dimen/theme_option_font_min_text_size"
             android:autoSizeTextType="uniform"
diff --git a/res/layout/theme_preview_app_icon_shape.xml b/res/layout/theme_preview_app_icon_shape.xml
new file mode 100644
index 0000000..c6f4cd9
--- /dev/null
+++ b/res/layout/theme_preview_app_icon_shape.xml
@@ -0,0 +1,122 @@
+<?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"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingHorizontal="@dimen/preview_theme_app_icon_shape_padding_horizontal"
+    android:orientation="vertical">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingTop="@dimen/preview_theme_app_icon_shape_padding_top"
+        android:orientation="horizontal">
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:gravity="center_horizontal"
+            android:orientation="vertical">
+            <ImageView
+                android:id="@+id/shape_preview_icon_0"
+                android:layout_width="@dimen/preview_theme_shape_size"
+                android:layout_height="@dimen/preview_theme_shape_size"
+                android:elevation="4dp"/>
+            <TextView
+                android:id="@+id/shape_preview_icon_app_name_0"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="@dimen/preview_theme_app_icon_shape_text_margin_top"
+                android:textSize="@dimen/preview_theme_app_icon_shape_text_size"
+                android:lineHeight="20dp"/>
+        </LinearLayout>
+
+        <Space
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"/>
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:gravity="center_horizontal"
+            android:orientation="vertical">
+            <ImageView
+                android:id="@+id/shape_preview_icon_1"
+                android:layout_width="@dimen/preview_theme_shape_size"
+                android:layout_height="@dimen/preview_theme_shape_size"
+                android:elevation="4dp"/>
+            <TextView
+                android:id="@+id/shape_preview_icon_app_name_1"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="@dimen/preview_theme_app_icon_shape_text_margin_top"
+                android:textSize="@dimen/preview_theme_app_icon_shape_text_size"
+                android:lineHeight="20dp"/>
+        </LinearLayout>
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingTop="@dimen/preview_theme_app_icon_shape_padding_top"
+        android:orientation="horizontal">
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:gravity="center_horizontal"
+            android:orientation="vertical">
+            <ImageView
+                android:id="@+id/shape_preview_icon_2"
+                android:layout_width="@dimen/preview_theme_shape_size"
+                android:layout_height="@dimen/preview_theme_shape_size"
+                android:elevation="4dp"/>
+            <TextView
+                android:id="@+id/shape_preview_icon_app_name_2"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="@dimen/preview_theme_app_icon_shape_text_margin_top"
+                android:textSize="@dimen/preview_theme_app_icon_shape_text_size"
+                android:lineHeight="20dp"/>
+        </LinearLayout>
+
+        <Space
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"/>
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:gravity="center_horizontal"
+            android:orientation="vertical">
+            <ImageView
+                android:id="@+id/shape_preview_icon_3"
+                android:layout_width="@dimen/preview_theme_shape_size"
+                android:layout_height="@dimen/preview_theme_shape_size"
+                android:elevation="4dp"/>
+            <TextView
+                android:id="@+id/shape_preview_icon_app_name_3"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="@dimen/preview_theme_app_icon_shape_text_margin_top"
+                android:textSize="@dimen/preview_theme_app_icon_shape_text_size"
+                android:lineHeight="20dp"/>
+        </LinearLayout>
+    </LinearLayout>
+</LinearLayout>
diff --git a/res/layout/theme_preview_card_v2.xml b/res/layout/theme_preview_card_v2.xml
new file mode 100644
index 0000000..74d1e68
--- /dev/null
+++ b/res/layout/theme_preview_card_v2.xml
@@ -0,0 +1,39 @@
+<?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.
+-->
+<androidx.cardview.widget.CardView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    style="@style/FullContentPreviewCard"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layout_gravity="center">
+
+    <ImageView
+        android:id="@+id/wallpaper_preview_image"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@color/primary_color" />
+
+    <SurfaceView
+        android:id="@+id/wallpaper_preview_surface"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+    <FrameLayout
+        android:id="@+id/theme_preview_container"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+</androidx.cardview.widget.CardView>
\ No newline at end of file
diff --git a/res/layout/theme_preview_color_icons.xml b/res/layout/theme_preview_color_icons.xml
new file mode 100644
index 0000000..7a068ee
--- /dev/null
+++ b/res/layout/theme_preview_color_icons.xml
@@ -0,0 +1,169 @@
+<?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.
+-->
+<androidx.cardview.widget.CardView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/color_icons_section"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="@color/primary_color">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingHorizontal="@dimen/preview_theme_color_icons_padding_horizontal"
+        android:orientation="vertical">
+
+        <!-- Title -->
+        <TextView
+            android:id="@+id/color_icons_section_title"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/preview_theme_color_icons_title_margin_top"
+            android:layout_marginBottom="@dimen/preview_theme_color_icons_title_margin_bottom"
+            android:text="@string/theme_preview_icons_section_title"
+            android:textSize="@dimen/preview_theme_color_icons_title_text_size"
+            android:lineHeight="16dp"
+            android:gravity="center"/>
+
+        <!-- QS icons -->
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal">
+            <FrameLayout
+                android:layout_width="@dimen/preview_theme_color_icons_icon_size"
+                android:layout_height="@dimen/preview_theme_color_icons_icon_size">
+                <ImageView
+                    android:id="@+id/preview_color_qs_0_bg"
+                    android:layout_width="@dimen/preview_theme_color_icons_icon_size"
+                    android:layout_height="@dimen/preview_theme_color_icons_icon_size"/>
+                <ImageView
+                    android:id="@+id/preview_color_qs_0_icon"
+                    android:layout_width="@dimen/preview_theme_color_icons_tile_size"
+                    android:layout_height="@dimen/preview_theme_color_icons_tile_size"
+                    android:tint="@color/tile_enabled_icon_color"
+                    android:layout_gravity="center"/>
+            </FrameLayout>
+            <Space
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"/>
+            <FrameLayout
+                android:layout_width="@dimen/preview_theme_color_icons_icon_size"
+                android:layout_height="@dimen/preview_theme_color_icons_icon_size"
+                android:layout_gravity="center_horizontal">
+                <ImageView
+                    android:id="@+id/preview_color_qs_1_bg"
+                    android:layout_width="@dimen/preview_theme_color_icons_icon_size"
+                    android:layout_height="@dimen/preview_theme_color_icons_icon_size"/>
+                <ImageView
+                    android:id="@+id/preview_color_qs_1_icon"
+                    android:layout_width="@dimen/preview_theme_color_icons_tile_size"
+                    android:layout_height="@dimen/preview_theme_color_icons_tile_size"
+                    android:tint="@color/tile_enabled_icon_color"
+                    android:layout_gravity="center"/>
+            </FrameLayout>
+            <Space
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"/>
+            <FrameLayout
+                android:layout_width="@dimen/preview_theme_color_icons_icon_size"
+                android:layout_height="@dimen/preview_theme_color_icons_icon_size">
+                <ImageView
+                    android:id="@+id/preview_color_qs_2_bg"
+                    android:layout_width="@dimen/preview_theme_color_icons_icon_size"
+                    android:layout_height="@dimen/preview_theme_color_icons_icon_size"/>
+                <ImageView
+                    android:id="@+id/preview_color_qs_2_icon"
+                    android:layout_width="@dimen/preview_theme_color_icons_tile_size"
+                    android:layout_height="@dimen/preview_theme_color_icons_tile_size"
+                    android:tint="@color/tile_enabled_icon_color"
+                    android:layout_gravity="center"/>
+            </FrameLayout>
+            <Space
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"/>
+            <FrameLayout
+                android:layout_width="@dimen/preview_theme_color_icons_icon_size"
+                android:layout_height="@dimen/preview_theme_color_icons_icon_size">
+                <ImageView
+                    android:id="@+id/preview_color_qs_3_bg"
+                    android:layout_width="@dimen/preview_theme_color_icons_icon_size"
+                    android:layout_height="@dimen/preview_theme_color_icons_icon_size"/>
+                <ImageView
+                    android:id="@+id/preview_color_qs_3_icon"
+                    android:layout_width="@dimen/preview_theme_color_icons_tile_size"
+                    android:layout_height="@dimen/preview_theme_color_icons_tile_size"
+                    android:tint="@color/tile_enabled_icon_color"
+                    android:layout_gravity="center"/>
+            </FrameLayout>
+        </LinearLayout>
+
+        <!-- Icons of CheckBox/RadioButton/Switch. -->
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/preview_theme_color_icons_buttons_margin_top"
+            android:layout_marginBottom="@dimen/preview_theme_color_icons_buttons_margin_bottom"
+            android:orientation="horizontal">
+            <FrameLayout
+                android:layout_width="@dimen/preview_theme_icon_size"
+                android:layout_height="@dimen/preview_theme_icon_size">
+                <CheckBox
+                    android:id="@+id/preview_check_selected"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="center"
+                    android:checked="true"
+                    android:enabled="false"/>
+            </FrameLayout>
+            <Space
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="1" />
+            <FrameLayout
+                android:layout_width="@dimen/preview_theme_icon_size"
+                android:layout_height="@dimen/preview_theme_icon_size">
+                <RadioButton
+                    android:id="@+id/preview_radio_selected"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="center"
+                    android:checked="true"
+                    android:enabled="false"/>
+            </FrameLayout>
+            <Space
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="1" />
+            <FrameLayout
+                android:layout_width="@dimen/preview_theme_icon_size"
+                android:layout_height="@dimen/preview_theme_icon_size">
+                <Switch
+                    android:id="@+id/preview_toggle_selected"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="center"
+                    android:checked="true"
+                    android:enabled="false"/>
+            </FrameLayout>
+        </LinearLayout>
+
+    </LinearLayout>
+</androidx.cardview.widget.CardView>
diff --git a/res/layout/theme_preview_content_v2.xml b/res/layout/theme_preview_content_v2.xml
new file mode 100644
index 0000000..ea5db19
--- /dev/null
+++ b/res/layout/theme_preview_content_v2.xml
@@ -0,0 +1,50 @@
+<?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"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:paddingTop="@dimen/preview_theme_content_padding_top"
+    android:paddingHorizontal="@dimen/preview_theme_content_padding"
+    android:paddingBottom="@dimen/preview_theme_content_padding"
+    android:orientation="vertical">
+
+    <include layout="@layout/theme_preview_topbar" />
+
+    <TextView
+        android:id="@+id/smart_space_date"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:textSize="@dimen/preview_theme_smart_space_date_size"
+        android:lineHeight="24dp"
+        android:gravity="center"
+        android:layout_marginTop="@dimen/preview_theme_smart_space_margin_top"/>
+
+    <include layout="@layout/theme_preview_app_icon_shape" />
+
+    <include layout="@layout/theme_preview_color_icons"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/preview_theme_color_icons_margin_top"/>
+
+    <Space
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1"/>
+
+    <include layout="@layout/theme_cover_qsb" />
+</LinearLayout>
\ No newline at end of file
diff --git a/res/values/bools.xml b/res/values/bools.xml
index 078b50c..409538d 100644
--- a/res/values/bools.xml
+++ b/res/values/bools.xml
@@ -16,5 +16,5 @@
      limitations under the License.
 -->
 <resources>
-    <bool name="use_grid_for_options">false</bool>
+    <bool name="use_grid_for_options">true</bool>
 </resources>
\ No newline at end of file
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 3c78967..ec21329 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -30,20 +30,22 @@
     <dimen name="options_container_height">120dp</dimen>
     <dimen name="options_container_width">0dp</dimen>
     <dimen name="option_tile_width">88dp</dimen>
-    <dimen name="theme_option_icon_sample_height">18dp</dimen>
-    <dimen name="theme_option_icon_sample_width">18dp</dimen>
-    <dimen name="theme_option_shape_sample_height">16dp</dimen>
-    <dimen name="theme_option_shape_sample_width">16dp</dimen>
-    <dimen name="theme_option_font_sample_height">16dp</dimen>
+    <dimen name="option_icon_size">16dp</dimen>
+    <dimen name="theme_option_icon_sample_height">22dp</dimen>
+    <dimen name="theme_option_icon_sample_width">22dp</dimen>
+    <dimen name="theme_option_shape_sample_height">20dp</dimen>
+    <dimen name="theme_option_shape_sample_width">20dp</dimen>
+    <dimen name="theme_option_font_sample_height">20dp</dimen>
     <dimen name="theme_option_font_sample_width">52dp</dimen>
-    <dimen name="theme_option_sample_padding">5dp</dimen>
+    <dimen name="theme_option_sample_margin">10dp</dimen>
     <!-- Note, using dp instead of sp as this is just the "+" symbol, not text -->
-    <dimen name="option_tile_padding_vertical">16dp</dimen>
-    <dimen name="option_tile_padding_horizontal">16dp</dimen>
+    <dimen name="option_tile_padding_vertical">18dp</dimen>
+    <dimen name="option_tile_padding_horizontal">18dp</dimen>
 
     <dimen name="option_bottom_margin">8dp</dimen>
+    <dimen name="option_padding_horizontal">2dp</dimen>
     <!-- Note, using dp instead of sp as this text is more like a "snapshot" of the font -->
-    <dimen name="theme_option_font_text_size">16dp</dimen>
+    <dimen name="theme_option_font_text_size">20dp</dimen>
     <dimen name="theme_option_font_min_text_size">15dp</dimen>
 
     <dimen name="option_tile_margin_horizontal">6dp</dimen>
@@ -64,12 +66,36 @@
     <dimen name="preview_theme_icon_size">30dp</dimen>
     <dimen name="preview_theme_tile_size">16dp</dimen>
     <dimen name="preview_theme_shape_size">36dp</dimen>
-    <dimen name="preview_theme_cover_topbar_clock_size">14sp</dimen>
+    <dimen name="preview_theme_cover_topbar_clock_size">12sp</dimen>
     <dimen name="preview_theme_cover_topbar_icon_size">16dp</dimen>
     <dimen name="preview_theme_cover_content_extra_margin">16dp</dimen>
     <dimen name="preview_theme_content_bottom">@dimen/min_taptarget_height</dimen>
     <dimen name="preview_theme_cover_content_bottom">@dimen/preview_theme_content_bottom</dimen>
 
+    <!--  For the new preview of theme picker.  -->
+    <dimen name="preview_theme_content_padding_top">12dp</dimen>
+    <dimen name="preview_theme_content_padding">@dimen/preview_card_padding</dimen>
+    <dimen name="preview_theme_smart_space_margin_top">48dp</dimen>
+    <dimen name="preview_theme_smart_space_date_size">16sp</dimen>
+    <dimen name="preview_theme_app_icon_shape_padding_horizontal">36dp</dimen>
+    <dimen name="preview_theme_app_icon_shape_padding_top">28dp</dimen>
+    <dimen name="preview_theme_app_icon_shape_text_margin_top">8dp</dimen>
+    <dimen name="preview_theme_app_icon_shape_text_size">16sp</dimen>
+    <dimen name="preview_theme_color_icons_margin_top">40dp</dimen>
+    <dimen name="preview_theme_color_icons_padding_horizontal">18dp</dimen>
+    <dimen name="preview_theme_color_icons_title_text_size">12sp</dimen>
+    <dimen name="preview_theme_color_icons_title_margin_top">12dp</dimen>
+    <dimen name="preview_theme_color_icons_title_margin_bottom">20dp</dimen>
+    <dimen name="preview_theme_color_icons_icon_size">@dimen/preview_theme_icon_size</dimen>
+    <dimen name="preview_theme_color_icons_tile_size">@dimen/preview_theme_tile_size</dimen>
+    <dimen name="preview_theme_color_icons_buttons_margin_top">32dp</dimen>
+    <dimen name="preview_theme_color_icons_buttons_margin_bottom">28dp</dimen>
+
+    <!--  For the customization previews on the picker. -->
+    <dimen name="preview_content_height">@dimen/preview_pager_height</dimen>
+    <dimen name="preview_content_padding_top">@dimen/preview_page_top_margin</dimen>
+    <dimen name="preview_content_padding_bottom">@dimen/indicator_container_height</dimen>
+
     <dimen name="font_preview_body_width">200dp</dimen>
     <dimen name="font_preview_divider_gap">24dp</dimen>
 
@@ -91,4 +117,11 @@
 
     <dimen name="min_taptarget_height">48dp</dimen>
 
+    <!--  For the style info preview sheet. -->
+    <dimen name="theme_info_margin">12dp</dimen>
+    <dimen name="theme_info_icon_size">24dp</dimen>
+    <dimen name="theme_info_app_preview_icon_margin">2dp</dimen>
+    <dimen name="theme_info_app_preview_icon_elevation">2dp</dimen>
+    <dimen name="theme_info_text_size">28sp</dimen>
+
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index c0aef7a..f1cd169 100755
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -119,6 +119,9 @@
         [CHAR LIMIT=20] -->
     <string name="custom_theme_previous">Previous</string>
 
+    <!-- Title for "Custom theme" option. [CHAR LIMIT=15] -->
+    <string name="custom_theme">Custom</string>
+
     <!-- Generic label for one system Style/Theme (combination of fonts/colors/icons) that is
         defined and customized by the user (note there could be more than one so the label includes
         a number, eg "Custom 1, Custom 2, etc") [CHAR LIMIT=15] -->
@@ -189,4 +192,11 @@
 
     <!-- Generic error message [CHAR_LIMIT=NONE] -->
     <string name="something_went_wrong">Oops! Something went wrong.</string>
+
+    <!-- Title for a section of a style preview screen that shows a preview of the style color and icons. [CHAR_LIMIT=30]-->
+    <string name="theme_preview_icons_section_title">Color / Icons</string>
+
+    <!-- Bottom sheet dialog which displaying different theme's info. [CHAR_LIMIT=30]
+    (eg, Fonts, icons, shape, color will change to the "Default" Style) -->
+    <string name="style_info_description">Fonts, icons, shape, color will change to the <xliff:g name="style_name">%1$s</xliff:g> Style</string>
 </resources>
diff --git a/src/com/android/customization/model/theme/DefaultThemeProvider.java b/src/com/android/customization/model/theme/DefaultThemeProvider.java
index 7c99f9c..60eb66d 100644
--- a/src/com/android/customization/model/theme/DefaultThemeProvider.java
+++ b/src/com/android/customization/model/theme/DefaultThemeProvider.java
@@ -28,6 +28,7 @@
 import static com.android.customization.model.ResourceConstants.SYSUI_PACKAGE;
 
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources.NotFoundException;
 import android.text.TextUtils;
@@ -78,9 +79,6 @@
     private static final String THEME_TITLE_FIELD = "_theme_title";
     private static final String THEME_ID_FIELD = "_theme_id";
 
-    // Maximum number of themes allowed (including default, pre-bundled and custom)
-    private static final int MAX_TOTAL_THEMES = 10;
-
     private final OverlayThemeExtractor mOverlayProvider;
     private List<ThemeBundle> mThemes;
     private final CustomizationPreferences mCustomizationPreferences;
@@ -109,6 +107,10 @@
     }
 
     private void loadAll() {
+        // Add "Custom" option at the first.
+        mThemes.add(new CustomTheme(CustomTheme.newId(), mContext.getString(
+                R.string.custom_theme), new HashMap<>(), null));
+
         addDefaultTheme();
 
         String[] themeNames = getItemsFromStub(THEMES_ARRAY);
@@ -211,6 +213,11 @@
             try {
                 builder.addShapePreviewIcon(
                         mContext.getPackageManager().getApplicationIcon(packageName));
+                // Add the shape icon app name.
+                ApplicationInfo appInfo = mContext.getPackageManager()
+                        .getApplicationInfo(packageName, /* flag= */ 0);
+                builder.addShapePreviewIconName(
+                        String.valueOf(mContext.getPackageManager().getApplicationLabel(appInfo)));
             } catch (NameNotFoundException e) {
                 Log.d(TAG, "Couldn't find app " + packageName + ", won't use it for icon shape"
                         + "preview");
@@ -321,15 +328,15 @@
                         new HashMap<>(), null));
             }
         }
-
-        if (mThemes.size() < MAX_TOTAL_THEMES) {
-            // Add an empty one at the end.
-            mThemes.add(new CustomTheme(CustomTheme.newId(), mContext.getString(
-                    R.string.custom_theme_title, customThemesCount + 1), new HashMap<>(), null));
-        }
-
     }
 
+    @Nullable
+    @Override
+    public ThemeBundle.Builder parseThemeBundle(String serializedTheme) throws JSONException {
+        return parseCustomTheme(serializedTheme);
+    }
+
+    @Nullable
     @Override
     public CustomTheme.Builder parseCustomTheme(String serializedTheme) throws JSONException {
         JSONObject theme = new JSONObject(serializedTheme);
diff --git a/src/com/android/customization/model/theme/OverlayThemeExtractor.java b/src/com/android/customization/model/theme/OverlayThemeExtractor.java
index 9c85fa4..f1698f6 100644
--- a/src/com/android/customization/model/theme/OverlayThemeExtractor.java
+++ b/src/com/android/customization/model/theme/OverlayThemeExtractor.java
@@ -8,6 +8,7 @@
 import android.content.Context;
 import android.content.om.OverlayInfo;
 import android.content.om.OverlayManager;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
 import android.content.res.Resources.NotFoundException;
@@ -102,6 +103,11 @@
                 builder.addShapePreviewIcon(
                         mContext.getPackageManager().getApplicationIcon(
                                 packageName));
+                // Add the shape icon app name.
+                ApplicationInfo appInfo = mContext.getPackageManager()
+                        .getApplicationInfo(packageName, /* flag= */ 0);
+                builder.addShapePreviewIconName(
+                        String.valueOf(mContext.getPackageManager().getApplicationLabel(appInfo)));
             } catch (NameNotFoundException e) {
                 Log.d(TAG, "Couldn't find app " + packageName
                         + ", won't use it for icon shape preview");
diff --git a/src/com/android/customization/model/theme/ThemeBundle.java b/src/com/android/customization/model/theme/ThemeBundle.java
index 14b2dcd..9928c76 100644
--- a/src/com/android/customization/model/theme/ThemeBundle.java
+++ b/src/com/android/customization/model/theme/ThemeBundle.java
@@ -251,12 +251,13 @@
         public final List<Drawable> icons;
         public final Drawable shapeDrawable;
         public final List<Drawable> shapeAppIcons;
+        public final List<String> shapeAppIconNames;
         @Dimension public final int bottomSheeetCornerRadius;
 
         private PreviewInfo(Context context, Typeface bodyFontFamily, Typeface headlineFontFamily,
                 int colorAccentLight, int colorAccentDark, List<Drawable> icons,
                 Drawable shapeDrawable, @Dimension int cornerRadius,
-                List<Drawable> shapeAppIcons) {
+                List<Drawable> shapeAppIcons, List<String> shapeAppIconNames) {
             this.bodyFontFamily = bodyFontFamily;
             this.headlineFontFamily = headlineFontFamily;
             this.colorAccentLight = colorAccentLight;
@@ -265,6 +266,7 @@
             this.shapeDrawable = shapeDrawable;
             this.bottomSheeetCornerRadius = cornerRadius;
             this.shapeAppIcons = shapeAppIcons;
+            this.shapeAppIconNames = shapeAppIconNames;
         }
 
         /**
@@ -292,6 +294,7 @@
         @Dimension private int mCornerRadius;
         protected Map<String, String> mPackages = new HashMap<>();
         private List<Drawable> mAppIcons = new ArrayList<>();
+        private List<String> mAppIconNames = new ArrayList<>();
 
         public ThemeBundle build(Context context) {
             return new ThemeBundle(mTitle, mPackages, mIsDefault, createPreviewInfo(context));
@@ -322,7 +325,8 @@
                 }
             }
             return new PreviewInfo(context, mBodyFontFamily, mHeadlineFontFamily, mColorAccentLight,
-                    mColorAccentDark, mIcons, shapeDrawable, mCornerRadius, shapeIcons);
+                    mColorAccentDark, mIcons, shapeDrawable, mCornerRadius, shapeIcons,
+                    mAppIconNames);
         }
 
         public Map<String, String> getPackages() {
@@ -388,6 +392,11 @@
             return this;
         }
 
+        public Builder addShapePreviewIconName(String appIconName) {
+            mAppIconNames.add(appIconName);
+            return this;
+        }
+
         public Builder setBottomSheetCornerRadius(@Dimension int radius) {
             mCornerRadius = radius;
             return this;
diff --git a/src/com/android/customization/model/theme/ThemeBundleProvider.java b/src/com/android/customization/model/theme/ThemeBundleProvider.java
index fac11cd..34342b3 100644
--- a/src/com/android/customization/model/theme/ThemeBundleProvider.java
+++ b/src/com/android/customization/model/theme/ThemeBundleProvider.java
@@ -43,6 +43,8 @@
 
     void removeCustomTheme(CustomTheme theme);
 
+    @Nullable ThemeBundle.Builder parseThemeBundle(String serializedTheme) throws JSONException;
+
     @Nullable CustomTheme.Builder parseCustomTheme(String serializedTheme) throws JSONException;
 
     ThemeBundle findEquivalent(ThemeBundle other);
diff --git a/src/com/android/customization/picker/TimeTicker.java b/src/com/android/customization/picker/TimeTicker.java
deleted file mode 100644
index 2cf5dbf..0000000
--- a/src/com/android/customization/picker/TimeTicker.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package com.android.customization.picker;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-
-import androidx.annotation.Nullable;
-
-/**
- * BroadcastReceiver that can notify a listener when the system time (minutes) changes.
- * Use {@link #registerNewReceiver(Context, TimeListener)} to create a new instance that will be
- * automatically registered using the given Context.
- */
-public class TimeTicker extends BroadcastReceiver {
-
-    public interface TimeListener {
-        void onCurrentTimeChanged();
-    }
-
-    public static TimeTicker registerNewReceiver(Context context, TimeListener listener) {
-        TimeTicker receiver = new TimeTicker(listener);
-        // Register broadcast receiver for time tick
-        final IntentFilter filter = new IntentFilter(Intent.ACTION_TIME_TICK);
-        context.registerReceiver(receiver, filter);
-        return receiver;
-    }
-
-    @Nullable private TimeListener mListener;
-
-    private TimeTicker(TimeListener listener) {
-        mListener = listener;
-    }
-
-    @Override
-    public void onReceive(Context context, Intent intent) {
-        if (mListener != null) {
-            mListener.onCurrentTimeChanged();
-        }
-    }
-}
diff --git a/src/com/android/customization/picker/ViewOnlyFullPreviewActivity.java b/src/com/android/customization/picker/ViewOnlyFullPreviewActivity.java
index 19324da..2afbc16 100644
--- a/src/com/android/customization/picker/ViewOnlyFullPreviewActivity.java
+++ b/src/com/android/customization/picker/ViewOnlyFullPreviewActivity.java
@@ -15,9 +15,12 @@
  */
 package com.android.customization.picker;
 
+import static com.android.customization.picker.theme.ThemeFullPreviewFragment.EXTRA_THEME_OPTION_TITLE;
+
 import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
+import android.text.TextUtils;
 
 import androidx.annotation.IntDef;
 import androidx.annotation.Nullable;
@@ -25,6 +28,7 @@
 import androidx.fragment.app.FragmentActivity;
 
 import com.android.customization.picker.grid.GridFullPreviewFragment;
+import com.android.customization.picker.theme.ThemeFullPreviewFragment;
 import com.android.wallpaper.R;
 import com.android.wallpaper.widget.BottomActionBar;
 import com.android.wallpaper.widget.BottomActionBar.BottomActionBarHost;
@@ -63,8 +67,15 @@
         @Section final int section = intent.getIntExtra(EXTRA_PREVIEW_SECTION, 0);
         final Bundle bundle = intent.getBundleExtra(EXTRA_PREVIEW_BUNDLE);
         if (section == SECTION_GRID) {
-            showFragment(
-                    GridFullPreviewFragment.newInstance(getString(R.string.grid_title), bundle));
+            showFragment(GridFullPreviewFragment.newInstance(
+                    getString(R.string.grid_title), bundle));
+        } else if (section == SECTION_STYLE) {
+            final String themeTitle = bundle.getString(EXTRA_THEME_OPTION_TITLE);
+            showFragment(ThemeFullPreviewFragment.newInstance(
+                    TextUtils.isEmpty(themeTitle)
+                            ? getString(R.string.theme_title)
+                            : themeTitle,
+                    bundle));
         }
     }
 
diff --git a/src/com/android/customization/picker/WallpaperPreviewer.java b/src/com/android/customization/picker/WallpaperPreviewer.java
new file mode 100644
index 0000000..0185925
--- /dev/null
+++ b/src/com/android/customization/picker/WallpaperPreviewer.java
@@ -0,0 +1,219 @@
+/*
+ * 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.android.customization.picker;
+
+import static android.view.View.MeasureSpec.EXACTLY;
+import static android.view.View.MeasureSpec.makeMeasureSpec;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.service.wallpaper.WallpaperService;
+import android.view.Surface;
+import android.view.SurfaceControlViewHost;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import androidx.annotation.MainThread;
+import androidx.cardview.widget.CardView;
+import androidx.core.content.ContextCompat;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleObserver;
+import androidx.lifecycle.OnLifecycleEvent;
+
+import com.android.wallpaper.R;
+import com.android.wallpaper.model.LiveWallpaperInfo;
+import com.android.wallpaper.model.WallpaperInfo;
+import com.android.wallpaper.util.ScreenSizeCalculator;
+import com.android.wallpaper.util.SizeCalculator;
+import com.android.wallpaper.util.WallpaperConnection;
+import com.android.wallpaper.widget.LiveTileOverlay;
+
+/** A class to load the wallpaper to the view. */
+public class WallpaperPreviewer implements LifecycleObserver {
+
+    private final Rect mPreviewLocalRect = new Rect();
+    private final Rect mPreviewGlobalRect = new Rect();
+    private final int[] mLivePreviewLocation = new int[2];
+
+    private final Activity mActivity;
+    private final ImageView mHomePreview;
+    private final SurfaceView mWallpaperSurface;
+
+    private WallpaperInfo mWallpaper;
+    private WallpaperConnection mWallpaperConnection;
+    // Home workspace surface is behind the app window, and so must the home image wallpaper like
+    // the live wallpaper. This view is rendered on mWallpaperSurface for home image wallpaper.
+    private ImageView mHomeImageWallpaper;
+
+    public WallpaperPreviewer(Lifecycle lifecycle, Activity activity, ImageView homePreview,
+                              SurfaceView wallpaperSurface) {
+        lifecycle.addObserver(this);
+
+        mActivity = activity;
+        mHomePreview = homePreview;
+        mWallpaperSurface = wallpaperSurface;
+        mWallpaperSurface.setZOrderMediaOverlay(false);
+        mWallpaperSurface.getHolder().addCallback(mSurfaceCallback);
+    }
+
+    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
+    @MainThread
+    public void onResume() {
+        if (mWallpaperConnection != null) {
+            mWallpaperConnection.setVisibility(true);
+        }
+    }
+
+    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
+    @MainThread
+    public void onPause() {
+        if (mWallpaperConnection != null) {
+            mWallpaperConnection.setVisibility(false);
+        }
+    }
+
+    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
+    @MainThread
+    public void onDestroy() {
+        LiveTileOverlay.INSTANCE.detach(mHomePreview.getOverlay());
+        if (mWallpaperConnection != null) {
+            mWallpaperConnection.disconnect();
+            mWallpaperConnection = null;
+        }
+
+        mWallpaperSurface.getHolder().removeCallback(mSurfaceCallback);
+        Surface surface = mWallpaperSurface.getHolder().getSurface();
+        if (surface != null) {
+            surface.release();
+        }
+    }
+
+    /** Updates the preview card view radius to match the device radius. */
+    public void updatePreviewCardRadius() {
+        final float screenAspectRatio =
+                ScreenSizeCalculator.getInstance().getScreenAspectRatio(mActivity);
+        CardView cardView = (CardView) mHomePreview.getParent();
+        final int cardWidth = (int) (cardView.getMeasuredHeight() / screenAspectRatio);
+        ViewGroup.LayoutParams layoutParams = cardView.getLayoutParams();
+        layoutParams.width = cardWidth;
+        cardView.setLayoutParams(layoutParams);
+        cardView.setRadius(SizeCalculator.getPreviewCornerRadius(mActivity, cardWidth));
+    }
+
+    /** Loads the wallpaper. */
+    public void setWallpaper(WallpaperInfo wallpaperInfo) {
+        mWallpaper =  wallpaperInfo;
+        setUpWallpaperPreview();
+    }
+
+    private void setUpWallpaperPreview() {
+        if (mWallpaper != null && mHomeImageWallpaper != null) {
+            boolean renderInImageWallpaperSurface = !(mWallpaper instanceof LiveWallpaperInfo);
+            mWallpaper.getThumbAsset(mActivity.getApplicationContext())
+                    .loadPreviewImage(mActivity,
+                            renderInImageWallpaperSurface ? mHomeImageWallpaper : mHomePreview,
+                            mActivity.getResources().getColor(R.color.secondary_color));
+            LiveTileOverlay.INSTANCE.detach(mHomePreview.getOverlay());
+            if (mWallpaper instanceof LiveWallpaperInfo) {
+                mWallpaper.getThumbAsset(mActivity.getApplicationContext())
+                        .loadPreviewImage(
+                                mActivity,
+                                mHomeImageWallpaper,
+                                mActivity.getColor(R.color.secondary_color));
+                setUpLiveWallpaperPreview(mWallpaper);
+            } else {
+                if (mWallpaperConnection != null) {
+                    mWallpaperConnection.disconnect();
+                    mWallpaperConnection = null;
+                }
+            }
+        }
+    }
+
+    private void setUpLiveWallpaperPreview(WallpaperInfo homeWallpaper) {
+        if (mActivity == null || mActivity.isFinishing()) {
+            return;
+        }
+
+        if (mWallpaperConnection != null) {
+            mWallpaperConnection.disconnect();
+        }
+
+        mHomePreview.getLocationOnScreen(mLivePreviewLocation);
+        mPreviewGlobalRect.set(0, 0, mHomePreview.getMeasuredWidth(),
+                mHomePreview.getMeasuredHeight());
+        mPreviewLocalRect.set(mPreviewGlobalRect);
+        mPreviewGlobalRect.offset(mLivePreviewLocation[0], mLivePreviewLocation[1]);
+
+        mWallpaperConnection = new WallpaperConnection(
+                getWallpaperIntent(homeWallpaper.getWallpaperComponent()), mActivity,
+                new WallpaperConnection.WallpaperConnectionListener() {
+                    @Override
+                    public void onEngineShown() {}
+                }, mPreviewGlobalRect);
+
+        LiveTileOverlay.INSTANCE.update(new RectF(mPreviewLocalRect),
+                ((CardView) mHomePreview.getParent()).getRadius());
+
+        mWallpaperConnection.setVisibility(true);
+        mHomePreview.post(() -> {
+            if (!mWallpaperConnection.connect()) {
+                mWallpaperConnection = null;
+                LiveTileOverlay.INSTANCE.detach(mHomePreview.getOverlay());
+            }
+        });
+    }
+
+    private final SurfaceHolder.Callback mSurfaceCallback = new SurfaceHolder.Callback() {
+
+        private Surface mLastSurface;
+
+        @Override
+        public void surfaceCreated(SurfaceHolder holder) {
+            if (mLastSurface != holder.getSurface()) {
+                mLastSurface = holder.getSurface();
+                mHomeImageWallpaper = new ImageView(mActivity);
+                mHomeImageWallpaper.setBackgroundColor(
+                        ContextCompat.getColor(mActivity, R.color.primary_color));
+                mHomeImageWallpaper.measure(makeMeasureSpec(mHomePreview.getWidth(), EXACTLY),
+                        makeMeasureSpec(mHomePreview.getHeight(), EXACTLY));
+                mHomeImageWallpaper.layout(0, 0, mHomePreview.getWidth(), mHomePreview.getHeight());
+
+                SurfaceControlViewHost host = new SurfaceControlViewHost(mActivity,
+                        mActivity.getDisplay(), mWallpaperSurface.getHostToken());
+                host.setView(mHomeImageWallpaper, mHomeImageWallpaper.getWidth(),
+                        mHomeImageWallpaper.getHeight());
+                mWallpaperSurface.setChildSurfacePackage(host.getSurfacePackage());
+            }
+            setUpWallpaperPreview();
+        }
+
+        @Override
+        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}
+
+        @Override
+        public void surfaceDestroyed(SurfaceHolder holder) {}
+    };
+
+    private static Intent getWallpaperIntent(android.app.WallpaperInfo info) {
+        return new Intent(WallpaperService.SERVICE_INTERFACE)
+                .setClassName(info.getPackageName(), info.getServiceName());
+    }
+}
diff --git a/src/com/android/customization/picker/grid/GridFragment.java b/src/com/android/customization/picker/grid/GridFragment.java
index c9af437..a8f6eda 100644
--- a/src/com/android/customization/picker/grid/GridFragment.java
+++ b/src/com/android/customization/picker/grid/GridFragment.java
@@ -23,30 +23,19 @@
 import static com.android.customization.picker.grid.GridFullPreviewFragment.EXTRA_WALLPAPER_INFO;
 import static com.android.wallpaper.widget.BottomActionBar.BottomAction.APPLY;
 
-import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
-import android.content.res.Resources;
-import android.graphics.drawable.BitmapDrawable;
-import android.net.Uri;
 import android.os.Bundle;
-import android.os.Message;
-import android.os.RemoteException;
-import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.LayoutInflater;
-import android.view.Surface;
-import android.view.SurfaceHolder;
 import android.view.SurfaceView;
 import android.view.View;
-import android.view.View.OnLayoutChangeListener;
 import android.view.ViewGroup;
 import android.widget.ImageView;
 import android.widget.Toast;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import androidx.cardview.widget.CardView;
 import androidx.core.widget.ContentLoadingProgressBar;
 import androidx.recyclerview.widget.RecyclerView;
 
@@ -55,23 +44,17 @@
 import com.android.customization.model.grid.GridOption;
 import com.android.customization.model.grid.GridOptionsManager;
 import com.android.customization.module.ThemesUserEventLogger;
-import com.android.customization.picker.BasePreviewAdapter;
-import com.android.customization.picker.BasePreviewAdapter.PreviewPage;
 import com.android.customization.picker.ViewOnlyFullPreviewActivity;
+import com.android.customization.picker.WallpaperPreviewer;
 import com.android.customization.widget.OptionSelectorController;
 import com.android.wallpaper.R;
-import com.android.wallpaper.asset.Asset;
-import com.android.wallpaper.asset.ContentUriAsset;
 import com.android.wallpaper.model.WallpaperInfo;
 import com.android.wallpaper.module.CurrentWallpaperInfoFactory;
 import com.android.wallpaper.module.InjectorProvider;
 import com.android.wallpaper.picker.AppbarFragment;
-import com.android.wallpaper.util.SurfaceViewUtils;
 import com.android.wallpaper.widget.BottomActionBar;
-import com.android.wallpaper.widget.PreviewPager;
 
 import com.bumptech.glide.Glide;
-import com.bumptech.glide.request.RequestOptions;
 
 import java.util.List;
 
@@ -80,8 +63,6 @@
  */
 public class GridFragment extends AppbarFragment {
 
-    static final int PREVIEW_FADE_DURATION_MS = 100;
-
     private static final int FULL_PREVIEW_REQUEST_CODE = 1000;
 
     private static final String TAG = "GridFragment";
@@ -100,23 +81,20 @@
     }
 
     private WallpaperInfo mHomeWallpaper;
-    private float mScreenAspectRatio;
-    private int mCardHeight;
-    private int mCardWidth;
-    private BitmapDrawable mCardBackground;
-    private GridPreviewAdapter mAdapter;
     private RecyclerView mOptionsContainer;
     private OptionSelectorController<GridOption> mOptionsController;
     private GridOptionsManager mGridManager;
     private GridOption mSelectedOption;
-    private PreviewPager mPreviewPager;
     private ContentLoadingProgressBar mLoading;
+    private ViewGroup mGridPreviewContainer;
     private View mContent;
     private View mError;
     private BottomActionBar mBottomActionBar;
     private ThemesUserEventLogger mEventLogger;
     private boolean mReloadOptionsAfterApplying;
 
+    private GridOptionPreviewer mGridOptionPreviewer;
+
     private final Callback mApplyGridCallback = new Callback() {
         @Override
         public void onSuccess() {
@@ -166,42 +144,50 @@
         View view = inflater.inflate(
                 R.layout.fragment_grid_picker, container, /* attachToRoot */ false);
         setUpToolbar(view);
+        mGridPreviewContainer = view.findViewById(R.id.grid_preview_container);
         mContent = view.findViewById(R.id.content_section);
-        mPreviewPager = view.findViewById(R.id.grid_preview_pager);
         mOptionsContainer = view.findViewById(R.id.options_container);
         mLoading = view.findViewById(R.id.loading_indicator);
         mError = view.findViewById(R.id.error_section);
-        final Resources res = getResources();
-        DisplayMetrics dm = res.getDisplayMetrics();
-        mScreenAspectRatio = (float) dm.heightPixels / dm.widthPixels;
 
         // Clear memory cache whenever grid fragment view is being loaded.
         Glide.get(getContext()).clearMemory();
         setUpOptions();
 
+        ImageView wallpaperPreviewImage = view.findViewById(R.id.wallpaper_preview_image);
+        wallpaperPreviewImage.setOnClickListener(v -> showFullPreview());
+        SurfaceView wallpaperSurface = view.findViewById(R.id.wallpaper_preview_surface);
+        WallpaperPreviewer wallpaperPreviewer = new WallpaperPreviewer(
+                getLifecycle(), getActivity(), wallpaperPreviewImage, wallpaperSurface);
+
+        // Loads current Wallpaper.
         CurrentWallpaperInfoFactory factory = InjectorProvider.getInjector()
                 .getCurrentWallpaperFactory(getContext().getApplicationContext());
-
         factory.createCurrentWallpaperInfos((homeWallpaper, lockWallpaper, presentationMode) -> {
             mHomeWallpaper = homeWallpaper;
-            loadWallpaperBackground();
-
+            wallpaperPreviewer.setWallpaper(mHomeWallpaper);
         }, false);
-        view.addOnLayoutChangeListener(new OnLayoutChangeListener() {
+
+        view.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
             @Override
             public void onLayoutChange(View v, int left, int top, int right, int bottom,
-                    int oldLeft, int oldTop, int oldRight, int oldBottom) {
-                mCardHeight = mPreviewPager.getHeight() - mPreviewPager.getPaddingTop() -
-                        res.getDimensionPixelSize(R.dimen.indicator_container_height);
-                mCardWidth = (int) (mCardHeight / mScreenAspectRatio);
+                                       int oldLeft, int oldTop, int oldRight, int oldBottom) {
+                wallpaperPreviewer.updatePreviewCardRadius();
                 view.removeOnLayoutChangeListener(this);
-                loadWallpaperBackground();
             }
         });
         return view;
     }
 
     @Override
+    public void onDestroy() {
+        super.onDestroy();
+        if (mGridOptionPreviewer != null) {
+            mGridOptionPreviewer.release();
+        }
+    }
+
+    @Override
     public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
         super.onActivityResult(requestCode, resultCode, data);
         if (requestCode == FULL_PREVIEW_REQUEST_CODE && resultCode == RESULT_OK) {
@@ -222,23 +208,13 @@
         mGridManager.apply(gridOption, mApplyGridCallback);
     }
 
-    private void loadWallpaperBackground() {
-        if (mHomeWallpaper != null && mCardHeight > 0 && mCardWidth > 0) {
-            mHomeWallpaper.getThumbAsset(getContext()).decodeBitmap(mCardWidth,
-                    mCardHeight,
-                    bitmap -> {
-                        mCardBackground =
-                                new BitmapDrawable(getResources(), bitmap);
-                        if (mAdapter != null) {
-                            mAdapter.onWallpaperInfoLoaded();
-                        }
-                    });
+    private void updatePreview() {
+        if (mGridOptionPreviewer != null) {
+            mGridOptionPreviewer.release();
         }
-    }
-
-    private void createAdapter() {
-        mAdapter = new GridPreviewAdapter(mSelectedOption);
-        mPreviewPager.setAdapter(mAdapter);
+        mGridOptionPreviewer = new GridOptionPreviewer(
+                getContext(), mGridManager, mGridPreviewContainer);
+        mGridOptionPreviewer.setGridOption(mSelectedOption, mGridManager.usesSurfaceView());
     }
 
     private void setUpOptions() {
@@ -258,11 +234,11 @@
                     }
                     mBottomActionBar.show();
                     mEventLogger.logGridSelected(mSelectedOption);
-                    createAdapter();
+                    updatePreview();
                 });
                 mOptionsController.initOptions(mGridManager);
                 mSelectedOption = getSelectedOption(options);
-                createAdapter();
+                updatePreview();
             }
 
             @Override
@@ -302,123 +278,4 @@
         Intent intent = ViewOnlyFullPreviewActivity.newIntent(getContext(), SECTION_GRID, bundle);
         startActivityForResult(intent, FULL_PREVIEW_REQUEST_CODE);
     }
-
-    private class GridPreviewPage extends PreviewPage {
-        private final int mPageId;
-        private final Asset mPreviewAsset;
-        private final int mCols;
-        private final int mRows;
-        private final Activity mActivity;
-
-        private final String mName;
-
-        private ImageView mPreview;
-        private SurfaceView mPreviewSurface;
-
-        private final SurfaceHolder.Callback mSurfaceCallback = new SurfaceHolder.Callback() {
-
-            private Surface mLastSurface;
-            private Message mCallback;
-
-            @Override
-            public void surfaceCreated(SurfaceHolder holder) {
-                if (mLastSurface != holder.getSurface()) {
-                    mLastSurface = holder.getSurface();
-                    Bundle result = mGridManager.renderPreview(
-                            SurfaceViewUtils.createSurfaceViewRequest(mPreviewSurface), mName);
-                    if (result != null) {
-                        mPreviewSurface.setChildSurfacePackage(
-                                SurfaceViewUtils.getSurfacePackage(result));
-                        mCallback = SurfaceViewUtils.getCallback(result);
-                    }
-                }
-            }
-
-            @Override
-            public void surfaceChanged(SurfaceHolder holder, int format, int width,
-                    int height) {}
-
-            @Override
-            public void surfaceDestroyed(SurfaceHolder holder) {
-                if (mCallback != null) {
-                    try {
-                        mCallback.replyTo.send(mCallback);
-                    } catch (RemoteException e) {
-                        e.printStackTrace();
-                    } finally {
-                        mCallback = null;
-                    }
-                }
-            }
-        };
-
-        private GridPreviewPage(Activity activity, int id, Uri previewUri, String name, int rows,
-                int cols) {
-            super(null, activity);
-            mPageId = id;
-            mPreviewAsset = new ContentUriAsset(activity, previewUri,
-                    RequestOptions.fitCenterTransform());
-            mName = name;
-            mRows = rows;
-            mCols = cols;
-            mActivity = activity;
-        }
-
-        @Override
-        public void setCard(CardView card) {
-            super.setCard(card);
-            mPreview = card.findViewById(R.id.grid_preview_image);
-            mPreviewSurface = card.findViewById(R.id.grid_preview_surface);
-            // PreviewSurface is the top of its window(card view), due to #setZOrderOnTop(true).
-            mPreviewSurface.setOnClickListener(view -> showFullPreview());
-        }
-
-        public void bindPreviewContent() {
-            Resources resources = card.getResources();
-            bindWallpaperIfAvailable();
-            final boolean usesSurfaceViewForPreview = mGridManager.usesSurfaceView();
-            mPreview.setVisibility(usesSurfaceViewForPreview ? View.GONE : View.VISIBLE);
-            mPreviewSurface.setVisibility(usesSurfaceViewForPreview ? View.VISIBLE : View.GONE);
-            if (usesSurfaceViewForPreview) {
-                mPreviewSurface.setZOrderOnTop(true);
-                mPreviewSurface.getHolder().addCallback(mSurfaceCallback);
-            } else {
-                mPreviewAsset.loadDrawableWithTransition(mActivity,
-                        mPreview /* imageView */,
-                        PREVIEW_FADE_DURATION_MS /* duration */,
-                        null /* drawableLoadedListener */,
-                        resources.getColor(android.R.color.transparent,
-                                null) /* placeHolderColorJ */);
-            }
-        }
-
-        void bindWallpaperIfAvailable() {
-            if (card != null && mCardBackground != null) {
-                mPreview.setBackground(mCardBackground);
-                mPreviewSurface.setBackground(mCardBackground);
-            }
-        }
-    }
-    /**
-     * Adapter class for mPreviewPager.
-     * This is a ViewPager as it allows for a nice pagination effect (ie, pages snap on swipe,
-     * we don't want to just scroll)
-     */
-    class GridPreviewAdapter extends BasePreviewAdapter<GridPreviewPage> {
-
-        GridPreviewAdapter(GridOption gridOption) {
-            super(getContext(), R.layout.grid_preview_card);
-            for (int i = 0; i < gridOption.previewPagesCount; i++) {
-                addPage(new GridPreviewPage(getActivity(), i,
-                        gridOption.previewImageUri.buildUpon().appendPath("" + i).build(),
-                        gridOption.name, gridOption.rows, gridOption.cols));
-            }
-        }
-
-        void onWallpaperInfoLoaded() {
-            for (GridPreviewPage page : mPages) {
-                page.bindWallpaperIfAvailable();
-            }
-        }
-    }
 }
diff --git a/src/com/android/customization/picker/grid/GridFullPreviewFragment.java b/src/com/android/customization/picker/grid/GridFullPreviewFragment.java
index 29d74de..2be77d6 100644
--- a/src/com/android/customization/picker/grid/GridFullPreviewFragment.java
+++ b/src/com/android/customization/picker/grid/GridFullPreviewFragment.java
@@ -16,25 +16,13 @@
 package com.android.customization.picker.grid;
 
 import static android.app.Activity.RESULT_OK;
-import static android.view.View.MeasureSpec.EXACTLY;
-import static android.view.View.MeasureSpec.makeMeasureSpec;
 
-import static com.android.customization.picker.grid.GridFragment.PREVIEW_FADE_DURATION_MS;
 import static com.android.wallpaper.widget.BottomActionBar.BottomAction.APPLY;
 
 import android.app.Activity;
 import android.content.Intent;
-import android.graphics.Rect;
-import android.graphics.RectF;
 import android.os.Bundle;
-import android.os.Message;
-import android.os.RemoteException;
-import android.service.wallpaper.WallpaperService;
-import android.util.DisplayMetrics;
 import android.view.LayoutInflater;
-import android.view.Surface;
-import android.view.SurfaceControlViewHost;
-import android.view.SurfaceHolder;
 import android.view.SurfaceView;
 import android.view.View;
 import android.view.ViewGroup;
@@ -42,28 +30,21 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import androidx.cardview.widget.CardView;
-import androidx.core.content.ContextCompat;
 
 import com.android.customization.model.grid.GridOption;
 import com.android.customization.model.grid.GridOptionsManager;
 import com.android.customization.model.grid.LauncherGridOptionsProvider;
 import com.android.customization.module.CustomizationInjector;
 import com.android.customization.module.ThemesUserEventLogger;
+import com.android.customization.picker.WallpaperPreviewer;
 import com.android.wallpaper.R;
-import com.android.wallpaper.asset.Asset;
-import com.android.wallpaper.asset.ContentUriAsset;
-import com.android.wallpaper.model.LiveWallpaperInfo;
 import com.android.wallpaper.model.WallpaperInfo;
 import com.android.wallpaper.module.InjectorProvider;
 import com.android.wallpaper.picker.AppbarFragment;
-import com.android.wallpaper.util.SizeCalculator;
-import com.android.wallpaper.util.SurfaceViewUtils;
-import com.android.wallpaper.util.WallpaperConnection;
 import com.android.wallpaper.widget.BottomActionBar;
-import com.android.wallpaper.widget.LiveTileOverlay;
 
-import com.bumptech.glide.request.RequestOptions;
+import com.bumptech.glide.Glide;
+
 
 /** A Fragment for grid full preview page. */
 public class GridFullPreviewFragment extends AppbarFragment {
@@ -72,24 +53,12 @@
     static final String EXTRA_GRID_OPTION = "grid_option";
     static final String EXTRA_GRID_USES_SURFACE_VIEW = "uses_surface_view";
 
-    private final Rect mPreviewLocalRect = new Rect();
-    private final Rect mPreviewGlobalRect = new Rect();
-    private final int[] mLivePreviewLocation = new int[2];
-
-    private GridOptionsManager mGridManager;
     private WallpaperInfo mWallpaper;
     private GridOption mGridOption;
     private boolean mUsesSurfaceView;
 
-    private CardView mCardView;
-    private ImageView mHomePreview;
-    private SurfaceView mGridOptionSurface;
-    private SurfaceView mWallpaperSurface;
-    private WallpaperConnection mWallpaperConnection;
-
-    // Home workspace surface is behind the app window, and so must the home image wallpaper like
-    // the live wallpaper. This view is rendered on mWallpaperSurface for home image wallpaper.
-    private ImageView mHomeImageWallpaper;
+    private WallpaperPreviewer mWallpaperPreviewer;
+    private GridOptionPreviewer mGridOptionPreviewer;
 
     /**
      * Returns a new {@link GridFullPreviewFragment} with the provided title and bundle arguments
@@ -110,14 +79,6 @@
         mWallpaper = getArguments().getParcelable(EXTRA_WALLPAPER_INFO);
         mGridOption = getArguments().getParcelable(EXTRA_GRID_OPTION);
         mUsesSurfaceView = getArguments().getBoolean(EXTRA_GRID_USES_SURFACE_VIEW);
-
-        CustomizationInjector injector = (CustomizationInjector) InjectorProvider.getInjector();
-        ThemesUserEventLogger eventLogger = (ThemesUserEventLogger) injector.getUserEventLogger(
-                getContext());
-
-        mGridManager = new GridOptionsManager(new LauncherGridOptionsProvider(getContext(),
-                getString(R.string.grid_control_metadata_name)),
-                eventLogger);
     }
 
     @Nullable
@@ -128,25 +89,31 @@
                 R.layout.fragment_grid_full_preview, container, /* attachToRoot */ false);
         setUpToolbar(view);
 
-        mCardView = view.findViewById(R.id.grid_full_preview_card);
-        mHomePreview = view.findViewById(R.id.grid_full_preview_image);
-        mGridOptionSurface = view.findViewById(R.id.grid_full_preview_option_surface);
-        mWallpaperSurface = view.findViewById(R.id.grid_full_preview_wallpaper_surface);
-        mGridOptionSurface.setVisibility(View.GONE);
+        // Clear memory cache whenever grid fragment view is being loaded.
+        Glide.get(getContext()).clearMemory();
 
-        final DisplayMetrics dm = getResources().getDisplayMetrics();
-        float screenAspectRatio = (float) dm.heightPixels / dm.widthPixels;
+        ImageView wallpaperPreviewImage = view.findViewById(R.id.wallpaper_preview_image);
+        SurfaceView wallpaperSurface = view.findViewById(R.id.wallpaper_preview_surface);
+        mWallpaperPreviewer = new WallpaperPreviewer(
+                getLifecycle(), getActivity(), wallpaperPreviewImage, wallpaperSurface);
+
+        CustomizationInjector injector = (CustomizationInjector) InjectorProvider.getInjector();
+        ThemesUserEventLogger eventLogger = (ThemesUserEventLogger) injector.getUserEventLogger(
+                getContext());
+        final GridOptionsManager gridManager = new GridOptionsManager(
+                new LauncherGridOptionsProvider(getContext(),
+                        getString(R.string.grid_control_metadata_name)),
+                eventLogger);
+
+        ViewGroup gridPreviewContainer = view.findViewById(R.id.grid_preview_container);
+        mGridOptionPreviewer = new GridOptionPreviewer(
+                getContext(), gridManager, gridPreviewContainer);
 
         view.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
             @Override
             public void onLayoutChange(View v, int left, int top, int right, int bottom,
                                        int oldLeft, int oldTop, int oldRight, int oldBottom) {
-                int cardWidth = (int) (mCardView.getMeasuredHeight() / screenAspectRatio);
-                ViewGroup.LayoutParams layoutParams = mCardView.getLayoutParams();
-                layoutParams.width = cardWidth;
-                mCardView.setLayoutParams(layoutParams);
-                mCardView.setRadius(SizeCalculator.getPreviewCornerRadius(
-                        getActivity(), mCardView.getMeasuredWidth()));
+                mWallpaperPreviewer.updatePreviewCardRadius();
                 view.removeOnLayoutChangeListener(this);
             }
         });
@@ -156,44 +123,25 @@
     @Override
     public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
         super.onViewCreated(view, savedInstanceState);
-        updateWallpaperSurface();
-        updateWorkspaceSurface();
+        mWallpaperPreviewer.setWallpaper(mWallpaper);
+        mGridOptionPreviewer.setGridOption(mGridOption, mUsesSurfaceView);
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        if (mGridOptionPreviewer != null) {
+            mGridOptionPreviewer.release();
+        }
     }
 
     @Override
     protected void onBottomActionBarReady(BottomActionBar bottomActionBar) {
-        bottomActionBar.bindBackButtonToSystemBackKey(getActivity());
         bottomActionBar.showActionsOnly(APPLY);
         bottomActionBar.setActionClickListener(APPLY, v -> finishActivityWithResultOk());
         bottomActionBar.show();
     }
 
-    @Override
-    public void onResume() {
-        super.onResume();
-        if (mWallpaperConnection != null) {
-            mWallpaperConnection.setVisibility(true);
-        }
-    }
-
-    @Override
-    public void onPause() {
-        super.onPause();
-        if (mWallpaperConnection != null) {
-            mWallpaperConnection.setVisibility(false);
-        }
-    }
-
-    @Override
-    public void onDestroyView() {
-        super.onDestroyView();
-        LiveTileOverlay.INSTANCE.detach(mHomePreview.getOverlay());
-        if (mWallpaperConnection != null) {
-            mWallpaperConnection.disconnect();
-            mWallpaperConnection = null;
-        }
-    }
-
     private void finishActivityWithResultOk() {
         Activity activity = requireActivity();
         activity.overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
@@ -202,159 +150,4 @@
         activity.setResult(RESULT_OK, intent);
         activity.finish();
     }
-
-    private void updateWallpaperSurface() {
-        mWallpaperSurface.setZOrderMediaOverlay(false);
-        mWallpaperSurface.getHolder().addCallback(mWallpaperSurfaceCallback);
-    }
-
-    private void updateWorkspaceSurface() {
-        if (mUsesSurfaceView) {
-            mGridOptionSurface.setZOrderOnTop(true);
-            mGridOptionSurface.getHolder().addCallback(mGridOptionSurfaceCallback);
-            mGridOptionSurface.setVisibility(View.VISIBLE);
-        } else {
-            final Asset previewAsset = new ContentUriAsset(
-                    getContext(),
-                    mGridOption.previewImageUri,
-                    RequestOptions.fitCenterTransform());
-            previewAsset.loadDrawableWithTransition(getContext(),
-                    mHomePreview /* imageView */,
-                    PREVIEW_FADE_DURATION_MS /* duration */,
-                    null /* drawableLoadedListener */,
-                    getResources().getColor(android.R.color.transparent,
-                            null) /* placeHolderColorJ */);
-        }
-    }
-
-    private void setUpWallpaperPreview() {
-        if (mWallpaper != null && mHomeImageWallpaper != null) {
-            boolean renderInImageWallpaperSurface = !(mWallpaper instanceof LiveWallpaperInfo);
-            mWallpaper.getThumbAsset(getContext())
-                    .loadPreviewImage(getActivity(),
-                            renderInImageWallpaperSurface ? mHomeImageWallpaper : mHomePreview,
-                            getResources().getColor(R.color.secondary_color));
-            LiveTileOverlay.INSTANCE.detach(mHomePreview.getOverlay());
-            if (mWallpaper instanceof LiveWallpaperInfo) {
-                mWallpaper.getThumbAsset(getContext().getApplicationContext())
-                        .loadPreviewImage(
-                                getActivity(),
-                                mHomeImageWallpaper,
-                                getContext().getColor(R.color.secondary_color));
-                setUpLiveWallpaperPreview(mWallpaper);
-            } else {
-                if (mWallpaperConnection != null) {
-                    mWallpaperConnection.disconnect();
-                    mWallpaperConnection = null;
-                }
-            }
-        }
-    }
-
-    private void setUpLiveWallpaperPreview(WallpaperInfo homeWallpaper) {
-        Activity activity = getActivity();
-        if (activity == null) {
-            return;
-        }
-
-        if (mWallpaperConnection != null) {
-            mWallpaperConnection.disconnect();
-        }
-
-        mHomePreview.getLocationOnScreen(mLivePreviewLocation);
-        mPreviewGlobalRect.set(0, 0, mHomePreview.getMeasuredWidth(),
-                mHomePreview.getMeasuredHeight());
-        mPreviewLocalRect.set(mPreviewGlobalRect);
-        mPreviewGlobalRect.offset(mLivePreviewLocation[0], mLivePreviewLocation[1]);
-
-        mWallpaperConnection = new WallpaperConnection(
-                getWallpaperIntent(homeWallpaper.getWallpaperComponent()), activity,
-                new WallpaperConnection.WallpaperConnectionListener() {
-                    @Override
-                    public void onEngineShown() {}
-                }, mPreviewGlobalRect);
-
-        LiveTileOverlay.INSTANCE.update(new RectF(mPreviewLocalRect), mCardView.getRadius());
-
-        mWallpaperConnection.setVisibility(true);
-        mHomePreview.post(() -> {
-            if (!mWallpaperConnection.connect()) {
-                mWallpaperConnection = null;
-                LiveTileOverlay.INSTANCE.detach(mHomePreview.getOverlay());
-            }
-        });
-    }
-
-    private Intent getWallpaperIntent(android.app.WallpaperInfo info) {
-        return new Intent(WallpaperService.SERVICE_INTERFACE)
-                .setClassName(info.getPackageName(), info.getServiceName());
-    }
-
-    private final SurfaceHolder.Callback mGridOptionSurfaceCallback = new SurfaceHolder.Callback() {
-
-        private Surface mLastSurface;
-        private Message mCallback;
-
-        @Override
-        public void surfaceCreated(SurfaceHolder holder) {
-            if (mLastSurface != holder.getSurface()) {
-                mLastSurface = holder.getSurface();
-                Bundle result = mGridManager.renderPreview(
-                        SurfaceViewUtils.createSurfaceViewRequest(mGridOptionSurface),
-                        mGridOption.name);
-                if (result != null) {
-                    mGridOptionSurface.setChildSurfacePackage(
-                            SurfaceViewUtils.getSurfacePackage(result));
-                    mCallback = SurfaceViewUtils.getCallback(result);
-                }
-            }
-        }
-
-        @Override
-        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}
-
-        @Override
-        public void surfaceDestroyed(SurfaceHolder holder) {
-            if (mCallback != null) {
-                try {
-                    mCallback.replyTo.send(mCallback);
-                } catch (RemoteException e) {
-                    e.printStackTrace();
-                } finally {
-                    mCallback = null;
-                }
-            }
-        }
-    };
-
-    private final SurfaceHolder.Callback mWallpaperSurfaceCallback = new SurfaceHolder.Callback() {
-
-        private Surface mLastSurface;
-
-        @Override
-        public void surfaceCreated(SurfaceHolder holder) {
-            if (mLastSurface != holder.getSurface()) {
-                mLastSurface = holder.getSurface();
-                mHomeImageWallpaper = new ImageView(getContext());
-                mHomeImageWallpaper.setBackgroundColor(
-                        ContextCompat.getColor(getContext(), R.color.primary_color));
-                mHomeImageWallpaper.measure(makeMeasureSpec(mHomePreview.getWidth(), EXACTLY),
-                        makeMeasureSpec(mHomePreview.getHeight(), EXACTLY));
-                mHomeImageWallpaper.layout(0, 0, mHomePreview.getWidth(), mHomePreview.getHeight());
-
-                SurfaceControlViewHost host = new SurfaceControlViewHost(getContext(),
-                        getContext().getDisplay(), mWallpaperSurface.getHostToken());
-                host.setView(mHomeImageWallpaper, mHomeImageWallpaper.getWidth(),
-                        mHomeImageWallpaper.getHeight());
-                mWallpaperSurface.setChildSurfacePackage(host.getSurfacePackage());
-            }
-            setUpWallpaperPreview();
-        }
-
-        @Override
-        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}
-
-        @Override
-        public void surfaceDestroyed(SurfaceHolder holder) {}
-    };
 }
diff --git a/src/com/android/customization/picker/grid/GridOptionPreviewer.java b/src/com/android/customization/picker/grid/GridOptionPreviewer.java
new file mode 100644
index 0000000..6d31689
--- /dev/null
+++ b/src/com/android/customization/picker/grid/GridOptionPreviewer.java
@@ -0,0 +1,144 @@
+/*
+ * 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.android.customization.picker.grid;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Message;
+import android.os.RemoteException;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import com.android.customization.model.grid.GridOption;
+import com.android.customization.model.grid.GridOptionsManager;
+import com.android.wallpaper.asset.Asset;
+import com.android.wallpaper.asset.ContentUriAsset;
+import com.android.wallpaper.util.SurfaceViewUtils;
+
+import com.bumptech.glide.request.RequestOptions;
+
+/** A class to load the {@link GridOption} preview to the view. */
+class GridOptionPreviewer {
+
+    private static final int PREVIEW_FADE_DURATION_MS = 100;
+
+    private final Context mContext;
+    private final GridOptionsManager mGridManager;
+    private final ViewGroup mPreviewContainer;
+
+    private SurfaceView mGridOptionSurface;
+    private GridOption mGridOption;
+
+    GridOptionPreviewer(Context context, GridOptionsManager gridManager,
+                        ViewGroup previewContainer) {
+        mContext = context;
+        mGridManager = gridManager;
+        mPreviewContainer = previewContainer;
+    }
+
+    /** Loads the Grid option into the container view. */
+    public void setGridOption(GridOption gridOption, boolean usesSurfaceView) {
+        mGridOption = gridOption;
+        updateWorkspacePreview(usesSurfaceView);
+    }
+
+    /** Releases the view resource. */
+    public void release() {
+        if (mGridOptionSurface != null) {
+            mGridOptionSurface.getHolder().removeCallback(mSurfaceCallback);
+            Surface surface = mGridOptionSurface.getHolder().getSurface();
+            if (surface != null) {
+                surface.release();
+            }
+        }
+        mPreviewContainer.removeAllViews();
+    }
+
+    private void updateWorkspacePreview(boolean usesSurfaceView) {
+        if (mGridOption == null) {
+            return;
+        }
+        mPreviewContainer.removeAllViews();
+
+        if (usesSurfaceView) {
+            mGridOptionSurface = new SurfaceView(mContext);
+            setUpView(mGridOptionSurface);
+            mGridOptionSurface.setZOrderMediaOverlay(true);
+            mGridOptionSurface.getHolder().addCallback(mSurfaceCallback);
+        } else {
+            final ImageView previewImage = new ImageView(mContext);
+            setUpView(previewImage);
+            final Asset previewAsset = new ContentUriAsset(
+                    mContext,
+                    mGridOption.previewImageUri,
+                    RequestOptions.fitCenterTransform());
+            previewAsset.loadDrawableWithTransition(mContext,
+                    previewImage /* imageView */,
+                    PREVIEW_FADE_DURATION_MS /* duration */,
+                    null /* drawableLoadedListener */,
+                    mContext.getResources().getColor(android.R.color.transparent,
+                            null) /* placeHolderColorJ */);
+        }
+    }
+
+    private void setUpView(View view) {
+        view.setLayoutParams(new ViewGroup.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT,
+                ViewGroup.LayoutParams.MATCH_PARENT));
+        mPreviewContainer.addView(view);
+    }
+
+    private final SurfaceHolder.Callback mSurfaceCallback = new SurfaceHolder.Callback() {
+        private Surface mLastSurface;
+        private Message mCallback;
+
+        @Override
+        public void surfaceCreated(SurfaceHolder holder) {
+            if (mLastSurface != holder.getSurface() && mGridOption != null) {
+                mLastSurface = holder.getSurface();
+                Bundle result = mGridManager.renderPreview(
+                        SurfaceViewUtils.createSurfaceViewRequest(mGridOptionSurface),
+                        mGridOption.name);
+                if (result != null) {
+                    mGridOptionSurface.setChildSurfacePackage(
+                            SurfaceViewUtils.getSurfacePackage(result));
+                    mCallback = SurfaceViewUtils.getCallback(result);
+                }
+            }
+        }
+
+        @Override
+        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}
+
+        @Override
+        public void surfaceDestroyed(SurfaceHolder holder) {
+            if (mCallback != null) {
+                try {
+                    mCallback.replyTo.send(mCallback);
+                } catch (RemoteException e) {
+                    e.printStackTrace();
+                } finally {
+                    mCallback.recycle();
+                    mCallback = null;
+                }
+            }
+        }
+    };
+}
diff --git a/src/com/android/customization/picker/theme/CustomThemeNameFragment.java b/src/com/android/customization/picker/theme/CustomThemeNameFragment.java
index 45e4324..8075ffa 100644
--- a/src/com/android/customization/picker/theme/CustomThemeNameFragment.java
+++ b/src/com/android/customization/picker/theme/CustomThemeNameFragment.java
@@ -35,7 +35,6 @@
 import androidx.cardview.widget.CardView;
 
 import com.android.customization.model.theme.ThemeBundle.PreviewInfo;
-import com.android.customization.picker.TimeTicker;
 import com.android.customization.picker.theme.ThemePreviewPage.ThemeCoverPage;
 import com.android.wallpaper.R;
 import com.android.wallpaper.asset.Asset;
@@ -43,6 +42,7 @@
 import com.android.wallpaper.module.CurrentWallpaperInfoFactory;
 import com.android.wallpaper.module.InjectorProvider;
 import com.android.wallpaper.picker.AppbarFragment;
+import com.android.wallpaper.util.TimeTicker;
 
 public class CustomThemeNameFragment extends CustomThemeStepFragment {
 
diff --git a/src/com/android/customization/picker/theme/ThemeFragment.java b/src/com/android/customization/picker/theme/ThemeFragment.java
index e04d8d2..3da41c6 100644
--- a/src/com/android/customization/picker/theme/ThemeFragment.java
+++ b/src/com/android/customization/picker/theme/ThemeFragment.java
@@ -15,29 +15,24 @@
  */
 package com.android.customization.picker.theme;
 
-import android.app.Activity;
-import android.app.WallpaperColors;
+import static android.app.Activity.RESULT_OK;
+
+import static com.android.customization.picker.ViewOnlyFullPreviewActivity.SECTION_STYLE;
+import static com.android.customization.picker.theme.ThemeFullPreviewFragment.EXTRA_THEME_OPTION;
+import static com.android.customization.picker.theme.ThemeFullPreviewFragment.EXTRA_THEME_OPTION_TITLE;
+import static com.android.customization.picker.theme.ThemeFullPreviewFragment.EXTRA_WALLPAPER_INFO;
+import static com.android.wallpaper.widget.BottomActionBar.BottomAction.APPLY;
+import static com.android.wallpaper.widget.BottomActionBar.BottomAction.CUSTOMIZE;
+import static com.android.wallpaper.widget.BottomActionBar.BottomAction.INFORMATION;
+
 import android.content.Context;
 import android.content.Intent;
-import android.content.res.ColorStateList;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
 import android.os.Bundle;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.View.OnLayoutChangeListener;
 import android.view.ViewGroup;
-import android.widget.CompoundButton;
 import android.widget.ImageView;
-import android.widget.SeekBar;
-import android.widget.Switch;
-import android.widget.TextView;
 import android.widget.Toast;
 
 import androidx.annotation.NonNull;
@@ -48,24 +43,20 @@
 import com.android.customization.model.CustomizationManager.Callback;
 import com.android.customization.model.CustomizationManager.OptionsFetchedListener;
 import com.android.customization.model.theme.ThemeBundle;
-import com.android.customization.model.theme.ThemeBundle.PreviewInfo;
 import com.android.customization.model.theme.ThemeManager;
 import com.android.customization.model.theme.custom.CustomTheme;
 import com.android.customization.module.ThemesUserEventLogger;
-import com.android.customization.picker.BasePreviewAdapter;
-import com.android.customization.picker.TimeTicker;
-import com.android.customization.picker.theme.ThemePreviewPage.ThemeCoverPage;
-import com.android.customization.picker.theme.ThemePreviewPage.TimeContainer;
+import com.android.customization.picker.ViewOnlyFullPreviewActivity;
+import com.android.customization.picker.WallpaperPreviewer;
 import com.android.customization.widget.OptionSelectorController;
+import com.android.customization.widget.ThemeInfoView;
 import com.android.wallpaper.R;
-import com.android.wallpaper.asset.Asset;
-import com.android.wallpaper.asset.Asset.CenterCropBitmapTask;
-import com.android.wallpaper.asset.BitmapCachingAsset;
 import com.android.wallpaper.model.WallpaperInfo;
 import com.android.wallpaper.module.CurrentWallpaperInfoFactory;
 import com.android.wallpaper.module.InjectorProvider;
 import com.android.wallpaper.picker.AppbarFragment;
-import com.android.wallpaper.widget.PreviewPager;
+import com.android.wallpaper.widget.BottomActionBar;
+import com.android.wallpaper.widget.WallpaperColorsLoader;
 
 import java.util.List;
 
@@ -76,6 +67,7 @@
 
     private static final String TAG = "ThemeFragment";
     private static final String KEY_SELECTED_THEME = "ThemeFragment.SelectedThemeBundle";
+    private static final int FULL_PREVIEW_REQUEST_CODE = 1000;
 
     /**
      * Interface to be implemented by an Activity hosting a {@link ThemeFragment}
@@ -94,16 +86,16 @@
     private ThemeManager mThemeManager;
     private ThemesUserEventLogger mEventLogger;
     private ThemeBundle mSelectedTheme;
-    private ThemePreviewAdapter mAdapter;
-    private PreviewPager mPreviewPager;
     private ContentLoadingProgressBar mLoading;
     private View mContent;
     private View mError;
-    private boolean mUseMyWallpaper = true;
     private WallpaperInfo mCurrentHomeWallpaper;
-    private Asset mCurrentWallpaperThumbAsset;
     private CurrentWallpaperInfoFactory mCurrentWallpaperFactory;
-    private TimeTicker mTicker;
+    private BottomActionBar mBottomActionBar;
+    private WallpaperPreviewer mWallpaperPreviewer;
+    private ImageView mWallpaperImage;
+    private ThemeOptionPreviewer mThemeOptionPreviewer;
+    private ThemeInfoView mThemeInfoView;
 
     @Override
     public void onAttach(Context context) {
@@ -126,29 +118,91 @@
         mError = view.findViewById(R.id.error_section);
         mCurrentWallpaperFactory = InjectorProvider.getInjector()
                 .getCurrentWallpaperFactory(getActivity().getApplicationContext());
-        mPreviewPager = view.findViewById(R.id.theme_preview_pager);
         mOptionsContainer = view.findViewById(R.id.options_container);
-        view.findViewById(R.id.apply_button).setOnClickListener(v -> {
+
+        // Set Wallpaper background.
+        mWallpaperImage = view.findViewById(R.id.wallpaper_preview_image);
+        mWallpaperPreviewer = new WallpaperPreviewer(
+                getLifecycle(),
+                getActivity(),
+                mWallpaperImage,
+                view.findViewById(R.id.wallpaper_preview_surface));
+        mCurrentWallpaperFactory.createCurrentWallpaperInfos(
+                (homeWallpaper, lockWallpaper, presentationMode) -> {
+                    mCurrentHomeWallpaper = homeWallpaper;
+                    mWallpaperPreviewer.setWallpaper(mCurrentHomeWallpaper);
+                    updateThemePreviewColorPerWallpaper();
+                }, false);
+
+        ViewGroup previewContainer = view.findViewById(R.id.theme_preview_container);
+        previewContainer.setOnClickListener(v -> showFullPreview());
+        mThemeOptionPreviewer = new ThemeOptionPreviewer(
+                getLifecycle(),
+                getContext(),
+                previewContainer);
+
+        view.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+            @Override
+            public void onLayoutChange(View v, int left, int top, int right, int bottom,
+                                       int oldLeft, int oldTop, int oldRight, int oldBottom) {
+                mWallpaperPreviewer.updatePreviewCardRadius();
+                updateThemePreviewColorPerWallpaper();
+                view.removeOnLayoutChangeListener(this);
+            }
+        });
+        return view;
+    }
+
+    @Override
+    protected void onBottomActionBarReady(BottomActionBar bottomActionBar) {
+        mBottomActionBar = bottomActionBar;
+        mBottomActionBar.showActionsOnly(INFORMATION, APPLY);
+        mBottomActionBar.setActionClickListener(APPLY, v -> {
+            mBottomActionBar.disableActions();
             applyTheme();
         });
-        setUpOptions(savedInstanceState);
+        mThemeInfoView = (ThemeInfoView) LayoutInflater.from(getContext()).inflate(
+                R.layout.theme_info_view, /* root= */ null);
+        mBottomActionBar.attachViewToBottomSheetAndBindAction(mThemeInfoView, INFORMATION);
+        mBottomActionBar.setActionClickListener(CUSTOMIZE, this::onCustomizeClicked);
+    }
 
-        return view;
+    @Override
+    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+        // Setup options here when all views are ready(including BottomActionBar), since we need to
+        // update views after options are loaded.
+        setUpOptions(savedInstanceState);
+    }
+
+    private void updateThemePreviewColorPerWallpaper() {
+        if (mCurrentHomeWallpaper != null && mWallpaperImage.getMeasuredWidth() > 0
+                && mWallpaperImage.getMeasuredHeight() > 0) {
+            WallpaperColorsLoader.getWallpaperColors(
+                    mCurrentHomeWallpaper.getThumbAsset(getContext()),
+                    mWallpaperImage.getMeasuredWidth(),
+                    mWallpaperImage.getMeasuredHeight(),
+                    mThemeOptionPreviewer::updateColorForLauncherWidgets);
+        }
     }
 
     private void applyTheme() {
         mThemeManager.apply(mSelectedTheme, new Callback() {
             @Override
             public void onSuccess() {
+                // Since we disabled it when clicked apply button.
+                mBottomActionBar.enableActions();
+                mBottomActionBar.hide();
                 Toast.makeText(getContext(), R.string.applied_theme_msg,
                         Toast.LENGTH_LONG).show();
-                getActivity().finish();
-                getActivity().overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
             }
 
             @Override
             public void onError(@Nullable Throwable throwable) {
                 Log.w(TAG, "Error applying theme", throwable);
+                // Since we disabled it when clicked apply button.
+                mBottomActionBar.enableActions();
+                mBottomActionBar.hide();
                 Toast.makeText(getContext(), R.string.apply_theme_error_msg,
                         Toast.LENGTH_LONG).show();
             }
@@ -156,28 +210,6 @@
     }
 
     @Override
-    public void onResume() {
-        super.onResume();
-        mTicker = TimeTicker.registerNewReceiver(getContext(), this::updateTime);
-        reloadWallpaper();
-        updateTime();
-    }
-
-    private void updateTime() {
-        if (mAdapter != null) {
-            mAdapter.updateTime();
-        }
-    }
-
-    @Override
-    public void onPause() {
-        super.onPause();
-        if (getContext() != null) {
-            getContext().unregisterReceiver(mTicker);
-        }
-    }
-
-    @Override
     public void onSaveInstanceState(@NonNull Bundle outState) {
         super.onSaveInstanceState(outState);
         if (mSelectedTheme != null && !mSelectedTheme.isActive(mThemeManager)) {
@@ -196,35 +228,20 @@
             } else {
                 if (mSelectedTheme != null) {
                     mOptionsController.setSelectedOption(mSelectedTheme);
+                    // Set selected option above will show BottomActionBar,
+                    // hide BottomActionBar for the mis-trigger.
+                    mBottomActionBar.hide();
                 } else {
                     reloadOptions();
                 }
             }
+        } else if (requestCode == FULL_PREVIEW_REQUEST_CODE && resultCode == RESULT_OK) {
+            applyTheme();
         }
         super.onActivityResult(requestCode, resultCode, data);
     }
 
-    private void reloadWallpaper() {
-        mCurrentWallpaperFactory.createCurrentWallpaperInfos(
-                (homeWallpaper, lockWallpaper, presentationMode) -> {
-                    mCurrentHomeWallpaper = homeWallpaper;
-                    mCurrentWallpaperThumbAsset = new BitmapCachingAsset(getContext(),
-                            mCurrentHomeWallpaper.getThumbAsset(getContext()));
-                    if (mSelectedTheme != null && mAdapter != null) {
-                        mAdapter.setWallpaperAsset(mCurrentWallpaperThumbAsset);
-                        mAdapter.rebindWallpaperIfAvailable();
-                    }
-        }, false);
-    }
-
-    private void createAdapter(List<ThemeBundle> options) {
-        mAdapter = new ThemePreviewAdapter(getActivity(), mSelectedTheme,
-                mCurrentWallpaperThumbAsset,
-                mSelectedTheme instanceof CustomTheme ? this::onEditClicked : null);
-        mPreviewPager.setAdapter(mAdapter);
-    }
-
-    private void onEditClicked(View view) {
+    private void onCustomizeClicked(View view) {
         if (mSelectedTheme instanceof CustomTheme) {
             navigateToCustomTheme((CustomTheme) mSelectedTheme);
         }
@@ -254,14 +271,20 @@
                         navigateToCustomTheme((CustomTheme) selected);
                     } else {
                         mSelectedTheme = (ThemeBundle) selected;
-                        if (mUseMyWallpaper || mSelectedTheme instanceof CustomTheme) {
-                            mSelectedTheme.setOverrideThemeWallpaper(mCurrentHomeWallpaper);
-                        } else {
-                            mSelectedTheme.setOverrideThemeWallpaper(null);
-                        }
+                        mSelectedTheme.setOverrideThemeWallpaper(mCurrentHomeWallpaper);
                         mEventLogger.logThemeSelected(mSelectedTheme,
                                 selected instanceof CustomTheme);
-                        createAdapter(options);
+                        mThemeOptionPreviewer.setPreviewInfo(mSelectedTheme.getPreviewInfo());
+                        if (mThemeInfoView != null && mSelectedTheme != null) {
+                            mThemeInfoView.populateThemeInfo(mSelectedTheme);
+                        }
+
+                        if (selected instanceof CustomTheme) {
+                            mBottomActionBar.showActionsOnly(INFORMATION, CUSTOMIZE, APPLY);
+                        } else {
+                            mBottomActionBar.showActionsOnly(INFORMATION, APPLY);
+                        }
+                        mBottomActionBar.show();
                     }
                 });
                 mOptionsController.initOptions(mThemeManager);
@@ -278,13 +301,15 @@
                 }
                 if (mSelectedTheme == null) {
                     // Select the default theme if there is no matching custom enabled theme
-                    // TODO(b/124796742): default to custom if there is no matching theme bundle
-                    mSelectedTheme = options.get(0);
+                    mSelectedTheme = findDefaultThemeBundle(options);
                 } else {
                     // Only show show checkmark if we found a matching theme
                     mOptionsController.setAppliedOption(mSelectedTheme);
                 }
                 mOptionsController.setSelectedOption(mSelectedTheme);
+                // Set selected option above will show BottomActionBar when entering the tab. But
+                // it should not show when entering the tab.
+                mBottomActionBar.hide();
             }
             @Override
             public void onError(@Nullable Throwable throwable) {
@@ -307,16 +332,29 @@
             }
             if (mSelectedTheme == null) {
                 // Select the default theme if there is no matching custom enabled theme
-                // TODO(b/124796742): default to custom if there is no matching theme bundle
-                mSelectedTheme = options.get(0);
+                mSelectedTheme = findDefaultThemeBundle(options);
             } else {
                 // Only show show checkmark if we found a matching theme
                 mOptionsController.setAppliedOption(mSelectedTheme);
             }
             mOptionsController.setSelectedOption(mSelectedTheme);
+            // Set selected option above will show BottomActionBar,
+            // hide BottomActionBar for the mis-trigger.
+            mBottomActionBar.hide();
         }, true);
     }
 
+    private ThemeBundle findDefaultThemeBundle(List<ThemeBundle> options) {
+        String defaultThemeTitle =
+                getActivity().getResources().getString(R.string.default_theme_title);
+        for (ThemeBundle bundle : options) {
+            if (bundle.getTitle().equals(defaultThemeTitle)) {
+                return bundle;
+            }
+        }
+        return null;
+    }
+
     private void navigateToCustomTheme(CustomTheme themeToEdit) {
         Intent intent = new Intent(getActivity(), CustomThemeActivity.class);
         intent.putExtra(CustomThemeActivity.EXTRA_THEME_TITLE, themeToEdit.getTitle());
@@ -326,241 +364,12 @@
         startActivityForResult(intent, CustomThemeActivity.REQUEST_CODE_CUSTOM_THEME);
     }
 
-    /**
-     * Adapter class for mPreviewPager.
-     * This is a ViewPager as it allows for a nice pagination effect (ie, pages snap on swipe,
-     * we don't want to just scroll)
-     */
-    private static class ThemePreviewAdapter extends BasePreviewAdapter<ThemePreviewPage> {
-
-        private int[] mIconIds = {
-                R.id.preview_icon_0, R.id.preview_icon_1, R.id.preview_icon_2, R.id.preview_icon_3,
-                R.id.preview_icon_4, R.id.preview_icon_5
-        };
-        private int[] mColorButtonIds = {
-            R.id.preview_check_selected, R.id.preview_radio_selected, R.id.preview_toggle_selected
-        };
-        private int[] mColorTileIds = {
-            R.id.preview_color_qs_0_bg, R.id.preview_color_qs_1_bg, R.id.preview_color_qs_2_bg
-        };
-        private int[][] mColorTileIconIds = {
-                new int[]{ R.id.preview_color_qs_0_icon, 0},
-                new int[]{ R.id.preview_color_qs_1_icon, 1},
-                new int[] { R.id.preview_color_qs_2_icon, 3}
-        };
-
-        private int[] mShapeIconIds = {
-                R.id.shape_preview_icon_0, R.id.shape_preview_icon_1, R.id.shape_preview_icon_2,
-                R.id.shape_preview_icon_3, R.id.shape_preview_icon_4, R.id.shape_preview_icon_5
-        };
-        private Asset mWallpaperAsset;
-
-        ThemePreviewAdapter(Activity activity, ThemeBundle theme, @Nullable Asset wallpaperAsset,
-                @Nullable OnClickListener editClickListener) {
-            super(activity, R.layout.theme_preview_card);
-            mWallpaperAsset = wallpaperAsset;
-            final Resources res = activity.getResources();
-            final PreviewInfo previewInfo = theme.getPreviewInfo();
-
-            Drawable coverScrim = theme instanceof CustomTheme
-                    ? res.getDrawable(R.drawable.theme_cover_scrim, activity.getTheme())
-                    : null;
-
-            WallpaperPreviewLayoutListener wallpaperListener = new WallpaperPreviewLayoutListener(
-                    () -> mWallpaperAsset, previewInfo, coverScrim, true);
-
-            addPage(new ThemeCoverPage(activity, theme.getTitle(),
-                    previewInfo.resolveAccentColor(res), previewInfo.icons,
-                    previewInfo.headlineFontFamily, previewInfo.bottomSheeetCornerRadius,
-                    previewInfo.shapeDrawable, previewInfo.shapeAppIcons, editClickListener,
-                    mColorButtonIds, mColorTileIds, mColorTileIconIds, mShapeIconIds,
-                    wallpaperListener));
-            addPage(new ThemePreviewPage(activity, R.string.preview_name_font, R.drawable.ic_font,
-                    R.layout.preview_card_font_content,
-                    previewInfo.resolveAccentColor(res)) {
-                @Override
-                protected void bindBody(boolean forceRebind) {
-                    TextView title = card.findViewById(R.id.font_card_title);
-                    title.setTypeface(previewInfo.headlineFontFamily);
-                    TextView body = card.findViewById(R.id.font_card_body);
-                    body.setTypeface(previewInfo.bodyFontFamily);
-                    card.findViewById(R.id.font_card_divider).setBackgroundColor(accentColor);
-                }
-            });
-            if (previewInfo.icons.size() >= mIconIds.length) {
-                addPage(new ThemePreviewPage(activity, R.string.preview_name_icon,
-                        R.drawable.ic_wifi_24px, R.layout.preview_card_icon_content,
-                        previewInfo.resolveAccentColor(res)) {
-                    @Override
-                    protected void bindBody(boolean forceRebind) {
-                        for (int i = 0; i < mIconIds.length && i < previewInfo.icons.size(); i++) {
-                            ((ImageView) card.findViewById(mIconIds[i]))
-                                    .setImageDrawable(previewInfo.icons.get(i)
-                                            .getConstantState().newDrawable().mutate());
-                        }
-                    }
-                });
-            }
-            if (previewInfo.colorAccentDark != -1 && previewInfo.colorAccentLight != -1) {
-                addPage(new ThemePreviewPage(activity, R.string.preview_name_color,
-                        R.drawable.ic_colorize_24px, R.layout.preview_card_color_content,
-                        previewInfo.resolveAccentColor(res)) {
-                    @Override
-                    protected void bindBody(boolean forceRebind) {
-                        int controlGreyColor = res.getColor(R.color.control_grey);
-                        ColorStateList tintList = new ColorStateList(
-                                new int[][]{
-                                    new int[]{android.R.attr.state_selected},
-                                    new int[]{android.R.attr.state_checked},
-                                    new int[]{-android.R.attr.state_enabled},
-                                },
-                                new int[] {
-                                    accentColor,
-                                    accentColor,
-                                    controlGreyColor
-                                }
-                            );
-
-                        for (int i = 0; i < mColorButtonIds.length; i++) {
-                            CompoundButton button = card.findViewById(mColorButtonIds[i]);
-                            button.setButtonTintList(tintList);
-                        }
-
-                        Switch enabledSwitch = card.findViewById(R.id.preview_toggle_selected);
-                        enabledSwitch.setThumbTintList(tintList);
-                        enabledSwitch.setTrackTintList(tintList);
-
-                        ColorStateList seekbarTintList = ColorStateList.valueOf(accentColor);
-                        SeekBar seekbar = card.findViewById(R.id.preview_seekbar);
-                        seekbar.setThumbTintList(seekbarTintList);
-                        seekbar.setProgressTintList(seekbarTintList);
-                        seekbar.setProgressBackgroundTintList(seekbarTintList);
-                        // Disable seekbar
-                        seekbar.setOnTouchListener((view, motionEvent) -> true);
-
-                        int iconFgColor = res.getColor(R.color.tile_enabled_icon_color, null);
-                        for (int i = 0; i < mColorTileIds.length && i < previewInfo.icons.size();
-                                i++) {
-                            Drawable icon = previewInfo.icons.get(mColorTileIconIds[i][1])
-                                    .getConstantState().newDrawable().mutate();
-                            icon.setTint(iconFgColor);
-                            Drawable bgShape =
-                                    previewInfo.shapeDrawable.getConstantState().newDrawable();
-                            bgShape.setTint(accentColor);
-
-                            ImageView bg = card.findViewById(mColorTileIds[i]);
-                            bg.setImageDrawable(bgShape);
-                            ImageView fg = card.findViewById(mColorTileIconIds[i][0]);
-                            fg.setImageDrawable(icon);
-                        }
-                    }
-                });
-            }
-            if (!previewInfo.shapeAppIcons.isEmpty()) {
-                addPage(new ThemePreviewPage(activity, R.string.preview_name_shape,
-                        R.drawable.ic_shapes_24px, R.layout.preview_card_shape_content,
-                        previewInfo.resolveAccentColor(res)) {
-                    @Override
-                    protected void bindBody(boolean forceRebind) {
-                        for (int i = 0; i < mShapeIconIds.length
-                                && i < previewInfo.shapeAppIcons.size(); i++) {
-                            ImageView iconView = card.findViewById(mShapeIconIds[i]);
-                            iconView.setBackground(
-                                    previewInfo.shapeAppIcons.get(i));
-                        }
-                    }
-                });
-            }
-        }
-
-        public void rebindWallpaperIfAvailable() {
-            for (ThemePreviewPage page : mPages) {
-                if (page.containsWallpaper()) {
-                    page.bindBody(true);
-                }
-            }
-        }
-
-        public void updateTime() {
-            for (ThemePreviewPage page : mPages) {
-                if (page instanceof TimeContainer) {
-                    ((TimeContainer)page).updateTime();
-                }
-            }
-        }
-
-        public void setWallpaperAsset(Asset wallpaperAsset) {
-            mWallpaperAsset = wallpaperAsset;
-        }
-
-        private static class WallpaperPreviewLayoutListener implements OnLayoutChangeListener {
-            interface WallpaperPreviewAssetProvider {
-                Asset getAsset();
-            }
-            private final WallpaperPreviewAssetProvider mWallpaperPreviewAssetProvider;
-            private final PreviewInfo mPreviewInfo;
-            private final Drawable mScrim;
-            private final boolean mIsTranslucent;
-
-            WallpaperPreviewLayoutListener(
-                    WallpaperPreviewAssetProvider wallpaperPreviewAssetProvider,
-                    PreviewInfo previewInfo, Drawable scrim, boolean translucent) {
-                mWallpaperPreviewAssetProvider = wallpaperPreviewAssetProvider;
-                mPreviewInfo = previewInfo;
-                mScrim = scrim;
-                mIsTranslucent = translucent;
-            }
-
-            @Override
-            public void onLayoutChange(View view, int left, int top, int right,
-                    int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
-                int targetWidth = right - left;
-                int targetHeight = bottom - top;
-                if (targetWidth > 0 && targetHeight > 0) {
-                    Asset wallpaperPreviewAsset = mWallpaperPreviewAssetProvider.getAsset();
-                    if (wallpaperPreviewAsset != null) {
-                        wallpaperPreviewAsset.decodeBitmap(
-                                targetWidth, targetHeight,
-                                bitmap -> new CenterCropBitmapTask(bitmap, view,
-                                        croppedBitmap -> setWallpaperBitmap(view, croppedBitmap))
-                                .execute());
-                    }
-                    view.removeOnLayoutChangeListener(this);
-                }
-            }
-
-            private void setWallpaperBitmap(View view, Bitmap bitmap) {
-                Resources res = view.getContext().getResources();
-                Drawable background = new BitmapDrawable(res, bitmap);
-                if (mIsTranslucent) {
-                    background.setAlpha(ThemeCoverPage.COVER_PAGE_WALLPAPER_ALPHA);
-                }
-                if (mScrim != null) {
-                    background = new LayerDrawable(new Drawable[]{background, mScrim});
-                }
-                view.findViewById(R.id.theme_preview_card_background).setBackground(background);
-                if (mScrim == null && !mIsTranslucent) {
-                    boolean shouldRecycle = false;
-                    if (bitmap.getConfig() == Config.HARDWARE) {
-                        bitmap = bitmap.copy(Config.ARGB_8888, false);
-                        shouldRecycle = true;
-                    }
-                    int colorsHint = WallpaperColors.fromBitmap(bitmap).getColorHints();
-                    if (shouldRecycle) {
-                        bitmap.recycle();
-                    }
-                    TextView header = view.findViewById(R.id.theme_preview_card_header);
-                    if ((colorsHint & WallpaperColors.HINT_SUPPORTS_DARK_TEXT) == 0) {
-                        int colorLight = res.getColor(R.color.text_color_light, null);
-                        header.setTextColor(colorLight);
-                        header.setCompoundDrawableTintList(ColorStateList.valueOf(colorLight));
-                    } else {
-                        header.setTextColor(res.getColor(R.color.text_color_dark, null));
-                        header.setCompoundDrawableTintList(ColorStateList.valueOf(
-                                mPreviewInfo.colorAccentLight));
-                    }
-                }
-            }
-        }
+    private void showFullPreview() {
+        Bundle bundle = new Bundle();
+        bundle.putParcelable(EXTRA_WALLPAPER_INFO, mCurrentHomeWallpaper);
+        bundle.putString(EXTRA_THEME_OPTION, mSelectedTheme.getSerializedPackages());
+        bundle.putString(EXTRA_THEME_OPTION_TITLE, mSelectedTheme.getTitle());
+        Intent intent = ViewOnlyFullPreviewActivity.newIntent(getContext(), SECTION_STYLE, bundle);
+        startActivityForResult(intent, FULL_PREVIEW_REQUEST_CODE);
     }
 }
diff --git a/src/com/android/customization/picker/theme/ThemeFullPreviewFragment.java b/src/com/android/customization/picker/theme/ThemeFullPreviewFragment.java
new file mode 100644
index 0000000..3b6923c
--- /dev/null
+++ b/src/com/android/customization/picker/theme/ThemeFullPreviewFragment.java
@@ -0,0 +1,154 @@
+/*
+ * 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.android.customization.picker.theme;
+
+import static android.app.Activity.RESULT_OK;
+
+import static com.android.wallpaper.widget.BottomActionBar.BottomAction.APPLY;
+import static com.android.wallpaper.widget.BottomActionBar.BottomAction.INFORMATION;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.customization.model.theme.DefaultThemeProvider;
+import com.android.customization.model.theme.ThemeBundle;
+import com.android.customization.model.theme.ThemeBundleProvider;
+import com.android.customization.module.CustomizationInjector;
+import com.android.customization.picker.WallpaperPreviewer;
+import com.android.customization.widget.ThemeInfoView;
+import com.android.wallpaper.R;
+import com.android.wallpaper.model.WallpaperInfo;
+import com.android.wallpaper.module.InjectorProvider;
+import com.android.wallpaper.picker.AppbarFragment;
+import com.android.wallpaper.widget.BottomActionBar;
+import com.android.wallpaper.widget.WallpaperColorsLoader;
+
+import com.bumptech.glide.Glide;
+
+import org.json.JSONException;
+
+/** A Fragment for theme full preview page. */
+public class ThemeFullPreviewFragment extends AppbarFragment {
+    private static final String TAG = "ThemeFullPreviewFragment";
+
+    public static final String EXTRA_THEME_OPTION_TITLE = "theme_option_title";
+    protected static final String EXTRA_THEME_OPTION = "theme_option";
+    protected static final String EXTRA_WALLPAPER_INFO = "wallpaper_info";
+
+    private WallpaperInfo mWallpaper;
+    private ThemeBundle mThemeBundle;
+
+    /**
+     * Returns a new {@link ThemeFullPreviewFragment} with the provided title and bundle arguments
+     * set.
+     */
+    public static ThemeFullPreviewFragment newInstance(CharSequence title, Bundle intentBundle) {
+        ThemeFullPreviewFragment fragment = new ThemeFullPreviewFragment();
+        Bundle bundle = new Bundle();
+        bundle.putAll(AppbarFragment.createArguments(title));
+        bundle.putAll(intentBundle);
+        fragment.setArguments(bundle);
+        return fragment;
+    }
+
+    @Override
+    public void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mWallpaper = getArguments().getParcelable(EXTRA_WALLPAPER_INFO);
+        CustomizationInjector injector = (CustomizationInjector) InjectorProvider.getInjector();
+        ThemeBundleProvider themeProvider = new DefaultThemeProvider(
+                getContext(), injector.getCustomizationPreferences(getContext()));
+        try {
+            ThemeBundle.Builder builder = themeProvider.parseThemeBundle(
+                    getArguments().getString(EXTRA_THEME_OPTION));
+            if (builder != null) {
+                builder.setTitle(getArguments().getString(EXTRA_THEME_OPTION_TITLE));
+                mThemeBundle = builder.build(getContext());
+            }
+        } catch (JSONException e) {
+            Log.w(TAG, "Couldn't parse provided custom theme, will override it");
+            // TODO(chihhangchuang): Handle the error case.
+        }
+    }
+
+    @Nullable
+    @Override
+    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
+                             @Nullable Bundle savedInstanceState) {
+        View view = inflater.inflate(
+                R.layout.fragment_theme_full_preview, container, /* attachToRoot */ false);
+        setUpToolbar(view);
+        Glide.get(getContext()).clearMemory();
+
+        // Set wallpaper background.
+        ImageView wallpaperImageView = view.findViewById(R.id.wallpaper_preview_image);
+        final WallpaperPreviewer wallpaperPreviewer = new WallpaperPreviewer(
+                getLifecycle(),
+                getActivity(),
+                wallpaperImageView,
+                view.findViewById(R.id.wallpaper_preview_surface));
+        wallpaperPreviewer.setWallpaper(mWallpaper);
+
+        // Set theme option.
+        final ThemeOptionPreviewer themeOptionPreviewer = new ThemeOptionPreviewer(
+                getLifecycle(),
+                getContext(),
+                view.findViewById(R.id.theme_preview_container));
+        themeOptionPreviewer.setPreviewInfo(mThemeBundle.getPreviewInfo());
+        view.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+            @Override
+            public void onLayoutChange(View v, int left, int top, int right, int bottom,
+                                       int oldLeft, int oldTop, int oldRight, int oldBottom) {
+                wallpaperPreviewer.updatePreviewCardRadius();
+                WallpaperColorsLoader.getWallpaperColors(
+                        mWallpaper.getThumbAsset(getContext()),
+                        wallpaperImageView.getMeasuredWidth(),
+                        wallpaperImageView.getMeasuredHeight(),
+                        themeOptionPreviewer::updateColorForLauncherWidgets);
+                view.removeOnLayoutChangeListener(this);
+            }
+        });
+        return view;
+    }
+
+    @Override
+    protected void onBottomActionBarReady(BottomActionBar bottomActionBar) {
+        bottomActionBar.showActionsOnly(INFORMATION, APPLY);
+        bottomActionBar.setActionClickListener(APPLY, v -> finishActivityWithResultOk());
+        ThemeInfoView themeInfoView = (ThemeInfoView) LayoutInflater.from(getContext()).inflate(
+                R.layout.theme_info_view, /* root= */ null);
+        themeInfoView.populateThemeInfo(mThemeBundle);
+        bottomActionBar.attachViewToBottomSheetAndBindAction(themeInfoView, INFORMATION);
+        bottomActionBar.show();
+    }
+
+    private void finishActivityWithResultOk() {
+        Activity activity = requireActivity();
+        activity.overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
+        Intent intent = new Intent();
+        activity.setResult(RESULT_OK, intent);
+        activity.finish();
+    }
+}
diff --git a/src/com/android/customization/picker/theme/ThemeOptionPreviewer.java b/src/com/android/customization/picker/theme/ThemeOptionPreviewer.java
new file mode 100644
index 0000000..6096762
--- /dev/null
+++ b/src/com/android/customization/picker/theme/ThemeOptionPreviewer.java
@@ -0,0 +1,359 @@
+/*
+ * 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.android.customization.picker.theme;
+
+import static android.view.View.MeasureSpec.EXACTLY;
+import static android.view.View.MeasureSpec.makeMeasureSpec;
+
+import android.app.WallpaperColors;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CompoundButton;
+import android.widget.ImageView;
+import android.widget.Switch;
+import android.widget.TextView;
+
+import androidx.annotation.MainThread;
+import androidx.cardview.widget.CardView;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleObserver;
+import androidx.lifecycle.OnLifecycleEvent;
+
+import com.android.customization.model.theme.ThemeBundle;
+import com.android.customization.model.theme.ThemeBundle.PreviewInfo;
+import com.android.wallpaper.R;
+import com.android.wallpaper.util.ScreenSizeCalculator;
+import com.android.wallpaper.util.TimeTicker;
+
+import java.text.DateFormat;
+import java.text.FieldPosition;
+import java.util.Calendar;
+import java.util.List;
+import java.util.TimeZone;
+
+/** A class to load the {@link ThemeBundle} preview to the view. */
+class ThemeOptionPreviewer implements LifecycleObserver {
+    // Maps which icon from ResourceConstants#ICONS_FOR_PREVIEW.
+    private static final int ICON_WIFI = 0;
+    private static final int ICON_BLUETOOTH = 1;
+    private static final int ICON_FLASHLIGHT = 3;
+    private static final int ICON_AUTO_ROTATE = 4;
+    private static final int ICON_CELLULAR_SIGNAL = 6;
+    private static final int ICON_BATTERY = 7;
+
+    // Icons in the top bar (fake "status bar") with the particular order.
+    private static final int [] sTopBarIconToPreviewIcon = new int [] {
+            ICON_WIFI, ICON_CELLULAR_SIGNAL, ICON_BATTERY };
+
+    // Ids of app icon shape preview.
+    private int[] mShapeAppIconIds = {
+            R.id.shape_preview_icon_0, R.id.shape_preview_icon_1,
+            R.id.shape_preview_icon_2, R.id.shape_preview_icon_3
+    };
+    private int[] mShapeIconAppNameIds = {
+            R.id.shape_preview_icon_app_name_0, R.id.shape_preview_icon_app_name_1,
+            R.id.shape_preview_icon_app_name_2, R.id.shape_preview_icon_app_name_3
+    };
+
+    // Ids of color/icons section.
+    private int[][] mColorTileIconIds = {
+            new int[] { R.id.preview_color_qs_0_icon, ICON_WIFI},
+            new int[] { R.id.preview_color_qs_1_icon, ICON_BLUETOOTH},
+            new int[] { R.id.preview_color_qs_2_icon, ICON_FLASHLIGHT},
+            new int[] { R.id.preview_color_qs_3_icon, ICON_AUTO_ROTATE},
+    };
+    private int[] mColorTileIds = {
+            R.id.preview_color_qs_0_bg, R.id.preview_color_qs_1_bg,
+            R.id.preview_color_qs_2_bg, R.id.preview_color_qs_3_bg
+    };
+    private int[] mColorButtonIds = {
+            R.id.preview_check_selected, R.id.preview_radio_selected, R.id.preview_toggle_selected
+    };
+
+    private final Context mContext;
+
+    private View mContentView;
+    private TextView mClock;
+    private TimeTicker mTicker;
+
+    ThemeOptionPreviewer(Lifecycle lifecycle, Context context, ViewGroup previewContainer) {
+        lifecycle.addObserver(this);
+
+        mContext = context;
+        mContentView = LayoutInflater.from(context).inflate(
+                R.layout.theme_preview_content_v2, /* root= */ null);
+        mClock = mContentView.findViewById(R.id.theme_preview_clock);
+        updateTime();
+        final float screenAspectRatio =
+                ScreenSizeCalculator.getInstance().getScreenAspectRatio(mContext);
+        previewContainer.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+            @Override
+            public void onLayoutChange(View view, int left, int top, int right, int bottom,
+                                       int oldLeft, int oldTop, int oldRight, int oldBottom) {
+                // Calculate the full preview card height and width.
+                int fullPreviewCardHeight = getFullPreviewCardHeight();
+                int fullPreviewCardWidth = (int) (getFullPreviewCardHeight() / screenAspectRatio);
+
+                // Relayout the content view to match full preview card size.
+                mContentView.measure(
+                        makeMeasureSpec(fullPreviewCardWidth, EXACTLY),
+                        makeMeasureSpec(fullPreviewCardHeight, EXACTLY));
+                mContentView.layout(0, 0, fullPreviewCardWidth, fullPreviewCardHeight);
+
+                // Scale the content view from full preview size to the container size. For full
+                // preview, the scale value is 1.
+                float scale =
+                        (float) previewContainer.getMeasuredHeight() / getFullPreviewCardHeight();
+                mContentView.setScaleX(scale);
+                mContentView.setScaleY(scale);
+                // The pivot point is centered by default, set to (0, 0).
+                mContentView.setPivotX(0f);
+                mContentView.setPivotY(0f);
+
+                // Ensure there will be only one content view in the container.
+                previewContainer.removeAllViews();
+                // Finally, add the content view to the container.
+                previewContainer.addView(
+                        mContentView,
+                        mContentView.getMeasuredWidth(),
+                        mContentView.getMeasuredHeight());
+
+                previewContainer.removeOnLayoutChangeListener(this);
+            }
+        });
+    }
+
+    /** Loads the Theme option preview into the container view. */
+    public void setPreviewInfo(PreviewInfo previewInfo) {
+        setHeadlineFont(previewInfo.headlineFontFamily);
+        setTopBarIcons(previewInfo.icons);
+        setAppIconShape(previewInfo.shapeAppIcons);
+        setAppIconName(previewInfo.shapeAppIconNames);
+        setColorAndIconsSection(previewInfo.icons, previewInfo.shapeDrawable,
+                previewInfo.resolveAccentColor(mContext.getResources()));
+        setColorAndIconsBoxRadius(previewInfo.bottomSheeetCornerRadius);
+        setQsbRadius(previewInfo.bottomSheeetCornerRadius);
+    }
+
+    /**
+     * Updates the color of widgets in launcher (like top status bar, smart space, and app name
+     * text) which will change its content color according to different wallpapers.
+     */
+    public void updateColorForLauncherWidgets(WallpaperColors colors) {
+        int color = mContext.getColor(
+                (colors.getColorHints() & WallpaperColors.HINT_SUPPORTS_DARK_TEXT) == 0
+                        ? R.color.text_color_light
+                        : R.color.text_color_dark);
+        // Update the top status bar clock text color.
+        mClock.setTextColor(color);
+        // Update the top status bar icon color.
+        ViewGroup iconsContainer = mContentView.findViewById(R.id.theme_preview_top_bar_icons);
+        for (int i = 0; i < iconsContainer.getChildCount(); i++) {
+            ((ImageView) iconsContainer.getChildAt(i))
+                    .setImageTintList(ColorStateList.valueOf(color));
+        }
+        // Update smart space date color.
+        ((TextView) mContentView.findViewById(R.id.smart_space_date)).setTextColor(color);
+        // Update shape app icon name text color.
+        for (int id : mShapeIconAppNameIds) {
+            ((TextView) mContentView.findViewById(id)).setTextColor(color);
+        }
+    }
+
+    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
+    @MainThread
+    public void onResume() {
+        mTicker = TimeTicker.registerNewReceiver(mContext, this::updateTime);
+        updateTime();
+    }
+
+    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
+    @MainThread
+    public void onPause() {
+        if (mContext != null) {
+            mContext.unregisterReceiver(mTicker);
+        }
+    }
+
+    private void setHeadlineFont(Typeface headlineFont) {
+        // Update font of status bar clock.
+        mClock.setTypeface(headlineFont);
+
+        // Update font of the smart space date.
+        TextView date = mContentView.findViewById(R.id.smart_space_date);
+        date.setTypeface(headlineFont);
+        // TODO(chihhangchuang): Use real date.
+        date.setText("Friday, Nov 12");
+
+        // Update font of app names.
+        for (int id : mShapeIconAppNameIds) {
+            TextView appName = mContentView.findViewById(id);
+            appName.setTypeface(headlineFont);
+        }
+
+        // Update font of color/icons section title.
+        TextView colorIconsSectionTitle = mContentView.findViewById(R.id.color_icons_section_title);
+        colorIconsSectionTitle.setTypeface(headlineFont);
+    }
+
+    private void setTopBarIcons(List<Drawable> icons) {
+        ViewGroup iconsContainer = mContentView.findViewById(R.id.theme_preview_top_bar_icons);
+        for (int i = 0; i < iconsContainer.getChildCount(); i++) {
+            int iconIndex = sTopBarIconToPreviewIcon[i];
+            if (iconIndex < icons.size()) {
+                ((ImageView) iconsContainer.getChildAt(i))
+                        .setImageDrawable(icons.get(iconIndex).getConstantState()
+                                .newDrawable().mutate());
+            } else {
+                iconsContainer.getChildAt(i).setVisibility(View.GONE);
+            }
+        }
+    }
+
+    private void setAppIconShape(List<Drawable> appIcons) {
+        for (int i = 0; i < mShapeAppIconIds.length && i < appIcons.size(); i++) {
+            ImageView iconView = mContentView.findViewById(mShapeAppIconIds[i]);
+            iconView.setBackground(appIcons.get(i));
+        }
+    }
+
+    private void setAppIconName(List<String> appIconNames) {
+        for (int i = 0; i < mShapeIconAppNameIds.length && i < appIconNames.size(); i++) {
+            TextView appName = mContentView.findViewById(mShapeIconAppNameIds[i]);
+            appName.setText(appIconNames.get(i));
+        }
+    }
+
+    private void setColorAndIconsSection(List<Drawable> icons, Drawable shapeDrawable,
+                                         int accentColor) {
+        // Set QS icons and background.
+        for (int i = 0; i < mColorTileIconIds.length && i < icons.size(); i++) {
+            Drawable icon = icons.get(mColorTileIconIds[i][1]).getConstantState()
+                    .newDrawable().mutate();
+            Drawable bgShape = shapeDrawable.getConstantState().newDrawable();
+            bgShape.setTint(accentColor);
+
+            ImageView bg = mContentView.findViewById(mColorTileIds[i]);
+            bg.setImageDrawable(bgShape);
+            ImageView fg = mContentView.findViewById(mColorTileIconIds[i][0]);
+            fg.setImageDrawable(icon);
+        }
+
+        // Set color for Buttons (CheckBox, RadioButton, and Switch).
+        ColorStateList tintList = getColorStateList(accentColor);
+        for (int mColorButtonId : mColorButtonIds) {
+            CompoundButton button = mContentView.findViewById(mColorButtonId);
+            button.setButtonTintList(tintList);
+            if (button instanceof Switch) {
+                ((Switch) button).setThumbTintList(tintList);
+                ((Switch) button).setTrackTintList(tintList);
+            }
+        }
+    }
+
+    private void setColorAndIconsBoxRadius(int cornerRadius) {
+        ((CardView) mContentView.findViewById(R.id.color_icons_section)).setRadius(cornerRadius);
+    }
+
+    private void setQsbRadius(int cornerRadius) {
+        View qsb = mContentView.findViewById(R.id.theme_qsb);
+        if (qsb != null && qsb.getVisibility() == View.VISIBLE) {
+            if (qsb.getBackground() instanceof GradientDrawable) {
+                GradientDrawable bg = (GradientDrawable) qsb.getBackground();
+                float radius = useRoundedQSB(cornerRadius)
+                        ? (float) qsb.getLayoutParams().height / 2 : cornerRadius;
+                bg.setCornerRadii(new float[]{
+                        radius, radius, radius, radius,
+                        radius, radius, radius, radius});
+            }
+        }
+    }
+
+    private void updateTime() {
+        if (mClock != null) {
+            mClock.setText(getFormattedTime());
+        }
+    }
+
+    private String getFormattedTime() {
+        DateFormat df = DateFormat.getTimeInstance(DateFormat.SHORT);
+        StringBuffer time = new StringBuffer();
+        FieldPosition amPmPosition = new FieldPosition(DateFormat.Field.AM_PM);
+        df.format(Calendar.getInstance(TimeZone.getDefault()).getTime(), time, amPmPosition);
+        if (amPmPosition.getBeginIndex() > 0) {
+            time.delete(amPmPosition.getBeginIndex(), amPmPosition.getEndIndex());
+        }
+        return time.toString();
+    }
+
+    private boolean useRoundedQSB(int cornerRadius) {
+        return cornerRadius >= mContext.getResources().getDimensionPixelSize(
+                R.dimen.roundCornerThreshold);
+    }
+
+    private ColorStateList getColorStateList(int accentColor) {
+        int controlGreyColor = mContext.getColor(R.color.control_grey);
+        return new ColorStateList(
+                new int[][]{
+                        new int[]{android.R.attr.state_selected},
+                        new int[]{android.R.attr.state_checked},
+                        new int[]{-android.R.attr.state_enabled},
+                },
+                new int[] {
+                        accentColor,
+                        accentColor,
+                        controlGreyColor
+                }
+        );
+    }
+
+    /**
+     * Gets the screen height which does not include the system status bar and bottom navigation
+     * bar.
+     */
+    private int getDisplayHeight() {
+        final DisplayMetrics dm = mContext.getResources().getDisplayMetrics();
+        return dm.heightPixels;
+    }
+
+    // The height of top tool bar (R.layout.section_header).
+    private int getTopToolBarHeight() {
+        final TypedValue typedValue = new TypedValue();
+        return mContext.getTheme().resolveAttribute(
+                android.R.attr.actionBarSize, typedValue, true)
+                ? TypedValue.complexToDimensionPixelSize(
+                        typedValue.data, mContext.getResources().getDisplayMetrics())
+                : 0;
+    }
+
+    private int getFullPreviewCardHeight() {
+        final Resources res = mContext.getResources();
+        return getDisplayHeight()
+                - getTopToolBarHeight()
+                - res.getDimensionPixelSize(R.dimen.bottom_navbar_height)
+                - res.getDimensionPixelSize(R.dimen.full_preview_page_default_padding_top)
+                - res.getDimensionPixelSize(R.dimen.full_preview_page_default_padding_bottom);
+    }
+}
diff --git a/src/com/android/customization/widget/OptionSelectorController.java b/src/com/android/customization/widget/OptionSelectorController.java
index 9c92079..19dc34e 100644
--- a/src/com/android/customization/widget/OptionSelectorController.java
+++ b/src/com/android/customization/widget/OptionSelectorController.java
@@ -245,6 +245,10 @@
             int numColumns = res.getInteger(R.integer.options_grid_num_columns);
             int widthPerItem = totalWidth / mAdapter.getItemCount();
             int extraSpace = availableWidth - widthPerItem * numColumns;
+            while (extraSpace < 0) {
+                numColumns -= 1;
+                extraSpace = availableWidth - widthPerItem * numColumns;
+            }
             int containerSidePadding = extraSpace / (numColumns + 1);
             mContainer.setLayoutManager(new GridLayoutManager(mContainer.getContext(), numColumns));
             mContainer.setPaddingRelative(containerSidePadding, 0, containerSidePadding, 0);
diff --git a/src/com/android/customization/widget/ThemeInfoView.java b/src/com/android/customization/widget/ThemeInfoView.java
new file mode 100644
index 0000000..5b05484
--- /dev/null
+++ b/src/com/android/customization/widget/ThemeInfoView.java
@@ -0,0 +1,85 @@
+/*
+ * 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.android.customization.widget;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.customization.model.theme.ThemeBundle;
+import com.android.wallpaper.R;
+
+/** A view for displaying style info. */
+public class ThemeInfoView extends LinearLayout {
+    private static final int WIFI_ICON_PREVIEW_INDEX = 0;
+    private static final int SHAPE_PREVIEW_INDEX = 0;
+
+    private TextView mTitle;
+    private TextView mFontPreviewTextView;
+    private ImageView mIconPreviewImageView;
+    private ImageView mAppPreviewImageView;
+    private ImageView mShapePreviewImageView;
+
+    public ThemeInfoView(Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mTitle = findViewById(R.id.style_info_title);
+        mFontPreviewTextView = findViewById(R.id.font_preview);
+        mIconPreviewImageView = findViewById(R.id.qs_preview_icon);
+        mAppPreviewImageView = findViewById(R.id.app_preview_icon);
+        mShapePreviewImageView = findViewById(R.id.shape_preview_icon);
+    }
+
+    /** Populates theme info. */
+    public void populateThemeInfo(@NonNull ThemeBundle selectedTheme) {
+        ThemeBundle.PreviewInfo previewInfo = selectedTheme.getPreviewInfo();
+
+        if (previewInfo != null) {
+            mTitle.setText(getContext().getString(
+                    R.string.style_info_description, selectedTheme.getTitle()));
+            if (previewInfo.headlineFontFamily != null) {
+                mTitle.setTypeface(previewInfo.headlineFontFamily);
+                mFontPreviewTextView.setTypeface(previewInfo.headlineFontFamily);
+            }
+
+            if (previewInfo.icons.get(WIFI_ICON_PREVIEW_INDEX) != null) {
+                mIconPreviewImageView.setImageDrawable(
+                        previewInfo.icons.get(WIFI_ICON_PREVIEW_INDEX));
+            }
+
+            if (previewInfo.shapeAppIcons.get(SHAPE_PREVIEW_INDEX) != null) {
+                mAppPreviewImageView.setBackground(
+                        previewInfo.shapeAppIcons.get(SHAPE_PREVIEW_INDEX));
+            }
+
+            if (previewInfo.shapeDrawable != null) {
+                mShapePreviewImageView.setImageDrawable(previewInfo.shapeDrawable);
+                mShapePreviewImageView.setImageTintList(
+                        ColorStateList.valueOf(previewInfo.resolveAccentColor(getResources())));
+            }
+        }
+    }
+}