Merge SP1A.210709.002

Change-Id: Iaa036c379d2b1c0e196fdecc1e4d3c4ddf6cabc6
diff --git a/Android.bp b/Android.bp
index 871e193..7e55969 100644
--- a/Android.bp
+++ b/Android.bp
@@ -27,11 +27,12 @@
     static_libs: [
         "androidx.appcompat_appcompat",
         "androidx.cardview_cardview",
+        "androidx-constraintlayout_constraintlayout",
         "androidx.exifinterface_exifinterface",
         "androidx.lifecycle_lifecycle-runtime-ktx",
         "androidx.recyclerview_recyclerview",
         "androidx.slice_slice-view",
-        "androidx-constraintlayout_constraintlayout",
+        "androidx.viewpager2_viewpager2",
         "com.google.android.material_material",
         "glide-prebuilt",
         "kotlinx-coroutines-android",
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 5c020af..72d721d 100755
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -91,7 +91,7 @@
 
     <activity-alias
         android:name="com.android.wallpaper.picker.CategoryPickerActivity"
-        android:targetActivity="com.android.wallpaper.picker.TopLevelPickerActivity"
+        android:targetActivity="com.android.wallpaper.picker.CustomizationPickerActivity"
         android:label="@string/app_name"
         android:exported="true">
       <intent-filter>
diff --git a/res/color-night/separated_tabs_indicator_color.xml b/res/color-night/separated_tabs_indicator_color.xml
new file mode 100644
index 0000000..7313a16
--- /dev/null
+++ b/res/color-night/separated_tabs_indicator_color.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+    <item android:color="?androidprv:attr/colorAccentSecondary" />
+</selector>
diff --git a/res/color-night/separated_tabs_text_color.xml b/res/color-night/separated_tabs_text_color.xml
new file mode 100644
index 0000000..8fb9488
--- /dev/null
+++ b/res/color-night/separated_tabs_text_color.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_selected="true" android:color="?android:textColorPrimaryInverse"/>
+    <item android:state_enabled="false" android:color="?android:textColorTertiary"/>
+    <item android:color="?android:textColorSecondary"/>
+</selector>
\ No newline at end of file
diff --git a/res/color/separated_tabs_indicator_color.xml b/res/color/separated_tabs_indicator_color.xml
new file mode 100644
index 0000000..cbce7d0
--- /dev/null
+++ b/res/color/separated_tabs_indicator_color.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+    <item android:color="?androidprv:attr/colorAccentPrimary" />
+</selector>
\ No newline at end of file
diff --git a/res/color/separated_tabs_text_color.xml b/res/color/separated_tabs_text_color.xml
new file mode 100644
index 0000000..96f3193
--- /dev/null
+++ b/res/color/separated_tabs_text_color.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_selected="true" android:color="?android:textColorPrimary"/>
+    <item android:state_enabled="false" android:color="?android:textColorTertiary"/>
+    <item android:color="?android:textColorSecondary"/>
+</selector>
\ No newline at end of file
diff --git a/res/color/tab_text_color.xml b/res/color/tab_text_color.xml
deleted file mode 100644
index 70b364a..0000000
--- a/res/color/tab_text_color.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_enabled="false" android:color="?android:textColorTertiary"/>
-    <item android:color="?android:textColorSecondary"/>
-</selector>
\ No newline at end of file
diff --git a/res/drawable/separated_tabs_indicator_background.xml b/res/drawable/separated_tabs_indicator_background.xml
index 942810a..05e52e5 100644
--- a/res/drawable/separated_tabs_indicator_background.xml
+++ b/res/drawable/separated_tabs_indicator_background.xml
@@ -15,11 +15,10 @@
      limitations under the License.
 -->
 <inset xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:insetLeft="@dimen/separated_tabs_inset_horizontal"
     android:insetRight="@dimen/separated_tabs_inset_horizontal">
     <shape android:shape="rectangle">
-        <solid android:color="?androidprv:attr/colorAccentPrimary" />
+        <solid android:color="@color/separated_tabs_indicator_color" />
         <corners android:radius="@dimen/separated_tabs_corner_radius" />
     </shape>
 </inset>
diff --git a/res/drawable/separated_tabs_ripple_mask.xml b/res/drawable/separated_tabs_ripple_mask.xml
new file mode 100644
index 0000000..ad39a50
--- /dev/null
+++ b/res/drawable/separated_tabs_ripple_mask.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+     Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="?android:colorControlHighlight">
+    <item android:id="@android:id/mask">
+        <inset
+            android:insetLeft="@dimen/separated_tabs_inset_horizontal"
+            android:insetRight="@dimen/separated_tabs_inset_horizontal">
+            <shape android:shape="rectangle">
+                <corners android:radius="@dimen/separated_tabs_corner_radius" />
+                <solid android:color="?android:colorControlHighlight" />
+            </shape>
+        </inset>
+    </item>
+</ripple>
diff --git a/res/layout/dialog_set_wallpaper.xml b/res/layout/dialog_set_wallpaper.xml
index 81d6253..f1102bd 100755
--- a/res/layout/dialog_set_wallpaper.xml
+++ b/res/layout/dialog_set_wallpaper.xml
@@ -19,6 +19,7 @@
     xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
+    android:background="?androidprv:attr/colorSurface"
     android:padding="@dimen/set_wallpaper_dialog_padding">
 
     <LinearLayout
diff --git a/res/layout/dialog_set_wallpaper_title.xml b/res/layout/dialog_set_wallpaper_title.xml
index 9332021..3267939 100755
--- a/res/layout/dialog_set_wallpaper_title.xml
+++ b/res/layout/dialog_set_wallpaper_title.xml
@@ -14,8 +14,10 @@
      limitations under the License.
 -->
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
+    android:background="?androidprv:attr/colorSurface"
     android:orientation="vertical">
 
     <Space
@@ -36,9 +38,15 @@
     <TextView
         android:id="@+id/dialog_set_wallpaper_title"
         android:layout_width="match_parent"
-        android:layout_height="64dp"
+        android:layout_height="wrap_content"
+        android:fontFamily="@*android:string/config_headlineFontFamily"
+        android:minHeight="32dp"
         android:textColor="?android:attr/textColorPrimary"
         android:textSize="24sp"
         android:gravity="center" />
 
+    <Space
+        android:layout_height="8dp"
+        android:layout_width="wrap_content" />
+
 </LinearLayout>
diff --git a/res/layout/separated_tabs.xml b/res/layout/separated_tabs.xml
index 1445101..12603e0 100644
--- a/res/layout/separated_tabs.xml
+++ b/res/layout/separated_tabs.xml
@@ -14,21 +14,18 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<com.google.android.material.tabs.TabLayout
+<com.android.wallpaper.widget.SeparatedTabLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/separated_tabs"
     android:layout_width="match_parent"
     android:layout_height="@dimen/separated_tabs_height"
     app:tabBackground="@drawable/separated_tabs_background"
-    app:tabRippleColor="?android:colorControlHighlight"
     app:tabIndicatorAnimationDuration="0"
     app:tabIndicator="@drawable/separated_tabs_indicator_background"
-    app:tabIndicatorColor="?androidprv:attr/colorAccentPrimary"
+    app:tabIndicatorColor="@color/separated_tabs_indicator_color"
     app:tabIndicatorFullWidth="true"
     app:tabIndicatorGravity="stretch"
     app:tabGravity="fill"
-    app:tabTextColor="@color/tab_text_color"
-    app:tabSelectedTextColor="?android:textColorPrimary"
+    app:tabTextColor="@color/separated_tabs_text_color"
     app:tabTextAppearance="@style/SeparatedTabsTextAppearance" />
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index e18c790..29ab0df 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -21,7 +21,7 @@
     <!-- no translation found for app_name (8773648973927541493) -->
     <skip />
     <string name="select_wallpaper_label" msgid="6989581259339646085">"Wallpaper categories"</string>
-    <string name="set_wallpaper_button_text" msgid="4426286890442731310">"Set Wallpaper"</string>
+    <string name="set_wallpaper_button_text" msgid="4426286890442731310">"Set wallpaper"</string>
     <string name="set_wallpaper_progress_message" msgid="7986528287618716715">"Setting wallpaper…"</string>
     <string name="try_again" msgid="8278874823700921234">"Try again"</string>
     <string name="set_wallpaper_error_message" msgid="6819986999041085130">"Unable to set wallpaper."</string>
diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml
index e18c790..29ab0df 100644
--- a/res/values-en-rCA/strings.xml
+++ b/res/values-en-rCA/strings.xml
@@ -21,7 +21,7 @@
     <!-- no translation found for app_name (8773648973927541493) -->
     <skip />
     <string name="select_wallpaper_label" msgid="6989581259339646085">"Wallpaper categories"</string>
-    <string name="set_wallpaper_button_text" msgid="4426286890442731310">"Set Wallpaper"</string>
+    <string name="set_wallpaper_button_text" msgid="4426286890442731310">"Set wallpaper"</string>
     <string name="set_wallpaper_progress_message" msgid="7986528287618716715">"Setting wallpaper…"</string>
     <string name="try_again" msgid="8278874823700921234">"Try again"</string>
     <string name="set_wallpaper_error_message" msgid="6819986999041085130">"Unable to set wallpaper."</string>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index e18c790..29ab0df 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -21,7 +21,7 @@
     <!-- no translation found for app_name (8773648973927541493) -->
     <skip />
     <string name="select_wallpaper_label" msgid="6989581259339646085">"Wallpaper categories"</string>
-    <string name="set_wallpaper_button_text" msgid="4426286890442731310">"Set Wallpaper"</string>
+    <string name="set_wallpaper_button_text" msgid="4426286890442731310">"Set wallpaper"</string>
     <string name="set_wallpaper_progress_message" msgid="7986528287618716715">"Setting wallpaper…"</string>
     <string name="try_again" msgid="8278874823700921234">"Try again"</string>
     <string name="set_wallpaper_error_message" msgid="6819986999041085130">"Unable to set wallpaper."</string>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index e18c790..29ab0df 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -21,7 +21,7 @@
     <!-- no translation found for app_name (8773648973927541493) -->
     <skip />
     <string name="select_wallpaper_label" msgid="6989581259339646085">"Wallpaper categories"</string>
-    <string name="set_wallpaper_button_text" msgid="4426286890442731310">"Set Wallpaper"</string>
+    <string name="set_wallpaper_button_text" msgid="4426286890442731310">"Set wallpaper"</string>
     <string name="set_wallpaper_progress_message" msgid="7986528287618716715">"Setting wallpaper…"</string>
     <string name="try_again" msgid="8278874823700921234">"Try again"</string>
     <string name="set_wallpaper_error_message" msgid="6819986999041085130">"Unable to set wallpaper."</string>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index a46d775..abca829 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -60,7 +60,7 @@
     <string name="currently_set_lock_wallpaper_thumbnail" msgid="2094209303934569997">"हाल लक स्क्रिनमा सेट गरिएको वालपेपरको थम्बनेल"</string>
     <string name="currently_set_wallpaper_thumbnail" msgid="8651887838745545107">"हाल सेट गरिएको वालपेपरको थम्बनेल"</string>
     <string name="wallpaper_thumbnail" msgid="569931475923605974">"वालपेपरको थम्बनेल"</string>
-    <string name="explore_home_screen" msgid="8756346794535765482">"गृह स्क्रिनको वालपेपरको अन्वेषण गर्नुहोस्"</string>
+    <string name="explore_home_screen" msgid="8756346794535765482">"होम स्क्रिनको वालपेपरको अन्वेषण गर्नुहोस्"</string>
     <string name="explore_lock_screen" msgid="268938342103703665">"लक स्क्रिनको वालपेपरको अन्वेषण गर्नुहोस्"</string>
     <string name="refresh_daily_wallpaper_home_content_description" msgid="2770445044556164259">"गृहपृष्ठको दैनिक वालपेपर पुनः ताजा गर्नुहोस्"</string>
     <string name="refresh_daily_wallpaper_content_description" msgid="4362142658237147583">"दैनिक वालपेपर पुनः ताजा गर्नुहोस्"</string>
diff --git a/res/values-notnight-v27/styles.xml b/res/values-notnight-v27/styles.xml
index 72098f6..95c5ee5 100755
--- a/res/values-notnight-v27/styles.xml
+++ b/res/values-notnight-v27/styles.xml
@@ -44,5 +44,8 @@
     <style name="LightDialogTheme" parent="@android:style/Theme.DeviceDefault.Light.Dialog.NoActionBar">
         <item name="android:layout">@layout/abc_alert_dialog_material</item>
         <item name="windowActionBar">false</item>
+        <item name="android:windowMinWidthMajor">@android:dimen/dialog_min_width_major</item>
+        <item name="android:windowMinWidthMinor">@android:dimen/dialog_min_width_minor</item>
+        <item name="android:dialogCornerRadius">24dp</item>
     </style>
 </resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 1b773ba..8e13fb0 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -89,7 +89,7 @@
     <dimen name="set_wallpaper_dialog_item_min_width">300dp</dimen>
     <dimen name="set_wallpaper_dialog_item_outer_corner_radius">12dp</dimen>
     <dimen name="set_wallpaper_dialog_item_inner_corner_radius">4dp</dimen>
-    <dimen name="set_wallpaper_dialog_icon_size">24dp</dimen>
+    <dimen name="set_wallpaper_dialog_icon_size">32dp</dimen>
 
     <!-- Dimensions for the "wallpaper disabled by administrator" UI. -->
     <dimen name="wallpaper_disabled_asset_margin_bottom">26dp</dimen>
@@ -126,7 +126,7 @@
 
     <!-- Dimensions for the "disabled by admin" UI in desktop mode. -->
     <dimen name="disabled_by_admin_desktop_message_text_size">13sp</dimen>
-    <dimen name="preview_bottom_sheet_corner_radius">?android:attr/dialogCornerRadius</dimen>
+    <dimen name="preview_bottom_sheet_corner_radius">12dp</dimen>
 
     <!-- Dimensions for PreviewPager and PageIndicator. -->
     <dimen name="preview_indicator_width">16dp</dimen>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 051ca4b..071792b 100755
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -88,6 +88,9 @@
     <style name="LightDialogTheme" parent="@android:style/Theme.DeviceDefault.Dialog.NoActionBar">
         <item name="android:layout">@layout/abc_alert_dialog_material</item>
         <item name="windowActionBar">false</item>
+        <item name="android:windowMinWidthMajor">@android:dimen/dialog_min_width_major</item>
+        <item name="android:windowMinWidthMinor">@android:dimen/dialog_min_width_minor</item>
+        <item name="android:dialogCornerRadius">24dp</item>
     </style>
 
     <style name="ProgressDialogThemePreL" parent="@style/Theme.AppCompat.Light.Dialog.Alert">
@@ -140,7 +143,7 @@
     <!-- Studio can't directly reference ?androidprv:attr/textColorOnAccent here,
      so we set the text color on the dialog items instead. -->
     <style name="set_wallpaper_destination_item">
-        <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
+        <item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item>
         <item name="android:minHeight">@dimen/set_wallpaper_dialog_item_min_height</item>
         <item name="android:minWidth">@dimen/set_wallpaper_dialog_item_min_width</item>
         <item name="android:textAlignment">center</item>
diff --git a/src/com/android/wallpaper/picker/CustomizationPickerActivity.java b/src/com/android/wallpaper/picker/CustomizationPickerActivity.java
index 16bfffe..d97f598 100644
--- a/src/com/android/wallpaper/picker/CustomizationPickerActivity.java
+++ b/src/com/android/wallpaper/picker/CustomizationPickerActivity.java
@@ -15,9 +15,11 @@
  */
 package com.android.wallpaper.picker;
 
+import static com.android.wallpaper.util.ActivityUtils.isSUWMode;
+import static com.android.wallpaper.util.ActivityUtils.isWallpaperOnlyMode;
+
 import android.app.Activity;
 import android.content.Intent;
-import android.os.Build;
 import android.os.Bundle;
 import android.provider.Settings;
 import android.text.TextUtils;
@@ -61,12 +63,7 @@
         FragmentTransactionChecker, PermissionRequester, CategorySelectorFragmentHost,
         IndividualPickerFragmentHost, WallpaperPreviewNavigator {
 
-    public static final String WALLPAPER_FLAVOR_EXTRA =
-            "com.android.launcher3.WALLPAPER_FLAVOR";
-    public static final String WALLPAPER_FOCUS = "focus_wallpaper";
-
     private static final String TAG = "CustomizationPickerActivity";
-    private static final String WALLPAPER_ONLY = "wallpaper_only";
 
     private WallpaperPickerDelegate mDelegate;
     private UserEventLogger mUserEventLogger;
@@ -87,17 +84,11 @@
 
         // Restore this Activity's state before restoring contained Fragments state.
         super.onCreate(savedInstanceState);
-        if (WALLPAPER_ONLY.equals(getIntent().getStringExtra(WALLPAPER_FLAVOR_EXTRA))
-                || !supportCustomizationSections()) {
-            skipToWallpaperPicker();
-            return;
-        }
-
         setContentView(R.layout.activity_customization_picker);
         mBottomActionBar = findViewById(R.id.bottom_actionbar);
 
         // See go/pdr-edge-to-edge-guide.
-        WindowCompat.setDecorFitsSystemWindows(getWindow(), /* decorFitsSystemWindows= */ false);
+        WindowCompat.setDecorFitsSystemWindows(getWindow(), isSUWMode(this));
 
         Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
         if (fragment == null) {
@@ -108,8 +99,10 @@
             injector.getPreferences(this).incrementAppLaunched();
             DailyLoggingAlarmScheduler.setAlarm(getApplicationContext());
 
-            // Switch to the customization picker fragment.
-            switchFragment(CustomizationPickerFragment.newInstance(getString(R.string.app_name)));
+            // Switch to the target fragment.
+            switchFragment(isWallpaperOnlyMode(getIntent())
+                    ? WallpaperOnlyFragment.newInstance(getString(R.string.wallpaper_app_name))
+                    : CustomizationPickerFragment.newInstance(getString(R.string.app_name)));
         }
 
         // Deep link case
@@ -146,8 +139,7 @@
     protected void onResume() {
         super.onResume();
         mIsSafeToCommitFragmentTransaction = true;
-        boolean wallpaperOnly =
-                WALLPAPER_ONLY.equals(getIntent().getStringExtra(WALLPAPER_FLAVOR_EXTRA));
+        boolean wallpaperOnly = isWallpaperOnlyMode(getIntent());
         boolean provisioned = Settings.Global.getInt(getContentResolver(),
                 Settings.Global.DEVICE_PROVISIONED, 0) != 0;
 
@@ -171,38 +163,6 @@
     }
 
     @Override
-    protected void onNewIntent(Intent intent) {
-        super.onNewIntent(intent);
-        if (WALLPAPER_ONLY.equals(intent.getStringExtra(WALLPAPER_FLAVOR_EXTRA))) {
-            Log.d(TAG, "WALLPAPER_ONLY intent, reverting to Wallpaper Picker");
-            skipToWallpaperPicker();
-        }
-    }
-
-    private void skipToWallpaperPicker() {
-        Intent intent = new Intent(this, TopLevelPickerActivity.class);
-
-        if (getIntent() != null && getIntent().getExtras() != null) {
-            intent.putExtras(getIntent().getExtras());
-        }
-
-        if (DeepLinkUtils.isDeepLink(getIntent())) {
-            intent.setData(getIntent().getData());
-        }
-        startActivity(intent);
-        finish();
-    }
-
-    private boolean supportCustomizationSections() {
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
-                || "S".equals(Build.VERSION.CODENAME)) {
-            return true;
-        }
-        Log.d(TAG, "Build version < S, customization sections feature is not supported");
-        return false;
-    }
-
-    @Override
     public void onBackPressed() {
         Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
         if (fragment instanceof BottomActionBarFragment
@@ -348,7 +308,11 @@
     protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
         super.onActivityResult(requestCode, resultCode, data);
         if (mDelegate.handleActivityResult(requestCode, resultCode, data)) {
-            finishActivityWithResultOk();
+            if (isSUWMode(this)) {
+                finishActivityForSUW();
+            } else {
+                finishActivityWithResultOk();
+            }
         }
     }
 
@@ -361,6 +325,13 @@
         LaunchUtils.launchHome(this);
     }
 
+    private void finishActivityForSUW() {
+        overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
+        // Return RESULT_CANCELED to make the "Change wallpaper" tile in SUW not be disabled.
+        setResult(Activity.RESULT_CANCELED);
+        finish();
+    }
+
     @Override
     public BottomActionBar getBottomActionBar() {
         return mBottomActionBar;
@@ -379,6 +350,6 @@
 
     @Override
     public boolean isUpArrowSupported() {
-        return true;
+        return !isSUWMode(this);
     }
 }
diff --git a/src/com/android/wallpaper/picker/CustomizationPickerFragment.java b/src/com/android/wallpaper/picker/CustomizationPickerFragment.java
index 9710f0f..8884c0e 100644
--- a/src/com/android/wallpaper/picker/CustomizationPickerFragment.java
+++ b/src/com/android/wallpaper/picker/CustomizationPickerFragment.java
@@ -15,6 +15,7 @@
  */
 package com.android.wallpaper.picker;
 
+import android.content.Context;
 import android.os.Bundle;
 import android.util.Log;
 import android.view.LayoutInflater;
@@ -87,8 +88,21 @@
 
         initSections(savedInstanceState);
         mSectionControllers.forEach(controller ->
-                sectionContainer.addView(controller.createView(getContext())));
-        restoreViewState(savedInstanceState);
+                mNestedScrollView.post(() -> {
+                            final Context context = getContext();
+                            if (context == null) {
+                                Log.w(TAG, "Adding section views with null context");
+                                return;
+                            }
+                            sectionContainer.addView(controller.createView(context));
+                        }
+                )
+        );
+        final Bundle savedInstanceStateRef = savedInstanceState;
+        // Post it to the end of adding views to ensure restoring view state the last task.
+        mNestedScrollView.post(() ->
+                restoreViewState(savedInstanceStateRef)
+        );
         return view;
     }
 
@@ -179,7 +193,7 @@
         mSectionControllers.addAll(getAvailableSections(allSectionControllers));
     }
 
-    private List<CustomizationSectionController<?>> getAvailableSections (
+    protected List<CustomizationSectionController<?>> getAvailableSections(
             List<CustomizationSectionController<?>> controllers) {
         return controllers.stream()
                 .filter(controller -> {
diff --git a/src/com/android/wallpaper/picker/ImagePreviewFragment.java b/src/com/android/wallpaper/picker/ImagePreviewFragment.java
index 0ceaa3d..c41d394 100755
--- a/src/com/android/wallpaper/picker/ImagePreviewFragment.java
+++ b/src/com/android/wallpaper/picker/ImagePreviewFragment.java
@@ -38,6 +38,7 @@
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.os.Bundle;
+import android.os.Handler;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.Surface;
@@ -72,7 +73,6 @@
 import com.android.wallpaper.widget.BottomActionBar;
 import com.android.wallpaper.widget.BottomActionBar.AccessibilityCallback;
 import com.android.wallpaper.widget.LockScreenPreviewer;
-import com.android.wallpaper.widget.WallpaperInfoView;
 
 import com.bumptech.glide.Glide;
 import com.bumptech.glide.MemoryCategory;
@@ -82,6 +82,8 @@
 import java.io.ByteArrayOutputStream;
 import java.util.Locale;
 import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -93,10 +95,14 @@
 
     private static final String TAG = "ImagePreviewFragment";
     private static final float DEFAULT_WALLPAPER_MAX_ZOOM = 8f;
+    private static final Executor sExecutor = Executors.newCachedThreadPool();
 
     private final WallpaperSurfaceCallback mWallpaperSurfaceCallback =
             new WallpaperSurfaceCallback();
 
+    private final AtomicInteger mImageScaleChangeCounter = new AtomicInteger(0);
+    private final AtomicInteger mRecalculateColorCounter = new AtomicInteger(0);
+
     private SubsamplingScaleImageView mFullResImageView;
     private Asset mWallpaperAsset;
     private Point mScreenSize;
@@ -105,8 +111,6 @@
     private TouchForwardingLayout mTouchForwardingLayout;
     private ConstraintLayout mContainer;
     private SurfaceView mWallpaperSurface;
-    private WallpaperInfoView mWallpaperInfoView;
-    private AtomicInteger mImageScaleChangeCounter = new AtomicInteger(0);
 
     protected SurfaceView mWorkspaceSurface;
     protected WorkspaceSurfaceHolderCallback mWorkspaceSurfaceCallback;
@@ -225,10 +229,8 @@
     @Override
     protected void onBottomActionBarReady(BottomActionBar bottomActionBar) {
         super.onBottomActionBarReady(bottomActionBar);
-        mWallpaperInfoView = (WallpaperInfoView)
-                LayoutInflater.from(getContext()).inflate(
-                        R.layout.wallpaper_info_view, /* root= */null);
-        mBottomActionBar.attachViewToBottomSheetAndBindAction(mWallpaperInfoView, INFORMATION);
+        mBottomActionBar.bindBottomSheetContentWithAction(
+                new WallpaperInfoContent(getContext()), INFORMATION);
         mBottomActionBar.showActionsOnly(INFORMATION, EDIT, APPLY);
 
         mBottomActionBar.setActionClickListener(APPLY, this::onSetWallpaperClicked);
@@ -255,8 +257,6 @@
         });
 
         mBottomActionBar.show();
-        // Loads wallpaper info and populate into view.
-        setUpExploreIntentAndLabel(this::populateWallpaperInfo);
         // To avoid applying the wallpaper when the wallpaper's not parsed.
         mBottomActionBar.disableActions();
         // If the wallpaper is parsed, enable the bottom action bar.
@@ -350,25 +350,31 @@
                 new BitmapCropper.Callback() {
                     @Override
                     public void onBitmapCropped(Bitmap croppedBitmap) {
-                        boolean shouldRecycle = false;
-                        ByteArrayOutputStream tmpOut = new ByteArrayOutputStream();
-                        if (croppedBitmap.compress(Bitmap.CompressFormat.PNG, 100, tmpOut)) {
-                            byte[] outByteArray = tmpOut.toByteArray();
-                            BitmapFactory.Options options = new BitmapFactory.Options();
-                            options.inPreferredColorSpace = ColorSpace.get(ColorSpace.Named.SRGB);
-                            Bitmap decodedPng = BitmapFactory.decodeByteArray(outByteArray, 0,
-                                    outByteArray.length);
-                            croppedBitmap = decodedPng;
-                        }
-                        if (croppedBitmap.getConfig() == Bitmap.Config.HARDWARE) {
-                            croppedBitmap = croppedBitmap.copy(Bitmap.Config.ARGB_8888, false);
-                            shouldRecycle = true;
-                        }
-                        WallpaperColors colors = WallpaperColors.fromBitmap(croppedBitmap);
-                        if (shouldRecycle) {
-                            croppedBitmap.recycle();
-                        }
-                        onWallpaperColorsChanged(colors);
+                        mRecalculateColorCounter.incrementAndGet();
+                        sExecutor.execute(() -> {
+                            boolean shouldRecycle = false;
+                            ByteArrayOutputStream tmpOut = new ByteArrayOutputStream();
+                            Bitmap cropped = croppedBitmap;
+                            if (cropped.compress(Bitmap.CompressFormat.PNG, 100, tmpOut)) {
+                                byte[] outByteArray = tmpOut.toByteArray();
+                                BitmapFactory.Options options = new BitmapFactory.Options();
+                                options.inPreferredColorSpace =
+                                        ColorSpace.get(ColorSpace.Named.SRGB);
+                                cropped = BitmapFactory.decodeByteArray(outByteArray, 0,
+                                        outByteArray.length);
+                            }
+                            if (cropped.getConfig() == Bitmap.Config.HARDWARE) {
+                                cropped = cropped.copy(Bitmap.Config.ARGB_8888, false);
+                                shouldRecycle = true;
+                            }
+                            WallpaperColors colors = WallpaperColors.fromBitmap(cropped);
+                            if (shouldRecycle) {
+                                cropped.recycle();
+                            }
+                            if (mRecalculateColorCounter.decrementAndGet() == 0) {
+                                Handler.getMain().post(() -> onWallpaperColorsChanged(colors));
+                            }
+                        });
                     }
 
                     @Override
@@ -378,17 +384,6 @@
                 });
     }
 
-    private void populateWallpaperInfo() {
-        if (mWallpaperInfoView != null && mWallpaper != null) {
-            mWallpaperInfoView.populateWallpaperInfo(
-                    mWallpaper,
-                    mActionLabel,
-                    WallpaperInfoHelper.shouldShowExploreButton(
-                            getContext(), mExploreIntent),
-                    this::onExploreClicked);
-        }
-    }
-
     /**
      * Makes the MosaicView visible with an alpha fade-in animation while fading out the loading
      * indicator.
diff --git a/src/com/android/wallpaper/picker/LivePreviewFragment.java b/src/com/android/wallpaper/picker/LivePreviewFragment.java
index 8b8ed44..ec17767 100644
--- a/src/com/android/wallpaper/picker/LivePreviewFragment.java
+++ b/src/com/android/wallpaper/picker/LivePreviewFragment.java
@@ -31,6 +31,7 @@
 import android.app.WallpaperInfo;
 import android.app.WallpaperManager;
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
@@ -73,9 +74,9 @@
 import com.android.wallpaper.util.WallpaperSurfaceCallback;
 import com.android.wallpaper.widget.BottomActionBar;
 import com.android.wallpaper.widget.BottomActionBar.AccessibilityCallback;
+import com.android.wallpaper.widget.BottomActionBar.BottomSheetContent;
 import com.android.wallpaper.widget.LockScreenPreviewer;
 import com.android.wallpaper.widget.WallpaperColorsLoader;
-import com.android.wallpaper.widget.WallpaperInfoView;
 
 import java.util.Locale;
 import java.util.concurrent.ExecutionException;
@@ -101,7 +102,6 @@
      * @see IWallpaperConnection
      */
     protected WallpaperConnection mWallpaperConnection;
-    protected WallpaperInfoView mWallpaperInfoView;
     protected CardView mHomePreviewCard;
     protected SurfaceView mWorkspaceSurface;
     protected WallpaperSurfaceCallback mWallpaperSurfaceCallback;
@@ -164,17 +164,6 @@
         Activity activity = requireActivity();
         mScreenSize = ScreenSizeCalculator.getInstance().getScreenSize(
                 activity.getWindowManager().getDefaultDisplay());
-
-        mWallpaperInfoView = (WallpaperInfoView) LayoutInflater.from(getContext())
-                .inflate(R.layout.wallpaper_info_view, /* root= */ null);
-        setUpExploreIntentAndLabel(
-                () -> mWallpaperInfoView.populateWallpaperInfo(
-                        mWallpaper,
-                        mActionLabel,
-                        WallpaperInfoHelper.shouldShowExploreButton(getContext(), mExploreIntent),
-                        this::onExploreClicked)
-        );
-
         mPreviewContainer = view.findViewById(R.id.live_wallpaper_preview);
         mTouchForwardingLayout = view.findViewById(R.id.touch_forwarding_layout);
         // Set aspect ratio on the preview card.
@@ -271,7 +260,8 @@
     @Override
     public void onDestroyView() {
         super.onDestroyView();
-        if (mSettingsLiveData != null && mSettingsLiveData.hasObservers()) {
+        if (mSettingsLiveData != null && mSettingsLiveData.hasObservers()
+                && mSettingsSliceView != null) {
             mSettingsLiveData.removeObserver(mSettingsSliceView);
             mSettingsLiveData = null;
         }
@@ -353,7 +343,8 @@
         super.onBottomActionBarReady(bottomActionBar);
         mBottomActionBar.showActionsOnly(INFORMATION, DELETE, EDIT, CUSTOMIZE, APPLY);
         mBottomActionBar.setActionClickListener(APPLY, unused -> onSetWallpaperClicked(null));
-        mBottomActionBar.attachViewToBottomSheetAndBindAction(mWallpaperInfoView, INFORMATION);
+        mBottomActionBar.bindBottomSheetContentWithAction(
+                new WallpaperInfoContent(getContext()), INFORMATION);
 
         View separatedTabsContainer = getView().findViewById(R.id.separated_tabs_container);
         // Update target view's accessibility param since it will be blocked by the bottom sheet
@@ -376,14 +367,9 @@
         });
         final Uri uriSettingsSlice = getSettingsSliceUri(mWallpaper.getWallpaperComponent());
         if (uriSettingsSlice != null) {
-            View previewPage = LayoutInflater.from(getContext())
-                    .inflate(R.layout.preview_customize_settings, null);
-            mSettingsSliceView = previewPage.findViewById(R.id.settings_slice);
-            mSettingsSliceView.setMode(SliceView.MODE_LARGE);
-            mSettingsSliceView.setScrollable(false);
             mSettingsLiveData = SliceLiveData.fromUri(requireContext(), uriSettingsSlice);
-            mSettingsLiveData.observeForever(mSettingsSliceView);
-            mBottomActionBar.attachViewToBottomSheetAndBindAction(previewPage, CUSTOMIZE);
+            mBottomActionBar.bindBottomSheetContentWithAction(
+                    new PreviewCustomizeSettingsContent(getContext()), CUSTOMIZE);
         } else {
             if (mSettingsIntent != null) {
                 mBottomActionBar.setActionClickListener(CUSTOMIZE, listener ->
@@ -546,4 +532,35 @@
     private boolean isPackagePreInstalled(ApplicationInfo info) {
         return info != null && (info.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
     }
+
+    private final class PreviewCustomizeSettingsContent extends BottomSheetContent<View> {
+
+        private PreviewCustomizeSettingsContent(Context context) {
+            super(context);
+        }
+
+        @Override
+        public int getViewId() {
+            return R.layout.preview_customize_settings;
+        }
+
+        @Override
+        public void onViewCreated(View previewPage) {
+            mSettingsSliceView = previewPage.findViewById(R.id.settings_slice);
+            mSettingsSliceView.setMode(SliceView.MODE_LARGE);
+            mSettingsSliceView.setScrollable(false);
+            if (mSettingsLiveData != null) {
+                mSettingsLiveData.observeForever(mSettingsSliceView);
+            }
+        }
+
+        @Override
+        public void onRecreateView(View oldPreviewPage) {
+            super.onRecreateView(oldPreviewPage);
+            if (mSettingsLiveData != null && mSettingsLiveData.hasObservers()
+                    && mSettingsSliceView != null) {
+                mSettingsLiveData.removeObserver(mSettingsSliceView);
+            }
+        }
+    }
 }
diff --git a/src/com/android/wallpaper/picker/PreviewFragment.java b/src/com/android/wallpaper/picker/PreviewFragment.java
index a94e1f6..74bdd6f 100755
--- a/src/com/android/wallpaper/picker/PreviewFragment.java
+++ b/src/com/android/wallpaper/picker/PreviewFragment.java
@@ -51,6 +51,8 @@
 import com.android.wallpaper.module.WallpaperSetter;
 import com.android.wallpaper.util.FullScreenAnimation;
 import com.android.wallpaper.widget.BottomActionBar;
+import com.android.wallpaper.widget.BottomActionBar.BottomSheetContent;
+import com.android.wallpaper.widget.WallpaperInfoView;
 
 import com.google.android.material.tabs.TabLayout;
 
@@ -119,11 +121,6 @@
     private static final int UNUSED_REQUEST_CODE = 1;
     private static final String TAG = "PreviewFragment";
 
-    @PreviewMode
-    protected int mPreviewMode;
-
-    protected boolean mViewAsHome;
-
     /**
      * When true, enables a test mode of operation -- in which certain UI features are disabled to
      * allow for UI tests to run correctly. Works around issue in ProgressDialog currently where the
@@ -136,13 +133,13 @@
     protected WallpaperSetter mWallpaperSetter;
     protected UserEventLogger mUserEventLogger;
     protected BottomActionBar mBottomActionBar;
-
-    protected Intent mExploreIntent;
-    protected CharSequence mActionLabel;
-
     // For full screen animations.
     protected View mRootView;
     protected FullScreenAnimation mFullScreenAnimation;
+    @PreviewMode protected int mPreviewMode;
+    protected boolean mViewAsHome;
+
+    private OnBackPressedCallback mOnBackPressedCallback;
 
     /**
      * Staged error dialog fragments that were unable to be shown when the hosting activity didn't
@@ -207,17 +204,34 @@
     protected void onBottomActionBarReady(BottomActionBar bottomActionBar) {
         super.onBottomActionBarReady(bottomActionBar);
         mBottomActionBar = bottomActionBar;
-        setBottomActionBarAndFullScreenActions();
-    }
-
-    private void setBottomActionBarAndFullScreenActions() {
         mBottomActionBar.setActionClickListener(EDIT, (view) -> {
             mFullScreenAnimation.startAnimation(/* toFullScreen= */ true);
             mBottomActionBar.deselectAction(EDIT);
         });
+        setFullScreenActions(mRootView.findViewById(R.id.fullscreen_buttons_container));
 
+        if (mOnBackPressedCallback == null) {
+            mOnBackPressedCallback = new OnBackPressedCallback(true) {
+                @Override
+                public void handleOnBackPressed() {
+                    if (mFullScreenAnimation.isFullScreen()) {
+                        mFullScreenAnimation.startAnimation(/* toFullScreen= */ false);
+                        return;
+                    }
+                    if (mBottomActionBar != null && !mBottomActionBar.isBottomSheetCollapsed()) {
+                        mBottomActionBar.collapseBottomSheetIfExpanded();
+                        return;
+                    }
+                    getActivity().finish();
+                }
+            };
+            getActivity().getOnBackPressedDispatcher().addCallback(this, mOnBackPressedCallback);
+        }
+    }
+
+    protected void setFullScreenActions(View container) {
         // Update the button text for the current workspace visibility.
-        Button hideUiPreviewButton = mRootView.findViewById(R.id.hide_ui_preview_button);
+        Button hideUiPreviewButton = container.findViewById(R.id.hide_ui_preview_button);
         hideUiPreviewButton.setText(mFullScreenAnimation.getWorkspaceVisibility()
                 ? R.string.hide_ui_preview_text
                 : R.string.show_ui_preview_text);
@@ -231,28 +245,10 @@
                     mFullScreenAnimation.setWorkspaceVisibility(!visible);
                 }
         );
-        mRootView.findViewById(R.id.set_as_wallpaper_button).setOnClickListener(
-                this::onSetWallpaperClicked
-        );
+        container.findViewById(R.id.set_as_wallpaper_button).setOnClickListener(
+                this::onSetWallpaperClicked);
 
         mFullScreenAnimation.ensureBottomActionBarIsCorrectlyLocated();
-
-        OnBackPressedCallback callback = new OnBackPressedCallback(true) {
-            @Override
-            public void handleOnBackPressed() {
-                if (mFullScreenAnimation.isFullScreen()) {
-                    mFullScreenAnimation.startAnimation(/* toFullScreen= */ false);
-                    return;
-                }
-                if (mBottomActionBar != null && !mBottomActionBar.isBottomSheetCollapsed()) {
-                    mBottomActionBar.collapseBottomSheetIfExpanded();
-                    return;
-                }
-                getActivity().finish();
-            }
-        };
-
-        getActivity().getOnBackPressedDispatcher().addCallback(this, callback);
     }
 
     protected List<String> getAttributions(Context context) {
@@ -290,23 +286,6 @@
         }
     }
 
-    protected void setUpExploreIntentAndLabel(@Nullable Runnable callback) {
-        Context context = getContext();
-        if (context == null) {
-            return;
-        }
-
-        WallpaperInfoHelper.loadExploreIntent(context, mWallpaper,
-                (actionLabel, exploreIntent) -> {
-                    mActionLabel = actionLabel;
-                    mExploreIntent = exploreIntent;
-                    if (callback != null) {
-                        callback.run();
-                    }
-                }
-        );
-    }
-
     protected abstract boolean isLoaded();
 
     @Override
@@ -348,20 +327,9 @@
                 mWallpaper instanceof LiveWallpaperInfo);
     }
 
-    protected void onExploreClicked(View button) {
-        if (getContext() == null) {
-            return;
-        }
-        Context context = getContext();
-        mUserEventLogger.logActionClicked(mWallpaper.getCollectionId(context),
-                mWallpaper.getActionLabelRes(context));
-
-        startActivity(mExploreIntent);
-    }
-
     protected void setUpTabs(TabLayout tabs) {
-        tabs.addTab(tabs.newTab().setText(getContext().getString(R.string.home_screen_message)));
-        tabs.addTab(tabs.newTab().setText(getContext().getString(R.string.lock_screen_message)));
+        tabs.addTab(tabs.newTab().setText(R.string.home_screen_message));
+        tabs.addTab(tabs.newTab().setText(R.string.lock_screen_message));
         tabs.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
             @Override
             public void onTabSelected(TabLayout.Tab tab) {
@@ -454,4 +422,70 @@
         return getResources().getConfiguration().getLayoutDirection()
                     == View.LAYOUT_DIRECTION_RTL;
     }
+
+    protected final class WallpaperInfoContent extends BottomSheetContent<WallpaperInfoView> {
+
+        @Nullable private Intent mExploreIntent;
+        private CharSequence mActionLabel;
+
+        protected WallpaperInfoContent(Context context) {
+            super(context);
+        }
+
+        @Override
+        public int getViewId() {
+            return R.layout.wallpaper_info_view;
+        }
+
+        @Override
+        public void onViewCreated(WallpaperInfoView view) {
+            if (mWallpaper == null) {
+                return;
+            }
+
+            if (mActionLabel == null) {
+                setUpExploreIntentAndLabel(() -> populateWallpaperInfo(view));
+            } else {
+                populateWallpaperInfo(view);
+            }
+        }
+
+        private void setUpExploreIntentAndLabel(@Nullable Runnable callback) {
+            Context context = getContext();
+            if (context == null) {
+                return;
+            }
+
+            WallpaperInfoHelper.loadExploreIntent(context, mWallpaper,
+                    (actionLabel, exploreIntent) -> {
+                        mActionLabel = actionLabel;
+                        mExploreIntent = exploreIntent;
+                        if (callback != null) {
+                            callback.run();
+                        }
+                    }
+            );
+        }
+
+        private void onExploreClicked(View button) {
+            Context context = getContext();
+            if (context == null) {
+                return;
+            }
+
+            mUserEventLogger.logActionClicked(mWallpaper.getCollectionId(context),
+                    mWallpaper.getActionLabelRes(context));
+
+            startActivity(mExploreIntent);
+        }
+
+        private void populateWallpaperInfo(WallpaperInfoView view) {
+            view.populateWallpaperInfo(
+                    mWallpaper,
+                    mActionLabel,
+                    WallpaperInfoHelper.shouldShowExploreButton(
+                            getContext(), mExploreIntent),
+                    this::onExploreClicked);
+        }
+    }
 }
diff --git a/src/com/android/wallpaper/picker/WallpaperOnlyFragment.java b/src/com/android/wallpaper/picker/WallpaperOnlyFragment.java
new file mode 100644
index 0000000..b59b853
--- /dev/null
+++ b/src/com/android/wallpaper/picker/WallpaperOnlyFragment.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wallpaper.picker;
+
+import com.android.wallpaper.model.CustomizationSectionController;
+import com.android.wallpaper.model.WallpaperSectionController;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/** The Fragment UI for wallpaper only section. */
+public class WallpaperOnlyFragment extends CustomizationPickerFragment {
+
+    /** Initiates WallpaperOnlyFragment instance. */
+    public static WallpaperOnlyFragment newInstance(CharSequence title) {
+        WallpaperOnlyFragment fragment = new WallpaperOnlyFragment();
+        fragment.setArguments(AppbarFragment.createArguments(title));
+        return fragment;
+    }
+
+    @Override
+    protected List<CustomizationSectionController<?>> getAvailableSections(
+            List<CustomizationSectionController<?>> controllers) {
+        List<CustomizationSectionController<?>> wallpaperOnlySections = controllers.stream()
+                .filter(controller -> controller instanceof WallpaperSectionController)
+                .collect(Collectors.toList());
+        return super.getAvailableSections(wallpaperOnlySections);
+    }
+}
diff --git a/src/com/android/wallpaper/picker/individual/IndividualPickerFragment.java b/src/com/android/wallpaper/picker/individual/IndividualPickerFragment.java
index 97c2eaa..a7d1f5c 100755
--- a/src/com/android/wallpaper/picker/individual/IndividualPickerFragment.java
+++ b/src/com/android/wallpaper/picker/individual/IndividualPickerFragment.java
@@ -582,6 +582,9 @@
         if (mCategory == null) {
             return;
         }
+        if (getContext() == null) {
+            return;
+        }
 
         // Wallpaper count could change, so we may need to change the layout(2 or 3 columns layout)
         GridLayoutManager gridLayoutManager = (GridLayoutManager) mImageGrid.getLayoutManager();
diff --git a/src/com/android/wallpaper/util/ActivityUtils.java b/src/com/android/wallpaper/util/ActivityUtils.java
index eb2adb2..1fccd2f 100755
--- a/src/com/android/wallpaper/util/ActivityUtils.java
+++ b/src/com/android/wallpaper/util/ActivityUtils.java
@@ -97,4 +97,14 @@
                 context.getContentResolver(), Settings.Secure.USER_SETUP_COMPLETE, SUW_COMPLETE)
                 == SUW_NOT_YET);
     }
+
+    /**
+     * Returns true if it's wallpaper only mode.
+     *
+     * @param intent activity intent.
+     */
+    public static boolean isWallpaperOnlyMode(Intent intent) {
+        return "wallpaper_only".equals(
+                intent.getStringExtra("com.android.launcher3.WALLPAPER_FLAVOR"));
+    }
 }
\ No newline at end of file
diff --git a/src/com/android/wallpaper/widget/BottomActionBar.java b/src/com/android/wallpaper/widget/BottomActionBar.java
index 8c0169b..c20d578 100644
--- a/src/com/android/wallpaper/widget/BottomActionBar.java
+++ b/src/com/android/wallpaper/widget/BottomActionBar.java
@@ -28,6 +28,7 @@
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 
+import androidx.annotation.LayoutRes;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.core.widget.ImageViewCompat;
@@ -96,6 +97,52 @@
         void onBottomSheetExpanded();
     }
 
+    /**
+     * Object to host content view for bottom sheet to display.
+     *
+     * <p> The view would be created in the constructor.
+     */
+    public static abstract class BottomSheetContent<T extends View> {
+
+        private T mContentView;
+        private boolean mIsVisible;
+
+        public BottomSheetContent(Context context) {
+            mContentView = createView(context);
+            setVisibility(false);
+        }
+
+        /** Gets the view id to inflate. */
+        @LayoutRes
+        public abstract int getViewId();
+
+        /** Gets called when the content view is created. */
+        public abstract void onViewCreated(T view);
+
+        /** Gets called when the current content view is going to recreate. */
+        public void onRecreateView(T oldView) {}
+
+        private void recreateView(Context context) {
+            // Inform that the view is going to recreate.
+            onRecreateView(mContentView);
+            // Create a new view with the given context.
+            mContentView = createView(context);
+            setVisibility(mIsVisible);
+        }
+
+        private T createView(Context context) {
+            T contentView = (T) LayoutInflater.from(context).inflate(getViewId(), null);
+            onViewCreated(contentView);
+            contentView.setFocusable(true);
+            return contentView;
+        }
+
+        private void setVisibility(boolean isVisible) {
+            mIsVisible = isVisible;
+            mContentView.setVisibility(mIsVisible ? VISIBLE : GONE);
+        }
+    }
+
     // TODO(b/154299462): Separate downloadable related actions from WallpaperPicker.
     /** The action items in the bottom action bar. */
     public enum BottomAction {
@@ -103,7 +150,8 @@
     }
 
     private final Map<BottomAction, View> mActionMap = new EnumMap<>(BottomAction.class);
-    private final Map<BottomAction, View> mContentViewMap = new EnumMap<>(BottomAction.class);
+    private final Map<BottomAction, BottomSheetContent<?>> mContentViewMap =
+            new EnumMap<>(BottomAction.class);
     private final Map<BottomAction, OnActionSelectedListener> mActionSelectedListeners =
             new EnumMap<>(BottomAction.class);
 
@@ -185,17 +233,16 @@
     }
 
     /**
-     * Adds content view to the bottom sheet and binds with a {@code BottomAction} to
-     * expand / collapse the bottom sheet.
+     * Binds the {@code bottomSheetContent} with the {@code action}, the {@code action} button
+     * would be able to expand/collapse the bottom sheet to show the content.
      *
-     * @param contentView the view with content to be added on the bottom sheet
+     * @param bottomSheetContent the content object with view being added to the bottom sheet
      * @param action the action to be bound to expand / collapse the bottom sheet
      */
-    public void attachViewToBottomSheetAndBindAction(View contentView, BottomAction action) {
-        contentView.setVisibility(GONE);
-        contentView.setFocusable(true);
-        mContentViewMap.put(action, contentView);
-        mBottomSheetView.addView(contentView);
+    public void bindBottomSheetContentWithAction(BottomSheetContent<?> bottomSheetContent,
+            BottomAction action) {
+        mContentViewMap.put(action, bottomSheetContent);
+        mBottomSheetView.addView(bottomSheetContent.mContentView);
         setActionClickListener(action, actionView -> {
             if (mBottomSheetBehavior.getState() == STATE_COLLAPSED) {
                 updateContentViewFor(action);
@@ -467,8 +514,18 @@
 
     /** Dynamic update color with {@code Context}. */
     public void setColor(Context context) {
+        // Set bottom sheet background.
         mBottomSheetView.setBackground(context.getDrawable(R.drawable.bottom_sheet_background));
+        if (mBottomSheetView.getChildCount() > 0) {
+            // Update the bottom sheet content view if any.
+            mBottomSheetView.removeAllViews();
+            mContentViewMap.values().forEach(bottomSheetContent -> {
+                bottomSheetContent.recreateView(context);
+                mBottomSheetView.addView(bottomSheetContent.mContentView);
+            });
+        }
 
+        // Set the bar background and action buttons.
         ViewGroup actionTabs = findViewById(R.id.action_tabs);
         actionTabs.setBackgroundColor(
                 ResourceUtils.getColorAttr(context, android.R.attr.colorBackground));
@@ -507,7 +564,7 @@
     }
 
     private void updateContentViewFor(BottomAction action) {
-        mContentViewMap.forEach((a, v) -> v.setVisibility(a.equals(action) ? VISIBLE : GONE));
+        mContentViewMap.forEach((a, content) -> content.setVisibility(a.equals(action)));
     }
 
     private boolean isExpandable(BottomAction action) {
diff --git a/src/com/android/wallpaper/widget/SeparatedTabLayout.java b/src/com/android/wallpaper/widget/SeparatedTabLayout.java
new file mode 100644
index 0000000..8642485
--- /dev/null
+++ b/src/com/android/wallpaper/widget/SeparatedTabLayout.java
@@ -0,0 +1,118 @@
+package com.android.wallpaper.widget;
+
+import static androidx.viewpager2.widget.ViewPager2.SCROLL_STATE_DRAGGING;
+import static androidx.viewpager2.widget.ViewPager2.SCROLL_STATE_IDLE;
+import static androidx.viewpager2.widget.ViewPager2.SCROLL_STATE_SETTLING;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.viewpager2.widget.ViewPager2;
+import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback;
+
+import com.android.wallpaper.R;
+
+import com.google.android.material.tabs.TabLayout;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Custom {@link TabLayout} for separated tabs.
+ *
+ * <p>Don't use {@code TabLayoutMediator} for the tab layout, which binds the tab scrolling
+ * animation that is unwanted for the separated tab design. Uses {@link
+ * SeparatedTabLayout#setViewPager} to bind a {@link ViewPager2} to use the proper tab effect.
+ */
+public final class SeparatedTabLayout extends TabLayout {
+
+    public SeparatedTabLayout(Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    @NonNull
+    public Tab newTab() {
+        Tab tab = super.newTab();
+        tab.view.setBackgroundResource(R.drawable.separated_tabs_ripple_mask);
+        return tab;
+    }
+
+    /** Binds the given {@code viewPager} to the {@link SeparatedTabLayout}. */
+    public void setViewPager(ViewPager2 viewPager) {
+        viewPager.registerOnPageChangeCallback(new SeparatedTabLayoutOnPageChangeCallback(this));
+        addOnTabSelectedListener(new SeparatedTabLayoutOnTabSelectedListener(viewPager));
+    }
+
+    private static class SeparatedTabLayoutOnTabSelectedListener implements
+            OnTabSelectedListener {
+        private final WeakReference<ViewPager2> mViewPagerRef;
+
+        private SeparatedTabLayoutOnTabSelectedListener(ViewPager2 viewPager) {
+            mViewPagerRef = new WeakReference<>(viewPager);
+        }
+
+        @Override
+        public void onTabSelected(Tab tab) {
+            ViewPager2 viewPager = mViewPagerRef.get();
+            if (viewPager != null && viewPager.getCurrentItem() != tab.getPosition()) {
+                viewPager.setCurrentItem(tab.getPosition());
+            }
+        }
+
+        @Override
+        public void onTabUnselected(Tab tab) {}
+
+        @Override
+        public void onTabReselected(Tab tab) {}
+    }
+
+    private static class SeparatedTabLayoutOnPageChangeCallback extends OnPageChangeCallback {
+        private final WeakReference<TabLayout> mTabLayoutRef;
+        private int mPreviousScrollState = SCROLL_STATE_IDLE;
+        private int mScrollState = SCROLL_STATE_IDLE;
+
+        private SeparatedTabLayoutOnPageChangeCallback(TabLayout tabLayout) {
+            mTabLayoutRef = new WeakReference<>(tabLayout);
+        }
+
+        @Override
+        public void onPageSelected(final int position) {
+            if (isUserDragging()) {
+                // Don't update tab position here, wait for page scrolling done to update the tabs.
+                return;
+            }
+            // ViewPager2#setCurrentItem would run into here.
+            updateTabPositionIfNeeded(position);
+        }
+
+        @Override
+        public void onPageScrollStateChanged(final int state) {
+            mPreviousScrollState = mScrollState;
+            mScrollState = state;
+        }
+
+        @Override
+        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+            // Update the tab when the scrolling page is full displayed and is user dragging case.
+            if (positionOffset == 0f && isUserDragging()) {
+                updateTabPositionIfNeeded(position);
+            }
+        }
+
+        private boolean isUserDragging() {
+            return mPreviousScrollState == SCROLL_STATE_DRAGGING
+                    && mScrollState == SCROLL_STATE_SETTLING;
+        }
+
+        private void updateTabPositionIfNeeded(int position) {
+            TabLayout tabLayout = mTabLayoutRef.get();
+            if (tabLayout != null
+                    && tabLayout.getSelectedTabPosition() != position
+                    && position < tabLayout.getTabCount()) {
+                tabLayout.selectTab(tabLayout.getTabAt(position), /* updateIndicator= */ true);
+            }
+        }
+    }
+}