Merge tag 'android-13.0.0_r32' into int/13/fp3

Android 13.0.0 release 32

* tag 'android-13.0.0_r32': (59 commits)
  Offset preview to start if on wallpaper display
  Import translations. DO NOT MERGE ANYWHERE
  Import translations. DO NOT MERGE ANYWHERE
  [WPP2] Make injectors kt
  Fix wallpaper preview misaligned on small screen.
  Renames customization content provider (4/7).
  Unified screen preview section (1/3).
  Moves common screen preview logic to WPP2 (1/2).
  Wallpaper picker reset support (1/3).
  Add logEffectProbe in UserEventLogger
  Add target for ThemePicker instrumented tests
  Tabbed navigation in Wallpaper Picker (1/3).
  [WPP2] Use system flag for fullscreen wallpaper preview
  Defines flag for ravemped WPP UI (3/5).
  [WPP2] Fix live wallpaper preview stretched
  Import translations. DO NOT MERGE ANYWHERE
  [WPP2] Fix fullscreen preview toolbar text color
  [WPP2] Avoid to create view in constructor of floating sheet content
  [WPP2] Fix colors
  Lock screen preview in quick affordance picker.
  ...

Change-Id: I5ff86d26876b2948e075046f4d128593242b22fc
diff --git a/Android.bp b/Android.bp
index dbffb77..eb37963 100644
--- a/Android.bp
+++ b/Android.bp
@@ -43,6 +43,7 @@
         "SettingsLibCollapsingToolbarBaseActivity",
         "subsampling-scale-image-view",
         "SystemUISharedLib",
+        "SystemUICustomizationLib",
         "volley",
         "SettingsLibActivityEmbedding",
     ],
@@ -61,7 +62,7 @@
 
     visibility: [
         ":__subpackages__",
-        "//packages/apps/ThemePicker",
+        "//packages/apps/ThemePicker:__subpackages__",
         "//vendor:__subpackages__",
     ],
 }
@@ -107,12 +108,8 @@
     name: "WallpaperPicker2_defaults",
 
     static_libs: [
-        "renderscript_toolkit",
         "wallpaper-common-deps",
-    ],
-
-    jni_libs: [
-        "librenderscript-toolkit",
+        "monet",
     ],
 
     srcs: [
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 4072227..6270836 100755
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -14,7 +14,10 @@
   <uses-permission android:name="android.permission.INTERNET"/>
   <uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
   <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
+  <uses-permission android:name="android.permission.READ_WALLPAPER_INTERNAL" />
   <uses-permission android:name="android.permission.SET_WALLPAPER"/>
+  <uses-permission android:name="android.permission.SET_WALLPAPER_COMPONENT" />
+  <uses-permission android:name="android.permission.BIND_WALLPAPER" />
   <uses-permission android:name="android.permission.WAKE_LOCK"/>
   <uses-permission android:name="com.android.wallpaper.NOTIFY_ROTATING_WALLPAPER_CHANGED"/>
 
@@ -41,6 +44,11 @@
       <intent>
           <action android:name="com.android.launcher3.action.PARTNER_CUSTOMIZATION" />
       </intent>
+      <!-- Intent filter used to query the launcher Activity for grid preview metadata -->
+        <intent>
+            <action android:name="android.intent.action.MAIN" />
+            <category android:name="android.intent.category.HOME" />
+        </intent>
   </queries>
 
   <application
diff --git a/ktfmt_includes.txt b/ktfmt_includes.txt
index 37bbd6a..112b4a1 100644
--- a/ktfmt_includes.txt
+++ b/ktfmt_includes.txt
@@ -1,9 +1,3 @@
 +src/
 +src_override/
 +tests/
--src/com/android/wallpaper/model/WallpaperColorsViewModel.kt
--src/com/android/wallpaper/model/WorkspaceViewModel.kt
--src/com/android/wallpaper/module/LargeScreenMultiPanesChecker.kt
--src/com/android/wallpaper/module/MultiPanesChecker.kt
--src/com/android/wallpaper/util/DisplayUtils.kt
--src/com/android/wallpaper/widget/GridRowSpacerDecoration.kt
diff --git a/res/color-night/separated_tabs_indicator_color.xml b/res/color-night/separated_tabs_indicator_color.xml
index 7313a16..e0f225e 100644
--- a/res/color-night/separated_tabs_indicator_color.xml
+++ b/res/color-night/separated_tabs_indicator_color.xml
@@ -14,7 +14,6 @@
      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 xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="@color/color_accent_secondary" />
 </selector>
diff --git a/res/color/bottom_action_button_color_tint.xml b/res/color/bottom_action_button_color_tint.xml
index 8381c2f..c139748 100644
--- a/res/color/bottom_action_button_color_tint.xml
+++ b/res/color/bottom_action_button_color_tint.xml
@@ -14,12 +14,11 @@
      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">
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item
         android:state_selected="false"
         android:color="?android:textColorTertiary" />
     <item
         android:state_selected="true"
-        android:color="?androidprv:attr/textColorOnAccent" />
+        android:color="@color/text_color_on_accent" />
 </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
index cbce7d0..28f443b 100644
--- a/res/color/separated_tabs_indicator_color.xml
+++ b/res/color/separated_tabs_indicator_color.xml
@@ -14,7 +14,6 @@
      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 xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="@color/color_accent_primary" />
 </selector>
\ No newline at end of file
diff --git a/res/color/tab_text_color.xml b/res/color/tab_text_color.xml
new file mode 100644
index 0000000..9ef5cad
--- /dev/null
+++ b/res/color/tab_text_color.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2022 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="@color/text_color_on_accent" />
+    <item android:color="@color/text_color_secondary" />
+</selector>
diff --git a/res/color/text_color_on_accent.xml b/res/color/text_color_on_accent.xml
new file mode 100644
index 0000000..27800d9
--- /dev/null
+++ b/res/color/text_color_on_accent.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2022 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.
+  -->
+<!-- Please see primary_text_material_light.xml -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false"
+        android:color="@android:color/system_neutral1_400"/>
+    <item android:color="@android:color/system_neutral1_900"/>
+</selector>
\ No newline at end of file
diff --git a/res/color/wallpaper_control_button_ic_color_tint.xml b/res/color/wallpaper_control_button_ic_color_tint.xml
new file mode 100644
index 0000000..84a7fe9
--- /dev/null
+++ b/res/color/wallpaper_control_button_ic_color_tint.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 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_checked="true" android:color="@color/text_color_on_accent" />
+    <item android:state_checked="false" android:color="?android:textColorPrimary" />
+</selector>
\ No newline at end of file
diff --git a/res/drawable/bottom_action_button_background.xml b/res/drawable/bottom_action_button_background.xml
index e1b3554..7b235ca 100644
--- a/res/drawable/bottom_action_button_background.xml
+++ b/res/drawable/bottom_action_button_background.xml
@@ -15,7 +15,6 @@
      limitations under the License.
 -->
 <ripple xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:color="?android:colorControlHighlight">
     <item>
         <selector>
@@ -27,7 +26,7 @@
             </item>
             <item android:state_selected="true">
                 <shape>
-                    <solid android:color="?androidprv:attr/colorAccentPrimary" />
+                    <solid android:color="@color/color_accent_primary" />
                     <corners android:radius="@dimen/bottom_action_button_radius" />
                 </shape>
             </item>
diff --git a/res/drawable/bottom_apply_button_background.xml b/res/drawable/bottom_apply_button_background.xml
index f43ec1f..2bb371f 100644
--- a/res/drawable/bottom_apply_button_background.xml
+++ b/res/drawable/bottom_apply_button_background.xml
@@ -14,11 +14,10 @@
      limitations under the License.
 -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:shape="rectangle">
     <corners android:radius="@dimen/apply_button_background_height" />
     <size
         android:width="@dimen/apply_button_background_width"
         android:height="@dimen/apply_button_background_height" />
-    <solid android:color="?androidprv:attr/colorAccentPrimaryVariant" />
+    <solid android:color="@color/color_accent_primary_variant" />
 </shape>
\ No newline at end of file
diff --git a/res/drawable/bottom_sheet_background.xml b/res/drawable/bottom_sheet_background.xml
index c0c3668..8edea28 100644
--- a/res/drawable/bottom_sheet_background.xml
+++ b/res/drawable/bottom_sheet_background.xml
@@ -16,8 +16,7 @@
      limitations under the License.
 -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:shape="rectangle">
-    <solid android:color="?androidprv:attr/colorSurface"/>
+    <solid android:color="@color/color_surface"/>
     <corners android:radius="@dimen/preview_bottom_sheet_corner_radius"/>
 </shape>
diff --git a/res/drawable/check_circle_accent_24dp.xml b/res/drawable/check_circle_accent_24dp.xml
index 01723d9..4372a27 100644
--- a/res/drawable/check_circle_accent_24dp.xml
+++ b/res/drawable/check_circle_accent_24dp.xml
@@ -17,7 +17,7 @@
     <item>
         <shape android:shape="oval">
             <size android:width="24dp" android:height="24dp" />
-            <solid android:color="?android:colorAccent" />
+            <solid android:color="@color/color_accent_primary" />
         </shape>
     </item>
     <item android:drawable="@drawable/ic_check_24dp" android:gravity="fill" />
diff --git a/res/drawable/dialog_option_background.xml b/res/drawable/dialog_option_background.xml
index efb5891..4d23500 100644
--- a/res/drawable/dialog_option_background.xml
+++ b/res/drawable/dialog_option_background.xml
@@ -15,11 +15,10 @@
      limitations under the License.
 -->
 <ripple xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:color="?android:colorControlHighlight">
     <item android:id="@android:id/background">
         <shape>
-            <solid android:color="?androidprv:attr/colorAccentPrimary" />
+            <solid android:color="@color/color_accent_primary" />
             <corners android:radius="@dimen/set_wallpaper_dialog_item_inner_corner_radius"/>
         </shape>
     </item>
diff --git a/res/drawable/duo_tabs_button_background.xml b/res/drawable/duo_tabs_button_background.xml
new file mode 100644
index 0000000..b06f36f
--- /dev/null
+++ b/res/drawable/duo_tabs_button_background.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 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">
+        <shape android:shape="rectangle">
+            <corners android:radius="@dimen/duo_tabs_button_corner_radius" />
+            <solid android:color="?android:colorControlHighlight" />
+        </shape>
+    </item>
+
+    <item android:drawable="@drawable/duo_tabs_button_background_base" />
+</ripple>
\ No newline at end of file
diff --git a/res/drawable/duo_tabs_button_background_base.xml b/res/drawable/duo_tabs_button_background_base.xml
new file mode 100644
index 0000000..20ab7a1
--- /dev/null
+++ b/res/drawable/duo_tabs_button_background_base.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <corners android:radius="@dimen/duo_tabs_button_corner_radius" />
+    <solid android:color="@color/color_surface" />
+</shape>
\ No newline at end of file
diff --git a/res/drawable/duo_tabs_button_indicator_background.xml b/res/drawable/duo_tabs_button_indicator_background.xml
new file mode 100644
index 0000000..86841c5
--- /dev/null
+++ b/res/drawable/duo_tabs_button_indicator_background.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 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">
+        <shape android:shape="rectangle">
+            <corners android:radius="@dimen/duo_tabs_button_corner_radius" />
+            <solid android:color="?android:colorControlHighlight" />
+        </shape>
+    </item>
+
+    <item android:drawable="@drawable/duo_tabs_button_indicator_background_base" />
+</ripple>
\ No newline at end of file
diff --git a/res/drawable/duo_tabs_button_indicator_background_base.xml b/res/drawable/duo_tabs_button_indicator_background_base.xml
new file mode 100644
index 0000000..a7693bd
--- /dev/null
+++ b/res/drawable/duo_tabs_button_indicator_background_base.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <corners android:radius="@dimen/duo_tabs_button_corner_radius" />
+    <solid android:color="@color/color_accent_primary" />
+</shape>
\ No newline at end of file
diff --git a/res/drawable/duo_tabs_divider.xml b/res/drawable/duo_tabs_divider.xml
new file mode 100644
index 0000000..8325048
--- /dev/null
+++ b/res/drawable/duo_tabs_divider.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <size
+        android:height="0dp"
+        android:width="@dimen/duo_tabs_divider_space" />
+</shape>
\ No newline at end of file
diff --git a/res/drawable/floating_sheet_background.xml b/res/drawable/floating_sheet_background.xml
new file mode 100644
index 0000000..d4caa2b
--- /dev/null
+++ b/res/drawable/floating_sheet_background.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <solid android:color="@color/color_surface"/>
+    <corners android:radius="@dimen/floating_sheet_corner_radius"/>
+</shape>
diff --git a/res/drawable/fullscreen_button_background.xml b/res/drawable/fullscreen_button_background.xml
index 87a3e1e..6de7663 100644
--- a/res/drawable/fullscreen_button_background.xml
+++ b/res/drawable/fullscreen_button_background.xml
@@ -15,11 +15,10 @@
      limitations under the License.
 -->
 <ripple xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:color="?android:colorControlHighlight">
     <item>
         <shape android:shape="rectangle">
-            <solid android:color="?androidprv:attr/colorAccentPrimary" />
+            <solid android:color="@color/color_accent_primary" />
             <corners android:radius="@dimen/separated_tabs_corner_radius" />
         </shape>
     </item>
diff --git a/res/drawable/horizontal_divider_8dp.xml b/res/drawable/horizontal_divider_8dp.xml
new file mode 100644
index 0000000..b45de7b
--- /dev/null
+++ b/res/drawable/horizontal_divider_8dp.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <size
+        android:width="8dp"
+        android:height="0dp" />
+</shape>
diff --git a/res/drawable/ic_check_wallpaper_24dp.xml b/res/drawable/ic_check_wallpaper_24dp.xml
index eeee07e..75cec26 100644
--- a/res/drawable/ic_check_wallpaper_24dp.xml
+++ b/res/drawable/ic_check_wallpaper_24dp.xml
@@ -14,12 +14,11 @@
      limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:width="24dp"
     android:height="24dp"
     android:viewportWidth="24"
     android:viewportHeight="24">
   <path
-      android:fillColor="?androidprv:attr/textColorOnAccent"
+      android:fillColor="@color/text_color_on_accent"
       android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41L9,16.17z"/>
 </vector>
diff --git a/res/drawable/ic_delete.xml b/res/drawable/ic_delete.xml
new file mode 100644
index 0000000..6b68d7d
--- /dev/null
+++ b/res/drawable/ic_delete.xml
@@ -0,0 +1,32 @@
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="@dimen/wallpaper_control_icon_size"
+    android:height="@dimen/wallpaper_control_icon_size"
+    android:viewportWidth="@dimen/wallpaper_control_icon_viewport_size"
+    android:viewportHeight="@dimen/wallpaper_control_icon_viewport_size"
+    android:tintMode="multiply"
+    android:tint="@color/wallpaper_control_button_ic_color_tint">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M15,4V3H9v1H4v2h1v13c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2V6h1V4H15zM17,19H7V6h10V19z"/>
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M9,8h2v9h-2z"/>
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M13,8h2v9h-2z"/>
+</vector>
diff --git a/res/drawable/ic_download_badge.xml b/res/drawable/ic_download_badge.xml
index 85e09aa..e897c38 100644
--- a/res/drawable/ic_download_badge.xml
+++ b/res/drawable/ic_download_badge.xml
@@ -1,12 +1,11 @@
 <?xml version="1.0" encoding="utf-8"?>
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
     <item>
         <shape android:shape="oval">
             <size
                 android:width="@dimen/grid_item_badge_size"
                 android:height="@dimen/grid_item_badge_size" />
-            <solid android:color="?androidprv:attr/colorSurfaceVariant" />
+            <solid android:color="@color/color_surface_variant" />
         </shape>
     </item>
     <item
diff --git a/res/drawable/ic_effect.xml b/res/drawable/ic_effect.xml
new file mode 100644
index 0000000..fe4acba
--- /dev/null
+++ b/res/drawable/ic_effect.xml
@@ -0,0 +1,26 @@
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="@dimen/wallpaper_control_icon_size"
+    android:height="@dimen/wallpaper_control_icon_size"
+    android:viewportHeight="@dimen/wallpaper_control_icon_viewport_size"
+    android:viewportWidth="@dimen/wallpaper_control_icon_viewport_size"
+    android:tintMode="multiply"
+    android:tint="@color/wallpaper_control_button_ic_color_tint">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M19.6352 8.72727L20.9984 5.72648L24 4.36364L20.9984 3.0008L19.6352 0L18.272 3.0008L15.2704 4.36364L18.272 5.72648L19.6352 8.72727ZM11.456 9.27113L8.7296 3.26953L6.0032 9.27113L0 11.9968L6.0032 14.7225L8.7296 20.7241L11.456 14.7225L17.4592 11.9968L11.456 9.27113ZM19.6352 15.2727L18.272 18.2735L15.2704 19.6364L18.272 20.9992L19.6352 24L20.9984 20.9992L24 19.6364L20.9984 18.2735L19.6352 15.2727Z" />
+</vector>
diff --git a/res/drawable/ic_file_download.xml b/res/drawable/ic_file_download.xml
new file mode 100644
index 0000000..78ecd17
--- /dev/null
+++ b/res/drawable/ic_file_download.xml
@@ -0,0 +1,26 @@
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="@dimen/wallpaper_control_icon_size"
+    android:height="@dimen/wallpaper_control_icon_size"
+    android:viewportHeight="@dimen/wallpaper_control_icon_viewport_size"
+    android:viewportWidth="@dimen/wallpaper_control_icon_viewport_size"
+    android:tint="@color/wallpaper_control_button_ic_color_tint"
+    android:tintMode="multiply">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M4,15h2v3h12v-3h2v3c0,1.1 -0.9,2 -2,2H6c-1.1,0 -2,-0.9 -2,-2m11.59,-8.41L13,12.17V4h-2v8.17L8.41,9.59 7,11l5,5 5,-5 -1.41,-1.41z" />
+</vector>
diff --git a/res/drawable/ic_info.xml b/res/drawable/ic_info.xml
new file mode 100644
index 0000000..1cbabe7
--- /dev/null
+++ b/res/drawable/ic_info.xml
@@ -0,0 +1,26 @@
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="@dimen/wallpaper_control_icon_size"
+    android:height="@dimen/wallpaper_control_icon_size"
+    android:viewportWidth="@dimen/wallpaper_control_icon_viewport_size"
+    android:viewportHeight="@dimen/wallpaper_control_icon_viewport_size"
+    android:tintMode="multiply"
+    android:tint="@color/wallpaper_control_button_ic_color_tint">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M11,7h2v2h-2zM11,11h2v6h-2zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z"/>
+</vector>
diff --git a/res/drawable/ic_tune.xml b/res/drawable/ic_tune.xml
new file mode 100644
index 0000000..4e2a83c
--- /dev/null
+++ b/res/drawable/ic_tune.xml
@@ -0,0 +1,26 @@
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="@dimen/wallpaper_control_icon_size"
+    android:height="@dimen/wallpaper_control_icon_size"
+    android:viewportWidth="@dimen/wallpaper_control_icon_viewport_size"
+    android:viewportHeight="@dimen/wallpaper_control_icon_viewport_size"
+    android:tintMode="multiply"
+    android:tint="@color/wallpaper_control_button_ic_color_tint">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M3,17v2h6v-2L3,17zM3,5v2h10L13,5L3,5zM13,21v-2h8v-2h-8v-2h-2v6h2zM7,9v2L3,11v2h4v2h2L9,9L7,9zM21,13v-2L11,11v2h10zM15,9h2L17,7h4L21,5h-4L17,3h-2v6z" />
+</vector>
diff --git a/res/drawable/menu_item_hide_ui_background.xml b/res/drawable/menu_item_hide_ui_background.xml
index 6a0b219..26e826d 100644
--- a/res/drawable/menu_item_hide_ui_background.xml
+++ b/res/drawable/menu_item_hide_ui_background.xml
@@ -14,7 +14,6 @@
      limitations under the License.
 -->
 <ripple xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:color="?android:colorControlHighlight">
     <item>
         <layer-list>
@@ -23,7 +22,7 @@
                     <size
                         android:width="@dimen/menu_item_hide_ui_background_width"
                         android:height="@dimen/menu_item_hide_ui_background_height" />
-                    <solid android:color="?androidprv:attr/colorAccentSecondary" />
+                    <solid android:color="@color/color_accent_secondary" />
                 </shape>
             </item>
             <item
diff --git a/res/drawable/menu_item_set_wallpaper_background.xml b/res/drawable/menu_item_set_wallpaper_background.xml
index 98becc0..fbf63b8 100644
--- a/res/drawable/menu_item_set_wallpaper_background.xml
+++ b/res/drawable/menu_item_set_wallpaper_background.xml
@@ -14,7 +14,6 @@
      limitations under the License.
 -->
 <ripple xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:color="?android:colorControlHighlight">
     <item>
         <shape
@@ -23,7 +22,7 @@
                 android:width="@dimen/menu_item_set_wallpaper_background_width"
                 android:height="@dimen/menu_item_set_wallpaper_background_height" />
             <corners android:radius="@dimen/menu_item_set_wallpaper_background_radius" />
-            <solid android:color="?androidprv:attr/colorAccentPrimary" />
+            <solid android:color="@color/color_accent_primary" />
         </shape>
     </item>
 </ripple>
\ No newline at end of file
diff --git a/res/drawable/separated_tabs_background.xml b/res/drawable/separated_tabs_background.xml
index 546c568..2737b4f 100644
--- a/res/drawable/separated_tabs_background.xml
+++ b/res/drawable/separated_tabs_background.xml
@@ -14,11 +14,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/colorSurface" />
+        <solid android:color="@color/color_surface" />
         <corners android:radius="@dimen/separated_tabs_corner_radius" />
     </shape>
 </inset>
diff --git a/res/drawable/set_wallpaper_button_background.xml b/res/drawable/set_wallpaper_button_background.xml
new file mode 100644
index 0000000..a19a3fc
--- /dev/null
+++ b/res/drawable/set_wallpaper_button_background.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 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">
+        <shape android:shape="rectangle">
+            <corners android:radius="@dimen/set_wallpaper_button_corner_radius" />
+            <padding
+                android:left="@dimen/set_wallpaper_button_horizontal_padding"
+                android:top="@dimen/set_wallpaper_button_vertical_padding"
+                android:right="@dimen/set_wallpaper_button_horizontal_padding"
+                android:bottom="@dimen/set_wallpaper_button_vertical_padding" />
+            <solid android:color="?android:colorControlHighlight" />
+        </shape>
+    </item>
+
+    <item android:drawable="@drawable/set_wallpaper_button_background_base" />
+</ripple>
\ No newline at end of file
diff --git a/res/drawable/set_wallpaper_button_background_base.xml b/res/drawable/set_wallpaper_button_background_base.xml
new file mode 100644
index 0000000..67f7776
--- /dev/null
+++ b/res/drawable/set_wallpaper_button_background_base.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <corners android:radius="@dimen/set_wallpaper_button_corner_radius" />
+    <padding
+        android:left="@dimen/set_wallpaper_button_horizontal_padding"
+        android:top="@dimen/set_wallpaper_button_vertical_padding"
+        android:right="@dimen/set_wallpaper_button_horizontal_padding"
+        android:bottom="@dimen/set_wallpaper_button_vertical_padding" />
+    <solid  android:color="@color/color_accent_primary" />
+</shape>
\ No newline at end of file
diff --git a/res/drawable/snackbar_background.xml b/res/drawable/snackbar_background.xml
index e7833c0..acc51d5 100644
--- a/res/drawable/snackbar_background.xml
+++ b/res/drawable/snackbar_background.xml
@@ -15,8 +15,7 @@
   -->
 
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:shape="rectangle">
     <corners android:radius="18dp" />
-    <solid android:color="?androidprv:attr/colorSurface" />
+    <solid android:color="@color/color_surface" />
 </shape>
\ No newline at end of file
diff --git a/res/drawable/tab_background.xml b/res/drawable/tab_background.xml
new file mode 100644
index 0000000..2f91a22
--- /dev/null
+++ b/res/drawable/tab_background.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2022 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">
+        <shape android:shape="rectangle">
+            <corners android:radius="12dp" />
+            <solid android:color="@color/color_accent_secondary" />
+        </shape>
+    </item>
+    <item>
+        <shape android:shape="rectangle">
+            <corners android:radius="12dp" />
+            <solid android:color="@color/color_surface" />
+        </shape>
+    </item>
+</selector>
\ No newline at end of file
diff --git a/res/drawable/wallpaper_check_circle_24dp.xml b/res/drawable/wallpaper_check_circle_24dp.xml
index 1249111..a83842e 100644
--- a/res/drawable/wallpaper_check_circle_24dp.xml
+++ b/res/drawable/wallpaper_check_circle_24dp.xml
@@ -13,12 +13,11 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
     <item>
         <shape android:shape="oval">
             <size android:width="24dp" android:height="24dp" />
-            <solid android:color="?androidprv:attr/colorAccentPrimary" />
+            <solid android:color="@color/color_accent_primary" />
         </shape>
     </item>
     <item
diff --git a/res/drawable/wallpaper_control_button_background.xml b/res/drawable/wallpaper_control_button_background.xml
new file mode 100644
index 0000000..e3954dc
--- /dev/null
+++ b/res/drawable/wallpaper_control_button_background.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 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_checked="true"
+        android:drawable="@drawable/wallpaper_control_button_on_background" />
+    <item android:drawable="@drawable/wallpaper_control_button_off_background" />
+</selector>
\ No newline at end of file
diff --git a/res/drawable/wallpaper_control_button_customize.xml b/res/drawable/wallpaper_control_button_customize.xml
new file mode 100644
index 0000000..246552d
--- /dev/null
+++ b/res/drawable/wallpaper_control_button_customize.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/wallpaper_control_button_background" />
+    <item android:drawable="@drawable/ic_tune" />
+</layer-list>
\ No newline at end of file
diff --git a/res/drawable/wallpaper_control_button_delete.xml b/res/drawable/wallpaper_control_button_delete.xml
new file mode 100644
index 0000000..dfc4b10
--- /dev/null
+++ b/res/drawable/wallpaper_control_button_delete.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/wallpaper_control_button_background" />
+    <item android:drawable="@drawable/ic_delete" />
+</layer-list>
\ No newline at end of file
diff --git a/res/drawable/wallpaper_control_button_download.xml b/res/drawable/wallpaper_control_button_download.xml
new file mode 100644
index 0000000..343a0cb
--- /dev/null
+++ b/res/drawable/wallpaper_control_button_download.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/wallpaper_control_button_background" />
+    <item android:drawable="@drawable/ic_file_download" />
+</layer-list>
\ No newline at end of file
diff --git a/res/drawable/wallpaper_control_button_effect.xml b/res/drawable/wallpaper_control_button_effect.xml
new file mode 100644
index 0000000..a3c975b
--- /dev/null
+++ b/res/drawable/wallpaper_control_button_effect.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/wallpaper_control_button_background" />
+    <item android:drawable="@drawable/ic_effect" />
+</layer-list>
\ No newline at end of file
diff --git a/res/drawable/wallpaper_control_button_group_divider.xml b/res/drawable/wallpaper_control_button_group_divider.xml
new file mode 100644
index 0000000..b8f115c
--- /dev/null
+++ b/res/drawable/wallpaper_control_button_group_divider.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <size
+        android:height="@dimen/wallpaper_control_button_group_divider_space"
+        android:width="0dp" />
+</shape>
\ No newline at end of file
diff --git a/res/drawable/wallpaper_control_button_info.xml b/res/drawable/wallpaper_control_button_info.xml
new file mode 100644
index 0000000..c3954c1
--- /dev/null
+++ b/res/drawable/wallpaper_control_button_info.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/wallpaper_control_button_background" />
+    <item android:drawable="@drawable/ic_info" />
+</layer-list>
\ No newline at end of file
diff --git a/res/drawable/wallpaper_control_button_off_background.xml b/res/drawable/wallpaper_control_button_off_background.xml
new file mode 100644
index 0000000..7c0b1bc
--- /dev/null
+++ b/res/drawable/wallpaper_control_button_off_background.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 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">
+        <shape android:shape="oval">
+            <padding
+                android:bottom="@dimen/wallpaper_control_button_padding"
+                android:left="@dimen/wallpaper_control_button_padding"
+                android:right="@dimen/wallpaper_control_button_padding"
+                android:top="@dimen/wallpaper_control_button_padding" />
+            <solid android:color="?android:colorControlHighlight" />
+        </shape>
+    </item>
+
+    <item android:drawable="@drawable/wallpaper_control_button_off_background_base" />
+</ripple>
\ No newline at end of file
diff --git a/res/drawable/wallpaper_control_button_off_background_base.xml b/res/drawable/wallpaper_control_button_off_background_base.xml
new file mode 100644
index 0000000..85e3e1f
--- /dev/null
+++ b/res/drawable/wallpaper_control_button_off_background_base.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="oval">
+    <padding
+        android:left="@dimen/wallpaper_control_button_padding"
+        android:right="@dimen/wallpaper_control_button_padding"
+        android:top="@dimen/wallpaper_control_button_padding"
+        android:bottom="@dimen/wallpaper_control_button_padding" />
+    <solid android:color="@color/color_surface" />
+</shape>
\ No newline at end of file
diff --git a/res/drawable/wallpaper_control_button_on_background.xml b/res/drawable/wallpaper_control_button_on_background.xml
new file mode 100644
index 0000000..3e35d09
--- /dev/null
+++ b/res/drawable/wallpaper_control_button_on_background.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 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">
+        <shape android:shape="oval">
+            <padding
+                android:bottom="@dimen/wallpaper_control_button_padding"
+                android:left="@dimen/wallpaper_control_button_padding"
+                android:right="@dimen/wallpaper_control_button_padding"
+                android:top="@dimen/wallpaper_control_button_padding" />
+            <solid android:color="?android:colorControlHighlight" />
+        </shape>
+    </item>
+
+    <item android:drawable="@drawable/wallpaper_control_button_on_background_base" />
+</ripple>
\ No newline at end of file
diff --git a/res/drawable/wallpaper_control_button_on_background_base.xml b/res/drawable/wallpaper_control_button_on_background_base.xml
new file mode 100644
index 0000000..0414188
--- /dev/null
+++ b/res/drawable/wallpaper_control_button_on_background_base.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+    android:shape="oval">
+    <padding
+        android:left="@dimen/wallpaper_control_button_padding"
+        android:right="@dimen/wallpaper_control_button_padding"
+        android:top="@dimen/wallpaper_control_button_padding"
+        android:bottom="@dimen/wallpaper_control_button_padding"/>
+    <solid android:color="@color/color_accent_primary" />
+</shape>
\ No newline at end of file
diff --git a/res/drawable/wallpaper_section_background.xml b/res/drawable/wallpaper_section_background.xml
index 208f543..f59f196 100644
--- a/res/drawable/wallpaper_section_background.xml
+++ b/res/drawable/wallpaper_section_background.xml
@@ -14,8 +14,7 @@
      limitations under the License.
 -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:shape="rectangle">
     <corners android:radius="28dp" />
-    <solid android:color="?androidprv:attr/colorSurface" />
+    <solid android:color="@color/color_surface" />
 </shape>
diff --git a/res/layout/button_download_wallpaper.xml b/res/layout/button_download_wallpaper.xml
new file mode 100644
index 0000000..882df49
--- /dev/null
+++ b/res/layout/button_download_wallpaper.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="@dimen/wallpaper_control_button_size"
+    android:layout_height="@dimen/wallpaper_control_button_size">
+    <ToggleButton
+        android:id="@+id/download_button"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@android:color/transparent"
+        android:contentDescription="@string/bottom_action_bar_download"
+        android:foreground="@drawable/wallpaper_control_button_download"
+        android:textOff=""
+        android:textOn="" />
+
+    <FrameLayout
+        android:id="@+id/action_download_progress"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@drawable/wallpaper_control_button_off_background"
+        android:visibility="gone">
+        <ProgressBar
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:indeterminateTint="?android:textColorTertiary"/>
+    </FrameLayout>
+</FrameLayout>
+
diff --git a/res/layout/dialog_effect_error_title.xml b/res/layout/dialog_effect_error_title.xml
index 1b6557f..90466a8 100755
--- a/res/layout/dialog_effect_error_title.xml
+++ b/res/layout/dialog_effect_error_title.xml
@@ -14,10 +14,9 @@
      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:background="@color/color_surface"
     android:orientation="vertical">
 
     <Space
@@ -41,7 +40,7 @@
         android:layout_height="wrap_content"
         android:fontFamily="@*android:string/config_headlineFontFamily"
         android:minHeight="32dp"
-        android:textColor="?android:attr/textColorPrimary"
+        android:textColor="@color/text_color_primary"
         android:textSize="24sp"
         android:gravity="center" />
 
diff --git a/res/layout/dialog_set_wallpaper.xml b/res/layout/dialog_set_wallpaper.xml
index f1102bd..550463f 100755
--- a/res/layout/dialog_set_wallpaper.xml
+++ b/res/layout/dialog_set_wallpaper.xml
@@ -16,10 +16,9 @@
 -->
 
 <FrameLayout 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="match_parent"
-    android:background="?androidprv:attr/colorSurface"
+    android:background="@color/color_surface"
     android:padding="@dimen/set_wallpaper_dialog_padding">
 
     <LinearLayout
@@ -37,7 +36,7 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:text="@string/set_wallpaper_home_screen_destination"
-            android:textColor="?androidprv:attr/textColorOnAccent" />
+            android:textColor="@color/text_color_on_accent" />
 
         <Button
             android:id="@+id/set_lock_wallpaper_button"
@@ -45,7 +44,7 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:text="@string/set_wallpaper_lock_screen_destination"
-            android:textColor="?androidprv:attr/textColorOnAccent" />
+            android:textColor="@color/text_color_on_accent" />
 
         <Button
             android:id="@+id/set_both_wallpaper_button"
@@ -53,7 +52,7 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:text="@string/set_wallpaper_both_destination"
-            android:textColor="?androidprv:attr/textColorOnAccent" />
+            android:textColor="@color/text_color_on_accent" />
 
     </LinearLayout>
 
diff --git a/res/layout/dialog_set_wallpaper_title.xml b/res/layout/dialog_set_wallpaper_title.xml
index 3267939..4dbc4f2 100755
--- a/res/layout/dialog_set_wallpaper_title.xml
+++ b/res/layout/dialog_set_wallpaper_title.xml
@@ -14,10 +14,9 @@
      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:background="@color/color_surface"
     android:orientation="vertical">
 
     <Space
@@ -41,7 +40,7 @@
         android:layout_height="wrap_content"
         android:fontFamily="@*android:string/config_headlineFontFamily"
         android:minHeight="32dp"
-        android:textColor="?android:attr/textColorPrimary"
+        android:textColor="@color/text_color_primary"
         android:textSize="24sp"
         android:gravity="center" />
 
diff --git a/res/layout/duo_tabs.xml b/res/layout/duo_tabs.xml
new file mode 100644
index 0000000..5a25376
--- /dev/null
+++ b/res/layout/duo_tabs.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 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:layout_gravity="bottom"
+    android:divider="@drawable/duo_tabs_divider"
+    android:orientation="horizontal"
+    android:showDividers="middle">
+
+    <Button
+        android:id="@+id/tab_primary"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/separated_tabs_height"
+        android:layout_weight="1"
+        android:background="@drawable/duo_tabs_button_background"
+        android:gravity="center" />
+
+    <Button
+        android:id="@+id/tab_secondary"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/separated_tabs_height"
+        android:layout_weight="1"
+        android:background="@drawable/duo_tabs_button_background"
+        android:gravity="center" />
+</LinearLayout>
diff --git a/res/layout/floating_sheet.xml b/res/layout/floating_sheet.xml
new file mode 100644
index 0000000..24aacfe
--- /dev/null
+++ b/res/layout/floating_sheet.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  Copyright (C) 2022 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.coordinatorlayout.widget.CoordinatorLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_height="match_parent"
+    android:layout_width="match_parent">
+    <!-- Bottom Sheet Behavior view should be a child view of CoordinatorLayout -->
+    <FrameLayout
+        android:id="@+id/floating_sheet_container"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:clickable="true"
+        app:behavior_hideable="true"
+        app:behavior_peekHeight="0dp"
+        app:behavior_skipCollapsed="true"
+        app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
+        <!-- To enable a floating sheet, content and styling are included as child view -->
+        <FrameLayout
+            android:id="@+id/floating_sheet_content"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:layout_marginBottom="@dimen/floating_sheet_margin"
+            android:layout_marginHorizontal="@dimen/floating_sheet_margin"
+            android:orientation="vertical"
+            android:theme="@style/WallpaperPicker.FloatingPaneStyle" />
+    </FrameLayout>
+</androidx.coordinatorlayout.widget.CoordinatorLayout>
\ No newline at end of file
diff --git a/res/layout/floating_sheet_wallpaper_info_view.xml b/res/layout/floating_sheet_wallpaper_info_view.xml
new file mode 100644
index 0000000..519a4e2
--- /dev/null
+++ b/res/layout/floating_sheet_wallpaper_info_view.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+
+     Copyright (C) 2022 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.wallpaper.widget.floatingsheetcontent.WallpaperInfoView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:padding="@dimen/wallpaper_info_pane_padding"
+    android:theme="@style/WallpaperPicker.BottomPaneStyle">
+
+    <TextView
+        android:id="@+id/wallpaper_info_title"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:textAppearance="@style/TitleTextAppearance"
+        android:textColor="?android:textColorPrimary" />
+
+    <TextView
+        android:id="@+id/wallpaper_info_subtitle1"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/wallpaper_info_pane_subtitle1_top_margin"
+        android:textColor="?android:textColorSecondary"
+        android:textSize="14sp"
+        android:visibility="gone" />
+
+    <TextView
+        android:id="@+id/wallpaper_info_subtitle2"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/wallpaper_info_pane_subtitle2_top_margin"
+        android:textColor="?android:textColorSecondary"
+        android:textSize="14sp"
+        android:visibility="gone" />
+
+    <Button
+        android:id="@+id/wallpaper_info_explore_button"
+        style="@style/ExploreButtonStyle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/wallpaper_info_pane_explore_button_top_margin"
+        android:text="@string/explore"
+        android:visibility="gone" />
+</com.android.wallpaper.widget.floatingsheetcontent.WallpaperInfoView>
diff --git a/res/layout/fragment_clock_custom_picker_demo.xml b/res/layout/fragment_clock_custom_picker_demo.xml
new file mode 100644
index 0000000..29bd941
--- /dev/null
+++ b/res/layout/fragment_clock_custom_picker_demo.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <FrameLayout
+        android:id="@+id/section_header_container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        app:layout_constraintBottom_toTopOf="@+id/clock_preview_card_list_demo"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent">
+        <include layout="@layout/section_header" />
+    </FrameLayout>
+    <androidx.recyclerview.widget.RecyclerView
+        android:id="@+id/clock_preview_card_list_demo"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        app:layout_constraintBottom_toBottomOf="parent"
+        android:paddingHorizontal="@dimen/grid_options_container_horizontal_margin"
+        app:layout_constraintTop_toBottomOf="@+id/section_header_container"
+        android:clipToPadding="false" />
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/res/layout/fragment_tabbed_customization_picker.xml b/res/layout/fragment_tabbed_customization_picker.xml
new file mode 100644
index 0000000..e056670
--- /dev/null
+++ b/res/layout/fragment_tabbed_customization_picker.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2022 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">
+
+    <com.android.wallpaper.widget.DuoTabs
+        android:id="@+id/duo_tabs"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:paddingHorizontal="24dp"
+        android:paddingVertical="16dp" />
+
+    <include
+        layout="@layout/fragment_customization_picker"
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        android:layout_weight="1" />
+
+</LinearLayout>
diff --git a/res/layout/fragment_wallpaper_preview.xml b/res/layout/fragment_wallpaper_preview.xml
new file mode 100644
index 0000000..bc73fa8
--- /dev/null
+++ b/res/layout/fragment_wallpaper_preview.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 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.
+-->
+<FrameLayout 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">
+
+    <com.android.wallpaper.picker.TouchForwardingLayout
+        android:id="@+id/touch_forwarding_layout"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@android:color/transparent"/>
+
+    <FrameLayout
+        android:id="@+id/hide_floating_sheet_touch_layout"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@android:color/transparent"
+        android:clickable="true"/>
+
+    <include layout="@layout/wallpaper_preview" />
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@android:color/transparent"
+        android:fitsSystemWindows="true">
+
+        <Toolbar
+            android:id="@+id/toolbar"
+            android:layout_width="0dp"
+            android:layout_height="?android:attr/actionBarSize"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toStartOf="@+id/barrier_start" />
+
+        <Button
+            android:id="@+id/button_set_wallpaper"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:background="@drawable/set_wallpaper_button_background"
+            android:gravity="center"
+            android:text="@string/set_wallpaper_button_text"
+            android:textColor="@color/text_color_on_accent"
+            android:layout_marginEnd="@dimen/set_wallpaper_button_margin_end"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintTop_toTopOf="@+id/toolbar"
+            app:layout_constraintBottom_toBottomOf="@+id/toolbar" />
+
+        <com.android.wallpaper.widget.WallpaperDownloadButton
+            android:id="@+id/button_download_wallpaper"
+            android:layout_width="@dimen/wallpaper_control_button_size"
+            android:layout_height="@dimen/wallpaper_control_button_size"
+            android:layout_marginEnd="@dimen/wallpaper_control_button_group_margin_end"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintTop_toTopOf="@+id/toolbar"
+            app:layout_constraintBottom_toBottomOf="@+id/toolbar"
+            android:visibility="gone"/>
+
+        <androidx.constraintlayout.widget.Barrier
+            android:id="@+id/barrier_start"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            app:barrierDirection="start"
+            app:constraint_referenced_ids="button_set_wallpaper, button_download_wallpaper" />
+
+        <androidx.constraintlayout.widget.Barrier
+            android:id="@+id/barrier_bottom"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            app:barrierDirection="bottom"
+            app:constraint_referenced_ids="button_set_wallpaper, button_download_wallpaper" />
+
+        <com.android.wallpaper.widget.WallpaperControlButtonGroup
+            android:id="@+id/wallpaper_control_button_group"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/wallpaper_control_button_group_margin_top"
+            android:layout_marginEnd="@dimen/wallpaper_control_button_group_margin_end"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintTop_toBottomOf="@+id/barrier_bottom" />
+
+        <com.android.wallpaper.widget.DuoTabs
+            android:id="@+id/overlay_tabs"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingHorizontal="@dimen/full_preview_page_tabs_horizontal_padding"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toStartOf="parent" />
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+    <com.android.wallpaper.widget.FloatingSheet
+        android:id="@+id/floating_sheet"
+        android:layout_height="match_parent"
+        android:layout_width="match_parent" />
+
+</FrameLayout>
diff --git a/res/layout/fullscreen_buttons.xml b/res/layout/fullscreen_buttons.xml
index e6a0387..d912149 100644
--- a/res/layout/fullscreen_buttons.xml
+++ b/res/layout/fullscreen_buttons.xml
@@ -16,7 +16,6 @@
 -->
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:layout_height="@dimen/separated_tabs_height"
     android:layout_width="match_parent"
     android:orientation="horizontal"
@@ -34,7 +33,7 @@
         android:text="@string/hide_ui_preview_text"
         android:textAlignment="center"
         android:textAppearance="@style/SeparatedTabsTextAppearance"
-        android:textColor="?androidprv:attr/textColorOnAccent"
+        android:textColor="@color/text_color_on_accent"
         android:visibility="visible">
     </Button>
 
@@ -50,7 +49,7 @@
         android:text="@string/set_wallpaper_button_text"
         android:textAlignment="center"
         android:textAppearance="@style/SeparatedTabsTextAppearance"
-        android:textColor="?androidprv:attr/textColorOnAccent"
+        android:textColor="@color/text_color_on_accent"
         android:visibility="visible">
     </Button>
 </LinearLayout>
diff --git a/res/layout/grid_item_header.xml b/res/layout/grid_item_header.xml
new file mode 100644
index 0000000..eb56993
--- /dev/null
+++ b/res/layout/grid_item_header.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+     Copyright (C) 2022 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.
+-->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:gravity="center_vertical"
+    android:lineHeight="@dimen/wallpaper_header_line_height"
+    android:textAppearance="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle"
+    android:textColor="?android:textColorPrimary"
+    android:textSize="@dimen/wallpaper_header_text_size" />
diff --git a/res/layout/lock_screen_preview.xml b/res/layout/lock_screen_preview.xml
index 1387603..32ee922 100644
--- a/res/layout/lock_screen_preview.xml
+++ b/res/layout/lock_screen_preview.xml
@@ -15,7 +15,6 @@
 -->
 <FrameLayout
     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/lock_overlay"
     android:layout_width="match_parent"
diff --git a/res/layout/menu_item_hide_ui.xml b/res/layout/menu_item_hide_ui.xml
index 1e8aaa2..2331efc 100644
--- a/res/layout/menu_item_hide_ui.xml
+++ b/res/layout/menu_item_hide_ui.xml
@@ -15,7 +15,6 @@
      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="wrap_content"
     android:layout_height="wrap_content">
 
@@ -27,5 +26,5 @@
         android:background="@drawable/menu_item_hide_ui_background"
         android:contentDescription="@string/hide_ui_preview_text"
         android:gravity="center"
-        android:textColor="?androidprv:attr/textColorOnAccent" />
+        android:textColor="@color/text_color_on_accent" />
 </LinearLayout>
diff --git a/res/layout/menu_item_set_wallpaper.xml b/res/layout/menu_item_set_wallpaper.xml
index e660313..4d9af60 100644
--- a/res/layout/menu_item_set_wallpaper.xml
+++ b/res/layout/menu_item_set_wallpaper.xml
@@ -15,7 +15,6 @@
      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="wrap_content"
     android:layout_height="wrap_content">
 
@@ -26,6 +25,6 @@
         android:background="@drawable/menu_item_set_wallpaper_background"
         android:gravity="center"
         android:text="@string/apply_btn"
-        android:textColor="?androidprv:attr/textColorOnAccent"
+        android:textColor="@color/text_color_on_accent"
         android:textStyle="bold" />
 </LinearLayout>
diff --git a/res/layout/screen_preview_section.xml b/res/layout/screen_preview_section.xml
new file mode 100644
index 0000000..fde6611
--- /dev/null
+++ b/res/layout/screen_preview_section.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 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.wallpaper.picker.customization.ui.section.ScreenPreviewView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingBottom="16dp">
+
+    <com.android.wallpaper.picker.DisplayAspectRatioFrameLayout
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/screen_preview_height">
+
+        <include
+            android:id="@+id/lock_preview"
+            layout="@layout/wallpaper_preview_card"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_gravity="center" />
+
+        <include
+            android:id="@+id/home_preview"
+            layout="@layout/wallpaper_preview_card"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_gravity="center" />
+
+    </com.android.wallpaper.picker.DisplayAspectRatioFrameLayout>
+
+</com.android.wallpaper.picker.customization.ui.section.ScreenPreviewView>
diff --git a/res/layout/wallpaper_control_button_group.xml b/res/layout/wallpaper_control_button_group.xml
new file mode 100644
index 0000000..ecc73fc
--- /dev/null
+++ b/res/layout/wallpaper_control_button_group.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/wallpaper_control_container"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:divider="@drawable/wallpaper_control_button_group_divider"
+    android:showDividers="middle">
+    <ToggleButton
+        android:id="@+id/delete_button"
+        android:layout_width="@dimen/wallpaper_control_button_size"
+        android:layout_height="@dimen/wallpaper_control_button_size"
+        android:background="@android:color/transparent"
+        android:contentDescription="@string/delete_live_wallpaper"
+        android:foreground="@drawable/wallpaper_control_button_delete"
+        android:textOff=""
+        android:textOn=""
+        android:visibility="gone" />
+
+    <ToggleButton
+        android:id="@+id/customize_button"
+        android:layout_width="@dimen/wallpaper_control_button_size"
+        android:layout_height="@dimen/wallpaper_control_button_size"
+        android:background="@android:color/transparent"
+        android:contentDescription="@string/tab_customize"
+        android:foreground="@drawable/wallpaper_control_button_customize"
+        android:textOff=""
+        android:textOn=""
+        android:visibility="gone" />
+
+    <ToggleButton
+        android:id="@+id/effects_button"
+        android:layout_width="@dimen/wallpaper_control_button_size"
+        android:layout_height="@dimen/wallpaper_control_button_size"
+        android:background="@android:color/transparent"
+        android:contentDescription="@string/tab_effects"
+        android:foreground="@drawable/wallpaper_control_button_effect"
+        android:textOff=""
+        android:textOn=""
+        android:visibility="gone" />
+
+    <ToggleButton
+        android:id="@+id/information_button"
+        android:layout_width="@dimen/wallpaper_control_button_size"
+        android:layout_height="@dimen/wallpaper_control_button_size"
+        android:background="@android:color/transparent"
+        android:contentDescription="@string/tab_info"
+        android:foreground="@drawable/wallpaper_control_button_info"
+        android:textOff=""
+        android:textOn=""
+        android:visibility="gone" />
+</LinearLayout>
+
diff --git a/res/layout/wallpaper_preview.xml b/res/layout/wallpaper_preview.xml
new file mode 100644
index 0000000..abf1043
--- /dev/null
+++ b/res/layout/wallpaper_preview.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+     Copyright (C) 2022 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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:contentDescription="@string/wallpaper_preview_card_content_description">
+
+    <SurfaceView
+        android:id="@+id/wallpaper_surface"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+    <SurfaceView
+        android:id="@+id/wallpaper_surface_cinematic"
+        android:visibility="gone"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+    <com.android.wallpaper.picker.FadeAnimationSurfaceView
+        android:id="@+id/workspace_surface"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:importantForAccessibility="noHideDescendants" />
+
+    <FrameLayout
+        android:id="@+id/lock_screen_preview_container"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:importantForAccessibility="noHideDescendants" />
+
+    <androidx.core.widget.ContentLoadingProgressBar
+        android:id="@+id/wallpaper_preview_spinner"
+        style="?android:progressBarStyleLarge"
+        android:background="@android:color/transparent"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:visibility="gone" />
+</FrameLayout>
diff --git a/res/layout/wallpaper_section_view.xml b/res/layout/wallpaper_section_view.xml
index 1c7fe24..76cfc94 100644
--- a/res/layout/wallpaper_section_view.xml
+++ b/res/layout/wallpaper_section_view.xml
@@ -16,7 +16,6 @@
 -->
 <com.android.wallpaper.picker.WallpaperSectionView
     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:layout_width="match_parent"
     android:layout_height="wrap_content"
@@ -71,10 +70,10 @@
             android:layout_marginVertical="@dimen/wallpaper_picker_entry_margin_vertical"
             android:paddingHorizontal="@dimen/wallpaper_picker_entry_horizontal_padding"
             android:background="@drawable/btn_transparent_background"
-            android:textColor="?androidprv:attr/colorAccentPrimaryVariant"
+            android:textColor="@color/color_accent_primary_variant"
             android:drawablePadding="@dimen/wallpaper_picker_entry_drawable_padding"
             android:drawableStart="@drawable/ic_nav_wallpaper"
-            android:drawableTint="?androidprv:attr/colorAccentPrimaryVariant"
+            android:drawableTint="@color/color_accent_primary_variant"
             android:text="@string/wallpaper_picker_entry_title"
             app:layout_constraintBottom_toBottomOf="parent"
             app:layout_constraintEnd_toEndOf="parent"
diff --git a/res/menu/undoable_customization_menu.xml b/res/menu/undoable_customization_menu.xml
new file mode 100644
index 0000000..35ee682
--- /dev/null
+++ b/res/menu/undoable_customization_menu.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2022 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.
+  ~
+  -->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:id="@+id/revert"
+        android:title="@string/reset"
+        android:showAsAction="always|withText"/>
+</menu>
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index 5882b0b..3d2919f 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Aktiveer asseblief lêers en media in instellings."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Aktiveer"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Maak My Foto\'s oop"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Sluitskerm"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Tuisskerm"</string>
+    <string name="reset" msgid="4945445169532850631">"Stel terug"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Is jy seker?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Dit sal alle veranderinge wat jy gemaak het, terugstel."</string>
 </resources>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 04e3574..60a485a 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"እባክዎ በቅንብሮች ውስጥ ፋይሎችን እና ሚዲያን ያንቁ።"</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"አንቃ"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"የእኔ ፎቶዎችን ክፈት"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"ማያ ገፅ ቁልፍ"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"መነሻ ማያ ገፅ"</string>
+    <string name="reset" msgid="4945445169532850631">"ዳግም አስጀምር"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"እርግጠኛ ነዎት?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"ይህ ሁሉንም ያደረጓቸውን ለውጦች ዳግም ያስጀምራቸዋል።"</string>
 </resources>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index fa76128..d2b6e1b 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"يُرجى تفعيل الملفات والوسائط في الإعدادات."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"تفعيل"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"فتح صوري"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"شاشة القفل"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"الشاشة الرئيسية"</string>
+    <string name="reset" msgid="4945445169532850631">"إعادة الضبط"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"هل أنت متأكد؟"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"سيؤدي هذا الإجراء إلى إعادة ضبط كل التغييرات التي أجريتها."</string>
 </resources>
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index 3fe46d0..136eaab 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"অনুগ্ৰহ কৰি ছেটিঙত ফাইল আৰু মিডিয়া সক্ষম কৰক।"</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"সক্ষম কৰক"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"My Photos খোলক"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"লক স্ক্ৰীন"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"গৃহ স্ক্ৰীন"</string>
+    <string name="reset" msgid="4945445169532850631">"ৰিছেট কৰক"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"আপুনি নিশ্চিতনে?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"এইটোৱে আপুনি কৰা আটাইবোৰ সালসলনি ৰিছেট কৰিব।"</string>
 </resources>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index 6614c85..3ab7f41 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Ayarlarda faylları və medianı aktiv edin."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Aktiv edin"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Fotolarımı açın"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Ekran kilidi"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Əsas ekran"</string>
+    <string name="reset" msgid="4945445169532850631">"Sıfırlayın"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Əminsiniz?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Bu, etdiyiniz bütün dəyişiklikləri sıfırlayacaq."</string>
 </resources>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index 90e006f..a005273 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Omogućite fajlove i medije u podešavanjima."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Omogući"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Otvori Moje slike"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Zaključani ekran"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Početni ekran"</string>
+    <string name="reset" msgid="4945445169532850631">"Resetuj"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Jeste li sigurni?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Ovim resetujete sve promene koje ste uneli."</string>
 </resources>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index 4e21600..7775699 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Уключыце файлы і мультымедыя ў наладах."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Уключыць"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Адкрыць \"Мае фота\""</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Экран блакіроўкі"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Галоўны экран"</string>
+    <string name="reset" msgid="4945445169532850631">"Скінуць"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Вы ўпэўненыя?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Будуць скінуты ўсе ўнесеныя вамі змяненні."</string>
 </resources>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index e8313b9..773bd1f 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Моля, активирайте разрешението за файлове и мултимедия от настройките."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Активиране"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Отваряне на „Моите снимки“"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Заключен екран"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Начален екран"</string>
+    <string name="reset" msgid="4945445169532850631">"Нулиране"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Наистина ли искате това?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Това действие ще нулира всички направени от вас промени."</string>
 </resources>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index 8524e59..c954c72 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"সেটিংসে ফাইল এবং মিডিয়ার সুবিধা চালু করুন।"</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"চালু করুন"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"আমার ফটো বিভাগ খুলুন"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"লক স্ক্রিন"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"হোম স্ক্রিন"</string>
+    <string name="reset" msgid="4945445169532850631">"রিসেট করুন"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"আপনি কি নিশ্চিত?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"এর ফলে আপনার করা সব পরিবর্তন রিসেট হয়ে যাবে।"</string>
 </resources>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index 8368b46..3432f9a 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Omogućite fajlove i medije u postavkama."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Omogući"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Otvori Moje fotografije"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Zaključavanje ekrana"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Početni ekran"</string>
+    <string name="reset" msgid="4945445169532850631">"Poništi"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Jeste li sigurni?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Ovim će se poništiti sve promjene koje ste izvršili."</string>
 </resources>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index f8510ff..802e09b 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Activa Fitxers i contingut multimèdia a la configuració."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Activa"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Obre les meves fotos"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Pantalla de bloqueig"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Pantalla d\'inici"</string>
+    <string name="reset" msgid="4945445169532850631">"Restableix"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Vols continuar?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Es restabliran tots els canvis que hagis fet."</string>
 </resources>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index b15bbd9..5635b89 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"V nastavení povolte soubory a média."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Povolit"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Otevřít Moje fotky"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Obrazovka uzamčení"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Plocha"</string>
+    <string name="reset" msgid="4945445169532850631">"Resetovat"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Jste si jisti?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Tímto resetujete všechny provedené změny."</string>
 </resources>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 8db34d6..a98c22a 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Aktivér adgang til filer og medier i indstillingerne."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Aktivér"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Åbn mine billeder"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Låseskærm"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Startskærm"</string>
+    <string name="reset" msgid="4945445169532850631">"Nulstil"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Er du sikker?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Dette vil nulstille alle dine ændringer."</string>
 </resources>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 97027a9..26059ce 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Bitte aktiviere „Dateien und Medien“ in den Einstellungen."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Aktivieren"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Meine Fotos öffnen"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Sperrbildschirm"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Startbildschirm"</string>
+    <string name="reset" msgid="4945445169532850631">"Zurücksetzen"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Bist du sicher?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Hierdurch werden alle vorgenommenen Änderungen rückgängig gemacht."</string>
 </resources>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 6ae2bea..931856d 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Ενεργοποιήστε την άδεια Αρχεία και μέσα στις ρυθμίσεις."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Ενεργοποίηση"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Άνοιγμα των φωτογραφιών μου"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Οθόνη κλειδώματος"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Αρχική οθόνη"</string>
+    <string name="reset" msgid="4945445169532850631">"Επαναφορά"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Είστε βέβαιοι;"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Αυτή η ενέργεια θα επαναφέρει όλες τις αλλαγές που κάνατε."</string>
 </resources>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index 796f7a9..42ef303 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Please enable files and media in settings."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Enable"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Open My photos"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Lock screen"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Home screen"</string>
+    <string name="reset" msgid="4945445169532850631">"Reset"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Are you sure?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"This will reset all changes that you made."</string>
 </resources>
diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml
index 796f7a9..799b6e4 100644
--- a/res/values-en-rCA/strings.xml
+++ b/res/values-en-rCA/strings.xml
@@ -111,4 +111,14 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Please enable files and media in settings."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Enable"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Open My photos"</string>
+    <!-- no translation found for lock_screen_tab (6672930765010407652) -->
+    <skip />
+    <!-- no translation found for home_screen_tab (1080445697837877526) -->
+    <skip />
+    <!-- no translation found for reset (4945445169532850631) -->
+    <skip />
+    <!-- no translation found for reset_confirmation_dialog_title (3006691785800178536) -->
+    <skip />
+    <!-- no translation found for reset_confirmation_dialog_message (4304013650135221616) -->
+    <skip />
 </resources>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 796f7a9..42ef303 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Please enable files and media in settings."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Enable"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Open My photos"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Lock screen"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Home screen"</string>
+    <string name="reset" msgid="4945445169532850631">"Reset"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Are you sure?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"This will reset all changes that you made."</string>
 </resources>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index 796f7a9..42ef303 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Please enable files and media in settings."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Enable"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Open My photos"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Lock screen"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Home screen"</string>
+    <string name="reset" msgid="4945445169532850631">"Reset"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Are you sure?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"This will reset all changes that you made."</string>
 </resources>
diff --git a/res/values-en-rXC/strings.xml b/res/values-en-rXC/strings.xml
index cefd818..7da2e4b 100644
--- a/res/values-en-rXC/strings.xml
+++ b/res/values-en-rXC/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‎‎‏‏‎‎‎‏‎‏‏‎‏‎‏‎‎‎‎‎‏‏‏‏‎‏‎‎‏‎‎‎‎‎‏‎‏‏‏‏‏‎‎‎‎‏‎‏‎‎‎‏‏‏‏‏‏‏‎‏‏‎‎Please enable files and media in settings.‎‏‎‎‏‎"</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‎‏‎‏‎‎‎‎‏‎‎‎‎‏‎‏‏‎‏‏‎‎‏‏‏‏‏‎‎‏‎‏‎‎‏‎‎‎‏‏‏‎‏‏‎‎‏‏‎‎‏‎‏‎‎‎Enable‎‏‎‎‏‎"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‎‏‏‏‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‎‏‏‎‏‏‎‎‏‎‏‏‏‎‎‏‏‎‎‏‏‏‎‏‎‏‏‎‏‏‏‎‎‏‎‏‏‏‎‏‎Open My Photos‎‏‎‎‏‎"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‎‎‏‏‎‏‏‎‎‎‎‎‎‏‏‎‎‏‎‏‏‎‏‎‎‎‏‎‎‏‏‎‎‏‏‏‎‎‎‏‎‎‏‎‎‎‎‏‏‏‎‎‏‎‎‎Lock screen‎‏‎‎‏‎"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‎‎‏‏‏‎‏‏‏‏‏‏‏‎‏‎‎‎‎‎‏‏‏‎‏‎‎‎‏‏‎‏‎‎‎‎‏‎‏‎‏‏‏‎‎‏‏‏‎‎‏‎‎‏‎‎‎‏‎‏‏‎‎Home screen‎‏‎‎‏‎"</string>
+    <string name="reset" msgid="4945445169532850631">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‏‎‏‎‎‎‎‏‏‏‎‎‎‎‎‎‎‎‏‎‏‏‎‏‏‏‏‏‏‎‏‎‎‎‏‎‏‏‎‏‎‏‎‏‎‎‎‏‏‏‎‎‎‏‏‏‎Reset‎‏‎‎‏‎"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‎‏‎‏‎‎‏‏‎‏‏‏‎‎‏‏‏‏‎‏‎‏‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‎‏‏‎‏‎‎‎‏‎‏‏‎‏‏‎‏‏‎‏‎‎‎‎Are you sure?‎‏‎‎‏‎"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‎‏‏‏‎‏‏‏‎‏‏‏‎‏‎‏‏‏‎‏‏‎‏‏‎‎‏‏‏‎‎‎‏‏‏‎‏‎‎‏‎‏‏‏‏‏‎‎‎‎‎‎‏‎‏‎‏‏‏‎‎‎‎‎This will reset all changes you made.‎‏‎‎‏‎"</string>
 </resources>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 8dca007..1cc7c21 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Habilita los archivos y el contenido multimedia en la configuración."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Habilitar"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Abrir Mis fotos"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Pantalla de bloqueo"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Pantalla principal"</string>
+    <string name="reset" msgid="4945445169532850631">"Restablecer"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"¿Confirmas esta acción?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Se restablecerán todos los cambios."</string>
 </resources>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index fdab109..822f315 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Ve a Ajustes y habilita Archivos y contenido multimedia."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Habilitar"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Abrir Mis fotos"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Pantalla de bloqueo"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Pantalla de inicio"</string>
+    <string name="reset" msgid="4945445169532850631">"Restablecer"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"¿Seguro que quieres hacerlo?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Se restablecerán todos los cambios que has hecho."</string>
 </resources>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index 28d52f4..4e60dc5 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Lubage seadetes failid ja meedia."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Luba"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Ava jaotis Minu fotod"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Lukustuskuva"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Avakuva"</string>
+    <string name="reset" msgid="4945445169532850631">"Lähtesta"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Kas olete kindel?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"See lähtestab kõik tehtud muudatused."</string>
 </resources>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index d03813d..bf5a9f9 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Gaitu fitxategiak eta multimedia-edukia Ezarpenak atalean."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Gaitu"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Ireki nire argazkiak"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Pantaila blokeatua"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Hasierako pantaila"</string>
+    <string name="reset" msgid="4945445169532850631">"Berrezarri"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Ziur zaude?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Egindako aldaketa guztiak berrezarriko dira."</string>
 </resources>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 37b7f8c..3efdf40 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"لطفاً فایل‌ها و رسانه‌ها را در تنظیمات فعال کنید."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"فعال کردن"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"باز کردن «عکس‌های من»"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"صفحه قفل"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"صفحه اصلی"</string>
+    <string name="reset" msgid="4945445169532850631">"بازنشانی"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"مطمئن هستید؟"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"با این کار، همه تغییرات ایجادشده بازنشانی خواهد شد."</string>
 </resources>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index b3e23e8..2467137 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Ota tiedostot ja media käyttöön asetuksista."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Ota käyttöön"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Avaa Omat kuvat"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Lukitusnäyttö"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Aloitusnäyttö"</string>
+    <string name="reset" msgid="4945445169532850631">"Nollaa"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Oletko varma?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Tämä nollaa kaikki tekemäsi muutokset."</string>
 </resources>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index ab17c8f..c4dbddf 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Veuillez activer les fichiers et les contenus multimédia dans les paramètres."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Activer"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Ouvrir mes photos"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Écran de verrouillage"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Écran d\'accueil"</string>
+    <string name="reset" msgid="4945445169532850631">"Réinitialiser"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Voulez-vous vraiment continuer?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Cette action réinitialisera toutes les modifications que vous avez apportées."</string>
 </resources>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index b7daa82..a3802c2 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Veuillez activer \"Fichiers et contenus multimédias\" dans les paramètres."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Activer"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Ouvrir mes photos"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Écran de verrouillage"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Écran d\'accueil"</string>
+    <string name="reset" msgid="4945445169532850631">"Réinitialiser"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Voulez-vous vraiment continuer ?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Cela réinitialisera toutes les modifications que vous avez effectuées."</string>
 </resources>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index 3d87796..fd4f05d 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Activa na configuración o uso de ficheiros e contido multimedia."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Activar"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Abrir As miñas fotos"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Pantalla de bloqueo"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Pantalla de inicio"</string>
+    <string name="reset" msgid="4945445169532850631">"Restablecer"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Seguro que queres continuar?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Restableceranse todos os cambios que fixeses."</string>
 </resources>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index b148a89..def244f 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"કૃપા કરીને સેટિંગમાં ફાઇલો અને મીડિયાનો ઍક્સેસ ચાલુ કરો."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"ચાલુ કરો"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"મારા ફોટા ખોલો"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"લૉક સ્ક્રીન"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"હોમ સ્ક્રીન"</string>
+    <string name="reset" msgid="4945445169532850631">"રીસેટ કરો"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"ખરેખર?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"તમે કરેલા બધા ફેરફારો, આનાથી રીસેટ થશે."</string>
 </resources>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 92a7df4..59133d1 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"कृपया सेटिंग में जाकर फ़ाइलों और मीडिया को ऐक्सेस करने की सुविधा चालू करें."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"चालू करें"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"मेरी फ़ोटो खोलो"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"लॉक स्क्रीन"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"होम स्क्रीन"</string>
+    <string name="reset" msgid="4945445169532850631">"रीसेट करें"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"क्या आपको बदलाव रीसेट करने हैं?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"ऐसा करने पर, आपके किए हुए सभी बदलाव रीसेट कर दिए जाएंगे."</string>
 </resources>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index b02717a..dd87a58 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"U postavkama omogućite datoteke i medije."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Omogući"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Otvori Moje fotografije"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Zaključan zaslon"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Početni zaslon"</string>
+    <string name="reset" msgid="4945445169532850631">"Poništi"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Jeste li sigurni?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Time će se poništiti sve promjene koje ste unijeli."</string>
 </resources>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index 12407f9..e942255 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Engedélyezze a fájlokat és a médiatartalmakat a beállításokban."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Engedélyezés"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Saját fotóim megnyitása"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Lezárási képernyő"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Kezdőképernyő"</string>
+    <string name="reset" msgid="4945445169532850631">"Alaphelyzet"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Biztos benne?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Ez visszaállítja az Ön által végzett összes módosítást."</string>
 </resources>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index 26af6e6..12e2bfe 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Թույլատրեք ֆայլերի և մեդիաֆայլերի օգտագործումը կարգավորումներում։"</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Թույլատրել"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Բացել «Իմ լուսանկարները»"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Կողպէկրան"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Հիմնական էկրան"</string>
+    <string name="reset" msgid="4945445169532850631">"Զրոյացնել"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Համոզվա՞ծ եք"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Ձեր կատարած բոլոր փոփոխությունները կզրոյացվեն։"</string>
 </resources>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 6d2e16c..3d00b19 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Aktifkan izin file dan media di setelan."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Aktifkan"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Buka Foto Saya"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Layar kunci"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Layar utama"</string>
+    <string name="reset" msgid="4945445169532850631">"Reset"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Yakin?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Tindakan ini akan mereset semua perubahan yang Anda lakukan."</string>
 </resources>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index 8b7094b..bfa6d9a 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Virkjaðu skrár og margmiðlunarefni í stillingum."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Virkja"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Opna myndirnar mínar"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Lásskjár"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Heimaskjár"</string>
+    <string name="reset" msgid="4945445169532850631">"Endurstilla"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Ertu viss?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Þetta mun endurstilla allar breytingarnar sem þú gerðir."</string>
 </resources>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 40a9a48..737d6b0 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Attiva l\'autorizzazione File e contenuti multimediali nelle impostazioni."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Attiva"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Apri Le mie foto"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Schermata di blocco"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Schermata Home"</string>
+    <string name="reset" msgid="4945445169532850631">"Reimposta"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Confermi?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Verranno reimpostate tutte le modifiche apportate."</string>
 </resources>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 88cb782..7fa7be0 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"עליך להפעיל את ההרשאה \'קבצים ומדיה\' בהגדרות."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"הפעלה"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"פתיחת \'התמונות שלי\'"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"מסך נעילה"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"מסך הבית"</string>
+    <string name="reset" msgid="4945445169532850631">"איפוס"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"להמשיך?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"הפעולה הזו תאפס את כל השינויים שביצעת."</string>
 </resources>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index bc77269..fdf14e1 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"ファイルとメディアへのアクセス権限を設定で有効にしてください。"</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"有効にする"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"マイフォトを開く"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"ロック画面"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"ホーム画面"</string>
+    <string name="reset" msgid="4945445169532850631">"リセット"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"本当によろしいですか?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"行った変更はすべてリセットされます。"</string>
 </resources>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index 1c7ab28..dcc0de2 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"ჩართეთ ფაილები და მედია პარამეტრებიდან."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"ჩართვა"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"ჩემი ფოტოების გახსნა"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"ჩაკეტილი ეკრანი"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"მთავარი ეკრანი"</string>
+    <string name="reset" msgid="4945445169532850631">"გადაყენება"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"დარწმუნებული ხართ?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"ამით გადაყენდება თქვენ მიერ განხორციელებული ყველა ცვლილება."</string>
 </resources>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index a5e0e4b..eb78e7b 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"\"Параметрлер\" бөлімінде \"Файлдар және мультимедиа\" рұқсатын қосыңыз."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Қосу"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Фотоларымды көру"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Экранды құлыптау"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Негізгі экран"</string>
+    <string name="reset" msgid="4945445169532850631">"Қайта орнату"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Сенімдісіз бе?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Жасалған барлық өзгеріс қайтарылады."</string>
 </resources>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index 68c9ccb..86c8d55 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"សូមបើកឯកសារ និង​មេឌៀនៅក្នុងការកំណត់។"</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"បើក"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"បើករូបថតរបស់ខ្ញុំ"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"អេក្រង់ចាក់សោ"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"អេក្រង់ដើម"</string>
+    <string name="reset" msgid="4945445169532850631">"កំណត់​ឡើងវិញ"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"តើអ្នកប្រាកដដែរទេ?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"ការធ្វើបែបនេះ​នឹងកំណត់​ការផ្លាស់ប្ដូរទាំងអស់​ដែលអ្នកបានធ្វើឡើងវិញ។"</string>
 </resources>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index ec59f17..91c92fc 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"ಸೆಟ್ಟಿಂಗ್‌ಗಳಲ್ಲಿ ಫೈಲ್‌ಗಳು ಮತ್ತು ಮಾಧ್ಯಮವನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಿ."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"ಸಕ್ರಿಯಗೊಳಿಸಿ"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"ನನ್ನ ಫೋಟೋಗಳನ್ನು ತೆರೆಯಿರಿ"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಮಾಡಿ"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"ಹೋಮ್ ಸ್ಕ್ರೀನ್"</string>
+    <string name="reset" msgid="4945445169532850631">"ರೀಸೆಟ್ ಮಾಡಿ"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"ರೀಸೆಟ್ ಮಾಡಲು ಬಯಸುತ್ತೀರಾ?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"ಇದು ನೀವು ಮಾಡಿದ ಎಲ್ಲಾ ಬದಲಾವಣೆಗಳನ್ನು ರೀಸೆಟ್ ಮಾಡುತ್ತದೆ."</string>
 </resources>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index 8b2cc38..6100cf4 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"설정에서 파일 및 미디어를 사용 설정하세요."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"사용"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"내 사진 열기"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"잠금 화면"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"홈 화면"</string>
+    <string name="reset" msgid="4945445169532850631">"초기화"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"계속하시겠습니까?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"이렇게 하면 모든 변경사항이 재설정됩니다."</string>
 </resources>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index a375d84..e3bb298 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Жөндөөлөрдөн файлдарды жана медианы иштетиңиз."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Иштетүү"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Сүрөттөрүмдү ачуу"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Экранды кулпулоо"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Башкы экран"</string>
+    <string name="reset" msgid="4945445169532850631">"Баштапкы абалга келтирүү"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Чын элеби?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Ушуну менен бардык өзгөртүүлөр баштапкы абалга келтирилет."</string>
 </resources>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index d7bd62f..82d5d5c 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"ກະລຸນາເປີດການນຳໃຊ້ໄຟລ໌ ແລະ ມີເດຍໃນການຕັ້ງຄ່າ"</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"ເປີດການນຳໃຊ້"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"ເປີດຮູບພາບຂອງຂ້ອຍ"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"ໜ້າຈໍລັອກ"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"ໂຮມສະກຣີນ"</string>
+    <string name="reset" msgid="4945445169532850631">"ຣີເຊັດ"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"ທ່ານແນ່ໃຈບໍ?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"ນີ້ຈະຣີເຊັດການປ່ຽນແປງທັງໝົດທີ່ທ່ານເຮັດ."</string>
 </resources>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 94c2be0..b4e4bd5 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Įgalinkite failus ir mediją nustatymuose."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Įgalinti"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Atidaryti skiltį „Mano nuotraukos“"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Užrakinimo ekranas"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Pagrindinis ekranas"</string>
+    <string name="reset" msgid="4945445169532850631">"Nust. iš n."</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Ar tikrai norite tai padaryti?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Bus iš naujo nustatyti visi atlikti pakeitimai."</string>
 </resources>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index dc36775..c39c931 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Lūdzu, iestatījumos iespējojiet atļauju izmantot failus un multivides saturu."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Iespējot"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Atvērt sadaļu “Mani fotoattēli”"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Bloķēšanas ekrāns"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Sākuma ekrāns"</string>
+    <string name="reset" msgid="4945445169532850631">"Atiestatīt"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Vai esat pārliecināts?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Tādējādi tiks atiestatītas visas jūsu veiktās izmaiņas."</string>
 </resources>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index 0c85413..727d149 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Овозможете „Датотеки и аудиовизуелни содржини“ во поставки."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Овозможи"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Отвори „Мои фотографии“"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Заклучување екран"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Почетен екран"</string>
+    <string name="reset" msgid="4945445169532850631">"Ресетирај"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Дали сте сигурни?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Ова ќе ги ресетира сите извршени промени."</string>
 </resources>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index 9dd8f11..b69d425 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"ക്രമീകരണത്തിൽ ഫയലുകളും മീഡിയയും പ്രവർത്തനക്ഷമമാക്കുക."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"പ്രവർത്തനക്ഷമമാക്കുക"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"എന്റെ ഫോട്ടോകൾ തുറക്കുക"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"സ്‌ക്രീൻ ലോക്ക് ചെയ്യുക"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"ഹോം സ്‌ക്രീൻ"</string>
+    <string name="reset" msgid="4945445169532850631">"റീസെറ്റ് ചെയ്യൂ"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"ഉറപ്പാണോ?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"നിങ്ങൾ വരുത്തിയ മാറ്റങ്ങളെല്ലാം ഇത് റീസെറ്റ് ചെയ്യും."</string>
 </resources>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index 8f702bc..87590b5 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Тохиргоо хэсгээс файл болон мeдиаг идэвхжүүлнэ үү."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Идэвхжүүлэх"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Миний Зургийг нээх"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Түгжигдсэн дэлгэц"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Үндсэн нүүр"</string>
+    <string name="reset" msgid="4945445169532850631">"Шинэчлэх"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Ta итгэлтэй байна уу?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Энэ нь таны хийсэн бүх өөрчлөлтийг шинэчилнэ."</string>
 </resources>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index a1853dc..7f4c6c5 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"कृपया सेटिंग्जमध्ये फाइल आणि मीडिया वापरण्याची ॲपला परवानगी द्या."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"परवानगी द्या"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"माझे फोटो उघडा"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"लॉक स्क्रीन"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"मुख्य स्क्रीन"</string>
+    <string name="reset" msgid="4945445169532850631">"रीसेट करा"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"तुमची खात्री आहे का?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"यामुळे तुम्ही केलेले सर्व बदल रीसेट होतील."</string>
 </resources>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index c498243..1cdccfa 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Sila dayakan fail dan media dalam tetapan."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Dayakan"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Buka Foto Saya"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Skrin kunci"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Skrin utama"</string>
+    <string name="reset" msgid="4945445169532850631">"Tetapkan semula"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Anda pasti?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Tindakan ini akan menetapkan semula semua perubahan yang telah dibuat."</string>
 </resources>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index f716ad8..a3e5f26 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"ဆက်တင်များတွင် Files နှင့် မီဒီယာကို ဖွင့်ပါ။"</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"ဖွင့်ရန်"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"ဓာတ်ပုံများဖွင့်ရန်"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"လော့ခ်မျက်နှာပြင်"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"ပင်မစာမျက်နှာ"</string>
+    <string name="reset" msgid="4945445169532850631">"ပြင်ဆင်ရန်"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"သေချာပါသလား။"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"၎င်းက သင့်အပြောင်းအလဲအားလုံးကို ပြင်ဆင်သတ်မှတ်မည်။"</string>
 </resources>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index f7cd1f2..9da3e42 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -44,7 +44,7 @@
     <string name="daily_refresh_tile_title" msgid="3270456074558525091">"Daglig bakgrunn"</string>
     <string name="daily_refresh_tile_subtitle" msgid="3976682014885446443">"Trykk for å slå på funksjonen"</string>
     <string name="start_rotation_dialog_body_live_wallpaper_needed" msgid="5132580257563846082">"Bakgrunnen endres automatisk hver dag. For å fullføre konfigureringen, trykk på &lt;strong&gt;Angi bakgrunn&lt;/strong&gt; på den neste skjermen."</string>
-    <string name="start_rotation_dialog_wifi_only_option_message" msgid="3126269859713666225">"Last ned nye bakgrunner bare via Wi-Fi"</string>
+    <string name="start_rotation_dialog_wifi_only_option_message" msgid="3126269859713666225">"Last ned nye bakgrunner bare via Wifi"</string>
     <string name="start_rotation_dialog_continue" msgid="276678987852274872">"Fortsett"</string>
     <string name="start_rotation_progress_message" msgid="7872623873682262083">"Laster ned den første bakgrunnen …"</string>
     <string name="start_rotation_error_message" msgid="3053799836719618972">"Kunne ikke laste ned den første bakgrunnen. Sjekk nettverksinnstillingene, og prøv igjen."</string>
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Slå på filer og medier i innstillingene."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Slå på"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Åpne Mine bilder"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Låseskjerm"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Startskjerm"</string>
+    <string name="reset" msgid="4945445169532850631">"Tilbakestill"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Er du sikker?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Dette tilbakestiller alle endringene du har gjort."</string>
 </resources>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index dc7499f..ced01b6 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"कृपया सेटिङहरूमा गई फाइल र मिडिया प्रयोग गर्न दिनुहोस्।"</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"अन गर्नुहोस्"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"\"मेरा फोटोहरू\" खोल्नुहोस्"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"स्क्रिन लक गर्नुहोस्"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"होम स्क्रिन"</string>
+    <string name="reset" msgid="4945445169532850631">"रिसेट गर्नुहोस्"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"तपाईंले यसो गर्न खोज्नुभएकै हो?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"तपाईंले यसो गर्नुभयो भने तपाईंले परिवर्तन गरेका सबै कुरा रिसेट हुने छन्।"</string>
 </resources>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index ced3ee4..bec1205 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Zet het recht voor bestanden en media aan in de instellingen."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Aanzetten"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Mijn foto\'s openen"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Vergrendelscherm"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Startscherm"</string>
+    <string name="reset" msgid="4945445169532850631">"Resetten"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Weet je het zeker?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Hiermee worden al je wijzigingen gereset."</string>
 </resources>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index fbca74a..ba538b9 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"ଦୟାକରି ସେଟିଂସରେ ଫାଇଲଗୁଡ଼ିକ ଏବଂ ମିଡିଆକୁ ସକ୍ଷମ କରନ୍ତୁ।"</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"ସକ୍ଷମ କରନ୍ତୁ"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"ମୋ ଫଟୋଗୁଡ଼ିକ ଖୋଲନ୍ତୁ"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"ଲକ ସ୍କ୍ରିନ"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"ହୋମ ସ୍କ୍ରିନ"</string>
+    <string name="reset" msgid="4945445169532850631">"ରିସେଟ କରନ୍ତୁ"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"ଆପଣ ନିଶ୍ଚିତ?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"ଏହା ଆପଣ କରିଥିବା ସମସ୍ତ ପରିବର୍ତ୍ତନକୁ ରିସେଟ କରିବ।"</string>
 </resources>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index 0ec041f..03c1ff7 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"ਕਿਰਪਾ ਕਰਕੇ ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਫ਼ਾਈਲਾਂ ਅਤੇ ਮੀਡੀਆ ਨੂੰ ਚਾਲੂ ਕਰੋ।"</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"ਚਾਲੂ ਕਰੋ"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"ਮੇਰੀਆਂ ਫ਼ੋਟੋਆਂ ਖੋਲ੍ਹੋ"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"ਲਾਕ ਸਕ੍ਰੀਨ"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"ਹੋਮ ਸਕ੍ਰੀਨ"</string>
+    <string name="reset" msgid="4945445169532850631">"ਰੀਸੈੱਟ ਕਰੋ"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"ਪੱਕਾ?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"ਇਸ ਨਾਲ ਤੁਹਾਡੇ ਵੱਲੋਂ ਕੀਤੀਆਂ ਸਾਰੀਆਂ ਤਬਦੀਲੀਆਂ ਰੀਸੈੱਟ ਹੋ ਜਾਣਗੀਆਂ।"</string>
 </resources>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 02e0430..c4e6f66 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"W ustawieniach włącz dostęp do plików i multimediów."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Włącz"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Otwórz Moje zdjęcia"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Ekran blokady"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Ekran główny"</string>
+    <string name="reset" msgid="4945445169532850631">"Resetuj"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Czy na pewno tego chcesz?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Spowoduje to cofnięcie wszystkich wprowadzonych zmian."</string>
 </resources>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 034ac26..75fa890 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Ative os ficheiros e multimédia nas definições."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Ativar"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Abrir As minhas fotos"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Ecrã de bloqueio"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Ecrã principal"</string>
+    <string name="reset" msgid="4945445169532850631">"Repor"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Tem a certeza?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Esta ação repõe todas as alterações que fez."</string>
 </resources>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 1756749..b02248e 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Ative a permissão de acesso a arquivos e mídia nas configurações."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Ativar"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Abrir minhas fotos"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Tela de bloqueio"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Tela inicial"</string>
+    <string name="reset" msgid="4945445169532850631">"Redefinir"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Tem certeza?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Essa ação redefine todas as mudanças feitas."</string>
 </resources>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 1ca9cb5..61d6b06 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Activează Fișiere și media în setări."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Activează"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Deschide Fotografiile mele"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Ecran de blocare"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Ecran de pornire"</string>
+    <string name="reset" msgid="4945445169532850631">"Resetează"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Confirmi?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Astfel, toate modificările pe care le-ai făcut vor fi resetate."</string>
 </resources>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 4890860..010fa89 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Разрешите доступ к файлам и медиаконтенту в настройках."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Разрешить"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Открыть \"Мои фото\""</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Заблокированный экран"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Главный экран"</string>
+    <string name="reset" msgid="4945445169532850631">"Сбросить"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Вы уверены?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Все внесенные изменения будут сброшены."</string>
 </resources>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index 6d4710b..923df35 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"කරුණාකර සැකසීම් තුළ ගොනු සහ මාධ්‍ය සබල කරන්න."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"සබල කරන්න"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"මගේ ඡායාරූප විවෘත කරන්න"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"අගුළු තිරය"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"මුල් තිරය"</string>
+    <string name="reset" msgid="4945445169532850631">"යළි සකසන්න"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"ඔබට විශ්වාස ද?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"මෙය ඔබ සිදු කළ සියලු වෙනස්කම් යළි සකසනු ඇත."</string>
 </resources>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index f644edf..264e772 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"V nastaveniach povoľte súbory a médiá."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Povoliť"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Otvoriť Moje fotky"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Uzamknutá obrazovka"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Plocha"</string>
+    <string name="reset" msgid="4945445169532850631">"Resetovať"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Naozaj to chcete urobiť?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Týmto resetujete všetky vykonané zmeny."</string>
 </resources>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index 7491e1c..516fd2b 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"V nastavitvah omogočite datoteke in predstavnost."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Omogoči"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Odpri Moje fotografije"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Zaklenjen zaslon"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Začetni zaslon"</string>
+    <string name="reset" msgid="4945445169532850631">"Ponastavi"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Ali ste prepričani?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"S tem boste ponastavili vse izvedene spremembe."</string>
 </resources>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index 6fc08df..78424cb 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Aktivizo skedarët dhe median te cilësimet."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Aktivizo"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Hap \"Fotografitë e mia\""</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Ekrani i kyçjes"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Ekrani bazë"</string>
+    <string name="reset" msgid="4945445169532850631">"Rivendos"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"E konfirmon?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Kjo do t\'i rivendosë të gjitha ndryshimet që bëre."</string>
 </resources>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index f2a1d96..c894c79 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Омогућите фајлове и медије у подешавањима."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Омогући"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Отвори Моје слике"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Закључани екран"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Почетни екран"</string>
+    <string name="reset" msgid="4945445169532850631">"Ресетуј"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Јесте ли сигурни?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Овим ресетујете све промене које сте унели."</string>
 </resources>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 1114bb6..6b6403e 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Aktivera Filer och media i inställningarna."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Aktivera"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Öppna Mina bilder"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Låsskärm"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Startskärm"</string>
+    <string name="reset" msgid="4945445169532850631">"Återställ"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Är du säker?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Detta återställer alla dina ändringar."</string>
 </resources>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 6f26e80..d6be66c 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Tafadhali washa faili na maudhui katika mipangilio."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Washa"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Fungua Picha Zangu"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Skrini iliyofungwa"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Skrini ya kwanza"</string>
+    <string name="reset" msgid="4945445169532850631">"Weka upya"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Una uhakika?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Hali hii itaweka upya mabadiliko yote uliyofanya."</string>
 </resources>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index 2111971..199e320 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"அமைப்புகளில் உள்ள ஃபைல்களையும் மீடியாவையும் இயக்கவும்."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"இயக்கு"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"எனது படங்களைத் திற"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"பூட்டுத் திரை"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"முகப்புத் திரை"</string>
+    <string name="reset" msgid="4945445169532850631">"மீட்டமை"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"நிச்சயமாக மீட்டமைக்க வேண்டுமா?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"இது நீங்கள் செய்த அனைத்து மாற்றங்களையும் மீட்டமைக்கும்."</string>
 </resources>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 96b14be..7fdb8c3 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -45,7 +45,7 @@
     <string name="daily_refresh_tile_subtitle" msgid="3976682014885446443">"ఆన్ చేయడానికి నొక్కండి"</string>
     <string name="start_rotation_dialog_body_live_wallpaper_needed" msgid="5132580257563846082">"వాల్‌పేపర్ ప్రతి రోజు ఆటోమేటిక్‌గా మారుతుంది. సెటప్‌ను ముగించడానికి, తదుపరి స్క్రీన్‌లో &lt;strong&gt;వాల్ పేపర్‌ను సెట్ చేయి&lt;/strong&gt;ని నొక్కండి."</string>
     <string name="start_rotation_dialog_wifi_only_option_message" msgid="3126269859713666225">"భవిష్యత్తులో వాల్‌పేపర్‌లను Wi-Fiలో ఉన్నప్పుడు మాత్రమే డౌన్‌లోడ్ చేయండి"</string>
-    <string name="start_rotation_dialog_continue" msgid="276678987852274872">"కొనసాగించు"</string>
+    <string name="start_rotation_dialog_continue" msgid="276678987852274872">"కొనసాగించండి"</string>
     <string name="start_rotation_progress_message" msgid="7872623873682262083">"మొదటి వాల్‌పేపర్‌ను డౌన్‌లోడ్ చేస్తోంది…"</string>
     <string name="start_rotation_error_message" msgid="3053799836719618972">"మొదటి వాల్‌పేపర్‌ను డౌన్‌లోడ్ చేయలేకపోయింది. దయచేసి మీ నెట్‌వర్క్ సెట్టింగ్‌లను చెక్ చేసి, ఆపై మళ్లీ ప్రయత్నించండి."</string>
     <string name="start_rotation_dialog_body" msgid="7903554799046364916">"వాల్‌పేపర్ ప్రతి రోజు ఆటోమేటిక్‌గా మారుతుంది"</string>
@@ -83,7 +83,7 @@
     <string name="tab_effects" msgid="3213606157589233901">"ఎఫెక్ట్‌లు"</string>
     <string name="my_photos" msgid="8613021349284084982">"నా ఫోటోలు"</string>
     <string name="configure_wallpaper" msgid="849882179182976621">"సెట్టింగ్‌లు…"</string>
-    <string name="delete_live_wallpaper" msgid="589212696102662329">"తొలగించు"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"తొలగించండి"</string>
     <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"మీ ఫోన్ నుండి ఈ వాల్‌పేపర్‌ను తొలగించాలా?"</string>
     <string name="bottom_action_bar_back" msgid="8237013112999946725">"వెనుకకు"</string>
     <string name="bottom_action_bar_edit" msgid="1214742990893082138">"ఎడిట్‌"</string>
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"దయచేసి సెట్టింగ్‌లలో ఫైల్‌లు, మీడియాను ఎనేబుల్ చేయండి."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"ఎనేబుల్ చేయండి"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"నా ఫోటోలను తెరవండి"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"లాక్ స్క్రీన్"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"మొదటి స్క్రీన్"</string>
+    <string name="reset" msgid="4945445169532850631">"రీసెట్ చేయి"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"మీరు రీసెట్ చేయాలనుకుంటున్నారా?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"ఇది మీరు చేసిన అన్ని మార్పులను రీసెట్ చేస్తుంది."</string>
 </resources>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 8b49f78..b7baded 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"โปรดเปิดใช้ไฟล์และสื่อในการตั้งค่า"</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"เปิดใช้"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"เปิดรูปภาพของฉัน"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"ล็อกหน้าจอ"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"หน้าจอหลัก"</string>
+    <string name="reset" msgid="4945445169532850631">"รีเซ็ต"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"คุณแน่ใจใช่ไหม"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"การดำเนินการนี้จะรีเซ็ตการเปลี่ยนแปลงทั้งหมดที่คุณทำ"</string>
 </resources>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index f6a19b7..b45da50 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -47,7 +47,7 @@
     <string name="start_rotation_dialog_wifi_only_option_message" msgid="3126269859713666225">"Mag-download lang ng mga wallpaper sa hinaharap gamit ang Wi-Fi"</string>
     <string name="start_rotation_dialog_continue" msgid="276678987852274872">"Magpatuloy"</string>
     <string name="start_rotation_progress_message" msgid="7872623873682262083">"Dina-download ang unang wallpaper…"</string>
-    <string name="start_rotation_error_message" msgid="3053799836719618972">"Hindi ma-download ang unang wallpaper. Pakisuri ang mga setting ng iyong network at subukang muli."</string>
+    <string name="start_rotation_error_message" msgid="3053799836719618972">"Hindi ma-download ang unang wallpaper. Pakisuri ang mga setting ng iyong network at subukan ulit."</string>
     <string name="start_rotation_dialog_body" msgid="7903554799046364916">"Awtomatikong magpapalit ang wallpaper araw-araw"</string>
     <string name="settings_button_label" msgid="8724734130079207955">"Mga Setting"</string>
     <string name="explore" msgid="7468719504199497281">"I-explore"</string>
@@ -55,7 +55,7 @@
     <string name="wallpaper_disabled_message" msgid="7309484130562148185">"Naka-disable sa device na ito ang pagtatakda ng wallpaper"</string>
     <string name="wallpaper_disabled_by_administrator_message" msgid="1551430406714747884">"Na-disable ng administrator ng iyong device ang pagtatakda ng wallpaper"</string>
     <string name="wallpaper_set_successfully_message" msgid="2958998799111688578">"Matagumpay na naitakda ang wallpaper"</string>
-    <string name="wallpapers_unavailable_offline_message" msgid="8136405438621689532">"Kailangan mo ng koneksyon sa Internet para tingnan ang mga wallpaper. Kumonekta at subukang muli."</string>
+    <string name="wallpapers_unavailable_offline_message" msgid="8136405438621689532">"Kailangan mo ng koneksyon sa Internet para tingnan ang mga wallpaper. Kumonekta at subukan ulit."</string>
     <string name="currently_set_home_wallpaper_thumbnail" msgid="4022381436821898917">"Thumbnail ng kasalukuyang nakatakdang wallpaper ng home screen"</string>
     <string name="currently_set_lock_wallpaper_thumbnail" msgid="2094209303934569997">"Thumbnail ng kasalukuyang nakatakdang wallpaper ng lock screen"</string>
     <string name="currently_set_wallpaper_thumbnail" msgid="8651887838745545107">"Thumbnail ng kasalukuyang nakatakdang wallpaper"</string>
@@ -65,7 +65,7 @@
     <string name="refresh_daily_wallpaper_home_content_description" msgid="2770445044556164259">"I-refresh ang pang-araw-araw na wallpaper ng home screen"</string>
     <string name="refresh_daily_wallpaper_content_description" msgid="4362142658237147583">"I-refresh ang pang-araw-araw na wallpaper"</string>
     <string name="refreshing_daily_wallpaper_dialog_message" msgid="1975910873362855761">"Nire-refresh ang pang-araw-araw na wallpaper…"</string>
-    <string name="refresh_daily_wallpaper_failed_message" msgid="4749879993812557166">"Hindi na-refresh ang pang-araw-araw na wallpaper. Pakitingnan ang iyong koneksyon sa network at subukang muli."</string>
+    <string name="refresh_daily_wallpaper_failed_message" msgid="4749879993812557166">"Hindi na-refresh ang pang-araw-araw na wallpaper. Pakitingnan ang iyong koneksyon sa network at subukan ulit."</string>
     <string name="on_device_wallpapers_category_title" msgid="805819102071369004">"Mga nasa device na wallpaper"</string>
     <string name="on_device_wallpapers_category_title_desktop" msgid="316919420410065369">"Nasa device"</string>
     <string name="on_device_wallpaper_title" msgid="5262564748034629524">"Wallpaper ng Android"</string>
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Paki-enable ang mga file at media sa mga setting."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"I-enable"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Buksan ang Mga Larawan Ko"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"I-lock ang screen"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Home screen"</string>
+    <string name="reset" msgid="4945445169532850631">"I-reset"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Sigurado ka ba?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Ire-reset nito ang lahat ng pagbabagong ginawa mo."</string>
 </resources>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index a5d5482..f4fc63e 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Lütfen ayarlarda dosyalar ve medya seçeneğini etkinleştirin."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Etkinleştir"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Fotoğraflarım\'ı aç"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Kilit ekranı"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Ana ekran"</string>
+    <string name="reset" msgid="4945445169532850631">"Sıfırla"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Emin misiniz?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Bu işlemle, yaptığınız tüm değişiklikler sıfırlanacak."</string>
 </resources>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 628b244..f8f148c 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Дозвольте використання файлів і мультимедіа в налаштуваннях."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Дозволити"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Відкрити мої фото"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Заблокований екран"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Головний екран"</string>
+    <string name="reset" msgid="4945445169532850631">"Скинути"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Продовжити?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Усі внесені зміни буде скасовано."</string>
 </resources>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index 38450be..8f212f7 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"براہ کرم ترتیبات میں فائلز اور میڈیا فعال کریں۔"</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"فعال کریں"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"میری تصاویر کھولیں"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"مقفل اسکرین"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"ہوم اسکرین"</string>
+    <string name="reset" msgid="4945445169532850631">"ری سیٹ کریں"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"آپ پُر یقین ہیں؟"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"یہ آپ کی کی گئی تمام تبدیلیوں کو ری سیٹ کر دے گا۔"</string>
 </resources>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index f9f9f3d..701b5c7 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Fayllar va mediani sozlamalar orqali yoqing."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Yoqish"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Suratlarimni ochish"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Ekran qulfi"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Bosh ekran"</string>
+    <string name="reset" msgid="4945445169532850631">"Yangilash"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Ishonchingiz komilmi?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Barcha oʻzgarishlarni asliga tiklaydi."</string>
 </resources>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index a45f44a..4899bbf 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Vui lòng bật quyền truy cập tệp và nội dung nghe nhìn trong phần cài đặt."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Bật"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Mở Ảnh của tôi"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Màn hình khoá"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Màn hình chính"</string>
+    <string name="reset" msgid="4945445169532850631">"Đặt lại"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Bạn có chắc chắn không?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Thao tác này sẽ đặt lại tất cả thay đổi bạn đã thực hiện."</string>
 </resources>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 91a9f23..79ae629 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"请在设置中启用文件和媒体。"</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"启用"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"打开“我的照片”"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"锁定屏幕"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"主屏幕"</string>
+    <string name="reset" msgid="4945445169532850631">"重置"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"确定吗?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"此操作将重置您做出的所有更改。"</string>
 </resources>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index 6e1fffd..4178489 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"請在設定中啟用檔案和媒體。"</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"啟用"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"開啟「我的相片」"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"上鎖畫面"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"主畫面"</string>
+    <string name="reset" msgid="4945445169532850631">"重設"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"您確定嗎?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"此操作將重設您所作的所有變更。"</string>
 </resources>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 2c86c5a..3015dd9 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"請前往設定啟用「檔案和媒體」權限。"</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"啟用"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"開啟我的相片"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"螢幕鎖定畫面"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"主畫面"</string>
+    <string name="reset" msgid="4945445169532850631">"重設"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"確定要這麼做嗎?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"這麼做會重設你進行的所有變更。"</string>
 </resources>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index adbdf03..27554a4 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -111,4 +111,9 @@
     <string name="settings_snackbar_description" msgid="890168814524778486">"Sicela unike amafayela nemidiya amandla kumasethingi."</string>
     <string name="settings_snackbar_enable" msgid="5992112808061426068">"Nika amandla"</string>
     <string name="open_my_photos" msgid="4107196465713868381">"Vula Izithombe Zami"</string>
+    <string name="lock_screen_tab" msgid="6672930765010407652">"Khiya isikrini"</string>
+    <string name="home_screen_tab" msgid="1080445697837877526">"Isikrini sasekhaya"</string>
+    <string name="reset" msgid="4945445169532850631">"Setha kabusha"</string>
+    <string name="reset_confirmation_dialog_title" msgid="3006691785800178536">"Uqinisekile?"</string>
+    <string name="reset_confirmation_dialog_message" msgid="4304013650135221616">"Lokhu kuzosetha kabusha zonke izinhlobo ozenzile."</string>
 </resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index ea3aaee..c6dc8ba 100755
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -42,7 +42,15 @@
     <color name="toolbar_color">@color/settingslib_colorSurfaceHeader</color>
     <color name="myphoto_background_color">@color/white</color>
     <color name="myphoto_background_cloud">@color/google_grey200</color>
+    <color name="preview_toolbar_text_light">@android:color/system_neutral1_50</color>
+    <color name="preview_toolbar_text_dark">@android:color/system_neutral1_900</color>
+
     <color name="color_accent_primary">@android:color/system_accent1_100</color>
     <color name="color_accent_primary_variant">@android:color/system_accent1_600</color>
-
+    <color name="color_accent_secondary">@color/settingslib_accent_secondary_device_default</color>
+    <color name="color_surface">@color/settingslib_colorSurface</color>
+    <color name="color_surface_variant">@color/settingslib_colorSurfaceVariant</color>
+    <color name="text_color_primary">@color/settingslib_text_color_primary</color>
+    <color name="text_color_secondary">@color/settingslib_text_color_secondary</color>
+    <color name="text_color_secondary_inverse">@color/settingslib_text_color_secondary_device_default</color>
 </resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 023aba4..7c27bd9 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -151,7 +151,7 @@
     <dimen name="full_preview_page_default_padding_top">24dp</dimen>
     <dimen name="full_preview_page_default_padding_bottom">32dp</dimen>
     <dimen name="full_preview_page_default_horizontal_padding">24dp</dimen>
-    <dimen name="full_preview_page_tabs_horizontal_padding">20dp</dimen>
+    <dimen name="full_preview_page_tabs_horizontal_padding">24dp</dimen>
 
     <!-- Dimensions for the bottom bar. -->
     <dimen name="bottom_actions_height">84dp</dimen>
@@ -238,4 +238,42 @@
     <dimen name="wallpaper_effect_failed_container_margin_top">24dp</dimen>
     <dimen name="wallpaper_effect_failed_button_size">16sp</dimen>
 
+    <!-- Clipping of the home screen overlay -->
+    <dimen name="home_screen_overlay_top_clipping">100dp</dimen>
+    <dimen name="home_screen_overlay_bottom_clipping">40dp</dimen>
+
+    <!-- Set wallpaper button -->
+    <dimen name="set_wallpaper_button_horizontal_padding">18dp</dimen>
+    <dimen name="set_wallpaper_button_vertical_padding">10dp</dimen>
+    <dimen name="set_wallpaper_button_corner_radius">20dp</dimen>
+    <dimen name="set_wallpaper_button_margin_end">16dp</dimen>
+
+    <!-- Wallpaper control button group -->
+    <dimen name="wallpaper_control_button_group_divider_space">16dp</dimen>
+    <dimen name="wallpaper_control_button_group_margin_top">24dp</dimen>
+    <dimen name="wallpaper_control_button_group_margin_top_when_no_set_wallpaper_button">16dp
+    </dimen>
+    <dimen name="wallpaper_control_button_group_margin_end">16dp</dimen>
+    <dimen name="wallpaper_control_button_padding">12dp</dimen>
+    <dimen name="wallpaper_control_button_size">48dp</dimen>
+    <dimen name="wallpaper_control_icon_size">24dp</dimen>
+    <item name="wallpaper_control_icon_viewport_size" format="float" type="dimen">24</item>
+
+    <!-- Duo tabs -->
+    <dimen name="duo_tabs_button_corner_radius">12dp</dimen>
+    <dimen name="duo_tabs_divider_space">8dp</dimen>
+
+    <!-- Floating Sheet -->
+    <dimen name="floating_sheet_corner_radius">12dp</dimen>
+    <dimen name="floating_sheet_margin">24dp</dimen>
+
+    <!-- Dimensions for "Wallpaper category headers" -->
+    <dimen name="wallpaper_header_text_size">16sp</dimen>
+    <dimen name="wallpaper_header_line_height">16dp</dimen>
+
+    <!--
+    Height of the screen preview. The width will be determined based on the aspect ratio of the
+    display.
+    -->
+    <dimen name="screen_preview_height">380dp</dimen>
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index c238961..e5b104f 100755
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -399,4 +399,49 @@
 
     <!-- Label for a button which lets user try to open their photos. [CHAR LIMIT=40] -->
     <string name="open_my_photos">Open My Photos</string>
+
+    <!--
+    Name of the authority corresponding to a ContentProvider in system UI that can provide remote
+    rendering of a preview of the lock screen.
+    -->
+    <string name="lock_screen_preview_provider_authority" translatable="false">com.android.systemui.customization</string>
+
+    <!--
+    Label for a navigational tab/button that takes the user to a UI that lets them customize the
+    lock screen.
+
+    [CHAR LIMIT=24].
+    -->
+    <string name="lock_screen_tab">Lock screen</string>
+
+    <!--
+    Label for a navigational tab/button that takes the user to a UI that lets them customize the
+    home screen.
+
+    [CHAR LIMIT=24].
+    -->
+    <string name="home_screen_tab">Home screen</string>
+
+    <!--
+    Label for button that lets the user reset the changes they have made.
+
+    [CHAR LIMIT=12].
+    -->
+    <string name="reset">Reset</string>
+
+    <!--
+    Title for dialog confirming with the user that they really wish to reset the customizations they
+    applied to the home and lock screens.
+
+    [CHAR LIMIT=32].
+    -->
+    <string name="reset_confirmation_dialog_title">Are you sure?</string>
+
+    <!--
+    Message for dialog confirming with the user that they really wish to reset the customizations
+    they applied to the home and lock screens.
+
+    [CHAR LIMIT=128].
+    -->
+    <string name="reset_confirmation_dialog_message">This will reset all changes you made.</string>
 </resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 2eb7417..b48fba0 100755
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -127,6 +127,9 @@
     <!-- Preview attribution pane styles -->
     <style name="WallpaperPicker.BottomPaneStyle" parent="@android:style/Theme.DeviceDefault.Settings"/>
 
+    <style name="WallpaperPicker.FloatingPaneStyle"
+        parent="@android:style/Theme.DeviceDefault.Settings" />
+
     <!-- Preview customization pane styles -->
     <style name="WallpaperPicker.CustomizationPaneStyle" parent="@android:style/Theme.DeviceDefault.Settings">
         <item name="tabTextAppearance">@style/WallpaperPicker.Preview.TextAppearance.NoAllCaps</item>
diff --git a/robolectric_tests/Android.mk b/robolectric_tests/Android.mk
deleted file mode 100644
index 666c08d..0000000
--- a/robolectric_tests/Android.mk
+++ /dev/null
@@ -1,59 +0,0 @@
-# Copyright (C) 2019 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.
-
-#############################################
-# WallPaperPicker2 Robolectric test target. #
-#############################################
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := WallPaperPicker2RoboTests
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_SDK_VERSION := current
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    androidx.test.runner \
-    androidx.test.rules \
-    mockito-robolectric-prebuilt \
-    truth-prebuilt
-LOCAL_JAVA_LIBRARIES := \
-    platform-robolectric-3.6.2-prebuilt
-
-LOCAL_JAVA_RESOURCE_DIRS := config
-
-LOCAL_INSTRUMENTATION_FOR := WallpaperPicker2
-LOCAL_MODULE_TAGS := optional
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-############################################
-# Target to run the previous target.       #
-############################################
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := RunWallPaperPicker2RoboTests
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_SDK_VERSION := current
-LOCAL_JAVA_LIBRARIES := \
-    WallPaperPicker2RoboTests
-
-LOCAL_TEST_PACKAGE := WallpaperPicker2
-
-LOCAL_INSTRUMENT_SOURCE_DIRS := packages/apps/WallpaperPicker2/src \
-
-LOCAL_ROBOTEST_TIMEOUT := 36000
-
-include prebuilts/misc/common/robolectric/3.6.2/run_robotests.mk
diff --git a/robolectric_tests/config/robolectric.properties b/robolectric_tests/config/robolectric.properties
deleted file mode 100644
index c222b11..0000000
--- a/robolectric_tests/config/robolectric.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-manifest=packages/apps/WallpaperPicker2/AndroidManifest.xml
-sdk=27
diff --git a/robolectric_tests/robolectric_gradle_config/robolectric.properties b/robolectric_tests/robolectric_gradle_config/robolectric.properties
deleted file mode 100644
index 2916ca9..0000000
--- a/robolectric_tests/robolectric_gradle_config/robolectric.properties
+++ /dev/null
@@ -1,3 +0,0 @@
-# Do not include the manifest definition in this version
-# as it is specified directly in the gradle file
-sdk=31
\ No newline at end of file
diff --git a/src/com/android/wallpaper/asset/Asset.java b/src/com/android/wallpaper/asset/Asset.java
index d2beb93..122c379 100755
--- a/src/com/android/wallpaper/asset/Asset.java
+++ b/src/com/android/wallpaper/asset/Asset.java
@@ -288,8 +288,11 @@
      * @param imageView        ImageView which is the target view of this asset.
      * @param placeholderColor Color of placeholder set to ImageView while waiting for image to
      *                         load.
+     * @param offsetToStart    true to let the preview show from the start of the image, false to
+     *                         center-aligned to the image.
      */
-    public void loadPreviewImage(Activity activity, ImageView imageView, int placeholderColor) {
+    public void loadPreviewImage(Activity activity, ImageView imageView, int placeholderColor,
+            boolean offsetToStart) {
         boolean needsTransition = imageView.getDrawable() == null;
         Drawable placeholderDrawable = new ColorDrawable(placeholderColor);
         if (needsTransition) {
@@ -306,7 +309,9 @@
             Point screenSize = ScreenSizeCalculator.getInstance().getScreenSize(defaultDisplay);
             Rect visibleRawWallpaperRect =
                     WallpaperCropUtils.calculateVisibleRect(dimensions, screenSize);
-            adjustCropRect(activity, dimensions, visibleRawWallpaperRect);
+
+            // TODO(b/264234793): Make offsetToStart general support or for the specific asset.
+            adjustCropRect(activity, dimensions, visibleRawWallpaperRect, offsetToStart);
 
             BitmapCropper bitmapCropper = InjectorProvider.getInjector().getBitmapCropper();
             bitmapCropper.cropAndScaleBitmap(this, /* scale= */ 1f, visibleRawWallpaperRect,
@@ -378,7 +383,8 @@
         void onDrawableLoaded();
     }
 
-    protected void adjustCropRect(Context context, Point assetDimensions, Rect cropRect) {
+    protected void adjustCropRect(Context context, Point assetDimensions, Rect cropRect,
+            boolean offsetToStart) {
         WallpaperCropUtils.adjustCropRect(context, cropRect, true /* zoomIn */);
     }
 
diff --git a/src/com/android/wallpaper/asset/BitmapCachingAsset.java b/src/com/android/wallpaper/asset/BitmapCachingAsset.java
index bc38a9e..cb37ee5 100644
--- a/src/com/android/wallpaper/asset/BitmapCachingAsset.java
+++ b/src/com/android/wallpaper/asset/BitmapCachingAsset.java
@@ -147,8 +147,9 @@
     }
 
     @Override
-    public void loadPreviewImage(Activity activity, ImageView imageView, int placeholderColor) {
+    public void loadPreviewImage(Activity activity, ImageView imageView, int placeholderColor,
+            boolean offsetToStart) {
         // Honor the original Asset's preview image loading
-        mOriginalAsset.loadPreviewImage(activity, imageView, placeholderColor);
+        mOriginalAsset.loadPreviewImage(activity, imageView, placeholderColor, offsetToStart);
     }
 }
diff --git a/src/com/android/wallpaper/asset/CurrentWallpaperAssetVN.java b/src/com/android/wallpaper/asset/CurrentWallpaperAssetVN.java
index 8f30cae..45f56eb 100755
--- a/src/com/android/wallpaper/asset/CurrentWallpaperAssetVN.java
+++ b/src/com/android/wallpaper/asset/CurrentWallpaperAssetVN.java
@@ -138,8 +138,11 @@
     }
 
     @Override
-    protected void adjustCropRect(Context context, Point assetDimensions, Rect cropRect) {
-        cropRect.offsetTo(0, 0);
+    protected void adjustCropRect(Context context, Point assetDimensions, Rect cropRect,
+            boolean offsetToStart) {
+        if (offsetToStart) {
+            cropRect.offsetTo(0, 0);
+        }
         WallpaperCropUtils.adjustCurrentWallpaperCropRect(context, assetDimensions, cropRect);
     }
 
diff --git a/src/com/android/wallpaper/config/BaseFlags.java b/src/com/android/wallpaper/config/BaseFlags.java
deleted file mode 100644
index 156fe35..0000000
--- a/src/com/android/wallpaper/config/BaseFlags.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.wallpaper.config;
-
-abstract class BaseFlags {
-    public static boolean skipDailyWallpaperButtonEnabled = true;
-    public static boolean desktopUiEnabled = false;
-    public static boolean dynamicStartRotationTileEnabled = true;
-    public static boolean stagingBackdropContentEnabled = false;
-    public static boolean performanceMonitoringEnabled = true;
-    public static boolean enableWallpaperEffect = false;
-}
diff --git a/src/com/android/wallpaper/config/BaseFlags.kt b/src/com/android/wallpaper/config/BaseFlags.kt
new file mode 100644
index 0000000..37f3d6e
--- /dev/null
+++ b/src/com/android/wallpaper/config/BaseFlags.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 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.config
+
+import android.content.Context
+import android.os.SystemProperties
+import com.android.systemui.shared.customization.data.content.CustomizationProviderClientImpl
+import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.runBlocking
+
+abstract class BaseFlags {
+    open fun isStagingBackdropContentEnabled() = false
+    open fun isEnableWallpaperEffect() = false
+    fun isMonochromaticFlagEnabled() =
+        SystemProperties.getBoolean("persist.sysui.monochromatic", false)
+    open fun isEnableEffectOnMultiplePanel() = false
+    open fun isFullscreenWallpaperPreview() = false
+    fun isUseRevampedUi(context: Context): Boolean {
+        return runBlocking { CustomizationProviderClientImpl(context, Dispatchers.IO).queryFlags() }
+            .firstOrNull { flag ->
+                flag.name == Contract.FlagsTable.FLAG_NAME_REVAMPED_WALLPAPER_UI
+            }
+            ?.value == true
+    }
+}
diff --git a/src/com/android/wallpaper/effects/EffectsController.java b/src/com/android/wallpaper/effects/EffectsController.java
index c0d3d56..8c74571 100644
--- a/src/com/android/wallpaper/effects/EffectsController.java
+++ b/src/com/android/wallpaper/effects/EffectsController.java
@@ -22,11 +22,18 @@
  * Utility class to provide methods to generate effects for the wallpaper.
  */
 public abstract class EffectsController {
+    public static final int RESULT_ORIGINAL_UNKNOWN = -1;
     public static final int RESULT_SUCCESS = 0;
     public static final int RESULT_ERROR_TRY_ANOTHER_PHOTO = 1;
     public static final int RESULT_ERROR_TRY_AGAIN_LATER = 2;
     public static final int RESULT_ERROR_CONTINUE = 4;
+    public static final int RESULT_ERROR_DEFAULT =
+            RESULT_ERROR_TRY_ANOTHER_PHOTO + RESULT_ERROR_CONTINUE;
     public static final int RESULT_ERROR_DISCONNECT_NO_BUTTON = 8;
+    public static final int RESULT_PROBE_SUCCESS = 16;
+    public static final int RESULT_PROBE_ERROR = 32;
+    public static final int RESULT_SUCCESS_REUSED = 64;
+    public static final int RESULT_SUCCESS_WITH_GENERATION_ERROR = 128;
     /**
      * Interface of the Effect enum.
      */
@@ -78,9 +85,10 @@
          * @param effect The effect that was generated.
          * @param bundle The data that the Service might have sent to the picker.
          * @param error The error code. if there's an error, value is greater than zero.
+         * @param originalStatusCode The original status code used for metrics logging.
          * @param errorMessage The error message.
          */
         void onEffectFinished(EffectEnumInterface effect, Bundle bundle, int error,
-                String errorMessage);
+                int originalStatusCode, String errorMessage);
     }
 }
diff --git a/src/com/android/wallpaper/model/CustomizationSectionController.java b/src/com/android/wallpaper/model/CustomizationSectionController.java
index b01f098..58ef178 100644
--- a/src/com/android/wallpaper/model/CustomizationSectionController.java
+++ b/src/com/android/wallpaper/model/CustomizationSectionController.java
@@ -34,6 +34,9 @@
     interface CustomizationSectionNavigationController {
         /** Navigates to the given {@code fragment}. */
         void navigateTo(Fragment fragment);
+
+        /** Navigates to a {@code fragment} that maps to the given destination ID. */
+        void navigateTo(String destinationId);
     }
 
     /** Returns {@code true} if the customization section is available. */
@@ -42,6 +45,16 @@
     /**
      * Returns a newly created {@link SectionView} for the section.
      *
+     * @param context The {@link Context} to inflate view.
+     * @param isOnLockScreen Whether we are on the lock screen.
+     */
+    default T createView(Context context, boolean isOnLockScreen) {
+        return createView(context);
+    }
+
+    /**
+     * Returns a newly created {@link SectionView} for the section.
+     *
      * @param context the {@link Context} to inflate view
      */
     T createView(Context context);
@@ -54,4 +67,7 @@
 
     /** Gets called when the section gets transitioned out. */
     default void onTransitionOut() {}
+
+    /** Notifies when the screen was switched. */
+    default void onScreenSwitched(boolean isOnLockScreen) {}
 }
diff --git a/src/com/android/wallpaper/model/DefaultWallpaperInfo.java b/src/com/android/wallpaper/model/DefaultWallpaperInfo.java
index 6ed0eeb..a3f00f4 100755
--- a/src/com/android/wallpaper/model/DefaultWallpaperInfo.java
+++ b/src/com/android/wallpaper/model/DefaultWallpaperInfo.java
@@ -49,7 +49,7 @@
 
     public DefaultWallpaperInfo() {}
 
-    private DefaultWallpaperInfo(Parcel in) {
+    protected DefaultWallpaperInfo(Parcel in) {
         super(in);
     }
 
diff --git a/src/com/android/wallpaper/model/LiveWallpaperInfo.java b/src/com/android/wallpaper/model/LiveWallpaperInfo.java
index a952ed5..47bf89a 100755
--- a/src/com/android/wallpaper/model/LiveWallpaperInfo.java
+++ b/src/com/android/wallpaper/model/LiveWallpaperInfo.java
@@ -141,7 +141,7 @@
 
     protected android.app.WallpaperInfo mInfo;
     protected LiveWallpaperThumbAsset mThumbAsset;
-    private boolean mVisibleTitle;
+    protected boolean mVisibleTitle;
     @Nullable private final String mCollectionId;
 
     /**
diff --git a/src/com/android/wallpaper/model/PartnerWallpaperInfo.java b/src/com/android/wallpaper/model/PartnerWallpaperInfo.java
index 908fb5d..285a84b 100755
--- a/src/com/android/wallpaper/model/PartnerWallpaperInfo.java
+++ b/src/com/android/wallpaper/model/PartnerWallpaperInfo.java
@@ -34,7 +34,7 @@
 /**
  * Represents a wallpaper from the "partner customization" APK installed on the system.
  */
-public class PartnerWallpaperInfo extends WallpaperInfo {
+public class PartnerWallpaperInfo extends DefaultWallpaperInfo {
     public static final Creator<PartnerWallpaperInfo> CREATOR =
             new Creator<PartnerWallpaperInfo>() {
                 @Override
@@ -80,7 +80,7 @@
             return wallpaperInfos;
         }
 
-        final int resId = partnerRes.getIdentifier(PartnerProvider.WALLPAPER_RES_ID, "array",
+        final int resId = partnerRes.getIdentifier(PartnerProvider.LEGACY_WALLPAPER_RES_ID, "array",
                 packageName);
         // Certain partner configurations don't have wallpapers provided, so need to check; return
         // early if they are missing.
@@ -143,6 +143,11 @@
     }
 
     @Override
+    public String getWallpaperId() {
+        return "" + mFullRes;
+    }
+
+    @Override
     public void showPreview(Activity srcActivity, InlinePreviewIntentFactory factory,
                             int requestCode) {
         srcActivity.startActivityForResult(factory.newIntent(srcActivity, this), requestCode);
diff --git a/src/com/android/wallpaper/model/WallpaperColorsViewModel.kt b/src/com/android/wallpaper/model/WallpaperColorsViewModel.kt
index cecadb1..3d5b771 100644
--- a/src/com/android/wallpaper/model/WallpaperColorsViewModel.kt
+++ b/src/com/android/wallpaper/model/WallpaperColorsViewModel.kt
@@ -19,22 +19,16 @@
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.ViewModel
 
-/**
- * ViewModel class to keep track of WallpaperColors for the current wallpaper
- */
+/** ViewModel class to keep track of WallpaperColors for the current wallpaper */
 class WallpaperColorsViewModel : ViewModel() {
 
-    /**
-     * WallpaperColors for the currently set home wallpaper
-     */
+    /** WallpaperColors for the currently set home wallpaper */
     val homeWallpaperColors: MutableLiveData<WallpaperColors> by lazy {
         MutableLiveData<WallpaperColors>()
     }
 
-    /**
-     * WallpaperColors for the currently set lock wallpaper
-     */
+    /** WallpaperColors for the currently set lock wallpaper */
     val lockWallpaperColors: MutableLiveData<WallpaperColors> by lazy {
         MutableLiveData<WallpaperColors>()
     }
-}
\ No newline at end of file
+}
diff --git a/src/com/android/wallpaper/model/WallpaperInfo.java b/src/com/android/wallpaper/model/WallpaperInfo.java
index 28c2f43..0fc0d27 100755
--- a/src/com/android/wallpaper/model/WallpaperInfo.java
+++ b/src/com/android/wallpaper/model/WallpaperInfo.java
@@ -18,6 +18,7 @@
 import android.app.Activity;
 import android.app.WallpaperColors;
 import android.content.Context;
+import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Color;
 import android.graphics.drawable.Drawable;
@@ -296,6 +297,23 @@
     }
 
     /**
+     * Returns a group name under which this Wallpaper should be grouped when displayed in
+     * a gallery, or an empty String if no grouping is required.
+     */
+    public String getGroupName(Context context) {
+        return "";
+    }
+
+    /**
+     * Returns the resource id of a drawable to use as a badge when displaying this wallpaper
+     * in a gallery, or {@link Resources#ID_NULL} if no badge is required.
+     */
+    @DrawableRes
+    public int getBadgeDrawableRes() {
+        return Resources.ID_NULL;
+    }
+
+    /**
      * Inner class to keep wallpaper colors and placeholder color.
      */
     public static class ColorInfo {
diff --git a/src/com/android/wallpaper/model/WallpaperSectionController.java b/src/com/android/wallpaper/model/WallpaperSectionController.java
index c8242ee..cf07e76 100644
--- a/src/com/android/wallpaper/model/WallpaperSectionController.java
+++ b/src/com/android/wallpaper/model/WallpaperSectionController.java
@@ -51,6 +51,7 @@
 import com.android.wallpaper.R;
 import com.android.wallpaper.asset.Asset;
 import com.android.wallpaper.asset.BitmapCachingAsset;
+import com.android.wallpaper.asset.CurrentWallpaperAssetVN;
 import com.android.wallpaper.model.WallpaperInfo.ColorInfo;
 import com.android.wallpaper.module.CurrentWallpaperInfoFactory;
 import com.android.wallpaper.module.InjectorProvider;
@@ -59,6 +60,8 @@
 import com.android.wallpaper.picker.MyPhotosStarter;
 import com.android.wallpaper.picker.WallpaperSectionView;
 import com.android.wallpaper.picker.WorkspaceSurfaceHolderCallback;
+import com.android.wallpaper.util.DisplayUtils;
+import com.android.wallpaper.util.PreviewUtils;
 import com.android.wallpaper.util.ResourceUtils;
 import com.android.wallpaper.util.WallpaperConnection;
 import com.android.wallpaper.util.WallpaperSurfaceCallback;
@@ -105,13 +108,15 @@
     private final CustomizationSectionNavigationController mSectionNavigationController;
     private final WallpaperPreviewNavigator mWallpaperPreviewNavigator;
     private final Bundle mSavedInstanceState;
+    private final DisplayUtils mDisplayUtils;
 
     public WallpaperSectionController(Activity activity, LifecycleOwner lifecycleOwner,
             PermissionRequester permissionRequester, WallpaperColorsViewModel colorsViewModel,
             WorkspaceViewModel workspaceViewModel,
             CustomizationSectionNavigationController sectionNavigationController,
             WallpaperPreviewNavigator wallpaperPreviewNavigator,
-            Bundle savedInstanceState) {
+            Bundle savedInstanceState,
+            DisplayUtils displayUtils) {
         mActivity = activity;
         mLifecycleOwner = lifecycleOwner;
         mPermissionRequester = permissionRequester;
@@ -121,6 +126,7 @@
         mSectionNavigationController = sectionNavigationController;
         mWallpaperPreviewNavigator = wallpaperPreviewNavigator;
         mSavedInstanceState = savedInstanceState;
+        mDisplayUtils = displayUtils;
     }
 
     @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
@@ -164,7 +170,9 @@
         mWorkspaceSurface = mHomePreviewCard.findViewById(R.id.workspace_surface);
         mHomePreviewProgress = mHomePreviewCard.findViewById(R.id.wallpaper_preview_spinner);
         mWorkspaceSurfaceCallback = new WorkspaceSurfaceHolderCallback(
-                mWorkspaceSurface, mAppContext);
+                mWorkspaceSurface,
+                new PreviewUtils(
+                        mAppContext, mAppContext.getString(R.string.grid_control_metadata_name)));
         mHomeWallpaperSurface = mHomePreviewCard.findViewById(R.id.wallpaper_surface);
 
         Future<ColorInfo> colorFuture = CompletableFuture.completedFuture(
@@ -174,7 +182,8 @@
         mHomeWallpaperSurfaceCallback = new WallpaperSurfaceCallback(mActivity, mHomePreviewCard,
                 mHomeWallpaperSurface, colorFuture, () -> {
             if (mHomePreviewWallpaperInfo != null) {
-                maybeLoadThumbnail(mHomePreviewWallpaperInfo, mHomeWallpaperSurfaceCallback);
+                maybeLoadThumbnail(mHomePreviewWallpaperInfo, mHomeWallpaperSurfaceCallback,
+                        mDisplayUtils.isOnWallpaperDisplay(mActivity));
             }
         });
 
@@ -188,7 +197,8 @@
         mLockWallpaperSurfaceCallback = new WallpaperSurfaceCallback(mActivity,
                 mLockscreenPreviewCard, mLockWallpaperSurface, colorFuture, () -> {
             if (mLockPreviewWallpaperInfo != null) {
-                maybeLoadThumbnail(mLockPreviewWallpaperInfo, mLockWallpaperSurfaceCallback);
+                maybeLoadThumbnail(mLockPreviewWallpaperInfo, mLockWallpaperSurfaceCallback,
+                        mDisplayUtils.isOnWallpaperDisplay(mActivity));
             }
         });
         mLockPreviewContainer = mLockscreenPreviewCard.findViewById(
@@ -331,7 +341,7 @@
      */
     private void refreshCurrentWallpapers(boolean forceRefresh) {
         CurrentWallpaperInfoFactory factory = InjectorProvider.getInjector()
-                .getCurrentWallpaperFactory(mAppContext);
+                .getCurrentWallpaperInfoFactory(mAppContext);
 
         factory.createCurrentWallpaperInfos(
                 (homeWallpaper, lockWallpaper, presentationMode) -> {
@@ -383,7 +393,8 @@
                 ? mHomeWallpaperSurfaceCallback : mLockWallpaperSurfaceCallback;
         // Load thumb regardless of live wallpaper to make sure we have a placeholder while
         // the live wallpaper initializes in that case.
-        maybeLoadThumbnail(wallpaperInfo, surfaceCallback);
+        maybeLoadThumbnail(wallpaperInfo, surfaceCallback,
+                mDisplayUtils.isOnWallpaperDisplay(mActivity));
 
         if (isHomeWallpaper) {
             if (mWallpaperConnection != null) {
@@ -404,13 +415,16 @@
 
     @NonNull
     private Asset maybeLoadThumbnail(WallpaperInfo wallpaperInfo,
-            WallpaperSurfaceCallback surfaceCallback) {
+            WallpaperSurfaceCallback surfaceCallback, boolean offsetToStart) {
         ImageView imageView = surfaceCallback.getHomeImageWallpaper();
-        Asset thumbAsset = new BitmapCachingAsset(mAppContext,
-                wallpaperInfo.getThumbAsset(mAppContext));
+        Asset thumbAsset = wallpaperInfo.getThumbAsset(mAppContext);
+        // Respect offsetToStart only for CurrentWallpaperAssetVN otherwise true.
+        offsetToStart = !(thumbAsset instanceof CurrentWallpaperAssetVN) || offsetToStart;
+        thumbAsset = new BitmapCachingAsset(mAppContext, thumbAsset);
         if (imageView != null && imageView.getDrawable() == null) {
             thumbAsset.loadPreviewImage(mActivity, imageView,
-                    ResourceUtils.getColorAttr(mActivity, android.R.attr.colorSecondary));
+                    ResourceUtils.getColorAttr(mActivity, android.R.attr.colorSecondary),
+                    offsetToStart);
         }
         return thumbAsset;
     }
diff --git a/src/com/android/wallpaper/model/WorkspaceViewModel.kt b/src/com/android/wallpaper/model/WorkspaceViewModel.kt
index bafd6aa..627406e 100644
--- a/src/com/android/wallpaper/model/WorkspaceViewModel.kt
+++ b/src/com/android/wallpaper/model/WorkspaceViewModel.kt
@@ -22,10 +22,8 @@
 class WorkspaceViewModel : ViewModel() {
 
     /**
-     * Triggers workspace updates through flipping the value from {@code false} to {@code true},
-     * or from {@code true} to {@code false}.
+     * Triggers workspace updates through flipping the value from {@code false} to {@code true}, or
+     * from {@code true} to {@code false}.
      */
-    val updateWorkspace: MutableLiveData<Boolean> by lazy {
-        MutableLiveData<Boolean>()
-    }
+    val updateWorkspace: MutableLiveData<Boolean> by lazy { MutableLiveData<Boolean>() }
 }
diff --git a/src/com/android/wallpaper/module/BaseWallpaperInjector.java b/src/com/android/wallpaper/module/BaseWallpaperInjector.java
deleted file mode 100755
index 6e72901..0000000
--- a/src/com/android/wallpaper/module/BaseWallpaperInjector.java
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.wallpaper.module;
-
-import android.content.Context;
-
-import com.android.wallpaper.compat.WallpaperManagerCompat;
-import com.android.wallpaper.effects.EffectsController;
-import com.android.wallpaper.network.Requester;
-import com.android.wallpaper.network.WallpaperRequester;
-import com.android.wallpaper.picker.individual.IndividualPickerFragment;
-import com.android.wallpaper.util.DisplayUtils;
-
-/**
- * Base implementation of Injector.
- */
-public abstract class BaseWallpaperInjector implements Injector {
-    private BitmapCropper mBitmapCropper;
-    private PartnerProvider mPartnerProvider;
-    private WallpaperPersister mWallpaperPersister;
-    private WallpaperPreferences mPrefs;
-    private WallpaperRefresher mWallpaperRefresher;
-    private Requester mRequester;
-    private WallpaperManagerCompat mWallpaperManagerCompat;
-    private WallpaperStatusChecker mWallpaperStatusChecker;
-    private CurrentWallpaperInfoFactory mCurrentWallpaperFactory;
-    private NetworkStatusNotifier mNetworkStatusNotifier;
-    private AlarmManagerWrapper mAlarmManagerWrapper;
-    private ExploreIntentChecker mExploreIntentChecker;
-    private SystemFeatureChecker mSystemFeatureChecker;
-    private PackageStatusNotifier mPackageStatusNotifier;
-    private LiveWallpaperInfoFactory mLiveWallpaperInfoFactory;
-    private DrawableLayerResolver mDrawableLayerResolver;
-    private CustomizationSections mCustomizationSections;
-    private DisplayUtils mDisplayUtils;
-
-    @Override
-    public synchronized BitmapCropper getBitmapCropper() {
-        if (mBitmapCropper == null) {
-            mBitmapCropper = new DefaultBitmapCropper();
-        }
-        return mBitmapCropper;
-    }
-
-    @Override
-    public synchronized PartnerProvider getPartnerProvider(Context context) {
-        if (mPartnerProvider == null) {
-            mPartnerProvider = new DefaultPartnerProvider(context.getApplicationContext());
-        }
-        return mPartnerProvider;
-    }
-
-    @Override
-    public synchronized WallpaperPreferences getPreferences(Context context) {
-        if (mPrefs == null) {
-            mPrefs = new DefaultWallpaperPreferences(context.getApplicationContext());
-        }
-        return mPrefs;
-    }
-
-    @Override
-    public synchronized WallpaperPersister getWallpaperPersister(Context context) {
-        if (mWallpaperPersister == null) {
-            mWallpaperPersister = new DefaultWallpaperPersister(context.getApplicationContext());
-        }
-        return mWallpaperPersister;
-    }
-
-    @Override
-    public synchronized WallpaperRefresher getWallpaperRefresher(Context context) {
-        if (mWallpaperRefresher == null) {
-            mWallpaperRefresher = new DefaultWallpaperRefresher(context.getApplicationContext());
-        }
-        return mWallpaperRefresher;
-    }
-
-    @Override
-    public synchronized Requester getRequester(Context context) {
-        if (mRequester == null) {
-            mRequester = new WallpaperRequester(context.getApplicationContext());
-        }
-        return mRequester;
-    }
-
-    @Override
-    public synchronized WallpaperManagerCompat getWallpaperManagerCompat(Context context) {
-        if (mWallpaperManagerCompat == null) {
-            mWallpaperManagerCompat = WallpaperManagerCompat.getInstance(context);
-        }
-        return mWallpaperManagerCompat;
-    }
-
-    @Override
-    public WallpaperStatusChecker getWallpaperStatusChecker() {
-        if (mWallpaperStatusChecker == null) {
-            mWallpaperStatusChecker = new DefaultWallpaperStatusChecker();
-        }
-        return mWallpaperStatusChecker;
-    }
-
-    @Override
-    public synchronized CurrentWallpaperInfoFactory getCurrentWallpaperFactory(Context context) {
-        if (mCurrentWallpaperFactory == null) {
-            mCurrentWallpaperFactory =
-                    new DefaultCurrentWallpaperInfoFactory(context.getApplicationContext());
-        }
-        return mCurrentWallpaperFactory;
-    }
-
-    @Override
-    public synchronized NetworkStatusNotifier getNetworkStatusNotifier(Context context) {
-        if (mNetworkStatusNotifier == null) {
-            mNetworkStatusNotifier = new DefaultNetworkStatusNotifier(context.getApplicationContext());
-        }
-        return mNetworkStatusNotifier;
-    }
-
-    @Override
-    public synchronized PackageStatusNotifier getPackageStatusNotifier(Context context) {
-        if (mPackageStatusNotifier == null) {
-            mPackageStatusNotifier = new DefaultPackageStatusNotifier(
-                    context.getApplicationContext());
-        }
-        return mPackageStatusNotifier;
-    }
-
-    @Override
-    public synchronized AlarmManagerWrapper getAlarmManagerWrapper(Context context) {
-        if (mAlarmManagerWrapper == null) {
-            mAlarmManagerWrapper = new DefaultAlarmManagerWrapper(context.getApplicationContext());
-        }
-        return mAlarmManagerWrapper;
-    }
-
-    @Override
-    public synchronized ExploreIntentChecker getExploreIntentChecker(Context context) {
-        if (mExploreIntentChecker == null) {
-            mExploreIntentChecker = new DefaultExploreIntentChecker(context.getApplicationContext());
-        }
-        return mExploreIntentChecker;
-    }
-
-    @Override
-    public synchronized SystemFeatureChecker getSystemFeatureChecker() {
-        if (mSystemFeatureChecker == null) {
-            mSystemFeatureChecker = new DefaultSystemFeatureChecker();
-        }
-        return mSystemFeatureChecker;
-    }
-
-    @Override
-    public synchronized IndividualPickerFragment getIndividualPickerFragment(String collectionId) {
-        return IndividualPickerFragment.newInstance(collectionId);
-    }
-
-    @Override
-    public LiveWallpaperInfoFactory getLiveWallpaperInfoFactory(Context context) {
-        if (mLiveWallpaperInfoFactory == null) {
-            mLiveWallpaperInfoFactory = new DefaultLiveWallpaperInfoFactory();
-        }
-        return mLiveWallpaperInfoFactory;
-    }
-
-    @Override
-    public DrawableLayerResolver getDrawableLayerResolver() {
-        if (mDrawableLayerResolver == null) {
-            mDrawableLayerResolver = new DefaultDrawableLayerResolver();
-        }
-        return mDrawableLayerResolver;
-    }
-
-    @Override
-    public CustomizationSections getCustomizationSections() {
-        if (mCustomizationSections == null) {
-            mCustomizationSections = new WallpaperPickerSections();
-        }
-        return mCustomizationSections;
-    }
-
-    @Override
-    public DisplayUtils getDisplayUtils(Context context) {
-        if (mDisplayUtils == null) {
-            mDisplayUtils = new DisplayUtils(context.getApplicationContext());
-        }
-        return mDisplayUtils;
-    }
-
-    @Override
-    public EffectsController createEffectsController(Context context,
-            EffectsController.EffectsServiceListener listener) {
-        return null;
-    }
-}
diff --git a/src/com/android/wallpaper/module/CustomizationSections.java b/src/com/android/wallpaper/module/CustomizationSections.java
index c611d50..66ae64d 100644
--- a/src/com/android/wallpaper/module/CustomizationSections.java
+++ b/src/com/android/wallpaper/module/CustomizationSections.java
@@ -1,9 +1,9 @@
 package com.android.wallpaper.module;
 
-import android.app.Activity;
 import android.os.Bundle;
 
 import androidx.annotation.Nullable;
+import androidx.fragment.app.FragmentActivity;
 import androidx.lifecycle.LifecycleOwner;
 
 import com.android.wallpaper.model.CustomizationSectionController;
@@ -12,12 +12,39 @@
 import com.android.wallpaper.model.WallpaperColorsViewModel;
 import com.android.wallpaper.model.WallpaperPreviewNavigator;
 import com.android.wallpaper.model.WorkspaceViewModel;
+import com.android.wallpaper.util.DisplayUtils;
 
 import java.util.List;
 
 /** Interface for carry {@link CustomizationSectionController}s. */
 public interface CustomizationSections {
 
+    /** Enumerates all screens supported by {@code getSectionControllersForScreen}. */
+    enum Screen {
+        LOCK_SCREEN,
+        HOME_SCREEN,
+    }
+
+    /**
+     * Gets a new instance of the section controller list for the given {@link Screen}.
+     *
+     * Note that the section views will be displayed by the list ordering.
+     *
+     * <p>Don't keep the section controllers as singleton since they contain views.
+     */
+    List<CustomizationSectionController<?>> getSectionControllersForScreen(
+            Screen screen,
+            FragmentActivity activity,
+            LifecycleOwner lifecycleOwner,
+            WallpaperColorsViewModel wallpaperColorsViewModel,
+            WorkspaceViewModel workspaceViewModel,
+            PermissionRequester permissionRequester,
+            WallpaperPreviewNavigator wallpaperPreviewNavigator,
+            CustomizationSectionNavigationController sectionNavigationController,
+            @Nullable Bundle savedInstanceState,
+            CurrentWallpaperInfoFactory wallpaperInfoFactory,
+            DisplayUtils displayUtils);
+
     /**
      * Gets a new instance of the section controller list.
      *
@@ -26,12 +53,13 @@
      * <p>Don't keep the section controllers as singleton since they contain views.
      */
     List<CustomizationSectionController<?>> getAllSectionControllers(
-            Activity activity,
+            FragmentActivity activity,
             LifecycleOwner lifecycleOwner,
             WallpaperColorsViewModel wallpaperColorsViewModel,
             WorkspaceViewModel workspaceViewModel,
             PermissionRequester permissionRequester,
             WallpaperPreviewNavigator wallpaperPreviewNavigator,
             CustomizationSectionNavigationController sectionNavigationController,
-            @Nullable Bundle savedInstanceState);
+            @Nullable Bundle savedInstanceState,
+            DisplayUtils displayUtils);
 }
diff --git a/src/com/android/wallpaper/module/DefaultWallpaperPreviewFragmentManager.kt b/src/com/android/wallpaper/module/DefaultWallpaperPreviewFragmentManager.kt
new file mode 100644
index 0000000..10f5834
--- /dev/null
+++ b/src/com/android/wallpaper/module/DefaultWallpaperPreviewFragmentManager.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2022 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.module
+
+import android.content.Context
+import android.os.Bundle
+import androidx.fragment.app.Fragment
+import com.android.wallpaper.model.LiveWallpaperInfo
+import com.android.wallpaper.model.WallpaperInfo
+import com.android.wallpaper.picker.ImagePreviewFragment
+import com.android.wallpaper.picker.LivePreviewFragment
+import com.android.wallpaper.picker.PreviewFragment
+
+class DefaultWallpaperPreviewFragmentManager : WallpaperPreviewFragmentManager {
+    override fun getPreviewFragment(
+        context: Context,
+        wallpaperInfo: WallpaperInfo,
+        @PreviewFragment.PreviewMode mode: Int,
+        viewAsHome: Boolean,
+        viewFullScreen: Boolean,
+        testingModeEnabled: Boolean
+    ): Fragment {
+        val args = Bundle()
+        args.putParcelable(PreviewFragment.ARG_WALLPAPER, wallpaperInfo)
+        args.putInt(PreviewFragment.ARG_PREVIEW_MODE, mode)
+        args.putBoolean(PreviewFragment.ARG_VIEW_AS_HOME, viewAsHome)
+        args.putBoolean(PreviewFragment.ARG_FULL_SCREEN, viewFullScreen)
+        args.putBoolean(PreviewFragment.ARG_TESTING_MODE_ENABLED, testingModeEnabled)
+        val fragment =
+            if (wallpaperInfo is LiveWallpaperInfo) LivePreviewFragment()
+            else ImagePreviewFragment()
+        fragment.arguments = args
+        return fragment
+    }
+}
diff --git a/src/com/android/wallpaper/module/FragmentFactory.kt b/src/com/android/wallpaper/module/FragmentFactory.kt
new file mode 100644
index 0000000..14ddce2
--- /dev/null
+++ b/src/com/android/wallpaper/module/FragmentFactory.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2022 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.module
+
+import androidx.fragment.app.Fragment
+
+interface FragmentFactory {
+    fun create(id: String): Fragment?
+}
diff --git a/src/com/android/wallpaper/module/Injector.java b/src/com/android/wallpaper/module/Injector.java
deleted file mode 100755
index 1fb4343..0000000
--- a/src/com/android/wallpaper/module/Injector.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.wallpaper.module;
-
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-
-import androidx.annotation.Nullable;
-import androidx.fragment.app.Fragment;
-
-import com.android.wallpaper.compat.WallpaperManagerCompat;
-import com.android.wallpaper.effects.EffectsController;
-import com.android.wallpaper.effects.EffectsController.EffectsServiceListener;
-import com.android.wallpaper.model.CategoryProvider;
-import com.android.wallpaper.model.WallpaperInfo;
-import com.android.wallpaper.monitor.PerformanceMonitor;
-import com.android.wallpaper.network.Requester;
-import com.android.wallpaper.picker.PreviewFragment.PreviewMode;
-import com.android.wallpaper.picker.individual.IndividualPickerFragment;
-import com.android.wallpaper.util.DisplayUtils;
-
-/**
- * Interface for a provider of "injected dependencies." (NOTE: The term "injector" is somewhat of a
- * misnomer; this is more aptly a service registry as part of a service locator design pattern.)
- */
-public interface Injector {
-    AlarmManagerWrapper getAlarmManagerWrapper(Context context);
-
-    BitmapCropper getBitmapCropper();
-
-    CategoryProvider getCategoryProvider(Context context);
-
-    CurrentWallpaperInfoFactory getCurrentWallpaperFactory(Context context);
-
-    ExploreIntentChecker getExploreIntentChecker(Context context);
-
-    LoggingOptInStatusProvider getLoggingOptInStatusProvider(Context context);
-
-    NetworkStatusNotifier getNetworkStatusNotifier(Context context);
-
-    PartnerProvider getPartnerProvider(Context context);
-
-    PerformanceMonitor getPerformanceMonitor();
-
-    Requester getRequester(Context context);
-
-    SystemFeatureChecker getSystemFeatureChecker();
-
-    UserEventLogger getUserEventLogger(Context context);
-
-    WallpaperManagerCompat getWallpaperManagerCompat(Context context);
-
-    WallpaperStatusChecker getWallpaperStatusChecker();
-
-    WallpaperPersister getWallpaperPersister(Context context);
-
-    WallpaperPreferences getPreferences(Context context);
-
-    WallpaperRefresher getWallpaperRefresher(Context context);
-
-    WallpaperRotationRefresher getWallpaperRotationRefresher();
-
-    Fragment getPreviewFragment(
-            Context context,
-            WallpaperInfo wallpaperInfo,
-            @PreviewMode int mode,
-            boolean viewAsHome,
-            boolean viewFullScreen,
-            boolean testingModeEnabled);
-
-    PackageStatusNotifier getPackageStatusNotifier(Context context);
-
-    IndividualPickerFragment getIndividualPickerFragment(String collectionId);
-
-    LiveWallpaperInfoFactory getLiveWallpaperInfoFactory(Context context);
-
-    DrawableLayerResolver getDrawableLayerResolver();
-
-    Intent getDeepLinkRedirectIntent(Context context, Uri uri);
-
-    String getDownloadableIntentAction();
-
-    CustomizationSections getCustomizationSections();
-
-    /**
-     * @return the singleton instance of {@link DisplayUtils}
-     */
-    DisplayUtils getDisplayUtils(Context context);
-
-    /**
-     * @return the singleton instance of {@link EffectsController}
-     */
-    @Nullable
-    EffectsController createEffectsController(Context context, EffectsServiceListener listener);
-}
diff --git a/src/com/android/wallpaper/module/Injector.kt b/src/com/android/wallpaper/module/Injector.kt
new file mode 100755
index 0000000..bcbf0e5
--- /dev/null
+++ b/src/com/android/wallpaper/module/Injector.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wallpaper.module
+
+import android.app.Activity
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
+import androidx.fragment.app.Fragment
+import com.android.wallpaper.compat.WallpaperManagerCompat
+import com.android.wallpaper.config.BaseFlags
+import com.android.wallpaper.effects.EffectsController
+import com.android.wallpaper.effects.EffectsController.EffectsServiceListener
+import com.android.wallpaper.model.CategoryProvider
+import com.android.wallpaper.model.WallpaperInfo
+import com.android.wallpaper.monitor.PerformanceMonitor
+import com.android.wallpaper.network.Requester
+import com.android.wallpaper.picker.PreviewFragment
+import com.android.wallpaper.picker.undo.domain.interactor.SnapshotRestorer
+import com.android.wallpaper.picker.undo.domain.interactor.UndoInteractor
+import com.android.wallpaper.util.DisplayUtils
+
+/**
+ * Interface for a provider of "injected dependencies." (NOTE: The term "injector" is somewhat of a
+ * misnomer; this is more aptly a service registry as part of a service locator design pattern.)
+ */
+interface Injector {
+    fun getAlarmManagerWrapper(context: Context): AlarmManagerWrapper
+
+    fun getBitmapCropper(): BitmapCropper
+
+    fun getCategoryProvider(context: Context): CategoryProvider
+
+    fun getCurrentWallpaperInfoFactory(context: Context): CurrentWallpaperInfoFactory
+
+    fun getCustomizationSections(activity: Activity): CustomizationSections
+
+    fun getDeepLinkRedirectIntent(context: Context, uri: Uri): Intent
+
+    fun getDisplayUtils(context: Context): DisplayUtils
+
+    fun getDownloadableIntentAction(): String?
+
+    fun getDrawableLayerResolver(): DrawableLayerResolver
+
+    fun getEffectsController(context: Context, listener: EffectsServiceListener): EffectsController?
+
+    fun getExploreIntentChecker(context: Context): ExploreIntentChecker
+
+    fun getIndividualPickerFragment(collectionId: String): Fragment
+
+    fun getLiveWallpaperInfoFactory(context: Context): LiveWallpaperInfoFactory
+
+    fun getNetworkStatusNotifier(context: Context): NetworkStatusNotifier
+
+    fun getPackageStatusNotifier(context: Context): PackageStatusNotifier
+
+    fun getPartnerProvider(context: Context): PartnerProvider
+
+    fun getPerformanceMonitor(): PerformanceMonitor?
+
+    // TODO b/242908637 Remove this method when migrating to the new wallpaper preview screen
+    fun getPreviewFragment(
+        context: Context,
+        wallpaperInfo: WallpaperInfo,
+        @PreviewFragment.PreviewMode mode: Int,
+        viewAsHome: Boolean,
+        viewFullScreen: Boolean,
+        testingModeEnabled: Boolean
+    ): Fragment
+
+    fun getRequester(context: Context): Requester
+
+    fun getSystemFeatureChecker(): SystemFeatureChecker
+
+    fun getUserEventLogger(context: Context): UserEventLogger
+
+    fun getWallpaperManagerCompat(context: Context): WallpaperManagerCompat
+
+    fun getWallpaperPersister(context: Context): WallpaperPersister
+
+    fun getPreferences(context: Context): WallpaperPreferences
+
+    fun getWallpaperPreviewFragmentManager(): WallpaperPreviewFragmentManager
+
+    fun getWallpaperRefresher(context: Context): WallpaperRefresher
+
+    fun getWallpaperRotationRefresher(): WallpaperRotationRefresher
+
+    fun getWallpaperStatusChecker(): WallpaperStatusChecker
+
+    fun getFragmentFactory(): FragmentFactory? {
+        return null
+    }
+
+    fun getFlags(): BaseFlags
+
+    fun getUndoInteractor(context: Context): UndoInteractor
+
+    fun getSnapshotRestorers(context: Context): Map<Int, SnapshotRestorer> {
+        // Empty because we don't support undoing in WallpaperPicker2.
+        return HashMap()
+    }
+}
diff --git a/src/com/android/wallpaper/module/LargeScreenMultiPanesChecker.kt b/src/com/android/wallpaper/module/LargeScreenMultiPanesChecker.kt
index b5b28a6..7e42d2d 100644
--- a/src/com/android/wallpaper/module/LargeScreenMultiPanesChecker.kt
+++ b/src/com/android/wallpaper/module/LargeScreenMultiPanesChecker.kt
@@ -21,9 +21,7 @@
 import android.content.pm.PackageManager.MATCH_DEFAULT_ONLY
 import android.provider.Settings.*
 
-/**
- * Utility class to check the support of multi panes integration (trampoline)
- */
+/** Utility class to check the support of multi panes integration (trampoline) */
 class LargeScreenMultiPanesChecker : MultiPanesChecker {
     companion object {
         private const val TAG = "LargeScreenMultiPanesChecker"
@@ -32,8 +30,8 @@
 
     override fun isMultiPanesEnabled(context: Context): Boolean {
         val pm = context.packageManager
-        val intent = getMultiPanesIntent(
-                Intent(ACTION_SET_WALLPAPER).setPackage(context.packageName))
+        val intent =
+            getMultiPanesIntent(Intent(ACTION_SET_WALLPAPER).setPackage(context.packageName))
 
         val resolveInfo = pm.resolveActivity(intent, MATCH_DEFAULT_ONLY)?.activityInfo?.enabled
         return resolveInfo != null
@@ -42,8 +40,10 @@
     override fun getMultiPanesIntent(intent: Intent): Intent {
         return Intent(ACTION_SETTINGS_EMBED_DEEP_LINK_ACTIVITY).apply {
             putExtra(EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_HIGHLIGHT_MENU_KEY, VALUE_HIGHLIGHT_MENU)
-            putExtra(EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_INTENT_URI,
-                    intent.toUri(Intent.URI_INTENT_SCHEME))
-        };
+            putExtra(
+                EXTRA_SETTINGS_EMBEDDED_DEEP_LINK_INTENT_URI,
+                intent.toUri(Intent.URI_INTENT_SCHEME)
+            )
+        }
     }
 }
diff --git a/src/com/android/wallpaper/module/LoggingOptInStatusProvider.java b/src/com/android/wallpaper/module/LoggingOptInStatusProvider.java
deleted file mode 100755
index ed6ea3e..0000000
--- a/src/com/android/wallpaper/module/LoggingOptInStatusProvider.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.wallpaper.module;
-
-/**
- * Provides the status of whether it is OK to opt in this user to logging.
- */
-public interface LoggingOptInStatusProvider {
-
-    /**
-     * Fetches the current opt-in state of logging and supplies it to the given receiver.
-     */
-    void fetchOptInValue(OptInValueReceiver receiver);
-
-    /**
-     * Interface for receivers of the usage & diagnostics opt-in value.
-     */
-    interface OptInValueReceiver {
-        void onOptInValueReady(boolean optedIn);
-    }
-}
diff --git a/src/com/android/wallpaper/module/MultiPanesChecker.kt b/src/com/android/wallpaper/module/MultiPanesChecker.kt
index 8a18b9c..b13b9fa 100644
--- a/src/com/android/wallpaper/module/MultiPanesChecker.kt
+++ b/src/com/android/wallpaper/module/MultiPanesChecker.kt
@@ -18,9 +18,7 @@
 import android.content.Context
 import android.content.Intent
 
-/**
- * Checks if the device is supporting multi panes' features
- */
+/** Checks if the device is supporting multi panes' features */
 interface MultiPanesChecker {
     /**
      * Checks if the device is supporting multi-panes landscape
diff --git a/src/com/android/wallpaper/module/NoOpUserEventLogger.java b/src/com/android/wallpaper/module/NoOpUserEventLogger.java
index 77e2a4b..070ad27 100755
--- a/src/com/android/wallpaper/module/NoOpUserEventLogger.java
+++ b/src/com/android/wallpaper/module/NoOpUserEventLogger.java
@@ -148,7 +148,12 @@
     }
 
     @Override
-    public void logEffectApply(String effect, int status) {
+    public void logEffectApply(String effect, int status, long timeElapsedMillis, int resultCode) {
+
+    }
+
+    @Override
+    public void logEffectProbe(String effect, int status) {
 
     }
 }
diff --git a/src/com/android/wallpaper/module/PartnerProvider.java b/src/com/android/wallpaper/module/PartnerProvider.java
index 0463f77..30ebee5 100755
--- a/src/com/android/wallpaper/module/PartnerProvider.java
+++ b/src/com/android/wallpaper/module/PartnerProvider.java
@@ -33,11 +33,16 @@
             "com.android.launcher3.action.PARTNER_CUSTOMIZATION";
 
     /**
-     * The resource ID in the partner APK for its list of wallpapers.
+     * The resource ID in the partner APK for an xml describing collections and wallpapers.
      */
     String WALLPAPER_RES_ID = "wallpapers";
 
     /**
+     * The resource ID in the partner APK for its list of wallpapers in legacy string-array format.
+     */
+    String LEGACY_WALLPAPER_RES_ID = "partner_wallpapers";
+
+    /**
      * Directory for system wallpapers in legacy versions of the partner APK.
      */
     String RES_LEGACY_SYSTEM_WALLPAPER_DIR = "system_wallpaper_directory";
diff --git a/src/com/android/wallpaper/module/UserEventLogger.java b/src/com/android/wallpaper/module/UserEventLogger.java
index 09e7b6b..8a2156d 100755
--- a/src/com/android/wallpaper/module/UserEventLogger.java
+++ b/src/com/android/wallpaper/module/UserEventLogger.java
@@ -197,7 +197,13 @@
     /**
      * Logs the action related to effect.
      */
-    void logEffectApply(String effect, @EffectStatus int status);
+    void logEffectApply(String effect, @EffectStatus int status, long timeElapsedMillis,
+            int resultCode);
+
+    /**
+     * Logs the effect probe result.
+     */
+    void logEffectProbe(String effect, @EffectStatus int status);
 
     /**
      * Possible results of a "set wallpaper" operation.
@@ -258,7 +264,7 @@
             EFFECT_PREFERENCE_UNSPECIFIED,
             EFFECT_APPLIED_ON_SUCCESS,
             EFFECT_APPLIED_ON_FAILED,
-            EFFECT_APPLIED_OFF,
+            EFFECT_APPLIED_OFF
             })
     @interface EffectStatus {
     }
diff --git a/src/com/android/wallpaper/module/WallpaperPersister.java b/src/com/android/wallpaper/module/WallpaperPersister.java
index 5d9992c..cfb3cab 100755
--- a/src/com/android/wallpaper/module/WallpaperPersister.java
+++ b/src/com/android/wallpaper/module/WallpaperPersister.java
@@ -15,6 +15,10 @@
  */
 package com.android.wallpaper.module;
 
+import static android.app.WallpaperManager.FLAG_LOCK;
+import static android.app.WallpaperManager.FLAG_SYSTEM;
+import static android.app.WallpaperManager.SetWallpaperFlags;
+
 import android.app.Activity;
 import android.graphics.Bitmap;
 import android.graphics.Rect;
@@ -194,4 +198,21 @@
             WALLPAPER_POSITION_STRETCH})
     @interface WallpaperPosition {
     }
+
+    /**
+     * Converts a {@link Destination} to the corresponding set of {@link SetWallpaperFlags}.
+     */
+    @SetWallpaperFlags
+    static int destinationToFlags(@Destination int destination) {
+        switch (destination) {
+            case DEST_HOME_SCREEN:
+                return FLAG_SYSTEM;
+            case DEST_LOCK_SCREEN:
+                return FLAG_LOCK;
+            case DEST_BOTH:
+                return FLAG_SYSTEM | FLAG_LOCK;
+            default:
+                throw new AssertionError("Unknown @Destination");
+        }
+    }
 }
diff --git a/src/com/android/wallpaper/module/WallpaperPicker2Injector.kt b/src/com/android/wallpaper/module/WallpaperPicker2Injector.kt
new file mode 100755
index 0000000..d97e4be
--- /dev/null
+++ b/src/com/android/wallpaper/module/WallpaperPicker2Injector.kt
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wallpaper.module
+
+import android.app.Activity
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
+import android.os.Bundle
+import androidx.fragment.app.Fragment
+import com.android.wallpaper.compat.WallpaperManagerCompat
+import com.android.wallpaper.config.BaseFlags
+import com.android.wallpaper.effects.EffectsController
+import com.android.wallpaper.effects.EffectsController.EffectsServiceListener
+import com.android.wallpaper.model.CategoryProvider
+import com.android.wallpaper.model.LiveWallpaperInfo
+import com.android.wallpaper.model.WallpaperInfo
+import com.android.wallpaper.monitor.PerformanceMonitor
+import com.android.wallpaper.network.Requester
+import com.android.wallpaper.network.WallpaperRequester
+import com.android.wallpaper.picker.CustomizationPickerActivity
+import com.android.wallpaper.picker.ImagePreviewFragment
+import com.android.wallpaper.picker.LivePreviewFragment
+import com.android.wallpaper.picker.PreviewFragment
+import com.android.wallpaper.picker.individual.IndividualPickerFragment
+import com.android.wallpaper.picker.undo.data.repository.UndoRepository
+import com.android.wallpaper.picker.undo.domain.interactor.UndoInteractor
+import com.android.wallpaper.util.DisplayUtils
+import kotlinx.coroutines.GlobalScope
+
+open class WallpaperPicker2Injector : Injector {
+    private var alarmManagerWrapper: AlarmManagerWrapper? = null
+    private var bitmapCropper: BitmapCropper? = null
+    private var categoryProvider: CategoryProvider? = null
+    private var currentWallpaperFactory: CurrentWallpaperInfoFactory? = null
+    private var customizationSections: CustomizationSections? = null
+    private var displayUtils: DisplayUtils? = null
+    private var drawableLayerResolver: DrawableLayerResolver? = null
+    private var exploreIntentChecker: ExploreIntentChecker? = null
+    private var liveWallpaperInfoFactory: LiveWallpaperInfoFactory? = null
+    private var networkStatusNotifier: NetworkStatusNotifier? = null
+    private var packageStatusNotifier: PackageStatusNotifier? = null
+    private var partnerProvider: PartnerProvider? = null
+    private var performanceMonitor: PerformanceMonitor? = null
+    private var requester: Requester? = null
+    private var systemFeatureChecker: SystemFeatureChecker? = null
+    private var userEventLogger: UserEventLogger? = null
+    private var wallpaperManagerCompat: WallpaperManagerCompat? = null
+    private var wallpaperPersister: WallpaperPersister? = null
+    private var prefs: WallpaperPreferences? = null
+    private var wallpaperPreviewFragmentManager: WallpaperPreviewFragmentManager? = null
+    private var wallpaperRefresher: WallpaperRefresher? = null
+    private var wallpaperRotationRefresher: WallpaperRotationRefresher? = null
+    private var wallpaperStatusChecker: WallpaperStatusChecker? = null
+    private var flags: BaseFlags? = null
+    private var undoInteractor: UndoInteractor? = null
+
+    @Synchronized
+    override fun getAlarmManagerWrapper(context: Context): AlarmManagerWrapper {
+        return alarmManagerWrapper
+            ?: DefaultAlarmManagerWrapper(context.applicationContext).also {
+                alarmManagerWrapper = it
+            }
+    }
+
+    @Synchronized
+    override fun getBitmapCropper(): BitmapCropper {
+        return bitmapCropper ?: DefaultBitmapCropper().also { bitmapCropper = it }
+    }
+
+    override fun getCategoryProvider(context: Context): CategoryProvider {
+        return categoryProvider
+            ?: DefaultCategoryProvider(context.applicationContext).also { categoryProvider = it }
+    }
+
+    @Synchronized
+    override fun getCurrentWallpaperInfoFactory(context: Context): CurrentWallpaperInfoFactory {
+        return currentWallpaperFactory
+            ?: DefaultCurrentWallpaperInfoFactory(context.applicationContext).also {
+                currentWallpaperFactory = it
+            }
+    }
+
+    override fun getCustomizationSections(activity: Activity): CustomizationSections {
+
+        return customizationSections
+            ?: WallpaperPickerSections().also { customizationSections = it }
+    }
+
+    override fun getDeepLinkRedirectIntent(context: Context, uri: Uri): Intent {
+        val intent = Intent()
+        intent.setClass(context, CustomizationPickerActivity::class.java)
+        intent.data = uri
+        intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
+        return intent
+    }
+
+    override fun getDisplayUtils(context: Context): DisplayUtils {
+        return displayUtils ?: DisplayUtils(context.applicationContext).also { displayUtils = it }
+    }
+
+    override fun getDownloadableIntentAction(): String? {
+        return null
+    }
+
+    override fun getDrawableLayerResolver(): DrawableLayerResolver {
+        return drawableLayerResolver
+            ?: DefaultDrawableLayerResolver().also { drawableLayerResolver = it }
+    }
+
+    override fun getEffectsController(
+        context: Context,
+        listener: EffectsServiceListener
+    ): EffectsController? {
+        return null
+    }
+
+    @Synchronized
+    override fun getExploreIntentChecker(context: Context): ExploreIntentChecker {
+        return exploreIntentChecker
+            ?: DefaultExploreIntentChecker(context.applicationContext).also {
+                exploreIntentChecker = it
+            }
+    }
+
+    @Synchronized
+    override fun getIndividualPickerFragment(collectionId: String): Fragment {
+        return IndividualPickerFragment.newInstance(collectionId)
+    }
+
+    override fun getLiveWallpaperInfoFactory(context: Context): LiveWallpaperInfoFactory {
+        return liveWallpaperInfoFactory
+            ?: DefaultLiveWallpaperInfoFactory().also { liveWallpaperInfoFactory = it }
+    }
+
+    @Synchronized
+    override fun getNetworkStatusNotifier(context: Context): NetworkStatusNotifier {
+        return networkStatusNotifier
+            ?: DefaultNetworkStatusNotifier(context.applicationContext).also {
+                networkStatusNotifier = it
+            }
+    }
+
+    @Synchronized
+    override fun getPackageStatusNotifier(context: Context): PackageStatusNotifier {
+        return packageStatusNotifier
+            ?: DefaultPackageStatusNotifier(context.applicationContext).also {
+                packageStatusNotifier = it
+            }
+    }
+
+    @Synchronized
+    override fun getPartnerProvider(context: Context): PartnerProvider {
+        return partnerProvider
+            ?: DefaultPartnerProvider(context.applicationContext).also { partnerProvider = it }
+    }
+
+    @Synchronized
+    override fun getPerformanceMonitor(): PerformanceMonitor? {
+
+        return performanceMonitor
+            ?: PerformanceMonitor {
+                    /** No Op */
+                }
+                .also { performanceMonitor = it }
+    }
+
+    override fun getPreviewFragment(
+        context: Context,
+        wallpaperInfo: WallpaperInfo,
+        mode: Int,
+        viewAsHome: Boolean,
+        viewFullScreen: Boolean,
+        testingModeEnabled: Boolean
+    ): Fragment {
+        val args = Bundle()
+        args.putParcelable(PreviewFragment.ARG_WALLPAPER, wallpaperInfo)
+        args.putInt(PreviewFragment.ARG_PREVIEW_MODE, mode)
+        args.putBoolean(PreviewFragment.ARG_VIEW_AS_HOME, viewAsHome)
+        args.putBoolean(PreviewFragment.ARG_FULL_SCREEN, viewFullScreen)
+        args.putBoolean(PreviewFragment.ARG_TESTING_MODE_ENABLED, testingModeEnabled)
+        val fragment =
+            if (wallpaperInfo is LiveWallpaperInfo) LivePreviewFragment()
+            else ImagePreviewFragment()
+        fragment.arguments = args
+        return fragment
+    }
+
+    @Synchronized
+    override fun getRequester(context: Context): Requester {
+        return requester ?: WallpaperRequester(context.applicationContext).also { requester = it }
+    }
+
+    @Synchronized
+    override fun getSystemFeatureChecker(): SystemFeatureChecker {
+        return systemFeatureChecker
+            ?: DefaultSystemFeatureChecker().also { systemFeatureChecker = it }
+    }
+
+    override fun getUserEventLogger(context: Context): UserEventLogger {
+        return userEventLogger ?: NoOpUserEventLogger().also { userEventLogger = it }
+    }
+
+    @Synchronized
+    override fun getWallpaperManagerCompat(context: Context): WallpaperManagerCompat {
+        return wallpaperManagerCompat
+            ?: WallpaperManagerCompat.getInstance(context).also { wallpaperManagerCompat = it }
+    }
+
+    @Synchronized
+    override fun getWallpaperPersister(context: Context): WallpaperPersister {
+        return wallpaperPersister
+            ?: DefaultWallpaperPersister(context.applicationContext).also {
+                wallpaperPersister = it
+            }
+    }
+
+    @Synchronized
+    override fun getPreferences(context: Context): WallpaperPreferences {
+        return prefs ?: DefaultWallpaperPreferences(context.applicationContext).also { prefs = it }
+    }
+
+    @Synchronized
+    override fun getWallpaperPreviewFragmentManager(): WallpaperPreviewFragmentManager {
+        return wallpaperPreviewFragmentManager
+            ?: DefaultWallpaperPreviewFragmentManager().also {
+                wallpaperPreviewFragmentManager = it
+            }
+    }
+
+    @Synchronized
+    override fun getWallpaperRefresher(context: Context): WallpaperRefresher {
+        return wallpaperRefresher
+            ?: DefaultWallpaperRefresher(context.applicationContext).also {
+                wallpaperRefresher = it
+            }
+    }
+
+    @Synchronized
+    override fun getWallpaperRotationRefresher(): WallpaperRotationRefresher {
+        return wallpaperRotationRefresher
+            ?: WallpaperRotationRefresher { _, listener ->
+                    // Not implemented
+                    listener.onError()
+                }
+                .also { wallpaperRotationRefresher = it }
+    }
+
+    override fun getWallpaperStatusChecker(): WallpaperStatusChecker {
+        return wallpaperStatusChecker
+            ?: DefaultWallpaperStatusChecker().also { wallpaperStatusChecker = it }
+    }
+
+    override fun getFlags(): BaseFlags {
+        return flags ?: object : BaseFlags() {}.also { flags = it }
+    }
+
+    override fun getUndoInteractor(context: Context): UndoInteractor {
+        return undoInteractor
+            ?: UndoInteractor(GlobalScope, UndoRepository(), getSnapshotRestorers(context)).also {
+                undoInteractor = it
+            }
+    }
+
+    companion object {
+        /**
+         * When this injector is overridden, this is the minimal value that should be used by
+         * restorers returns in [getSnapshotRestorers].
+         */
+        @JvmStatic protected val MIN_SNAPSHOT_RESTORER_KEY = 0
+    }
+}
diff --git a/src/com/android/wallpaper/module/WallpaperPickerSections.java b/src/com/android/wallpaper/module/WallpaperPickerSections.java
index b5e8d17..16782ce 100644
--- a/src/com/android/wallpaper/module/WallpaperPickerSections.java
+++ b/src/com/android/wallpaper/module/WallpaperPickerSections.java
@@ -1,9 +1,9 @@
 package com.android.wallpaper.module;
 
-import android.app.Activity;
 import android.os.Bundle;
 
 import androidx.annotation.Nullable;
+import androidx.fragment.app.FragmentActivity;
 import androidx.lifecycle.LifecycleOwner;
 
 import com.android.wallpaper.model.CustomizationSectionController;
@@ -13,6 +13,8 @@
 import com.android.wallpaper.model.WallpaperPreviewNavigator;
 import com.android.wallpaper.model.WallpaperSectionController;
 import com.android.wallpaper.model.WorkspaceViewModel;
+import com.android.wallpaper.picker.customization.ui.section.ScreenPreviewSectionController;
+import com.android.wallpaper.util.DisplayUtils;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -21,18 +23,49 @@
 public final class WallpaperPickerSections implements CustomizationSections {
 
     @Override
-    public List<CustomizationSectionController<?>> getAllSectionControllers(Activity activity,
-            LifecycleOwner lifecycleOwner, WallpaperColorsViewModel wallpaperColorsViewModel,
-            WorkspaceViewModel workspaceViewModel, PermissionRequester permissionRequester,
+    public List<CustomizationSectionController<?>> getSectionControllersForScreen(
+            Screen screen,
+            FragmentActivity activity,
+            LifecycleOwner lifecycleOwner,
+            WallpaperColorsViewModel wallpaperColorsViewModel,
+            WorkspaceViewModel workspaceViewModel,
+            PermissionRequester permissionRequester,
             WallpaperPreviewNavigator wallpaperPreviewNavigator,
             CustomizationSectionNavigationController sectionNavigationController,
-            @Nullable Bundle savedInstanceState) {
+            @Nullable Bundle savedInstanceState,
+            CurrentWallpaperInfoFactory wallpaperInfoFactory,
+            DisplayUtils displayUtils) {
+        List<CustomizationSectionController<?>> sectionControllers = new ArrayList<>();
+
+        sectionControllers.add(
+                new ScreenPreviewSectionController(
+                        activity,
+                        lifecycleOwner,
+                        screen,
+                        wallpaperInfoFactory,
+                        wallpaperColorsViewModel,
+                        displayUtils));
+
+        return sectionControllers;
+    }
+
+    @Override
+    public List<CustomizationSectionController<?>> getAllSectionControllers(
+            FragmentActivity activity,
+            LifecycleOwner lifecycleOwner,
+            WallpaperColorsViewModel wallpaperColorsViewModel,
+            WorkspaceViewModel workspaceViewModel,
+            PermissionRequester permissionRequester,
+            WallpaperPreviewNavigator wallpaperPreviewNavigator,
+            CustomizationSectionNavigationController sectionNavigationController,
+            @Nullable Bundle savedInstanceState,
+            DisplayUtils displayUtils) {
         List<CustomizationSectionController<?>> sectionControllers = new ArrayList<>();
 
         sectionControllers.add(new WallpaperSectionController(
                 activity, lifecycleOwner, permissionRequester, wallpaperColorsViewModel,
                 workspaceViewModel, sectionNavigationController, wallpaperPreviewNavigator,
-                savedInstanceState));
+                savedInstanceState, displayUtils));
 
         return sectionControllers;
     }
diff --git a/src/com/android/wallpaper/module/WallpaperPreviewFragmentManager.kt b/src/com/android/wallpaper/module/WallpaperPreviewFragmentManager.kt
new file mode 100644
index 0000000..fc6b8d4
--- /dev/null
+++ b/src/com/android/wallpaper/module/WallpaperPreviewFragmentManager.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2022 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.module
+
+import android.content.Context
+import androidx.fragment.app.Fragment
+import com.android.wallpaper.model.WallpaperInfo
+
+/** A manager that provides the wallpaper preview fragment. */
+interface WallpaperPreviewFragmentManager {
+    fun getPreviewFragment(
+        context: Context,
+        wallpaperInfo: WallpaperInfo,
+        mode: Int,
+        viewAsHome: Boolean,
+        viewFullScreen: Boolean,
+        testingModeEnabled: Boolean
+    ): Fragment
+}
diff --git a/src/com/android/wallpaper/module/WallpaperSetter.java b/src/com/android/wallpaper/module/WallpaperSetter.java
index a8db6de..6924a07 100644
--- a/src/com/android/wallpaper/module/WallpaperSetter.java
+++ b/src/com/android/wallpaper/module/WallpaperSetter.java
@@ -6,6 +6,7 @@
 import android.app.ProgressDialog;
 import android.app.WallpaperColors;
 import android.app.WallpaperManager;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.graphics.Point;
@@ -39,6 +40,7 @@
 import com.bumptech.glide.Glide;
 
 import java.io.IOException;
+import java.lang.reflect.Method;
 import java.util.Optional;
 
 /**
@@ -200,15 +202,11 @@
                         "Live wallpaper cannot be applied on lock screen only");
             }
             WallpaperManager wallpaperManager = WallpaperManager.getInstance(activity);
-            wallpaperManager.setWallpaperComponent(
-                    wallpaper.getWallpaperComponent().getComponent());
+            setWallpaperComponent(wallpaperManager, wallpaper, destination);
             wallpaperManager.setWallpaperOffsetSteps(0.5f /* xStep */, 0.0f /* yStep */);
             wallpaperManager.setWallpaperOffsets(
                     activity.getWindow().getDecorView().getRootView().getWindowToken(),
                     0.5f /* xOffset */, 0.0f /* yOffset */);
-            if (destination == WallpaperPersister.DEST_BOTH) {
-                wallpaperManager.clear(FLAG_LOCK);
-            }
             mPreferences.storeLatestHomeWallpaper(wallpaper.getWallpaperId(), wallpaper, colors);
             onWallpaperApplied(wallpaper, activity);
             if (callback != null) {
@@ -222,6 +220,24 @@
         }
     }
 
+    private void setWallpaperComponent(WallpaperManager wallpaperManager,
+            LiveWallpaperInfo wallpaper, int destination) throws IOException {
+        try {
+            Method m = wallpaperManager.getClass().getMethod("setWallpaperComponentWithFlags",
+                    ComponentName.class, int.class);
+            wallpaperManager.setWallpaperComponentWithFlags(
+                    wallpaper.getWallpaperComponent().getComponent(),
+                    WallpaperPersister.destinationToFlags(destination));
+        } catch (NoSuchMethodException e) {
+            Log.d(TAG, "setWallpaperComponentWithFlags not available, using setWallpaperComponent");
+            wallpaperManager.setWallpaperComponent(
+                    wallpaper.getWallpaperComponent().getComponent());
+        }
+        if (destination == WallpaperPersister.DEST_BOTH) {
+            wallpaperManager.clear(FLAG_LOCK);
+        }
+    }
+
     /**
      * Sets current live wallpaper to the device (restore case)
      *
@@ -240,11 +256,7 @@
                         "Live wallpaper cannot be applied on lock screen only");
             }
             WallpaperManager wallpaperManager = WallpaperManager.getInstance(context);
-            wallpaperManager.setWallpaperComponent(
-                    wallpaper.getWallpaperComponent().getComponent());
-            if (destination == WallpaperPersister.DEST_BOTH) {
-                wallpaperManager.clear(FLAG_LOCK);
-            }
+            setWallpaperComponent(wallpaperManager, wallpaper, destination);
             mPreferences.storeLatestHomeWallpaper(wallpaper.getWallpaperId(), wallpaper,
                     colors != null ? colors :
                             WallpaperColors.fromBitmap(wallpaper.getThumbAsset(context)
diff --git a/src/com/android/wallpaper/picker/CategorySelectorFragment.java b/src/com/android/wallpaper/picker/CategorySelectorFragment.java
index f0ca422..2b6c71d 100644
--- a/src/com/android/wallpaper/picker/CategorySelectorFragment.java
+++ b/src/com/android/wallpaper/picker/CategorySelectorFragment.java
@@ -20,7 +20,6 @@
 
 import android.app.Activity;
 import android.app.AlertDialog;
-import android.content.Context;
 import android.content.Intent;
 import android.content.res.TypedArray;
 import android.graphics.Color;
@@ -119,23 +118,19 @@
         void cleanUp();
     }
 
-    private final CategoryProvider mCategoryProvider;
-
     private RecyclerView mImageGrid;
     private CategoryAdapter mAdapter;
+    private CategoryProvider mCategoryProvider;
     private ArrayList<Category> mCategories = new ArrayList<>();
     private Point mTileSizePx;
     private boolean mAwaitingCategories;
     private boolean mIsFeaturedCollectionAvailable;
 
-    public CategorySelectorFragment() {
+    @Override
+    public void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
         mAdapter = new CategoryAdapter(mCategories);
-        mCategoryProvider = InjectorProvider.getInjector().getCategoryProvider(getContext());
-    }
-
-    public CategorySelectorFragment(Context context) {
-        mAdapter = new CategoryAdapter(mCategories);
-        mCategoryProvider = InjectorProvider.getInjector().getCategoryProvider(context);
+        mCategoryProvider = InjectorProvider.getInjector().getCategoryProvider(requireContext());
     }
 
     @Nullable
diff --git a/src/com/android/wallpaper/picker/CustomizationPickerActivity.java b/src/com/android/wallpaper/picker/CustomizationPickerActivity.java
index f76c09b..2351c72 100644
--- a/src/com/android/wallpaper/picker/CustomizationPickerActivity.java
+++ b/src/com/android/wallpaper/picker/CustomizationPickerActivity.java
@@ -35,6 +35,7 @@
 
 import com.android.wallpaper.R;
 import com.android.wallpaper.model.Category;
+import com.android.wallpaper.model.CustomizationSectionController.CustomizationSectionNavigationController;
 import com.android.wallpaper.model.PermissionRequester;
 import com.android.wallpaper.model.WallpaperCategory;
 import com.android.wallpaper.model.WallpaperInfo;
@@ -51,6 +52,7 @@
 import com.android.wallpaper.picker.CategorySelectorFragment.CategorySelectorFragmentHost;
 import com.android.wallpaper.picker.MyPhotosStarter.PermissionChangedListener;
 import com.android.wallpaper.picker.individual.IndividualPickerFragment.IndividualPickerFragmentHost;
+import com.android.wallpaper.picker.undo.domain.interactor.UndoInteractor;
 import com.android.wallpaper.util.ActivityUtils;
 import com.android.wallpaper.util.DeepLinkUtils;
 import com.android.wallpaper.util.LaunchUtils;
@@ -67,6 +69,7 @@
         WallpaperPreviewNavigator {
 
     private static final String TAG = "CustomizationPickerActivity";
+    private static final String EXTRA_DESTINATION = "destination";
 
     private WallpaperPickerDelegate mDelegate;
     private UserEventLogger mUserEventLogger;
@@ -76,6 +79,7 @@
 
     private BottomActionBar mBottomActionBar;
     private boolean mIsSafeToCommitFragmentTransaction;
+    @Nullable private UndoInteractor mUndoInteractor;
 
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
@@ -105,6 +109,8 @@
         // See go/pdr-edge-to-edge-guide.
         WindowCompat.setDecorFitsSystemWindows(getWindow(), isSUWMode(this));
 
+        final boolean isUseRevampedUi = injector.getFlags().isUseRevampedUi(this);
+
         Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
         if (fragment == null) {
             // App launch specific logic: log the "app launch source" event.
@@ -117,13 +123,28 @@
             // Switch to the target fragment.
             switchFragment(isWallpaperOnlyMode(getIntent())
                     ? new WallpaperOnlyFragment()
-                    : new CustomizationPickerFragment());
+                    : CustomizationPickerFragment.newInstance(isUseRevampedUi));
         }
 
-        // Deep link case
-        Intent intent = getIntent();
-        String deepLinkCollectionId = DeepLinkUtils.getCollectionId(intent);
-        if (!TextUtils.isEmpty(deepLinkCollectionId)) {
+        if (isUseRevampedUi) {
+            mUndoInteractor = injector.getUndoInteractor(this);
+            mUndoInteractor.startSession();
+        }
+
+        final Intent intent = getIntent();
+        final String navigationDestination = intent.getStringExtra(EXTRA_DESTINATION);
+        final String deepLinkCollectionId = DeepLinkUtils.getCollectionId(intent);
+
+        if (!TextUtils.isEmpty(navigationDestination)) {
+            // Navigation deep link case
+            fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
+            if (fragment instanceof CustomizationSectionNavigationController) {
+                final CustomizationSectionNavigationController navController =
+                        (CustomizationSectionNavigationController) fragment;
+                navController.navigateTo(navigationDestination);
+            }
+        } else if (!TextUtils.isEmpty(deepLinkCollectionId)) {
+            // Wallpaper Collection deep link case
             switchFragmentWithBackStack(new CategorySelectorFragment());
             switchFragmentWithBackStack(InjectorProvider.getInjector().getIndividualPickerFragment(
                     deepLinkCollectionId));
diff --git a/src/com/android/wallpaper/picker/CustomizationPickerFragment.java b/src/com/android/wallpaper/picker/CustomizationPickerFragment.java
index dd0719f..380fe13 100644
--- a/src/com/android/wallpaper/picker/CustomizationPickerFragment.java
+++ b/src/com/android/wallpaper/picker/CustomizationPickerFragment.java
@@ -37,7 +37,11 @@
 import com.android.wallpaper.model.WallpaperPreviewNavigator;
 import com.android.wallpaper.model.WorkspaceViewModel;
 import com.android.wallpaper.module.CustomizationSections;
+import com.android.wallpaper.module.FragmentFactory;
+import com.android.wallpaper.module.Injector;
 import com.android.wallpaper.module.InjectorProvider;
+import com.android.wallpaper.picker.customization.ui.binder.CustomizationPickerBinder;
+import com.android.wallpaper.picker.customization.ui.viewmodel.CustomizationPickerViewModel;
 import com.android.wallpaper.util.ActivityUtils;
 
 import java.util.ArrayList;
@@ -50,18 +54,34 @@
 
     private static final String TAG = "CustomizationPickerFragment";
     private static final String SCROLL_POSITION_Y = "SCROLL_POSITION_Y";
+    private static final String KEY_IS_USE_REVAMPED_UI = "is_use_revamped_ui";
+
+    /** Returns a new instance of {@link CustomizationPickerFragment}. */
+    public static CustomizationPickerFragment newInstance(boolean isUseRevampedUi) {
+        final CustomizationPickerFragment fragment = new CustomizationPickerFragment();
+        final Bundle args = new Bundle();
+        args.putBoolean(KEY_IS_USE_REVAMPED_UI, isUseRevampedUi);
+        fragment.setArguments(args);
+        return fragment;
+    }
 
     // Note that the section views will be displayed by the list ordering.
     private final List<CustomizationSectionController<?>> mSectionControllers = new ArrayList<>();
     private NestedScrollView mNestedScrollView;
     @Nullable private Bundle mBackStackSavedInstanceState;
+    private final FragmentFactory mFragmentFactory;
+    @Nullable private CustomizationPickerViewModel mViewModel;
+
+    public CustomizationPickerFragment() {
+        mFragmentFactory = InjectorProvider.getInjector().getFragmentFactory();
+    }
 
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
             @Nullable Bundle savedInstanceState) {
         final View view = inflater.inflate(R.layout.collapsing_toolbar_container_layout,
                 container, /* attachToRoot= */ false);
-        setContentView(view, R.layout.fragment_customization_picker);
+
         if (ActivityUtils.isLaunchedFromSettingsRelated(getActivity().getIntent())) {
             setUpToolbar(view, !ActivityEmbeddingUtils.shouldHideNavigateUpButton(
                     getActivity(), /* isSecondLayerPage= */ true));
@@ -69,39 +89,78 @@
             setUpToolbar(view, /* upArrow= */ false);
         }
 
-        ViewGroup sectionContainer = view.findViewById(R.id.section_container);
-        sectionContainer.setOnApplyWindowInsetsListener((v, windowInsets) -> {
-            v.setPadding(
-                    v.getPaddingLeft(),
-                    v.getPaddingTop(),
-                    v.getPaddingRight(),
-                    windowInsets.getSystemWindowInsetBottom());
-            return windowInsets.consumeSystemWindowInsets();
-        });
-        mNestedScrollView = view.findViewById(R.id.scroll_container);
+        final Injector injector = InjectorProvider.getInjector();
+        final Bundle args = getArguments();
+        final boolean isUseRevampedUi;
+        if (args != null && args.containsKey(KEY_IS_USE_REVAMPED_UI)) {
+            isUseRevampedUi = args.getBoolean(KEY_IS_USE_REVAMPED_UI);
+        } else {
+            throw new IllegalStateException(
+                    "Must contain KEY_IS_USE_REVAMPED_UI argument, did you instantiate directly"
+                            + " instead of using the newInstance function?");
+        }
+        if (isUseRevampedUi) {
+            setContentView(view, R.layout.fragment_tabbed_customization_picker);
+            mViewModel = new ViewModelProvider(
+                    this,
+                    CustomizationPickerViewModel.newFactory(
+                            this,
+                            savedInstanceState,
+                            injector.getUndoInteractor(requireContext()))
+            ).get(CustomizationPickerViewModel.class);
+
+            setUpToolbarMenu(R.menu.undoable_customization_menu);
+            final Bundle finalSavedInstanceState = savedInstanceState;
+            CustomizationPickerBinder.bind(
+                    view,
+                    getToolbarId(),
+                    mViewModel,
+                    this,
+                    isOnLockScreen -> getSectionControllers(
+                            isOnLockScreen
+                                    ? CustomizationSections.Screen.LOCK_SCREEN
+                                    : CustomizationSections.Screen.HOME_SCREEN,
+                            finalSavedInstanceState));
+        } else {
+            setContentView(view, R.layout.fragment_customization_picker);
+        }
 
         if (mBackStackSavedInstanceState != null) {
             savedInstanceState = mBackStackSavedInstanceState;
             mBackStackSavedInstanceState = null;
         }
 
-        initSections(savedInstanceState);
-        mSectionControllers.forEach(controller ->
-                mNestedScrollView.post(() -> {
-                            final Context context = getContext();
-                            if (context == null) {
-                                Log.w(TAG, "Adding section views with null context");
-                                return;
+        mNestedScrollView = view.findViewById(R.id.scroll_container);
+
+        if (!isUseRevampedUi) {
+            ViewGroup sectionContainer = view.findViewById(R.id.section_container);
+            sectionContainer.setOnApplyWindowInsetsListener((v, windowInsets) -> {
+                v.setPadding(
+                        v.getPaddingLeft(),
+                        v.getPaddingTop(),
+                        v.getPaddingRight(),
+                        windowInsets.getSystemWindowInsetBottom());
+                return windowInsets.consumeSystemWindowInsets();
+            });
+
+            initSections(savedInstanceState);
+            mSectionControllers.forEach(controller ->
+                    mNestedScrollView.post(() -> {
+                                final Context context = getContext();
+                                if (context == null) {
+                                    Log.w(TAG, "Adding section views with null context");
+                                    return;
+                                }
+                                sectionContainer.addView(controller.createView(context));
                             }
-                            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)
-        );
+                    )
+            );
+
+            final Bundle savedInstanceStateRef = savedInstanceState;
+            // Post it to the end of adding views to ensure restoring view state the last task.
+            view.post(() -> restoreViewState(savedInstanceStateRef));
+        }
+
         return view;
     }
 
@@ -175,6 +234,15 @@
         fragmentManager.executePendingTransactions();
     }
 
+    @Override
+    public void navigateTo(String destinationId) {
+        final Fragment fragment = mFragmentFactory.create(destinationId);
+
+        if (fragment != null) {
+            navigateTo(fragment);
+        }
+    }
+
     /** Saves state of the fragment. */
     private void onSaveInstanceStateInternal(Bundle savedInstanceState) {
         if (mNestedScrollView != null) {
@@ -188,18 +256,53 @@
         mSectionControllers.forEach(CustomizationSectionController::release);
         mSectionControllers.clear();
 
+        mSectionControllers.addAll(
+                getAvailableSections(getAvailableSectionControllers(savedInstanceState)));
+    }
+
+    private List<CustomizationSectionController<?>> getAvailableSectionControllers(
+            @Nullable Bundle savedInstanceState) {
+        return getSectionControllers(
+                null,
+                savedInstanceState);
+    }
+
+    private List<CustomizationSectionController<?>> getSectionControllers(
+            @Nullable CustomizationSections.Screen screen,
+            @Nullable Bundle savedInstanceState) {
+        final Injector injector = InjectorProvider.getInjector();
+
         WallpaperColorsViewModel wcViewModel = new ViewModelProvider(getActivity())
                 .get(WallpaperColorsViewModel.class);
         WorkspaceViewModel workspaceViewModel = new ViewModelProvider(getActivity())
                 .get(WorkspaceViewModel.class);
 
-        CustomizationSections sections = InjectorProvider.getInjector().getCustomizationSections();
-        List<CustomizationSectionController<?>> allSectionControllers =
-                sections.getAllSectionControllers(getActivity(), getViewLifecycleOwner(),
-                        wcViewModel, workspaceViewModel, getPermissionRequester(),
-                        getWallpaperPreviewNavigator(), this, savedInstanceState);
-
-        mSectionControllers.addAll(getAvailableSections(allSectionControllers));
+        CustomizationSections sections = injector.getCustomizationSections(getActivity());
+        if (screen == null) {
+            return sections.getAllSectionControllers(
+                    getActivity(),
+                    getViewLifecycleOwner(),
+                    wcViewModel,
+                    workspaceViewModel,
+                    getPermissionRequester(),
+                    getWallpaperPreviewNavigator(),
+                    this,
+                    savedInstanceState,
+                    injector.getDisplayUtils(getActivity()));
+        } else {
+            return sections.getSectionControllersForScreen(
+                    screen,
+                    getActivity(),
+                    getViewLifecycleOwner(),
+                    wcViewModel,
+                    workspaceViewModel,
+                    getPermissionRequester(),
+                    getWallpaperPreviewNavigator(),
+                    this,
+                    savedInstanceState,
+                    injector.getCurrentWallpaperInfoFactory(requireContext()),
+                    injector.getDisplayUtils(getActivity()));
+        }
     }
 
     protected List<CustomizationSectionController<?>> getAvailableSections(
diff --git a/src/com/android/wallpaper/picker/DisplayAspectRatioFrameLayout.kt b/src/com/android/wallpaper/picker/DisplayAspectRatioFrameLayout.kt
new file mode 100644
index 0000000..a2f1c67
--- /dev/null
+++ b/src/com/android/wallpaper/picker/DisplayAspectRatioFrameLayout.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2022 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 android.content.Context
+import android.util.AttributeSet
+import android.widget.FrameLayout
+import androidx.core.view.children
+import com.android.wallpaper.util.ScreenSizeCalculator
+
+/**
+ * [FrameLayout] that sizes its children using a fixed aspect ratio that is the same as that of the
+ * display.
+ */
+class DisplayAspectRatioFrameLayout(
+    context: Context,
+    attrs: AttributeSet?,
+) : FrameLayout(context, attrs) {
+
+    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
+        val screenAspectRatio = ScreenSizeCalculator.getInstance().getScreenAspectRatio(context)
+        // We're always forcing the width based on the height. This will only work if the
+        // DisplayAspectRatioFrameLayout is allowed to stretch to fill its parent (for example if
+        // the parent is a vertical LinearLayout and the DisplayAspectRatioFrameLayout has a height
+        // if 0 and a weight of 1.
+        //
+        // If you need to use this class to force the height dimension based on the width instead,
+        // you will need to flip the logic below.
+        children.forEach { child ->
+            child.measure(
+                MeasureSpec.makeMeasureSpec(
+                    (child.measuredHeight / screenAspectRatio).toInt(),
+                    MeasureSpec.EXACTLY
+                ),
+                MeasureSpec.makeMeasureSpec(
+                    child.measuredHeight,
+                    MeasureSpec.EXACTLY,
+                ),
+            )
+        }
+    }
+}
diff --git a/src/com/android/wallpaper/picker/FadeAnimationSurfaceView.kt b/src/com/android/wallpaper/picker/FadeAnimationSurfaceView.kt
new file mode 100644
index 0000000..c4b6695
--- /dev/null
+++ b/src/com/android/wallpaper/picker/FadeAnimationSurfaceView.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2022 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 android.content.Context
+import android.util.AttributeSet
+import android.view.SurfaceControl
+import android.view.SurfaceView
+
+/** A `SurfaceView` which allows the surface alpha to be adjusted by setting view alpha. */
+class FadeAnimationSurfaceView(context: Context, attrs: AttributeSet) :
+    SurfaceView(context, attrs) {
+
+    override fun onSetAlpha(alpha: Int): Boolean {
+        requestUpdateSurfacePositionAndScale()
+        return super.onSetAlpha(alpha)
+    }
+
+    override fun onSetSurfacePositionAndScale(
+        transaction: SurfaceControl.Transaction,
+        surface: SurfaceControl,
+        positionLeft: Int,
+        positionTop: Int,
+        postScaleX: Float,
+        postScaleY: Float
+    ) {
+        super.onSetSurfacePositionAndScale(
+            transaction,
+            surface,
+            positionLeft,
+            positionTop,
+            postScaleX,
+            postScaleY
+        )
+        transaction.setAlpha(surface, alpha)
+    }
+}
diff --git a/src/com/android/wallpaper/picker/ImagePreviewFragment.java b/src/com/android/wallpaper/picker/ImagePreviewFragment.java
index e2524ab..6d75c87 100755
--- a/src/com/android/wallpaper/picker/ImagePreviewFragment.java
+++ b/src/com/android/wallpaper/picker/ImagePreviewFragment.java
@@ -20,6 +20,7 @@
 import static android.view.View.MeasureSpec.EXACTLY;
 import static android.view.View.MeasureSpec.makeMeasureSpec;
 
+import static com.android.wallpaper.util.WallpaperSurfaceCallback.LOW_RES_BITMAP_BLUR_RADIUS;
 import static com.android.wallpaper.widget.BottomActionBar.BottomAction.APPLY;
 import static com.android.wallpaper.widget.BottomActionBar.BottomAction.EDIT;
 import static com.android.wallpaper.widget.BottomActionBar.BottomAction.INFORMATION;
@@ -37,6 +38,8 @@
 import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
+import android.graphics.RenderEffect;
+import android.graphics.Shader;
 import android.os.Bundle;
 import android.os.Handler;
 import android.util.Log;
@@ -69,6 +72,7 @@
 import com.android.wallpaper.module.LargeScreenMultiPanesChecker;
 import com.android.wallpaper.module.WallpaperPersister.Destination;
 import com.android.wallpaper.module.WallpaperPreferences;
+import com.android.wallpaper.util.DisplayUtils;
 import com.android.wallpaper.util.FullScreenAnimation;
 import com.android.wallpaper.util.ResourceUtils;
 import com.android.wallpaper.util.ScreenSizeCalculator;
@@ -133,6 +137,7 @@
     protected SubsamplingScaleImageView mFullResImageView;
     protected Asset mWallpaperAsset;
     private Future<ColorInfo> mColorFuture;
+    private DisplayUtils mDisplayUtils;
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
@@ -153,12 +158,13 @@
         View view = super.onCreateView(inflater, container, savedInstanceState);
 
         Activity activity = requireActivity();
+        mDisplayUtils = mInjector.getDisplayUtils(activity);
         ScreenSizeCalculator screenSizeCalculator = ScreenSizeCalculator.getInstance();
         mScreenSize = screenSizeCalculator.getScreenSize(
                 activity.getWindowManager().getDefaultDisplay());
         // "Wallpaper screen" size will be the size of the largest screen available
         mWallpaperScreenSize = screenSizeCalculator.getScreenSize(
-                mInjector.getDisplayUtils(activity).getWallpaperDisplay());
+                mDisplayUtils.getWallpaperDisplay());
 
         mContainer = view.findViewById(R.id.container);
         mTouchForwardingLayout = mContainer.findViewById(R.id.touch_forwarding_layout);
@@ -512,7 +518,7 @@
         Point crop = new Point(cropWidth, cropHeight);
         Rect visibleRawWallpaperRect =
                 WallpaperCropUtils.calculateVisibleRect(mRawWallpaperSize, crop);
-        if (offsetToStart) {
+        if (offsetToStart && mDisplayUtils.isOnWallpaperDisplay(requireActivity())) {
             if (WallpaperCropUtils.isRtl(requireContext())) {
                 visibleRawWallpaperRect.offsetTo(mRawWallpaperSize.x
                                 - visibleRawWallpaperRect.width(), visibleRawWallpaperRect.top);
@@ -601,6 +607,9 @@
                         R.layout.fullscreen_wallpaper_preview, null);
                 mFullResImageView = wallpaperPreviewContainer.findViewById(R.id.full_res_image);
                 mLowResImageView = wallpaperPreviewContainer.findViewById(R.id.low_res_image);
+                mLowResImageView.setRenderEffect(
+                        RenderEffect.createBlurEffect(LOW_RES_BITMAP_BLUR_RADIUS,
+                                LOW_RES_BITMAP_BLUR_RADIUS, Shader.TileMode.CLAMP));
                 mWallpaperAsset.decodeRawDimensions(getActivity(), dimensions -> {
                     // Don't continue loading the wallpaper if the Fragment is detached.
                     if (getActivity() == null) {
@@ -633,11 +642,14 @@
                 int origWidth = mWallpaperSurface.getWidth();
                 int origHeight = mWallpaperSurface.getHeight();
 
-                if (!mScreenSize.equals(mWallpaperScreenSize)) {
+                int scaledOrigWidth = origWidth;
+                if (!mDisplayUtils.isOnWallpaperDisplay(requireActivity())) {
+                    // Scale the width of the mWallpaperSurface if the current screen is not the
+                    // largest screen (wallpaper screen).
                     float previewToScreenScale = (float) origWidth / mScreenSize.x;
-                    origWidth = (int) (mWallpaperScreenSize.x * previewToScreenScale);
+                    scaledOrigWidth = (int) (mWallpaperScreenSize.x * previewToScreenScale);
                 }
-                int width = (int) (origWidth * scale);
+                int width = (int) (scaledOrigWidth * scale);
                 int height = (int) (origHeight * scale);
                 int left = (origWidth - width) / 2;
                 int top = (origHeight - height) / 2;
diff --git a/src/com/android/wallpaper/picker/LivePreviewFragment.java b/src/com/android/wallpaper/picker/LivePreviewFragment.java
index ccc1740..62e15b9 100644
--- a/src/com/android/wallpaper/picker/LivePreviewFragment.java
+++ b/src/com/android/wallpaper/picker/LivePreviewFragment.java
@@ -304,6 +304,7 @@
                 return;
             }
             if (mWallpaperSurfaceCallback.getHomeImageWallpaper() != null) {
+                mWallpaperSurfaceCallback.setHomeImageWallpaperBlur(true);
                 ColorInfo colorInfo = getColorInfo();
                 Integer placeholderColor = colorInfo.getPlaceholderColor();
                 mWallpaper.getThumbAsset(activity.getApplicationContext())
diff --git a/src/com/android/wallpaper/picker/PreviewFragment.java b/src/com/android/wallpaper/picker/PreviewFragment.java
index 0b26ca7..e9e78c8 100755
--- a/src/com/android/wallpaper/picker/PreviewFragment.java
+++ b/src/com/android/wallpaper/picker/PreviewFragment.java
@@ -58,6 +58,7 @@
 import com.android.wallpaper.module.WallpaperPreferences;
 import com.android.wallpaper.module.WallpaperSetter;
 import com.android.wallpaper.util.FullScreenAnimation;
+import com.android.wallpaper.util.PreviewUtils;
 import com.android.wallpaper.util.ResourceUtils;
 import com.android.wallpaper.widget.BottomActionBar;
 import com.android.wallpaper.widget.BottomActionBar.BottomSheetContent;
@@ -105,25 +106,6 @@
     public static final String ARG_FULL_SCREEN = "view_full_screen";
     public static final String ARG_TESTING_MODE_ENABLED = "testing_mode_enabled";
 
-    /**
-     * Creates and returns new instance of {@link ImagePreviewFragment} with the provided wallpaper
-     * set as an argument.
-     */
-    public static PreviewFragment newInstance(WallpaperInfo wallpaperInfo, @PreviewMode int mode,
-            boolean viewAsHome, boolean viewFullScreen, boolean testingModeEnabled) {
-        Bundle args = new Bundle();
-        args.putParcelable(ARG_WALLPAPER, wallpaperInfo);
-        args.putInt(ARG_PREVIEW_MODE, mode);
-        args.putBoolean(ARG_VIEW_AS_HOME, viewAsHome);
-        args.putBoolean(ARG_FULL_SCREEN, viewFullScreen);
-        args.putBoolean(ARG_TESTING_MODE_ENABLED, testingModeEnabled);
-
-        PreviewFragment fragment = wallpaperInfo instanceof LiveWallpaperInfo
-                ? new LivePreviewFragment() : new ImagePreviewFragment();
-        fragment.setArguments(args);
-        return fragment;
-    }
-
     private static final String TAG_LOAD_WALLPAPER_ERROR_DIALOG_FRAGMENT =
             "load_wallpaper_error_dialog";
     private static final String TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT =
@@ -273,7 +255,7 @@
             return;
         }
         startActivity(FullPreviewActivity.newIntent(getActivity(), wallpaperInfo,
-                /* viewAsHome= */ mLastSelectedTabPositionOptional.orElse(0) == 0),
+                        /* viewAsHome= */ mLastSelectedTabPositionOptional.orElse(0) == 0),
                 ActivityOptions.makeSceneTransitionAnimation(getActivity()).toBundle());
     }
 
@@ -336,7 +318,11 @@
 
     protected WorkspaceSurfaceHolderCallback createWorkspaceSurfaceCallback(
             SurfaceView workspaceSurface) {
-        return new WorkspaceSurfaceHolderCallback(workspaceSurface, getContext());
+        return new WorkspaceSurfaceHolderCallback(
+                workspaceSurface,
+                new PreviewUtils(
+                        getContext(),
+                        getContext().getString(R.string.grid_control_metadata_name)));
     }
 
     @Override
diff --git a/src/com/android/wallpaper/picker/WallpaperPreviewBitmapTransformation.java b/src/com/android/wallpaper/picker/WallpaperPreviewBitmapTransformation.java
index fa5dea5..064192f 100755
--- a/src/com/android/wallpaper/picker/WallpaperPreviewBitmapTransformation.java
+++ b/src/com/android/wallpaper/picker/WallpaperPreviewBitmapTransformation.java
@@ -77,7 +77,7 @@
                     cropSize.width(), cropSize.height());
         }
 
-        return BitmapProcessor.blur(mContext, cropped, cropped.getWidth(), cropped.getHeight());
+        return BitmapProcessor.createLowResBitmap(cropped, cropped.getWidth(), cropped.getHeight());
     }
 
     @Override
diff --git a/src/com/android/wallpaper/picker/WallpapersApplication.java b/src/com/android/wallpaper/picker/WallpapersApplication.java
index 2b423f4..fe299ce 100755
--- a/src/com/android/wallpaper/picker/WallpapersApplication.java
+++ b/src/com/android/wallpaper/picker/WallpapersApplication.java
@@ -18,7 +18,7 @@
 import android.app.Application;
 
 import com.android.wallpaper.module.InjectorProvider;
-import com.android.wallpaper.module.WallpapersInjector;
+import com.android.wallpaper.module.WallpaperPicker2Injector;
 
 /**
  * Application subclass that initializes the injector.
@@ -30,6 +30,6 @@
         super.onCreate();
 
         // Initialize the injector.
-        InjectorProvider.setInjector(new WallpapersInjector());
+        InjectorProvider.setInjector(new WallpaperPicker2Injector());
     }
 }
diff --git a/src/com/android/wallpaper/picker/WorkspaceSurfaceHolderCallback.java b/src/com/android/wallpaper/picker/WorkspaceSurfaceHolderCallback.java
index 2661923..bb4c0d3 100644
--- a/src/com/android/wallpaper/picker/WorkspaceSurfaceHolderCallback.java
+++ b/src/com/android/wallpaper/picker/WorkspaceSurfaceHolderCallback.java
@@ -16,7 +16,6 @@
 package com.android.wallpaper.picker;
 
 import android.app.WallpaperColors;
-import android.content.Context;
 import android.os.Bundle;
 import android.os.Message;
 import android.os.RemoteException;
@@ -27,7 +26,6 @@
 
 import androidx.annotation.Nullable;
 
-import com.android.wallpaper.R;
 import com.android.wallpaper.util.PreviewUtils;
 import com.android.wallpaper.util.SurfaceViewUtils;
 
@@ -60,9 +58,12 @@
     private WorkspaceRenderListener mListener;
 
     private boolean mNeedsToCleanUp;
+    @Nullable private final Bundle mExtras;
 
-    public WorkspaceSurfaceHolderCallback(SurfaceView workspaceSurface, Context context) {
-        this(workspaceSurface, context, false);
+    public WorkspaceSurfaceHolderCallback(
+            SurfaceView workspaceSurface,
+            PreviewUtils previewUtils) {
+        this(workspaceSurface, previewUtils, false, null);
     }
 
     /**
@@ -73,12 +74,33 @@
      *                                 the surface is created and wallpaper colors are set via
      *                                 {@link #setWallpaperColors(WallpaperColors)}
      */
-    public WorkspaceSurfaceHolderCallback(SurfaceView workspaceSurface, Context context,
+    public WorkspaceSurfaceHolderCallback(
+            SurfaceView workspaceSurface,
+            PreviewUtils previewUtils,
             boolean shouldUseWallpaperColors) {
+        this(
+                workspaceSurface,
+                previewUtils,
+                shouldUseWallpaperColors,
+                null);
+    }
+
+    public WorkspaceSurfaceHolderCallback(
+            SurfaceView workspaceSurface,
+            PreviewUtils previewUtils,
+            @Nullable Bundle extras) {
+        this(workspaceSurface, previewUtils, false, extras);
+    }
+
+    private WorkspaceSurfaceHolderCallback(
+            SurfaceView workspaceSurface,
+            PreviewUtils previewUtils,
+            boolean shouldUseWallpaperColors,
+            @Nullable Bundle extras) {
         mWorkspaceSurface = workspaceSurface;
-        mPreviewUtils = new PreviewUtils(context,
-                context.getString(R.string.grid_control_metadata_name));
+        mPreviewUtils = previewUtils;
         mShouldUseWallpaperColors = shouldUseWallpaperColors;
+        mExtras = extras;
     }
 
     @Override
@@ -96,7 +118,7 @@
      *
      * @param colors WallpaperColors extracted from the current wallpaper preview, or {@code null}
      *               if none are available.
-     * @see #WorkspaceSurfaceHolderCallback(SurfaceView, Context, boolean)
+     * @see #WorkspaceSurfaceHolderCallback(SurfaceView, PreviewUtils, boolean)
      */
     public void setWallpaperColors(@Nullable WallpaperColors colors) {
         if (!mShouldUseWallpaperColors) {
@@ -141,6 +163,27 @@
     public void surfaceDestroyed(SurfaceHolder holder) {
     }
 
+    /**
+     * Sends a message to the remote renderer.
+     *
+     * @param what An ID for the message (the remote side can pick this up through
+     * {@link Message#what}.
+     * @param bundle The data of the message (the remote side can pick this up through
+     * {@link Message#getData()}.
+     */
+    public void send(final int what, @Nullable Bundle bundle) {
+        if (mCallback != null) {
+            try {
+                final Message message = new Message();
+                message.what = what;
+                message.setData(bundle);
+                mCallback.replyTo.send(message);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Couldn't send message to workspace preview", e);
+            }
+        }
+    }
+
     public void cleanUp() {
         if (mCallback != null) {
             try {
@@ -170,7 +213,7 @@
                             + "crash");
             return;
         }
-        Bundle request = SurfaceViewUtils.createSurfaceViewRequest(workspaceSurface);
+        Bundle request = SurfaceViewUtils.createSurfaceViewRequest(workspaceSurface, mExtras);
         if (mWallpaperColors != null) {
             request.putParcelable(KEY_WALLPAPER_COLORS, mWallpaperColors);
         }
diff --git a/src/com/android/wallpaper/picker/customization/ui/binder/CustomizationPickerBinder.kt b/src/com/android/wallpaper/picker/customization/ui/binder/CustomizationPickerBinder.kt
new file mode 100644
index 0000000..53c8375
--- /dev/null
+++ b/src/com/android/wallpaper/picker/customization/ui/binder/CustomizationPickerBinder.kt
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2022 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.customization.ui.binder
+
+import android.view.View
+import android.view.ViewGroup
+import android.view.WindowInsets
+import android.widget.FrameLayout
+import androidx.annotation.IdRes
+import androidx.core.view.children
+import androidx.core.view.updateLayoutParams
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.wallpaper.R
+import com.android.wallpaper.model.CustomizationSectionController
+import com.android.wallpaper.model.WallpaperSectionController
+import com.android.wallpaper.picker.SectionView
+import com.android.wallpaper.picker.customization.ui.section.ScreenPreviewSectionController
+import com.android.wallpaper.picker.customization.ui.viewmodel.CustomizationPickerViewModel
+import com.android.wallpaper.picker.undo.ui.binder.RevertToolbarButtonBinder
+import kotlinx.coroutines.launch
+
+typealias SectionController = CustomizationSectionController<*>
+
+/** Binds view to view-model for the customization picker. */
+object CustomizationPickerBinder {
+    @JvmStatic
+    fun bind(
+        view: View,
+        @IdRes toolbarViewId: Int,
+        viewModel: CustomizationPickerViewModel,
+        lifecycleOwner: LifecycleOwner,
+        sectionControllerProvider: (isOnLockScreen: Boolean) -> List<SectionController>,
+    ) {
+        RevertToolbarButtonBinder.bind(
+            view = view.requireViewById(toolbarViewId),
+            viewModel = viewModel.undo,
+            lifecycleOwner = lifecycleOwner,
+        )
+
+        CustomizationPickerTabsBinder.bind(
+            view = view,
+            viewModel = viewModel,
+            lifecycleOwner = lifecycleOwner,
+        )
+
+        val sectionContainer = view.findViewById<ViewGroup>(R.id.section_container)
+        sectionContainer.setOnApplyWindowInsetsListener { v: View, windowInsets: WindowInsets ->
+            v.setPadding(
+                v.paddingLeft,
+                v.paddingTop,
+                v.paddingRight,
+                windowInsets.systemWindowInsetBottom
+            )
+            windowInsets.consumeSystemWindowInsets()
+        }
+        sectionContainer.updateLayoutParams<FrameLayout.LayoutParams> {
+            // We don't want the top margin from the XML because our tabs have that as padding so
+            // they can be collapsed into the toolbar with spacing from the actual title text.
+            topMargin = 0
+        }
+
+        lifecycleOwner.lifecycleScope.launch {
+            lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
+                launch {
+                    viewModel.isOnLockScreen.collect { isOnLockScreen ->
+                        // These are the available section controllers we should use now.
+                        val newSectionControllers =
+                            sectionControllerProvider.invoke(isOnLockScreen).filter {
+                                it.isAvailable(view.context)
+                            }
+
+                        check(
+                            newSectionControllers[0] is WallpaperSectionController ||
+                                newSectionControllers[0] is ScreenPreviewSectionController
+                        ) {
+                            "The first section must always be the preview or the assumption below" +
+                                " must be updated."
+                        }
+
+                        val firstTime = sectionContainer.childCount == 0
+                        if (!firstTime) {
+                            // Remove all views, except the very first one, which we assume is for
+                            // the wallpaper preview section.
+                            sectionContainer.removeViews(1, sectionContainer.childCount - 1)
+
+                            // The old controllers for the removed views should be released, except
+                            // for the very first one, which is for the wallpaper preview section;
+                            // that one we keep but just tell it that we switched screens.
+                            sectionContainer.children
+                                .mapNotNull { it.tag as? SectionController }
+                                .forEachIndexed { index, oldController ->
+                                    if (index == 0) {
+                                        // We assume that index 0 is the wallpaper preview section.
+                                        // We keep it because it's an expensive section (as it needs
+                                        // to maintain a wallpaper connection that seems to be
+                                        // making assumptions about its SurfaceView always remaining
+                                        // attached to the window).
+                                        oldController.onScreenSwitched(isOnLockScreen)
+                                    } else {
+                                        // All other old controllers will be thrown out so let's
+                                        // release them.
+                                        oldController.release()
+                                    }
+                                }
+                        }
+
+                        // Let's add the new controllers and views.
+                        newSectionControllers.forEachIndexed { index, controller ->
+                            if (firstTime || index > 0) {
+                                val addedView = controller.createView(view.context, isOnLockScreen)
+                                addedView.tag = controller
+                                sectionContainer.addView(addedView)
+                            }
+                        }
+                    }
+                }
+            }
+
+            // This happens when the lifecycle is stopped.
+            sectionContainer.children
+                .mapNotNull { it.tag as? CustomizationSectionController<out SectionView> }
+                .forEach { controller -> controller.release() }
+            sectionContainer.removeAllViews()
+        }
+    }
+}
diff --git a/src/com/android/wallpaper/picker/customization/ui/binder/CustomizationPickerTabsBinder.kt b/src/com/android/wallpaper/picker/customization/ui/binder/CustomizationPickerTabsBinder.kt
new file mode 100644
index 0000000..5c06f03
--- /dev/null
+++ b/src/com/android/wallpaper/picker/customization/ui/binder/CustomizationPickerTabsBinder.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2022 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.customization.ui.binder
+
+import android.view.View
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.wallpaper.R
+import com.android.wallpaper.picker.customization.ui.viewmodel.CustomizationPickerViewModel
+import com.android.wallpaper.widget.DuoTabs
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.launch
+
+/** Binds view to view-model for the customization picker tabs. */
+object CustomizationPickerTabsBinder {
+    @JvmStatic
+    fun bind(
+        view: View,
+        viewModel: CustomizationPickerViewModel,
+        lifecycleOwner: LifecycleOwner,
+    ) {
+        val tabs: DuoTabs = view.requireViewById(R.id.duo_tabs)
+        tabs.setTabText(
+            view.context.getString(R.string.lock_screen_tab),
+            view.context.getString(R.string.home_screen_tab),
+        )
+
+        lifecycleOwner.lifecycleScope.launch {
+            lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
+                launch {
+                    combine(viewModel.lockScreenTab, viewModel.homeScreenTab) {
+                            lockScreenTabViewModel,
+                            homeScreenTabViewModel ->
+                            lockScreenTabViewModel to homeScreenTabViewModel
+                        }
+                        .collect { (lockScreenTabViewModel, homeScreenTabViewModel) ->
+                            tabs.setOnTabSelectedListener(null)
+                            tabs.selectTab(
+                                when {
+                                    lockScreenTabViewModel.isSelected -> DuoTabs.TAB_PRIMARY
+                                    else -> DuoTabs.TAB_SECONDARY
+                                },
+                            )
+                            tabs.setOnTabSelectedListener { tabId ->
+                                when (tabId) {
+                                    DuoTabs.TAB_PRIMARY ->
+                                        lockScreenTabViewModel.onClicked?.invoke()
+                                    DuoTabs.TAB_SECONDARY ->
+                                        homeScreenTabViewModel.onClicked?.invoke()
+                                }
+                            }
+                        }
+                }
+            }
+        }
+    }
+}
diff --git a/src/com/android/wallpaper/picker/customization/ui/binder/ScreenPreviewBinder.kt b/src/com/android/wallpaper/picker/customization/ui/binder/ScreenPreviewBinder.kt
new file mode 100644
index 0000000..9df609e
--- /dev/null
+++ b/src/com/android/wallpaper/picker/customization/ui/binder/ScreenPreviewBinder.kt
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2022 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.customization.ui.binder
+
+import android.app.Activity
+import android.app.WallpaperColors
+import android.content.Intent
+import android.os.Bundle
+import android.service.wallpaper.WallpaperService
+import android.view.SurfaceView
+import androidx.cardview.widget.CardView
+import androidx.core.view.isVisible
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleEventObserver
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.lifecycleScope
+import com.android.wallpaper.R
+import com.android.wallpaper.asset.Asset
+import com.android.wallpaper.asset.BitmapCachingAsset
+import com.android.wallpaper.asset.CurrentWallpaperAssetVN
+import com.android.wallpaper.model.LiveWallpaperInfo
+import com.android.wallpaper.model.WallpaperInfo
+import com.android.wallpaper.picker.WorkspaceSurfaceHolderCallback
+import com.android.wallpaper.picker.customization.ui.viewmodel.ScreenPreviewViewModel
+import com.android.wallpaper.util.ResourceUtils
+import com.android.wallpaper.util.WallpaperConnection
+import com.android.wallpaper.util.WallpaperSurfaceCallback
+import java.util.concurrent.CompletableFuture
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.launch
+
+/**
+ * Binds between view and view-model for rendering the preview of the home screen or the lock
+ * screen.
+ */
+@OptIn(ExperimentalCoroutinesApi::class)
+object ScreenPreviewBinder {
+    interface Binding {
+        fun show()
+        fun hide()
+        fun sendMessage(
+            id: Int,
+            args: Bundle = Bundle.EMPTY,
+        )
+    }
+
+    @JvmStatic
+    fun bind(
+        activity: Activity,
+        previewView: CardView,
+        viewModel: ScreenPreviewViewModel,
+        lifecycleOwner: LifecycleOwner,
+        offsetToStart: Boolean,
+    ): Binding {
+        val workspaceSurface: SurfaceView = previewView.requireViewById(R.id.workspace_surface)
+        val wallpaperSurface: SurfaceView = previewView.requireViewById(R.id.wallpaper_surface)
+
+        previewView.radius =
+            previewView.resources.getDimension(R.dimen.wallpaper_picker_entry_card_corner_radius)
+
+        var previewSurfaceCallback: WorkspaceSurfaceHolderCallback? = null
+        var wallpaperSurfaceCallback: WallpaperSurfaceCallback? = null
+        var wallpaperConnection: WallpaperConnection? = null
+        var wallpaperInfo: WallpaperInfo? = null
+
+        lifecycleOwner.lifecycle.addObserver(
+            LifecycleEventObserver { _, event ->
+                when (event) {
+                    Lifecycle.Event.ON_CREATE -> {
+                        previewSurfaceCallback =
+                            WorkspaceSurfaceHolderCallback(
+                                workspaceSurface,
+                                viewModel.previewUtils,
+                                viewModel.getInitialExtras(),
+                            )
+                        workspaceSurface.holder.addCallback(previewSurfaceCallback)
+                        workspaceSurface.setZOrderMediaOverlay(true)
+
+                        wallpaperSurfaceCallback =
+                            WallpaperSurfaceCallback(
+                                previewView.context,
+                                previewView,
+                                wallpaperSurface,
+                                CompletableFuture.completedFuture(
+                                    WallpaperInfo.ColorInfo(
+                                        /* wallpaperColors= */ null,
+                                        ResourceUtils.getColorAttr(
+                                            previewView.context,
+                                            android.R.attr.colorSecondary,
+                                        )
+                                    )
+                                ),
+                            ) {
+                                maybeLoadThumbnail(
+                                    activity = activity,
+                                    wallpaperInfo = wallpaperInfo,
+                                    surfaceCallback = wallpaperSurfaceCallback,
+                                    offsetToStart = offsetToStart,
+                                )
+                            }
+                        wallpaperSurface.holder.addCallback(wallpaperSurfaceCallback)
+                        wallpaperSurface.setZOrderMediaOverlay(true)
+                    }
+                    Lifecycle.Event.ON_DESTROY -> {
+                        workspaceSurface.holder.removeCallback(previewSurfaceCallback)
+                        previewSurfaceCallback?.cleanUp()
+                        wallpaperSurface.holder.removeCallback(wallpaperSurfaceCallback)
+                        wallpaperSurfaceCallback?.cleanUp()
+                    }
+                    Lifecycle.Event.ON_RESUME -> {
+                        lifecycleOwner.lifecycleScope.launch {
+                            wallpaperInfo = viewModel.getWallpaperInfo()
+                            (wallpaperInfo as? LiveWallpaperInfo)?.let { liveWallpaperInfo ->
+                                if (WallpaperConnection.isPreviewAvailable()) {
+                                    wallpaperConnection =
+                                        WallpaperConnection(
+                                            Intent(WallpaperService.SERVICE_INTERFACE).apply {
+                                                setClassName(
+                                                    liveWallpaperInfo.wallpaperComponent
+                                                        .packageName,
+                                                    liveWallpaperInfo.wallpaperComponent.serviceName
+                                                )
+                                            },
+                                            previewView.context,
+                                            object :
+                                                WallpaperConnection.WallpaperConnectionListener {
+                                                override fun onWallpaperColorsChanged(
+                                                    colors: WallpaperColors?,
+                                                    displayId: Int
+                                                ) {
+                                                    viewModel.onWallpaperColorsChanged(colors)
+                                                }
+                                            },
+                                            wallpaperSurface,
+                                            null,
+                                        )
+
+                                    wallpaperConnection?.connect()
+                                    wallpaperConnection?.setVisibility(true)
+                                }
+                            }
+                            maybeLoadThumbnail(
+                                activity = activity,
+                                wallpaperInfo = wallpaperInfo,
+                                surfaceCallback = wallpaperSurfaceCallback,
+                                offsetToStart = offsetToStart,
+                            )
+                        }
+                    }
+                    Lifecycle.Event.ON_PAUSE -> {
+                        wallpaperConnection?.setVisibility(false)
+                    }
+                    Lifecycle.Event.ON_STOP -> {
+                        wallpaperConnection?.disconnect()
+                    }
+                    else -> Unit
+                }
+            }
+        )
+
+        return object : Binding {
+            override fun show() {
+                previewView.isVisible = true
+                wallpaperSurface.isVisible = true
+                workspaceSurface.isVisible = true
+            }
+
+            override fun hide() {
+                previewView.isVisible = false
+                wallpaperSurface.isVisible = false
+                workspaceSurface.isVisible = false
+            }
+
+            override fun sendMessage(id: Int, args: Bundle) {
+                previewSurfaceCallback?.send(id, args)
+            }
+        }
+    }
+
+    private fun maybeLoadThumbnail(
+        activity: Activity,
+        wallpaperInfo: WallpaperInfo?,
+        surfaceCallback: WallpaperSurfaceCallback?,
+        offsetToStart: Boolean,
+    ) {
+        if (wallpaperInfo == null || surfaceCallback == null) {
+            return
+        }
+
+        val imageView = surfaceCallback.homeImageWallpaper
+        val thumbAsset: Asset = wallpaperInfo.getThumbAsset(activity)
+        if (imageView != null && imageView.drawable == null) {
+            // Respect offsetToStart only for CurrentWallpaperAssetVN otherwise true.
+            BitmapCachingAsset(activity, thumbAsset)
+                .loadPreviewImage(
+                    activity,
+                    imageView,
+                    ResourceUtils.getColorAttr(activity, android.R.attr.colorSecondary),
+                    /* offsetToStart= */ thumbAsset !is CurrentWallpaperAssetVN || offsetToStart
+                )
+        }
+    }
+}
diff --git a/src/com/android/wallpaper/picker/customization/ui/section/ScreenPreviewSectionController.kt b/src/com/android/wallpaper/picker/customization/ui/section/ScreenPreviewSectionController.kt
new file mode 100644
index 0000000..6cb7d06
--- /dev/null
+++ b/src/com/android/wallpaper/picker/customization/ui/section/ScreenPreviewSectionController.kt
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2022 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.customization.ui.section
+
+import android.annotation.SuppressLint
+import android.app.Activity
+import android.app.WallpaperColors
+import android.content.Context
+import android.view.LayoutInflater
+import androidx.cardview.widget.CardView
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.lifecycleScope
+import com.android.wallpaper.R
+import com.android.wallpaper.model.CustomizationSectionController
+import com.android.wallpaper.model.WallpaperColorsViewModel
+import com.android.wallpaper.model.WallpaperInfo
+import com.android.wallpaper.module.CurrentWallpaperInfoFactory
+import com.android.wallpaper.module.CustomizationSections
+import com.android.wallpaper.picker.customization.ui.binder.ScreenPreviewBinder
+import com.android.wallpaper.picker.customization.ui.viewmodel.ScreenPreviewViewModel
+import com.android.wallpaper.util.DisplayUtils
+import com.android.wallpaper.util.PreviewUtils
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.suspendCancellableCoroutine
+import kotlinx.coroutines.withContext
+
+/** Controls the screen preview section. */
+@OptIn(ExperimentalCoroutinesApi::class)
+class ScreenPreviewSectionController(
+    private val activity: Activity,
+    private val lifecycleOwner: LifecycleOwner,
+    private val initialScreen: CustomizationSections.Screen,
+    private val wallpaperInfoFactory: CurrentWallpaperInfoFactory,
+    private val colorViewModel: WallpaperColorsViewModel,
+    private val displayUtils: DisplayUtils,
+) : CustomizationSectionController<ScreenPreviewView> {
+
+    private lateinit var lockScreenBinding: ScreenPreviewBinder.Binding
+    private lateinit var homeScreenBinding: ScreenPreviewBinder.Binding
+
+    override fun isAvailable(context: Context?): Boolean {
+        // Assumption is that, if this section controller is included, we are using the revamped UI
+        // so it should always be shown.
+        return true
+    }
+
+    @SuppressLint("InflateParams")
+    override fun createView(context: Context): ScreenPreviewView {
+        val view =
+            LayoutInflater.from(context)
+                .inflate(
+                    R.layout.screen_preview_section,
+                    /* parent= */ null,
+                ) as ScreenPreviewView
+        val lockScreenView: CardView = view.requireViewById(R.id.lock_preview)
+        val homeScreenView: CardView = view.requireViewById(R.id.home_preview)
+
+        lockScreenBinding =
+            ScreenPreviewBinder.bind(
+                activity = activity,
+                previewView = lockScreenView,
+                viewModel =
+                    ScreenPreviewViewModel(
+                        previewUtils =
+                            PreviewUtils(
+                                context = context,
+                                authority =
+                                    context.getString(
+                                        R.string.lock_screen_preview_provider_authority,
+                                    ),
+                            ),
+                        wallpaperInfoProvider = {
+                            suspendCancellableCoroutine { continuation ->
+                                wallpaperInfoFactory.createCurrentWallpaperInfos(
+                                    { homeWallpaper, lockWallpaper, _ ->
+                                        val wallpaper = lockWallpaper ?: homeWallpaper
+                                        loadInitialColors(
+                                            context = context,
+                                            wallpaper = wallpaper,
+                                            liveData = colorViewModel.lockWallpaperColors,
+                                        )
+                                        continuation.resume(wallpaper, null)
+                                    },
+                                    /* forceRefresh= */ true,
+                                )
+                            }
+                        },
+                        onWallpaperColorChanged = { colors ->
+                            colorViewModel.lockWallpaperColors.value = colors
+                        },
+                    ),
+                lifecycleOwner = lifecycleOwner,
+                offsetToStart = displayUtils.isOnWallpaperDisplay(activity),
+            )
+        homeScreenBinding =
+            ScreenPreviewBinder.bind(
+                activity = activity,
+                previewView = homeScreenView,
+                viewModel =
+                    ScreenPreviewViewModel(
+                        previewUtils =
+                            PreviewUtils(
+                                context = context,
+                                authorityMetadataKey =
+                                    context.getString(
+                                        R.string.grid_control_metadata_name,
+                                    ),
+                            ),
+                        wallpaperInfoProvider = {
+                            suspendCancellableCoroutine { continuation ->
+                                wallpaperInfoFactory.createCurrentWallpaperInfos(
+                                    { homeWallpaper, lockWallpaper, _ ->
+                                        val wallpaper = homeWallpaper ?: lockWallpaper
+                                        loadInitialColors(
+                                            context = context,
+                                            wallpaper = wallpaper,
+                                            liveData = colorViewModel.homeWallpaperColors,
+                                        )
+                                        continuation.resume(wallpaper, null)
+                                    },
+                                    /* forceRefresh= */ true,
+                                )
+                            }
+                        },
+                        onWallpaperColorChanged = { colors ->
+                            colorViewModel.lockWallpaperColors.value = colors
+                        },
+                    ),
+                lifecycleOwner = lifecycleOwner,
+                offsetToStart = displayUtils.isOnWallpaperDisplay(activity),
+            )
+
+        onScreenSwitched(isOnLockScreen = initialScreen == CustomizationSections.Screen.LOCK_SCREEN)
+
+        return view
+    }
+
+    override fun onScreenSwitched(isOnLockScreen: Boolean) {
+        if (isOnLockScreen) {
+            lockScreenBinding.show()
+            homeScreenBinding.hide()
+        } else {
+            lockScreenBinding.hide()
+            homeScreenBinding.show()
+        }
+    }
+
+    private fun loadInitialColors(
+        context: Context,
+        wallpaper: WallpaperInfo?,
+        liveData: MutableLiveData<WallpaperColors>,
+    ) {
+        lifecycleOwner.lifecycleScope.launch(Dispatchers.IO) {
+            val colors = wallpaper?.computeColorInfo(context)?.get()?.wallpaperColors
+            withContext(Dispatchers.Main) { liveData.value = colors }
+        }
+    }
+}
diff --git a/src/com/android/wallpaper/picker/customization/ui/section/ScreenPreviewView.kt b/src/com/android/wallpaper/picker/customization/ui/section/ScreenPreviewView.kt
new file mode 100644
index 0000000..ee55de4
--- /dev/null
+++ b/src/com/android/wallpaper/picker/customization/ui/section/ScreenPreviewView.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022 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.customization.ui.section
+
+import android.content.Context
+import android.util.AttributeSet
+import com.android.wallpaper.picker.SectionView
+
+class ScreenPreviewView(
+    context: Context,
+    attrs: AttributeSet?,
+) :
+    SectionView(
+        context,
+        attrs,
+    )
diff --git a/src/com/android/wallpaper/picker/customization/ui/viewmodel/CustomizationPickerTabViewModel.kt b/src/com/android/wallpaper/picker/customization/ui/viewmodel/CustomizationPickerTabViewModel.kt
new file mode 100644
index 0000000..67263f2
--- /dev/null
+++ b/src/com/android/wallpaper/picker/customization/ui/viewmodel/CustomizationPickerTabViewModel.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2022 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.customization.ui.viewmodel
+
+/** Models UI state for a single tab. */
+data class CustomizationPickerTabViewModel(
+    val isSelected: Boolean,
+    val onClicked: (() -> Unit)?,
+)
diff --git a/src/com/android/wallpaper/picker/customization/ui/viewmodel/CustomizationPickerViewModel.kt b/src/com/android/wallpaper/picker/customization/ui/viewmodel/CustomizationPickerViewModel.kt
new file mode 100644
index 0000000..7fd622c
--- /dev/null
+++ b/src/com/android/wallpaper/picker/customization/ui/viewmodel/CustomizationPickerViewModel.kt
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2022 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.customization.ui.viewmodel
+
+import android.os.Bundle
+import androidx.annotation.VisibleForTesting
+import androidx.lifecycle.AbstractSavedStateViewModelFactory
+import androidx.lifecycle.SavedStateHandle
+import androidx.lifecycle.ViewModel
+import androidx.savedstate.SavedStateRegistryOwner
+import com.android.wallpaper.picker.undo.domain.interactor.UndoInteractor
+import com.android.wallpaper.picker.undo.ui.viewmodel.UndoViewModel
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.map
+
+/** Models UI state for the customization picker. */
+class CustomizationPickerViewModel
+@VisibleForTesting
+constructor(
+    undoInteractor: UndoInteractor,
+    private val savedStateHandle: SavedStateHandle,
+) : ViewModel() {
+
+    val undo: UndoViewModel =
+        UndoViewModel(
+            interactor = undoInteractor,
+        )
+
+    private val _isOnLockScreen = MutableStateFlow(true)
+    /** Whether we are on the lock screen. If `false`, we are on the home screen. */
+    val isOnLockScreen: Flow<Boolean> = _isOnLockScreen.asStateFlow()
+
+    /** A view-model for the "lock screen" tab. */
+    val lockScreenTab: Flow<CustomizationPickerTabViewModel> =
+        isOnLockScreen.map { onLockScreen ->
+            CustomizationPickerTabViewModel(
+                isSelected = onLockScreen,
+                onClicked =
+                    if (!onLockScreen) {
+                        {
+                            _isOnLockScreen.value = true
+                            savedStateHandle[KEY_SAVED_STATE_IS_ON_LOCK_SCREEN] = true
+                        }
+                    } else {
+                        null
+                    }
+            )
+        }
+    /** A view-model for the "home screen" tab. */
+    val homeScreenTab: Flow<CustomizationPickerTabViewModel> =
+        isOnLockScreen.map { onLockScreen ->
+            CustomizationPickerTabViewModel(
+                isSelected = !onLockScreen,
+                onClicked =
+                    if (onLockScreen) {
+                        {
+                            _isOnLockScreen.value = false
+                            savedStateHandle[KEY_SAVED_STATE_IS_ON_LOCK_SCREEN] = false
+                        }
+                    } else {
+                        null
+                    }
+            )
+        }
+
+    init {
+        _isOnLockScreen.value = savedStateHandle[KEY_SAVED_STATE_IS_ON_LOCK_SCREEN] ?: true
+    }
+
+    companion object {
+        @JvmStatic
+        fun newFactory(
+            owner: SavedStateRegistryOwner,
+            defaultArgs: Bundle? = null,
+            undoInteractor: UndoInteractor,
+        ): AbstractSavedStateViewModelFactory =
+            object : AbstractSavedStateViewModelFactory(owner, defaultArgs) {
+                @Suppress("UNCHECKED_CAST")
+                override fun <T : ViewModel> create(
+                    key: String,
+                    modelClass: Class<T>,
+                    handle: SavedStateHandle,
+                ): T {
+                    return CustomizationPickerViewModel(
+                        undoInteractor = undoInteractor,
+                        savedStateHandle = handle,
+                    )
+                        as T
+                }
+            }
+
+        private const val KEY_SAVED_STATE_IS_ON_LOCK_SCREEN = "is_on_lock_screen"
+    }
+}
diff --git a/src/com/android/wallpaper/picker/customization/ui/viewmodel/ScreenPreviewViewModel.kt b/src/com/android/wallpaper/picker/customization/ui/viewmodel/ScreenPreviewViewModel.kt
new file mode 100644
index 0000000..79e18a3
--- /dev/null
+++ b/src/com/android/wallpaper/picker/customization/ui/viewmodel/ScreenPreviewViewModel.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 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.customization.ui.viewmodel
+
+import android.app.WallpaperColors
+import android.os.Bundle
+import com.android.wallpaper.model.WallpaperInfo
+import com.android.wallpaper.util.PreviewUtils
+
+/** Models the UI state for a preview of the home screen or lock screen. */
+class ScreenPreviewViewModel(
+    val previewUtils: PreviewUtils,
+    private val initialExtrasProvider: () -> Bundle? = { null },
+    private val wallpaperInfoProvider: suspend () -> WallpaperInfo?,
+    private val onWallpaperColorChanged: (WallpaperColors?) -> Unit = {},
+) {
+    fun getInitialExtras(): Bundle? {
+        return initialExtrasProvider.invoke()
+    }
+
+    suspend fun getWallpaperInfo(): WallpaperInfo? {
+        return wallpaperInfoProvider.invoke()
+    }
+
+    fun onWallpaperColorsChanged(colors: WallpaperColors?) {
+        onWallpaperColorChanged.invoke(colors)
+    }
+}
diff --git a/src/com/android/wallpaper/picker/individual/IndividualPickerFragment2.kt b/src/com/android/wallpaper/picker/individual/IndividualPickerFragment2.kt
new file mode 100644
index 0000000..4a21dbc
--- /dev/null
+++ b/src/com/android/wallpaper/picker/individual/IndividualPickerFragment2.kt
@@ -0,0 +1,787 @@
+/*
+ * Copyright (C) 2022 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.individual
+
+import android.app.Activity
+import android.app.ProgressDialog
+import android.app.WallpaperManager
+import android.content.DialogInterface
+import android.content.res.Configuration
+import android.content.res.Resources
+import android.content.res.Resources.ID_NULL
+import android.graphics.Point
+import android.os.Build
+import android.os.Build.VERSION_CODES
+import android.os.Bundle
+import android.service.wallpaper.WallpaperService
+import android.text.TextUtils
+import android.util.ArraySet
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.MenuItem
+import android.view.View
+import android.view.ViewGroup
+import android.view.WindowInsets
+import android.widget.ImageView
+import android.widget.RelativeLayout
+import android.widget.TextView
+import android.widget.Toast
+import androidx.annotation.DrawableRes
+import androidx.cardview.widget.CardView
+import androidx.core.widget.ContentLoadingProgressBar
+import androidx.fragment.app.DialogFragment
+import androidx.recyclerview.widget.GridLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import com.android.wallpaper.R
+import com.android.wallpaper.model.Category
+import com.android.wallpaper.model.CategoryProvider
+import com.android.wallpaper.model.CategoryReceiver
+import com.android.wallpaper.model.WallpaperCategory
+import com.android.wallpaper.model.WallpaperInfo
+import com.android.wallpaper.model.WallpaperRotationInitializer
+import com.android.wallpaper.model.WallpaperRotationInitializer.NetworkPreference
+import com.android.wallpaper.module.InjectorProvider
+import com.android.wallpaper.module.PackageStatusNotifier
+import com.android.wallpaper.picker.AppbarFragment
+import com.android.wallpaper.picker.FragmentTransactionChecker
+import com.android.wallpaper.picker.MyPhotosStarter.MyPhotosStarterProvider
+import com.android.wallpaper.picker.RotationStarter
+import com.android.wallpaper.picker.StartRotationDialogFragment
+import com.android.wallpaper.picker.StartRotationErrorDialogFragment
+import com.android.wallpaper.util.ActivityUtils
+import com.android.wallpaper.util.DiskBasedLogger
+import com.android.wallpaper.util.LaunchUtils
+import com.android.wallpaper.util.SizeCalculator
+import com.android.wallpaper.widget.GridPaddingDecoration
+import com.android.wallpaper.widget.WallpaperPickerRecyclerViewAccessibilityDelegate
+import com.android.wallpaper.widget.WallpaperPickerRecyclerViewAccessibilityDelegate.BottomSheetHost
+import com.bumptech.glide.Glide
+import com.bumptech.glide.MemoryCategory
+import java.util.Date
+
+/** Displays the Main UI for picking an individual wallpaper image. */
+class IndividualPickerFragment2 :
+    AppbarFragment(),
+    RotationStarter,
+    StartRotationErrorDialogFragment.Listener,
+    StartRotationDialogFragment.Listener {
+
+    companion object {
+        private const val TAG = "IndividualPickerFrag2"
+
+        /**
+         * Position of a special tile that doesn't belong to an individual wallpaper of the
+         * category, such as "my photos" or "daily rotation".
+         */
+        private const val SPECIAL_FIXED_TILE_ADAPTER_POSITION = 0
+
+        private const val ARG_CATEGORY_COLLECTION_ID = "category_collection_id"
+
+        private const val UNUSED_REQUEST_CODE = 1
+        private const val TAG_START_ROTATION_DIALOG = "start_rotation_dialog"
+        private const val TAG_START_ROTATION_ERROR_DIALOG = "start_rotation_error_dialog"
+        private const val PROGRESS_DIALOG_INDETERMINATE = true
+        private const val KEY_NIGHT_MODE = "IndividualPickerFragment.NIGHT_MODE"
+        private const val MAX_CAPACITY_IN_FEWER_COLUMN_LAYOUT = 8
+        private val PROGRESS_DIALOG_NO_TITLE = null
+
+        fun newInstance(collectionId: String?): IndividualPickerFragment2 {
+            val args = Bundle()
+            args.putString(ARG_CATEGORY_COLLECTION_ID, collectionId)
+            val fragment = IndividualPickerFragment2()
+            fragment.arguments = args
+            return fragment
+        }
+    }
+
+    private lateinit var imageGrid: RecyclerView
+    private var adapter: IndividualAdapter? = null
+    private lateinit var category: WallpaperCategory
+    private var wallpaperRotationInitializer: WallpaperRotationInitializer? = null
+    private lateinit var items: MutableList<PickerItem>
+    private var packageStatusNotifier: PackageStatusNotifier? = null
+
+    private var isWallpapersReceived = false
+    private var appStatusListener: PackageStatusNotifier.Listener? = null
+
+    private var progressDialog: ProgressDialog? = null
+    private var testingMode = false
+    private var loading: ContentLoadingProgressBar? = null
+    private lateinit var categoryProvider: CategoryProvider
+
+    /**
+     * Staged error dialog fragments that were unable to be shown when the activity didn't allow
+     * committing fragment transactions.
+     */
+    private var stagedStartRotationErrorDialogFragment: StartRotationErrorDialogFragment? = null
+
+    private var wallpaperManager: WallpaperManager? = null
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        val injector = InjectorProvider.getInjector()
+        val appContext = requireContext().applicationContext
+        wallpaperManager = WallpaperManager.getInstance(appContext)
+        packageStatusNotifier = injector.getPackageStatusNotifier(appContext)
+        items = ArrayList()
+
+        // Clear Glide's cache if night-mode changed to ensure thumbnails are reloaded
+        if (
+            savedInstanceState != null &&
+                (savedInstanceState.getInt(KEY_NIGHT_MODE) !=
+                    resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK)
+        ) {
+            Glide.get(requireContext()).clearMemory()
+        }
+        categoryProvider = injector.getCategoryProvider(appContext)
+        categoryProvider.fetchCategories(
+            object : CategoryReceiver {
+                override fun onCategoryReceived(category: Category) {
+                    // Do nothing.
+                }
+
+                override fun doneFetchingCategories() {
+                    val fetchedCategory =
+                        categoryProvider.getCategory(
+                            arguments?.getString(ARG_CATEGORY_COLLECTION_ID)
+                        )
+                    if (fetchedCategory != null && fetchedCategory !is WallpaperCategory) {
+                        return
+                    }
+
+                    if (fetchedCategory == null) {
+                        DiskBasedLogger.e(TAG, "Failed to find the category.", context)
+
+                        // The absence of this category in the CategoryProvider indicates a broken
+                        // state, see b/38030129. Hence, finish the activity and return.
+                        getIndividualPickerFragmentHost().moveToPreviousFragment()
+                        Toast.makeText(
+                                context,
+                                R.string.collection_not_exist_msg,
+                                Toast.LENGTH_SHORT
+                            )
+                            .show()
+                        return
+                    }
+                    category = fetchedCategory as WallpaperCategory
+                    onCategoryLoaded()
+                }
+            },
+            false
+        )
+    }
+
+    fun onCategoryLoaded() {
+        val fragmentHost = getIndividualPickerFragmentHost()
+        if (fragmentHost.isHostToolbarShown) {
+            fragmentHost.setToolbarTitle(category.title)
+        } else {
+            setTitle(category.title)
+        }
+        wallpaperRotationInitializer = category.wallpaperRotationInitializer
+        if (mToolbar != null && isRotationEnabled()) {
+            setUpToolbarMenu(R.menu.individual_picker_menu)
+        }
+        fetchWallpapers(false)
+        if (category.supportsThirdParty()) {
+            appStatusListener =
+                PackageStatusNotifier.Listener { pkgName: String?, status: Int ->
+                    if (
+                        status != PackageStatusNotifier.PackageStatus.REMOVED ||
+                            category.containsThirdParty(pkgName)
+                    ) {
+                        fetchWallpapers(true)
+                    }
+                }
+            packageStatusNotifier?.addListener(
+                appStatusListener,
+                WallpaperService.SERVICE_INTERFACE
+            )
+        }
+    }
+
+    private fun fetchWallpapers(forceReload: Boolean) {
+        items.clear()
+        isWallpapersReceived = false
+        updateLoading()
+        val context = requireContext()
+        category.fetchWallpapers(
+            context.applicationContext,
+            { fetchedWallpapers ->
+                isWallpapersReceived = true
+                updateLoading()
+                val byGroup = fetchedWallpapers.groupBy { it.getGroupName(context) }
+                val appliedWallpaperIds = getAppliedWallpaperIds()
+                byGroup.forEach { (groupName, wallpapers) ->
+                    if (!TextUtils.isEmpty(groupName)) {
+                        items.add(
+                            if (items.isEmpty()) {
+                                PickerItem.FirstHeaderItem(groupName)
+                            } else {
+                                PickerItem.HeaderItem(groupName)
+                            }
+                        )
+                    }
+                    items.addAll(
+                        wallpapers.map {
+                            PickerItem.WallpaperItem(
+                                it,
+                                appliedWallpaperIds.contains(it.wallpaperId)
+                            )
+                        }
+                    )
+                }
+                maybeSetUpImageGrid()
+
+                // Wallpapers may load after the adapter is initialized, in which case we have
+                // to explicitly notify that the data set has changed.
+                adapter?.notifyDataSetChanged()
+                if (fetchedWallpapers.isEmpty()) {
+                    // If there are no more wallpapers and we're on phone, just finish the
+                    // Activity.
+                    val activity: Activity? = activity
+                    activity?.finish()
+                }
+            },
+            forceReload
+        )
+    }
+
+    private fun updateLoading() {
+        if (isWallpapersReceived) {
+            loading?.hide()
+        } else {
+            loading?.show()
+        }
+    }
+
+    override fun onSaveInstanceState(outState: Bundle) {
+        super.onSaveInstanceState(outState)
+        outState.putInt(
+            KEY_NIGHT_MODE,
+            resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
+        )
+    }
+
+    override fun onCreateView(
+        inflater: LayoutInflater,
+        container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View {
+        val view: View = inflater.inflate(R.layout.fragment_individual_picker, container, false)
+        if (getIndividualPickerFragmentHost().isHostToolbarShown) {
+            view.findViewById<View>(R.id.header_bar).visibility = View.GONE
+            setUpArrowEnabled(/* upArrow= */ true)
+            if (isRotationEnabled()) {
+                getIndividualPickerFragmentHost().setToolbarMenu(R.menu.individual_picker_menu)
+            }
+        } else {
+            setUpToolbar(view)
+            if (isRotationEnabled()) {
+                setUpToolbarMenu(R.menu.individual_picker_menu)
+            }
+            setTitle(category.title)
+        }
+        imageGrid = view.findViewById<View>(R.id.wallpaper_grid) as RecyclerView
+        loading = view.findViewById(R.id.loading_indicator)
+        updateLoading()
+        maybeSetUpImageGrid()
+        // For nav bar edge-to-edge effect.
+        imageGrid.setOnApplyWindowInsetsListener { v: View, windowInsets: WindowInsets ->
+            v.setPadding(
+                v.paddingLeft,
+                v.paddingTop,
+                v.paddingRight,
+                windowInsets.systemWindowInsetBottom
+            )
+            windowInsets.consumeSystemWindowInsets()
+        }
+        return view
+    }
+
+    private fun getIndividualPickerFragmentHost():
+        IndividualPickerFragment.IndividualPickerFragmentHost {
+        val parentFragment = parentFragment
+        return if (parentFragment != null) {
+            parentFragment as IndividualPickerFragment.IndividualPickerFragmentHost
+        } else {
+            activity as IndividualPickerFragment.IndividualPickerFragmentHost
+        }
+    }
+
+    private fun maybeSetUpImageGrid() {
+        // Skip if mImageGrid been initialized yet
+        if (!this::imageGrid.isInitialized) {
+            return
+        }
+        // Skip if category hasn't loaded yet
+        if (!this::category.isInitialized) {
+            return
+        }
+        if (context == null) {
+            return
+        }
+
+        // Wallpaper count could change, so we may need to change the layout(2 or 3 columns layout)
+        val gridLayoutManager = imageGrid.layoutManager as GridLayoutManager?
+        val needUpdateLayout = gridLayoutManager?.spanCount != getNumColumns()
+
+        // Skip if the adapter was already created and don't need to change the layout
+        if (adapter != null && !needUpdateLayout) {
+            return
+        }
+
+        // Clear the old decoration
+        val decorationCount = imageGrid.itemDecorationCount
+        for (i in 0 until decorationCount) {
+            imageGrid.removeItemDecorationAt(i)
+        }
+        imageGrid.addItemDecoration(
+            GridPaddingDecoration(getGridItemPaddingHorizontal(), getGridItemPaddingBottom())
+        )
+        val edgePadding = getEdgePadding()
+        imageGrid.setPadding(
+            edgePadding,
+            imageGrid.paddingTop,
+            edgePadding,
+            imageGrid.paddingBottom
+        )
+        val tileSizePx =
+            if (isFewerColumnLayout()) {
+                SizeCalculator.getFeaturedIndividualTileSize(activity!!)
+            } else {
+                SizeCalculator.getIndividualTileSize(activity!!)
+            }
+        setUpImageGrid(tileSizePx)
+        imageGrid.setAccessibilityDelegateCompat(
+            WallpaperPickerRecyclerViewAccessibilityDelegate(
+                imageGrid,
+                parentFragment as BottomSheetHost?,
+                getNumColumns()
+            )
+        )
+    }
+
+    private fun isFewerColumnLayout(): Boolean =
+        items.count { it is PickerItem.WallpaperItem } <= MAX_CAPACITY_IN_FEWER_COLUMN_LAYOUT
+
+    private fun getGridItemPaddingHorizontal(): Int {
+        return if (isFewerColumnLayout()) {
+            resources.getDimensionPixelSize(
+                R.dimen.grid_item_featured_individual_padding_horizontal
+            )
+        } else {
+            resources.getDimensionPixelSize(R.dimen.grid_item_individual_padding_horizontal)
+        }
+    }
+
+    private fun getGridItemPaddingBottom(): Int {
+        return if (isFewerColumnLayout()) {
+            resources.getDimensionPixelSize(R.dimen.grid_item_featured_individual_padding_bottom)
+        } else {
+            resources.getDimensionPixelSize(R.dimen.grid_item_individual_padding_bottom)
+        }
+    }
+
+    private fun getEdgePadding(): Int {
+        return if (isFewerColumnLayout()) {
+            resources.getDimensionPixelSize(R.dimen.featured_wallpaper_grid_edge_space)
+        } else {
+            resources.getDimensionPixelSize(R.dimen.wallpaper_grid_edge_space)
+        }
+    }
+
+    /**
+     * Create the adapter and assign it to mImageGrid. Both mImageGrid and mCategory are guaranteed
+     * to not be null when this method is called.
+     */
+    private fun setUpImageGrid(tileSizePx: Point) {
+        adapter =
+            IndividualAdapter(
+                items,
+                category,
+                requireActivity(),
+                tileSizePx,
+                isRotationEnabled(),
+                isFewerColumnLayout()
+            )
+        imageGrid.adapter = adapter
+        val gridLayoutManager = GridLayoutManager(activity, getNumColumns())
+        gridLayoutManager.spanSizeLookup =
+            object : GridLayoutManager.SpanSizeLookup() {
+                override fun getSpanSize(position: Int): Int {
+                    return when (items[position]) {
+                        is PickerItem.FirstHeaderItem,
+                        is PickerItem.HeaderItem -> {
+                            gridLayoutManager.spanCount
+                        }
+                        else -> 1
+                    }
+                }
+            }
+        imageGrid.layoutManager = gridLayoutManager
+    }
+
+    override fun onResume() {
+        super.onResume()
+        val preferences = InjectorProvider.getInjector().getPreferences(requireActivity())
+        preferences.lastAppActiveTimestamp = Date().time
+
+        // Reset Glide memory settings to a "normal" level of usage since it may have been lowered
+        // in PreviewFragment.
+        Glide.get(requireContext()).setMemoryCategory(MemoryCategory.NORMAL)
+
+        // Show the staged 'start rotation' error dialog fragment if there is one that was unable to
+        // be shown earlier when this fragment's hosting activity didn't allow committing fragment
+        // transactions.
+        if (isAdded) {
+            stagedStartRotationErrorDialogFragment?.show(
+                parentFragmentManager,
+                TAG_START_ROTATION_ERROR_DIALOG
+            )
+        }
+        stagedStartRotationErrorDialogFragment = null
+    }
+
+    override fun onDestroyView() {
+        super.onDestroyView()
+        getIndividualPickerFragmentHost().removeToolbarMenu()
+    }
+
+    override fun onDestroy() {
+        super.onDestroy()
+        progressDialog?.dismiss()
+        if (appStatusListener != null) {
+            packageStatusNotifier?.removeListener(appStatusListener)
+        }
+    }
+
+    override fun onStartRotationDialogDismiss(dialog: DialogInterface) {
+        // TODO(b/159310028): Refactor fragment layer to make it able to restore from config change.
+        // This is to handle config change with StartRotationDialog popup,  the StartRotationDialog
+        // still holds a reference to the destroyed Fragment and is calling
+        // onStartRotationDialogDismissed on that destroyed Fragment.
+    }
+
+    override fun retryStartRotation(@NetworkPreference networkPreference: Int) {
+        startRotation(networkPreference)
+    }
+
+    /**
+     * Enable 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 dialog
+     * constantly keeps the UI thread alive and blocks a test forever.
+     *
+     * @param testingMode
+     */
+    fun setTestingMode(testingMode: Boolean) {
+        this.testingMode = testingMode
+    }
+
+    override fun startRotation(@NetworkPreference networkPreference: Int) {
+        if (!isRotationEnabled()) {
+            Log.e(TAG, "Rotation is not enabled for this category " + category.title)
+            return
+        }
+
+        // ProgressDialog endlessly updates the UI thread, keeping it from going idle which
+        // therefore causes Espresso to hang once the dialog is shown.
+        if (!testingMode) {
+            val themeResId =
+                if (Build.VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
+                    R.style.ProgressDialogThemePreL
+                } else {
+                    R.style.LightDialogTheme
+                }
+            val progressDialog = ProgressDialog(activity, themeResId)
+            progressDialog.setTitle(PROGRESS_DIALOG_NO_TITLE)
+            progressDialog.setMessage(resources.getString(R.string.start_rotation_progress_message))
+            progressDialog.isIndeterminate = PROGRESS_DIALOG_INDETERMINATE
+            progressDialog.show()
+            this.progressDialog = progressDialog
+        }
+        val appContext = activity!!.applicationContext
+        wallpaperRotationInitializer?.setFirstWallpaperInRotation(
+            appContext,
+            networkPreference,
+            object : WallpaperRotationInitializer.Listener {
+                override fun onFirstWallpaperInRotationSet() {
+                    progressDialog?.dismiss()
+
+                    // The fragment may be detached from its containing activity if the user exits
+                    // the app before the first wallpaper image in rotation finishes downloading.
+                    val activity: Activity? = activity
+                    if (wallpaperRotationInitializer!!.startRotation(appContext)) {
+                        if (activity != null) {
+                            try {
+                                Toast.makeText(
+                                        activity,
+                                        R.string.wallpaper_set_successfully_message,
+                                        Toast.LENGTH_SHORT
+                                    )
+                                    .show()
+                            } catch (e: Resources.NotFoundException) {
+                                Log.e(TAG, "Could not show toast $e")
+                            }
+                            activity.setResult(Activity.RESULT_OK)
+                            activity.finish()
+                            if (!ActivityUtils.isSUWMode(appContext)) {
+                                // Go back to launcher home.
+                                LaunchUtils.launchHome(appContext)
+                            }
+                        }
+                    } else { // Failed to start rotation.
+                        showStartRotationErrorDialog(networkPreference)
+                    }
+                }
+
+                override fun onError() {
+                    progressDialog?.dismiss()
+                    showStartRotationErrorDialog(networkPreference)
+                }
+            }
+        )
+    }
+
+    private fun showStartRotationErrorDialog(@NetworkPreference networkPreference: Int) {
+        val activity = activity as FragmentTransactionChecker?
+        if (activity != null) {
+            val startRotationErrorDialogFragment =
+                StartRotationErrorDialogFragment.newInstance(networkPreference)
+            startRotationErrorDialogFragment.setTargetFragment(
+                this@IndividualPickerFragment2,
+                UNUSED_REQUEST_CODE
+            )
+            if (activity.isSafeToCommitFragmentTransaction) {
+                startRotationErrorDialogFragment.show(
+                    parentFragmentManager,
+                    TAG_START_ROTATION_ERROR_DIALOG
+                )
+            } else {
+                stagedStartRotationErrorDialogFragment = startRotationErrorDialogFragment
+            }
+        }
+    }
+
+    private fun getNumColumns(): Int {
+        val activity = this.activity ?: return 1
+        return if (isFewerColumnLayout()) {
+            SizeCalculator.getNumFeaturedIndividualColumns(activity)
+        } else {
+            SizeCalculator.getNumIndividualColumns(activity)
+        }
+    }
+
+    /** Returns whether rotation is enabled for this category. */
+    private fun isRotationEnabled() = wallpaperRotationInitializer != null
+
+    override fun onMenuItemClick(item: MenuItem): Boolean {
+        if (item.itemId == R.id.daily_rotation) {
+            showRotationDialog()
+            return true
+        }
+        return super.onMenuItemClick(item)
+    }
+
+    /** Popups a daily rotation dialog for the uses to confirm. */
+    private fun showRotationDialog() {
+        val startRotationDialogFragment: DialogFragment = StartRotationDialogFragment()
+        startRotationDialogFragment.setTargetFragment(
+            this@IndividualPickerFragment2,
+            UNUSED_REQUEST_CODE
+        )
+        startRotationDialogFragment.show(parentFragmentManager, TAG_START_ROTATION_DIALOG)
+    }
+
+    private fun getAppliedWallpaperIds(): Set<String> {
+        val prefs = InjectorProvider.getInjector().getPreferences(requireContext())
+        val wallpaperInfo = wallpaperManager?.wallpaperInfo
+        val appliedWallpaperIds: MutableSet<String> = ArraySet()
+        val homeWallpaperId =
+            if (wallpaperInfo != null) {
+                wallpaperInfo.serviceName
+            } else {
+                prefs.homeWallpaperRemoteId
+            }
+        if (!TextUtils.isEmpty(homeWallpaperId)) {
+            appliedWallpaperIds.add(homeWallpaperId)
+        }
+        val isLockWallpaperApplied =
+            wallpaperManager!!.getWallpaperId(WallpaperManager.FLAG_LOCK) >= 0
+        val lockWallpaperId = prefs.lockWallpaperRemoteId
+        if (isLockWallpaperApplied && !TextUtils.isEmpty(lockWallpaperId)) {
+            appliedWallpaperIds.add(lockWallpaperId)
+        }
+        return appliedWallpaperIds
+    }
+
+    sealed class PickerItem(val title: CharSequence = "") {
+        class WallpaperItem(val wallpaperInfo: WallpaperInfo, val isApplied: Boolean) :
+            PickerItem()
+
+        class HeaderItem(title: CharSequence) : PickerItem(title)
+
+        class FirstHeaderItem(title: CharSequence) : PickerItem(title)
+    }
+
+    /** RecyclerView Adapter subclass for the wallpaper tiles in the RecyclerView. */
+    class IndividualAdapter(
+        private val items: List<PickerItem>,
+        private val category: Category,
+        private val activity: Activity,
+        private val tileSizePx: Point,
+        private val isRotationEnabled: Boolean,
+        private val isFewerColumnLayout: Boolean
+    ) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
+        companion object {
+            const val ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER = 2
+            const val ITEM_VIEW_TYPE_MY_PHOTOS = 3
+            const val ITEM_VIEW_TYPE_HEADER = 4
+            const val ITEM_VIEW_TYPE_HEADER_TOP = 5
+        }
+
+        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
+            return when (viewType) {
+                ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER -> createIndividualHolder(parent)
+                ITEM_VIEW_TYPE_MY_PHOTOS -> createMyPhotosHolder(parent)
+                ITEM_VIEW_TYPE_HEADER -> createTitleHolder(parent, /* removePaddingTop= */ false)
+                ITEM_VIEW_TYPE_HEADER_TOP -> createTitleHolder(parent, /* removePaddingTop= */ true)
+                else -> {
+                    throw RuntimeException("Unsupported viewType $viewType in IndividualAdapter")
+                }
+            }
+        }
+
+        override fun getItemViewType(position: Int): Int {
+            // A category cannot have both a "start rotation" tile and a "my photos" tile.
+            return if (
+                category.supportsCustomPhotos() &&
+                    !isRotationEnabled &&
+                    position == SPECIAL_FIXED_TILE_ADAPTER_POSITION
+            ) {
+                ITEM_VIEW_TYPE_MY_PHOTOS
+            } else {
+                when (items[position]) {
+                    is PickerItem.WallpaperItem -> ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER
+                    is PickerItem.HeaderItem -> ITEM_VIEW_TYPE_HEADER
+                    is PickerItem.FirstHeaderItem -> ITEM_VIEW_TYPE_HEADER_TOP
+                }
+            }
+        }
+
+        override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
+            when (val viewType = getItemViewType(position)) {
+                ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER -> bindIndividualHolder(holder, position)
+                ITEM_VIEW_TYPE_MY_PHOTOS -> (holder as MyPhotosViewHolder?)!!.bind()
+                ITEM_VIEW_TYPE_HEADER,
+                ITEM_VIEW_TYPE_HEADER_TOP -> {
+                    val textView = holder.itemView as TextView
+                    val item = items[position]
+                    textView.text = item.title
+                    textView.contentDescription = item.title
+                }
+                else -> Log.e(TAG, "Unexpected viewType $viewType in IndividualAdapter")
+            }
+        }
+
+        override fun getItemCount(): Int {
+            return if (category.supportsCustomPhotos()) {
+                items.size + 1
+            } else {
+                items.size
+            }
+        }
+
+        private fun createIndividualHolder(parent: ViewGroup): RecyclerView.ViewHolder {
+            val layoutInflater = LayoutInflater.from(activity)
+            val view: View = layoutInflater.inflate(R.layout.grid_item_image, parent, false)
+            return PreviewIndividualHolder(activity, tileSizePx.y, view)
+        }
+
+        private fun createMyPhotosHolder(parent: ViewGroup): RecyclerView.ViewHolder {
+            val layoutInflater = LayoutInflater.from(activity)
+            val view: View = layoutInflater.inflate(R.layout.grid_item_my_photos, parent, false)
+            return MyPhotosViewHolder(
+                activity,
+                (activity as MyPhotosStarterProvider).myPhotosStarter,
+                tileSizePx.y,
+                view
+            )
+        }
+
+        private fun createTitleHolder(
+            parent: ViewGroup,
+            removePaddingTop: Boolean
+        ): RecyclerView.ViewHolder {
+            val layoutInflater = LayoutInflater.from(activity)
+            val view =
+                layoutInflater.inflate(R.layout.grid_item_header, parent, /* attachToRoot= */ false)
+            if (removePaddingTop) {
+                view.setPadding(
+                    view.paddingStart,
+                    /* top= */ 0,
+                    view.paddingEnd,
+                    view.paddingBottom
+                )
+            }
+            return object : RecyclerView.ViewHolder(view) {}
+        }
+
+        private fun bindIndividualHolder(holder: RecyclerView.ViewHolder, position: Int) {
+            val wallpaperIndex = if (category.supportsCustomPhotos()) position - 1 else position
+            val item = items[wallpaperIndex] as PickerItem.WallpaperItem
+            val wallpaper = item.wallpaperInfo
+            wallpaper.computeColorInfo(holder.itemView.context)
+            (holder as IndividualHolder).bindWallpaper(wallpaper)
+            val container = holder.itemView.findViewById<CardView>(R.id.wallpaper_container)
+            val radiusId: Int =
+                if (isFewerColumnLayout) {
+                    R.dimen.grid_item_all_radius
+                } else {
+                    R.dimen.grid_item_all_radius_small
+                }
+            container.radius = activity.resources.getDimension(radiusId)
+            showBadge(holder, R.drawable.wallpaper_check_circle_24dp, item.isApplied)
+            if (!item.isApplied) {
+                showBadge(holder, wallpaper.badgeDrawableRes, wallpaper.badgeDrawableRes != ID_NULL)
+            }
+        }
+
+        private fun showBadge(
+            holder: RecyclerView.ViewHolder,
+            @DrawableRes icon: Int,
+            show: Boolean
+        ) {
+            val badge = holder.itemView.findViewById<ImageView>(R.id.indicator_icon)
+            if (show) {
+                val margin =
+                    if (isFewerColumnLayout) {
+                            activity.resources.getDimension(R.dimen.grid_item_badge_margin)
+                        } else {
+                            activity.resources.getDimension(R.dimen.grid_item_badge_margin_small)
+                        }
+                        .toInt()
+                val layoutParams = badge.layoutParams as RelativeLayout.LayoutParams
+                layoutParams.setMargins(margin, margin, margin, margin)
+                badge.layoutParams = layoutParams
+                badge.setBackgroundResource(icon)
+                badge.visibility = View.VISIBLE
+            } else {
+                badge.visibility = View.GONE
+            }
+        }
+    }
+}
diff --git a/src/com/android/wallpaper/picker/undo/data/repository/UndoRepository.kt b/src/com/android/wallpaper/picker/undo/data/repository/UndoRepository.kt
new file mode 100644
index 0000000..be9c035
--- /dev/null
+++ b/src/com/android/wallpaper/picker/undo/data/repository/UndoRepository.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2022 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.undo.data.repository
+
+import com.android.wallpaper.picker.undo.shared.model.RestorableSnapshot
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
+
+/** Encapsulates application state for the undo system. */
+class UndoRepository {
+    private val snapshotByOwnerId = mutableMapOf<Int, RestorableSnapshot>()
+    private val dirtyOwnerIds = MutableStateFlow(emptySet<Int>())
+
+    /** Whether any area is "dirty" right now (meaning, it could be undone). */
+    val isAnythingDirty: Flow<Boolean> =
+        dirtyOwnerIds.map { it.isNotEmpty() }.distinctUntilChanged()
+
+    /** Associates the given snapshot with the area with the given owner ID. */
+    fun putSnapshot(ownerId: Int, snapshot: RestorableSnapshot) {
+        snapshotByOwnerId[ownerId] = snapshot
+    }
+
+    /** Returns the latest snapshot for the area with the given owner ID. */
+    fun getSnapshot(ownerId: Int): RestorableSnapshot? {
+        return snapshotByOwnerId[ownerId]
+    }
+
+    /**
+     * Marks the area with the owner of the given ID as dirty or not dirty.
+     *
+     * A "dirty" area is one that contains changes that can be undone. An area that is not "dirty"
+     * does not currently have pending changes that can be undone.
+     */
+    fun putDirty(ownerId: Int, isDirty: Boolean) {
+        if (isDirty) {
+            dirtyOwnerIds.value = dirtyOwnerIds.value + setOf(ownerId)
+        } else {
+            dirtyOwnerIds.value = dirtyOwnerIds.value - setOf(ownerId)
+        }
+    }
+
+    /**
+     * Returns the set of IDs for owners of all areas that are currently marked as dirty (meaning
+     * all areas that can currently be undone).
+     */
+    fun getAllDirty(): Collection<Int> {
+        return dirtyOwnerIds.value.toSet()
+    }
+
+    /** Marks all areas as not dirty (meaning they can't be undone). */
+    fun clearAllDirty() {
+        dirtyOwnerIds.value = emptySet()
+    }
+}
diff --git a/src/com/android/wallpaper/picker/undo/domain/interactor/SnapshotRestorer.kt b/src/com/android/wallpaper/picker/undo/domain/interactor/SnapshotRestorer.kt
new file mode 100644
index 0000000..3047834
--- /dev/null
+++ b/src/com/android/wallpaper/picker/undo/domain/interactor/SnapshotRestorer.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2022 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.undo.domain.interactor
+
+import com.android.wallpaper.picker.undo.shared.model.RestorableSnapshot
+
+/** Defines interface for classes that can handle state restoration. */
+interface SnapshotRestorer {
+
+    /**
+     * Sets up the restorer.
+     *
+     * @param updater An updater the can be used when a new snapshot should be stored; invoke this
+     * in response to state changes that you wish could be restored when the user asks to reset the
+     * changes.
+     * @return A snapshot of the initial state as it was at the moment that this method was invoked.
+     */
+    suspend fun setUpSnapshotRestorer(updater: (RestorableSnapshot) -> Unit): RestorableSnapshot
+
+    /** Restores the state to what is described in the given snapshot. */
+    suspend fun restoreToSnapshot(snapshot: RestorableSnapshot)
+}
diff --git a/src/com/android/wallpaper/picker/undo/domain/interactor/UndoInteractor.kt b/src/com/android/wallpaper/picker/undo/domain/interactor/UndoInteractor.kt
new file mode 100644
index 0000000..3aa80ae
--- /dev/null
+++ b/src/com/android/wallpaper/picker/undo/domain/interactor/UndoInteractor.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2022 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.undo.domain.interactor
+
+import com.android.wallpaper.picker.undo.data.repository.UndoRepository
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.launch
+
+/**
+ * Encapsulates the "undo" business logic.
+ *
+ * ## Usage
+ * 1. Instantiate, injecting the supported [SnapshotRestorer] into it, one for each feature that
+ * ```
+ *    should support undo functionality.
+ * ```
+ * 2. Call [startSession] which will bootstrap all passed-in [SnapshotRestorer] instances and
+ * ```
+ *    hydrate our model with the latest snapshots from each one.
+ * ```
+ * 3. Observe [isUndoable] to know whether the UI for triggering an "undo" action should be made
+ * ```
+ *    visible to the user.
+ * ```
+ * 4. Call [revertAll] when the user wishes to revert everything.
+ */
+class UndoInteractor(
+    private val scope: CoroutineScope,
+    private val repository: UndoRepository,
+    private val restorerByOwnerId: Map<Int, SnapshotRestorer>,
+) {
+
+    /** Whether the current state is undoable. */
+    val isUndoable: Flow<Boolean> = repository.isAnythingDirty
+
+    /** Bootstraps the undo system, querying each undo-supporting area for the initial snapshot. */
+    fun startSession() {
+        // TODO(b/262924056): take in a saved instance state and reuse it instead.
+        repository.clearAllDirty()
+        restorerByOwnerId.forEach { (ownerId, restorer) ->
+            scope.launch {
+                val initialSnapshot =
+                    restorer.setUpSnapshotRestorer { subsequentSnapshot ->
+                        val initialSnapshot = repository.getSnapshot(ownerId)
+                        repository.putDirty(
+                            ownerId = ownerId,
+                            isDirty = initialSnapshot != subsequentSnapshot
+                        )
+                    }
+
+                repository.putSnapshot(
+                    ownerId = ownerId,
+                    snapshot = initialSnapshot,
+                )
+            }
+        }
+    }
+
+    /** Triggers a revert for all areas. */
+    fun revertAll() {
+        repository.getAllDirty().forEach { ownerId ->
+            val restorer = restorerByOwnerId[ownerId]
+            val snapshot = repository.getSnapshot(ownerId)
+            if (restorer != null && snapshot != null) {
+                scope.launch { restorer.restoreToSnapshot(snapshot) }
+            }
+        }
+
+        repository.clearAllDirty()
+    }
+}
diff --git a/src/com/android/wallpaper/picker/undo/shared/model/RestorableSnapshot.kt b/src/com/android/wallpaper/picker/undo/shared/model/RestorableSnapshot.kt
new file mode 100644
index 0000000..aac0e22
--- /dev/null
+++ b/src/com/android/wallpaper/picker/undo/shared/model/RestorableSnapshot.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2022 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.undo.shared.model
+
+/** Models a snapshot of the state of an undo-supporting feature at a given time. */
+data class RestorableSnapshot(
+    val args: Map<String, String>,
+)
diff --git a/src/com/android/wallpaper/picker/undo/ui/binder/RevertToolbarButtonBinder.kt b/src/com/android/wallpaper/picker/undo/ui/binder/RevertToolbarButtonBinder.kt
new file mode 100644
index 0000000..a1eb018
--- /dev/null
+++ b/src/com/android/wallpaper/picker/undo/ui/binder/RevertToolbarButtonBinder.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2022 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.undo.ui.binder
+
+import android.app.AlertDialog
+import android.app.Dialog
+import android.widget.Toolbar
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.wallpaper.R
+import com.android.wallpaper.picker.undo.ui.viewmodel.UndoDialogViewModel
+import com.android.wallpaper.picker.undo.ui.viewmodel.UndoViewModel
+import kotlinx.coroutines.launch
+
+object RevertToolbarButtonBinder {
+    /** Binds the given view to the given view-model. */
+    fun bind(
+        view: Toolbar,
+        viewModel: UndoViewModel,
+        lifecycleOwner: LifecycleOwner,
+    ) {
+        val menuItem = view.menu.findItem(R.id.revert)
+        menuItem.setOnMenuItemClickListener {
+            viewModel.onRevertButtonClicked()
+            true
+        }
+
+        var dialog: Dialog? = null
+
+        fun showDialog(viewModel: UndoDialogViewModel) {
+            dialog =
+                AlertDialog.Builder(view.context, R.style.LightDialogTheme)
+                    .setTitle(R.string.reset_confirmation_dialog_title)
+                    .setMessage(R.string.reset_confirmation_dialog_message)
+                    .setPositiveButton(R.string.reset) { _, _ -> viewModel.onConfirmed() }
+                    .setNegativeButton(R.string.cancel, null)
+                    .setOnDismissListener {
+                        dialog = null
+                        viewModel.onDismissed()
+                    }
+                    .create()
+            dialog?.show()
+        }
+
+        fun dismissDialog() {
+            dialog?.setOnDismissListener(null)
+            dialog?.dismiss()
+            dialog = null
+        }
+
+        lifecycleOwner.lifecycleScope.launch {
+            lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
+                launch { viewModel.isRevertButtonVisible.collect { menuItem.isVisible = it } }
+
+                launch {
+                    viewModel.dialog.collect { dialogViewModel ->
+                        if (dialogViewModel != null) {
+                            dismissDialog()
+                            showDialog(dialogViewModel)
+                        } else {
+                            dismissDialog()
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/src/com/android/wallpaper/picker/undo/ui/viewmodel/UndoDialogViewModel.kt b/src/com/android/wallpaper/picker/undo/ui/viewmodel/UndoDialogViewModel.kt
new file mode 100644
index 0000000..b7f754b
--- /dev/null
+++ b/src/com/android/wallpaper/picker/undo/ui/viewmodel/UndoDialogViewModel.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2022 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.undo.ui.viewmodel
+
+/** Models the UI state for an undo confirmation dialog. */
+data class UndoDialogViewModel(
+    val onConfirmed: () -> Unit,
+    val onDismissed: () -> Unit,
+)
diff --git a/src/com/android/wallpaper/picker/undo/ui/viewmodel/UndoViewModel.kt b/src/com/android/wallpaper/picker/undo/ui/viewmodel/UndoViewModel.kt
new file mode 100644
index 0000000..5e0701b
--- /dev/null
+++ b/src/com/android/wallpaper/picker/undo/ui/viewmodel/UndoViewModel.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2022 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.undo.ui.viewmodel
+
+import com.android.wallpaper.picker.undo.domain.interactor.UndoInteractor
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+/** Models the UI state of the undo system. */
+class UndoViewModel(
+    private val interactor: UndoInteractor,
+) {
+    /** Whether the "revert" button should be visible. */
+    val isRevertButtonVisible: Flow<Boolean> = interactor.isUndoable
+    private val _dialog = MutableStateFlow<UndoDialogViewModel?>(null)
+    /**
+     * A view-model of the undo confirmation dialog that should be shown, or `null` when no dialog
+     * should be shown.
+     */
+    val dialog: Flow<UndoDialogViewModel?> = _dialog.asStateFlow()
+
+    /** Notifies that the "revert" button has been clicked by the user. */
+    fun onRevertButtonClicked() {
+        _dialog.value =
+            UndoDialogViewModel(
+                onConfirmed = {
+                    interactor.revertAll()
+                    _dialog.value = null
+                },
+                onDismissed = { _dialog.value = null },
+            )
+    }
+}
diff --git a/src/com/android/wallpaper/util/BitmapProcessor.java b/src/com/android/wallpaper/util/BitmapProcessor.java
index 590164d..0487346 100644
--- a/src/com/android/wallpaper/util/BitmapProcessor.java
+++ b/src/com/android/wallpaper/util/BitmapProcessor.java
@@ -15,61 +15,38 @@
  */
 package com.android.wallpaper.util;
 
-import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.Rect;
 import android.util.Log;
 
-import com.google.android.renderscript.Toolkit;
-
 /**
  * Class with different bitmap processors to apply to bitmaps
  */
 public final class BitmapProcessor {
 
     private static final String TAG = "BitmapProcessor";
-    private static final float BLUR_RADIUS = 20f;
     private static final int DOWNSAMPLE = 5;
 
     private BitmapProcessor() {
     }
 
     /**
-     * Function that blurs the content of a bitmap.
+     * Function that transforms a bitmap into a lower resolution.
      *
-     * @param context   the Application Context
      * @param bitmap    the bitmap we want to blur.
      * @param outWidth  the end width of the blurred bitmap.
      * @param outHeight the end height of the blurred bitmap.
      * @return the blurred bitmap.
      */
-    public static Bitmap blur(Context context, Bitmap bitmap, int outWidth,
-            int outHeight) {
-        Bitmap inBitmap = null;
-
+    public static Bitmap createLowResBitmap(Bitmap bitmap, int outWidth, int outHeight) {
         try {
             Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
             WallpaperCropUtils.fitToSize(rect, outWidth / DOWNSAMPLE, outHeight / DOWNSAMPLE);
 
-            inBitmap = Bitmap.createScaledBitmap(bitmap, rect.width(), rect.height(),
+            return Bitmap.createScaledBitmap(bitmap, rect.width(), rect.height(),
                     true /* filter */);
-
-            // Render script blurs only support ARGB_8888, we need a conversion if we got a
-            // different bitmap config.
-            if (inBitmap.getConfig() != Bitmap.Config.ARGB_8888) {
-                Bitmap oldIn = inBitmap;
-                inBitmap = oldIn.copy(Bitmap.Config.ARGB_8888, false /* isMutable */);
-                oldIn.recycle();
-            }
-
-            return Toolkit.INSTANCE.blur(inBitmap, (int) BLUR_RADIUS);
-
         } catch (IllegalArgumentException ex) {
             Log.e(TAG, "error while blurring bitmap", ex);
-        } finally {
-            if (inBitmap != null) {
-                inBitmap.recycle();
-            }
         }
 
         return null;
diff --git a/src/com/android/wallpaper/util/DisplayUtils.kt b/src/com/android/wallpaper/util/DisplayUtils.kt
index 9f7c644..ce6980e 100644
--- a/src/com/android/wallpaper/util/DisplayUtils.kt
+++ b/src/com/android/wallpaper/util/DisplayUtils.kt
@@ -15,6 +15,7 @@
  */
 package com.android.wallpaper.util
 
+import android.app.Activity
 import android.content.Context
 import android.graphics.Point
 import android.hardware.display.DisplayManager
@@ -22,8 +23,8 @@
 import android.view.Display
 
 /**
- * Utility class to provide methods to find and obtain information about displays via
- * {@link DisplayManager}
+ * Utility class to provide methods to find and obtain information about displays via {@link
+ * DisplayManager}
  */
 class DisplayUtils(context: Context) {
     companion object {
@@ -35,7 +36,8 @@
     init {
         val appContext = context.applicationContext
         val dm = appContext.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
-        val allDisplays: Array<out Display> = dm.displays
+        val allDisplays: Array<out Display> =
+            dm.getDisplays(DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED)
         if (allDisplays.isEmpty()) {
             Log.e(TAG, "No displays found on context $appContext")
             throw RuntimeException("No displays found!")
@@ -43,12 +45,20 @@
         internalDisplays = allDisplays.filter { it.type == Display.TYPE_INTERNAL }
     }
 
-    /**
-     * Returns the {@link Display} to be used to calculate wallpaper size and cropping.
-     */
+    /** Returns the {@link Display} to be used to calculate wallpaper size and cropping. */
     fun getWallpaperDisplay(): Display {
         return internalDisplays.maxWithOrNull { a, b -> getRealSize(a) - getRealSize(b) }
-                ?: internalDisplays[0]
+            ?: internalDisplays[0]
+    }
+
+    /**
+     * Returns `true` if the current display is the wallpaper display on a multi-display device.
+     *
+     * On a multi-display device the wallpaper display is the largest display while on a single
+     * display device the only display is both the wallpaper display and the current display.
+     */
+    fun isOnWallpaperDisplay(activity: Activity): Boolean {
+        return activity.display.displayId == getWallpaperDisplay().displayId
     }
 
     private fun getRealSize(display: Display): Int {
diff --git a/src/com/android/wallpaper/util/PreviewUtils.java b/src/com/android/wallpaper/util/PreviewUtils.java
deleted file mode 100644
index 0b8977a..0000000
--- a/src/com/android/wallpaper/util/PreviewUtils.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * 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.wallpaper.util;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ProviderInfo;
-import android.content.pm.ResolveInfo;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.text.TextUtils;
-
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-/** Util class for wallpaper preview. */
-public class PreviewUtils {
-
-    private static final String PREVIEW = "preview";
-    private static final String METHOD_GET_PREVIEW = "get_preview";
-    private static final ExecutorService sExecutorService = Executors.newSingleThreadExecutor();
-
-    private final Context mContext;
-    private final String mProviderAuthority;
-    private ProviderInfo mProviderInfo;
-
-    public PreviewUtils(Context context, String authorityMetadataKey) {
-        mContext = context;
-        Intent homeIntent = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME);
-
-        ResolveInfo info = context.getPackageManager().resolveActivity(homeIntent,
-                PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA);
-        if (info != null && info.activityInfo != null && info.activityInfo.metaData != null) {
-            mProviderAuthority = info.activityInfo.metaData.getString(authorityMetadataKey);
-        } else {
-            mProviderAuthority = null;
-        }
-
-        mProviderInfo = TextUtils.isEmpty(mProviderAuthority) ? null
-                : mContext.getPackageManager().resolveContentProvider(mProviderAuthority, 0);
-        if (mProviderInfo != null && !TextUtils.isEmpty(mProviderInfo.readPermission)) {
-            if (context.checkSelfPermission(mProviderInfo.readPermission)
-                    != PackageManager.PERMISSION_GRANTED) {
-                mProviderInfo = null;
-            }
-        }
-    }
-
-    /**
-     * Render preview under the current grid option.
-     * @param bundle request options to pass on the call
-     * @param callback to receive the results, it will be called on the main thread.
-     */
-    public void renderPreview(Bundle bundle, WorkspacePreviewCallback callback) {
-        sExecutorService.submit(() -> {
-            Bundle result = mContext.getContentResolver().call(getUri(PREVIEW),
-                    METHOD_GET_PREVIEW, null, bundle);
-            new Handler(Looper.getMainLooper()).post(() -> callback.onPreviewRendered(result));
-        });
-    }
-
-    /** Easy way to generate a Uri with the provider info from this class. */
-    public Uri getUri(String path) {
-        return new Uri.Builder()
-                .scheme(ContentResolver.SCHEME_CONTENT)
-                .authority(mProviderInfo.authority)
-                .appendPath(path)
-                .build();
-    }
-
-    /** Return whether preview is supported. */
-    public boolean supportsPreview() {
-        return mProviderInfo != null;
-    }
-
-    /**
-     * Callback for a call to the provider to render preview
-     */
-    public interface WorkspacePreviewCallback {
-        /**
-         * Called with the result from the provider.
-         */
-        void onPreviewRendered(Bundle resultBundle);
-    }
-}
diff --git a/src/com/android/wallpaper/util/PreviewUtils.kt b/src/com/android/wallpaper/util/PreviewUtils.kt
new file mode 100644
index 0000000..a2e0275
--- /dev/null
+++ b/src/com/android/wallpaper/util/PreviewUtils.kt
@@ -0,0 +1,128 @@
+/*
+ * 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.wallpaper.util
+
+import android.content.ContentResolver
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.content.pm.ProviderInfo
+import android.net.Uri
+import android.os.Bundle
+import android.os.Handler
+import android.os.Looper
+import android.text.TextUtils
+import java.util.concurrent.Executors
+
+/** Util class for wallpaper preview. */
+class PreviewUtils(
+    private val context: Context,
+    authorityMetadataKey: String? = null,
+    authority: String? = null,
+) {
+    /** Callback for a call to the provider to render preview */
+    interface WorkspacePreviewCallback {
+        /** Called with the result from the provider. */
+        fun onPreviewRendered(resultBundle: Bundle?)
+    }
+
+    private var providerInfo: ProviderInfo?
+
+    constructor(
+        context: Context,
+        authorityMetadataKey: String,
+    ) : this(
+        context = context,
+        authorityMetadataKey = authorityMetadataKey,
+        authority = null,
+    )
+
+    init {
+        val providerAuthority =
+            authority ?: homeAuthority(context, checkNotNull(authorityMetadataKey))
+
+        providerInfo =
+            if (!TextUtils.isEmpty(providerAuthority)) {
+                context.packageManager.resolveContentProvider(
+                    providerAuthority,
+                    0,
+                )
+            } else {
+                null
+            }
+
+        providerInfo?.let {
+            if (!TextUtils.isEmpty(it.readPermission)) {
+                if (
+                    context.checkSelfPermission(it.readPermission) !=
+                        PackageManager.PERMISSION_GRANTED
+                ) {
+                    providerInfo = null
+                }
+            }
+        }
+    }
+
+    /**
+     * Render preview under the current grid option.
+     *
+     * @param bundle request options to pass on the call.
+     * @param callback to receive the results, it will be called on the main thread.
+     */
+    fun renderPreview(bundle: Bundle?, callback: WorkspacePreviewCallback) {
+        EXECUTOR_SERVICE.submit {
+            val result =
+                context.contentResolver.call(
+                    getUri(PREVIEW),
+                    METHOD_GET_PREVIEW,
+                    null,
+                    bundle,
+                )
+            Handler(Looper.getMainLooper()).post { callback.onPreviewRendered(result) }
+        }
+    }
+
+    /** Easy way to generate a Uri with the provider info from this class. */
+    fun getUri(path: String?): Uri {
+        return Uri.Builder()
+            .scheme(ContentResolver.SCHEME_CONTENT)
+            .authority(checkNotNull(providerInfo).authority)
+            .appendPath(path)
+            .build()
+    }
+
+    /** Return whether preview is supported. */
+    fun supportsPreview(): Boolean {
+        return providerInfo != null
+    }
+
+    companion object {
+        private const val PREVIEW = "preview"
+        private const val METHOD_GET_PREVIEW = "get_preview"
+        private val EXECUTOR_SERVICE = Executors.newSingleThreadExecutor()
+
+        private fun homeAuthority(context: Context, authorityMetadataKey: String): String? {
+            val homeIntent = Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME)
+            val info =
+                context.packageManager.resolveActivity(
+                    homeIntent,
+                    PackageManager.MATCH_DEFAULT_ONLY or PackageManager.GET_META_DATA,
+                )
+
+            return info?.activityInfo?.metaData?.getString(authorityMetadataKey)
+        }
+    }
+}
diff --git a/src/com/android/wallpaper/util/SurfaceViewUtils.java b/src/com/android/wallpaper/util/SurfaceViewUtils.java
index 1656b1b..1b74f77 100644
--- a/src/com/android/wallpaper/util/SurfaceViewUtils.java
+++ b/src/com/android/wallpaper/util/SurfaceViewUtils.java
@@ -20,6 +20,8 @@
 import android.view.SurfaceControlViewHost;
 import android.view.SurfaceView;
 
+import androidx.annotation.Nullable;
+
 /** Util class to generate surface view requests and parse responses */
 public class SurfaceViewUtils {
 
@@ -32,11 +34,21 @@
 
     /** Create a surface view request. */
     public static Bundle createSurfaceViewRequest(SurfaceView surfaceView) {
+        return createSurfaceViewRequest(surfaceView, null);
+    }
+
+    /** Create a surface view request. */
+    public static Bundle createSurfaceViewRequest(
+            SurfaceView surfaceView,
+            @Nullable Bundle extras) {
         Bundle bundle = new Bundle();
         bundle.putBinder(KEY_HOST_TOKEN, surfaceView.getHostToken());
         bundle.putInt(KEY_DISPLAY_ID, surfaceView.getDisplay().getDisplayId());
         bundle.putInt(KEY_VIEW_WIDTH, surfaceView.getWidth());
         bundle.putInt(KEY_VIEW_HEIGHT, surfaceView.getHeight());
+        if (extras != null) {
+            bundle.putAll(extras);
+        }
         return bundle;
     }
 
diff --git a/src/com/android/wallpaper/util/SystemColors.kt b/src/com/android/wallpaper/util/SystemColors.kt
new file mode 100644
index 0000000..ec1738c
--- /dev/null
+++ b/src/com/android/wallpaper/util/SystemColors.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2022 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.util
+
+import android.annotation.AttrRes
+import android.annotation.ColorInt
+import android.content.Context
+import android.util.TypedValue
+import androidx.core.content.ContextCompat
+
+object SystemColors {
+
+    /**
+     * Returns the color by fetching the resId from the theme. Throws an exception when resource Id
+     * is not available in the theme.
+     */
+    @JvmStatic
+    @ColorInt
+    fun getColor(context: Context, @AttrRes resId: Int): Int {
+        val colorValue = TypedValue()
+        val theme = context.theme
+        if (theme.resolveAttribute(resId, colorValue, /* resolveRefs= */ true)) {
+            if (
+                TypedValue.TYPE_FIRST_COLOR_INT <= colorValue.type &&
+                    colorValue.type <= TypedValue.TYPE_LAST_COLOR_INT
+            ) {
+                return colorValue.data
+            }
+            if (colorValue.type == TypedValue.TYPE_STRING) {
+                return ContextCompat.getColor(context, colorValue.resourceId)
+            }
+        }
+        throw IllegalArgumentException(
+            "Theme is missing expected color ${context.resources.getResourceName(resId)} " +
+                "($resId) references a missing resource."
+        )
+    }
+}
diff --git a/src/com/android/wallpaper/util/WallpaperConnection.java b/src/com/android/wallpaper/util/WallpaperConnection.java
index 06b4619..b840980 100644
--- a/src/com/android/wallpaper/util/WallpaperConnection.java
+++ b/src/com/android/wallpaper/util/WallpaperConnection.java
@@ -21,11 +21,13 @@
 import static android.graphics.Matrix.MSKEW_Y;
 
 import android.app.WallpaperColors;
+import android.app.WallpaperManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
 import android.graphics.Matrix;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.os.IBinder;
@@ -34,8 +36,8 @@
 import android.service.wallpaper.IWallpaperConnection;
 import android.service.wallpaper.IWallpaperEngine;
 import android.service.wallpaper.IWallpaperService;
-import android.util.DisplayMetrics;
 import android.util.Log;
+import android.view.Display;
 import android.view.SurfaceControl;
 import android.view.SurfaceHolder;
 import android.view.SurfaceHolder.Callback;
@@ -44,6 +46,9 @@
 
 import androidx.annotation.Nullable;
 
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
 /**
  * Implementation of {@link IWallpaperConnection} that handles communication with a
  * {@link android.service.wallpaper.WallpaperService}
@@ -69,6 +74,7 @@
     private final SurfaceView mSecondContainerView;
     private IWallpaperService mService;
     @Nullable private IWallpaperEngine mEngine;
+    @Nullable private Point mDisplayMetrics;
     private boolean mConnected;
     private boolean mIsVisible;
     private boolean mIsEngineVisible;
@@ -160,11 +166,22 @@
         mService = IWallpaperService.Stub.asInterface(service);
         try {
             int displayId = mContainerView.getDisplay().getDisplayId();
-
-            mService.attach(this, mContainerView.getWindowToken(),
-                    LayoutParams.TYPE_APPLICATION_MEDIA,
-                    true, mContainerView.getWidth(), mContainerView.getHeight(),
-                    new Rect(0, 0, 0, 0), displayId);
+            try {
+                Method preUMethod = mService.getClass().getMethod("attach",
+                        IWallpaperConnection.class, IBinder.class, int.class, boolean.class,
+                        int.class, int.class, Rect.class, int.class);
+                preUMethod.invoke(mService, this, mContainerView.getWindowToken(),
+                        LayoutParams.TYPE_APPLICATION_MEDIA, true, mContainerView.getWidth(),
+                        mContainerView.getHeight(), new Rect(0, 0, 0, 0), displayId);
+            } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
+                Log.d(TAG, "IWallpaperService#attach method without which argument not available, "
+                        + "will use newer version");
+                // Let's try the new attach method that takes "which" argument
+                mService.attach(this, mContainerView.getWindowToken(),
+                        LayoutParams.TYPE_APPLICATION_MEDIA, true, mContainerView.getWidth(),
+                        mContainerView.getHeight(), new Rect(0, 0, 0, 0), displayId,
+                        WallpaperManager.FLAG_SYSTEM);
+            }
         } catch (RemoteException e) {
             Log.w(TAG, "Failed attaching wallpaper; clearing", e);
         }
@@ -196,12 +213,22 @@
                     setEngineVisibility(true);
                 }
 
-                // Some wallpapers don't trigger #onWallpaperColorsChanged from remote. Requesting
-                // wallpaper color here to ensure the #onWallpaperColorsChanged would get called.
                 try {
+                    Point displayMetrics = getDisplayMetrics();
+                    // Reset the live wallpaper preview with the correct screen dimensions. It is
+                    // a known issue that the wallpaper service maybe get the Activity window size
+                    // which may differ from the actual physical device screen size, e.g. when in
+                    // 2-pane mode.
+                    // TODO b/262750854 Fix wallpaper service to get the actual physical device
+                    //      screen size instead of the window size that might be smaller when in
+                    //      2-pane mode.
+                    mEngine.resizePreview(new Rect(0, 0, displayMetrics.x, displayMetrics.y));
+                    // Some wallpapers don't trigger #onWallpaperColorsChanged from remote.
+                    // Requesting wallpaper color here to ensure the #onWallpaperColorsChanged
+                    // would get called.
                     mEngine.requestWallpaperColors();
-                } catch (RemoteException e) {
-                    Log.w(TAG, "Failed requesting wallpaper colors", e);
+                } catch (RemoteException | NullPointerException e) {
+                    Log.w(TAG, "Failed calling WallpaperEngine APIs", e);
                 }
             } else {
                 try {
@@ -238,7 +265,7 @@
     }
 
     @Override
-    public void engineShown(IWallpaperEngine engine)  {
+    public void engineShown(IWallpaperEngine engine) {
         mEngineReady = true;
         if (mContainerView != null) {
             mContainerView.post(() -> reparentWallpaperSurface(mContainerView));
@@ -327,7 +354,7 @@
             t.reparent(wallpaperMirrorSC, parentSC);
             t.show(wallpaperMirrorSC);
             t.apply();
-        } catch (RemoteException e) {
+        } catch (RemoteException | NullPointerException e) {
             Log.e(TAG, "Couldn't reparent wallpaper surface", e);
         }
     }
@@ -336,15 +363,31 @@
         Matrix m = new Matrix();
         float[] values = new float[9];
         Rect surfacePosition = parentSurface.getHolder().getSurfaceFrame();
-        DisplayMetrics metrics = DisplayMetricsRetriever.getInstance().getDisplayMetrics(
-                mContainerView.getResources(), mContainerView.getDisplay());
-        m.postScale(((float) surfacePosition.width()) / metrics.widthPixels,
-                ((float) surfacePosition.height()) / metrics.heightPixels);
+        Point displayMetrics = getDisplayMetrics();
+        m.postScale(((float) surfacePosition.width()) / displayMetrics.x,
+                ((float) surfacePosition.height()) / displayMetrics.y);
         m.getValues(values);
         return values;
     }
 
     /**
+     * Get display metrics. Only call this when the display is attached to the window.
+     */
+    private Point getDisplayMetrics() {
+        if (mDisplayMetrics != null) {
+            return mDisplayMetrics;
+        }
+        ScreenSizeCalculator screenSizeCalculator = ScreenSizeCalculator.getInstance();
+        Display display = mContainerView.getDisplay();
+        if (display == null) {
+            throw new NullPointerException(
+                    "Display is null due to the view not currently attached to a window.");
+        }
+        mDisplayMetrics = screenSizeCalculator.getScreenSize(display);
+        return mDisplayMetrics;
+    }
+
+    /**
      * Interface to be notified of connect/disconnect events from {@link WallpaperConnection}
      */
     public interface WallpaperConnectionListener {
diff --git a/src/com/android/wallpaper/util/WallpaperSurfaceCallback.java b/src/com/android/wallpaper/util/WallpaperSurfaceCallback.java
index 7a88ab1..8d73621 100644
--- a/src/com/android/wallpaper/util/WallpaperSurfaceCallback.java
+++ b/src/com/android/wallpaper/util/WallpaperSurfaceCallback.java
@@ -19,6 +19,8 @@
 import static android.view.View.MeasureSpec.makeMeasureSpec;
 
 import android.content.Context;
+import android.graphics.RenderEffect;
+import android.graphics.Shader;
 import android.service.wallpaper.WallpaperService;
 import android.util.Log;
 import android.view.Surface;
@@ -44,6 +46,8 @@
  */
 public class WallpaperSurfaceCallback implements SurfaceHolder.Callback {
 
+    public static final float LOW_RES_BITMAP_BLUR_RADIUS = 150f;
+
     /**
      * Listener used to be notified when this surface is created
      */
@@ -189,4 +193,20 @@
     public ImageView getHomeImageWallpaper() {
         return mHomeImageWallpaper;
     }
+
+    /**
+     * @param blur whether to blur the home image wallpaper
+     */
+    public void setHomeImageWallpaperBlur(boolean blur) {
+        if (mHomeImageWallpaper == null) {
+            return;
+        }
+        if (blur) {
+            mHomeImageWallpaper.setRenderEffect(
+                    RenderEffect.createBlurEffect(LOW_RES_BITMAP_BLUR_RADIUS,
+                            LOW_RES_BITMAP_BLUR_RADIUS, Shader.TileMode.CLAMP));
+        } else {
+            mHomeImageWallpaper.setRenderEffect(null);
+        }
+    }
 }
diff --git a/src/com/android/wallpaper/widget/DuoTabs.java b/src/com/android/wallpaper/widget/DuoTabs.java
new file mode 100644
index 0000000..a8f3024
--- /dev/null
+++ b/src/com/android/wallpaper/widget/DuoTabs.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2022 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.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.widget.Button;
+import android.widget.FrameLayout;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.wallpaper.R;
+import com.android.wallpaper.util.SystemColors;
+
+/**
+ * Custom layout for duo tabs.
+ */
+public final class DuoTabs extends FrameLayout {
+
+    public static final int TAB_PRIMARY = 0;
+    public static final int TAB_SECONDARY = 1;
+
+    /**
+     * Overlay tab
+     */
+    @IntDef({TAB_PRIMARY, TAB_SECONDARY})
+    public @interface Tab {
+    }
+
+    /**
+     * Overlay tab selected listener
+     */
+    public interface OnTabSelectedListener {
+
+        /**
+         * On tab selected
+         */
+        void onTabSelected(@Tab int tab);
+    }
+
+    OnTabSelectedListener mOnTabSelectedListener;
+    Button mPrimaryTab;
+    Button mSecondaryTab;
+    @Tab int mCurrentOverlayTab;
+
+    /**
+     * Constructor
+     */
+    public DuoTabs(@NonNull Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+        LayoutInflater.from(context).inflate(R.layout.duo_tabs, this, true);
+        mPrimaryTab = findViewById(R.id.tab_primary);
+        mSecondaryTab = findViewById(R.id.tab_secondary);
+        mPrimaryTab.setOnClickListener(v -> selectTab(TAB_PRIMARY));
+        mSecondaryTab.setOnClickListener(v -> selectTab(TAB_SECONDARY));
+    }
+
+    /**
+     * Set tab text
+     */
+    public void setTabText(String primaryTabText, String secondaryTabText) {
+        mPrimaryTab.setText(primaryTabText);
+        mSecondaryTab.setText(secondaryTabText);
+    }
+
+    /**
+     * Select a tab
+     */
+    public void selectTab(@Tab int tab) {
+        updateTabIndicator(tab);
+        if (mOnTabSelectedListener != null) {
+            mOnTabSelectedListener.onTabSelected(tab);
+        }
+        mCurrentOverlayTab = tab;
+    }
+
+    /**
+     * Set listener
+     */
+    public void setOnTabSelectedListener(
+            OnTabSelectedListener onTabSelectedListener) {
+        mOnTabSelectedListener = onTabSelectedListener;
+    }
+
+    /**
+     * Update the background color in case the context theme has changed.
+     */
+    public void updateBackgroundColor() {
+        mPrimaryTab.setBackground(null);
+        mSecondaryTab.setBackground(null);
+        updateTabIndicator(mCurrentOverlayTab);
+    }
+
+    private void updateTabIndicator(@Tab int tab) {
+        Context c = getContext();
+        mPrimaryTab.setBackgroundResource(
+                tab == TAB_PRIMARY
+                        ? R.drawable.duo_tabs_button_indicator_background
+                        : R.drawable.duo_tabs_button_background);
+        mPrimaryTab.setTextColor(
+                tab == TAB_PRIMARY
+                        ? getResources().getColor(R.color.text_color_on_accent)
+                        : SystemColors.getColor(c, android.R.attr.textColorPrimary));
+        mSecondaryTab.setBackgroundResource(
+                tab == TAB_SECONDARY
+                        ? R.drawable.duo_tabs_button_indicator_background
+                        : R.drawable.duo_tabs_button_background);
+        mSecondaryTab.setTextColor(
+                tab == TAB_SECONDARY
+                        ? getResources().getColor(R.color.text_color_on_accent)
+                        : SystemColors.getColor(c, android.R.attr.textColorPrimary));
+    }
+
+    public @Tab int getSelectedTab() {
+        return mCurrentOverlayTab;
+    }
+}
diff --git a/src/com/android/wallpaper/widget/FloatingSheet.kt b/src/com/android/wallpaper/widget/FloatingSheet.kt
new file mode 100644
index 0000000..7c8bbd7
--- /dev/null
+++ b/src/com/android/wallpaper/widget/FloatingSheet.kt
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2022 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.widget
+
+import android.annotation.IntDef
+import android.content.Context
+import android.transition.AutoTransition
+import android.transition.TransitionManager
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import androidx.appcompat.content.res.AppCompatResources
+import com.android.wallpaper.R
+import com.android.wallpaper.util.SizeCalculator
+import com.android.wallpaper.widget.floatingsheetcontent.FloatingSheetContent
+import com.google.android.material.bottomsheet.BottomSheetBehavior
+import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback
+import java.util.function.Consumer
+
+/** A `ViewGroup` which provides the specific actions for the user to interact with. */
+class FloatingSheet(context: Context, attrs: AttributeSet?) : FrameLayout(context, attrs) {
+
+    companion object {
+
+        @Target(AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.TYPE)
+        @IntDef(CUSTOMIZE, INFORMATION, EFFECTS)
+        @Retention(AnnotationRetention.SOURCE)
+        annotation class FloatingSheetContentType
+
+        const val CUSTOMIZE = 0
+        const val INFORMATION = 1
+        const val EFFECTS = 2
+    }
+
+    private val floatingSheetView: ViewGroup
+    private val floatingSheetContainer: ViewGroup
+    private val floatingSheetBehavior: BottomSheetBehavior<ViewGroup>
+    private val contentViewMap:
+        MutableMap<@FloatingSheetContentType Int, FloatingSheetContent<*>?> =
+        HashMap()
+
+    // The system "short" animation time duration, in milliseconds. This
+    // duration is ideal for subtle animations or animations that occur
+    // very frequently.
+    private val shortAnimTimeMillis: Long
+
+    init {
+        LayoutInflater.from(context).inflate(R.layout.floating_sheet, this, true)
+        floatingSheetView = requireViewById(R.id.floating_sheet_content)
+        SizeCalculator.adjustBackgroundCornerRadius(floatingSheetView)
+        setColor(context)
+        floatingSheetContainer = requireViewById(R.id.floating_sheet_container)
+        floatingSheetBehavior = BottomSheetBehavior.from(floatingSheetContainer)
+        floatingSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN
+        shortAnimTimeMillis = resources.getInteger(android.R.integer.config_shortAnimTime).toLong()
+    }
+
+    /**
+     * Binds the `floatingSheetContent` with an id that can be used identify and switch between
+     * floating sheet content
+     *
+     * @param floatingSheetContent the content object with view being added to the floating sheet
+     */
+    fun putFloatingSheetContent(
+        @FloatingSheetContentType type: Int,
+        floatingSheetContent: FloatingSheetContent<*>
+    ) {
+        floatingSheetContent.initView()
+        contentViewMap[type] = floatingSheetContent
+        floatingSheetView.addView(floatingSheetContent.contentView)
+    }
+
+    /** Dynamic update color with `Context`. */
+    fun setColor(context: Context) {
+        // Set floating sheet background.
+        floatingSheetView.background =
+            AppCompatResources.getDrawable(context, R.drawable.floating_sheet_background)
+        if (floatingSheetView.childCount > 0) {
+            // Update the bottom sheet content view if any.
+            floatingSheetView.removeAllViews()
+            contentViewMap.values.forEach(
+                Consumer { floatingSheetContent: FloatingSheetContent<*>? ->
+                    floatingSheetContent?.let {
+                        it.recreateView()
+                        floatingSheetView.addView(it.contentView)
+                    }
+                }
+            )
+        }
+    }
+
+    /** Returns `true` if the state of bottom sheet is collapsed. */
+    val isFloatingSheetCollapsed: Boolean
+        get() = floatingSheetBehavior.state == BottomSheetBehavior.STATE_HIDDEN
+
+    /** Expands [FloatingSheet]. */
+    fun expand() {
+        floatingSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
+    }
+
+    /** Collapses [FloatingSheet]. */
+    fun collapse() {
+        floatingSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN
+        endContentViewAnimation()
+    }
+
+    /**
+     * Updates content view of [FloatingSheet] with transition animation
+     *
+     * @param type the integer or enum used to identify the content view
+     */
+    fun updateContentViewWithAnimation(@FloatingSheetContentType type: Int) {
+        val transition = AutoTransition()
+        transition.duration = shortAnimTimeMillis
+        /**
+         * This line records changes you make to its views and applies a transition that animates
+         * the changes when the system redraws the user interface
+         */
+        TransitionManager.beginDelayedTransition(floatingSheetContainer, transition)
+
+        updateContentView(type)
+    }
+
+    fun endContentViewAnimation() {
+        TransitionManager.endTransitions(floatingSheetContainer)
+    }
+
+    /**
+     * Updates content view of [FloatingSheet]
+     *
+     * @param type the integer or enum used to identify the content view
+     */
+    fun updateContentView(@FloatingSheetContentType type: Int) {
+        contentViewMap.forEach { (i: Int, content: FloatingSheetContent<*>?) ->
+            content?.setVisibility(i == type)
+        }
+    }
+
+    /**
+     * Adds Floating Sheet Callback to connected [BottomSheetBehavior].
+     *
+     * @param callback the callback for floating sheet state changes, has to be in the type of
+     * [BottomSheetBehavior.BottomSheetCallback] since the floating sheet behavior is currently
+     * based on [BottomSheetBehavior]
+     */
+    fun addFloatingSheetCallback(callback: BottomSheetCallback?) {
+        floatingSheetBehavior.addBottomSheetCallback(callback!!)
+    }
+}
diff --git a/src/com/android/wallpaper/widget/GridRowSpacerDecoration.kt b/src/com/android/wallpaper/widget/GridRowSpacerDecoration.kt
index 8b1fcdb..c3b7afe 100644
--- a/src/com/android/wallpaper/widget/GridRowSpacerDecoration.kt
+++ b/src/com/android/wallpaper/widget/GridRowSpacerDecoration.kt
@@ -20,15 +20,14 @@
 import androidx.recyclerview.widget.GridLayoutManager
 import androidx.recyclerview.widget.RecyclerView
 
-/**
- * RecyclerView ItemDecorator that adds a vertical space between grid rows.
- */
+/** RecyclerView ItemDecorator that adds a vertical space between grid rows. */
 class GridRowSpacerDecoration(private val padding: Int) : RecyclerView.ItemDecoration() {
     override fun getItemOffsets(
-            outRect: Rect,
-            view: View,
-            parent: RecyclerView,
-            state: RecyclerView.State) {
+        outRect: Rect,
+        view: View,
+        parent: RecyclerView,
+        state: RecyclerView.State
+    ) {
         val layoutManager = parent.layoutManager
         if (layoutManager is GridLayoutManager) {
             val position = parent.getChildAdapterPosition(view)
diff --git a/src/com/android/wallpaper/widget/WallpaperControlButtonGroup.java b/src/com/android/wallpaper/widget/WallpaperControlButtonGroup.java
new file mode 100644
index 0000000..00a681a
--- /dev/null
+++ b/src/com/android/wallpaper/widget/WallpaperControlButtonGroup.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2022 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.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.widget.CompoundButton;
+import android.widget.FrameLayout;
+import android.widget.ToggleButton;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.content.res.AppCompatResources;
+
+import com.android.wallpaper.R;
+
+/**
+ * Custom layout for a group of wallpaper control buttons.
+ */
+public final class WallpaperControlButtonGroup extends FrameLayout {
+
+    public static final int DELETE = 0;
+    public static final int CUSTOMIZE = 1;
+    public static final int EFFECTS = 2;
+    public static final int INFORMATION = 3;
+
+    /**
+     * Overlay tab
+     */
+    @IntDef({DELETE, CUSTOMIZE, EFFECTS, INFORMATION})
+    public @interface WallpaperControlType {
+    }
+
+    final int[] mFloatingSheetControlButtonTypes = { CUSTOMIZE, EFFECTS, INFORMATION };
+
+    ToggleButton mDeleteButton;
+    ToggleButton mCustomizeButton;
+    ToggleButton mEffectsButton;
+    ToggleButton mInformationButton;
+
+    /**
+     * Constructor
+     */
+    public WallpaperControlButtonGroup(@NonNull Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+        LayoutInflater.from(context).inflate(R.layout.wallpaper_control_button_group, this, true);
+        mDeleteButton = findViewById(R.id.delete_button);
+        mCustomizeButton = findViewById(R.id.customize_button);
+        mEffectsButton = findViewById(R.id.effects_button);
+        mInformationButton = findViewById(R.id.information_button);
+    }
+
+    /**
+     * Show a button by giving a correspondent listener
+     */
+    public void showButton(@WallpaperControlType int type,
+            CompoundButton.OnCheckedChangeListener listener) {
+        ToggleButton button = getActionButton(type);
+        if (button != null) {
+            button.setVisibility(VISIBLE);
+            button.setOnCheckedChangeListener(listener);
+        }
+    }
+
+    private ToggleButton getActionButton(@WallpaperControlType int type) {
+        switch (type) {
+            case DELETE:
+                return mDeleteButton;
+            case CUSTOMIZE:
+                return mCustomizeButton;
+            case EFFECTS:
+                return mEffectsButton;
+            case INFORMATION:
+                return mInformationButton;
+            default:
+                return null;
+        }
+    }
+
+    /**
+     * Hide a button
+     */
+    public void hideButton(@WallpaperControlType int type) {
+        getActionButton(type).setVisibility(GONE);
+    }
+
+    /**
+     * Set checked for a button
+     */
+    public void setChecked(@WallpaperControlType int type, boolean checked) {
+        getActionButton(type).setChecked(checked);
+    }
+
+    /**
+     * Update the background color in case the context theme has changed.
+     */
+    public void updateBackgroundColor() {
+        Context context = getContext();
+        if (context == null) {
+            return;
+        }
+        mDeleteButton.setForeground(null);
+        mCustomizeButton.setForeground(null);
+        mEffectsButton.setForeground(null);
+        mInformationButton.setForeground(null);
+        mDeleteButton.setForeground(AppCompatResources.getDrawable(context,
+                R.drawable.wallpaper_control_button_delete));
+        mCustomizeButton.setForeground(AppCompatResources.getDrawable(context,
+                R.drawable.wallpaper_control_button_customize));
+        mEffectsButton.setForeground(AppCompatResources.getDrawable(context,
+                R.drawable.wallpaper_control_button_effect));
+        mInformationButton.setForeground(
+                AppCompatResources.getDrawable(context, R.drawable.wallpaper_control_button_info));
+    }
+
+    /**
+     * Ensures only one toggle button with a floating sheet is selected at a time
+     */
+    public void deselectOtherFloatingSheetControlButtons(@WallpaperControlType int selectedType) {
+        for (int type : mFloatingSheetControlButtonTypes) {
+            if (type != selectedType) {
+                getActionButton(type).setChecked(false);
+            }
+        }
+    }
+
+    /**
+     * Returns true if there is a floating sheet button selected, and false if not
+     */
+    public boolean isFloatingSheetControlButtonSelected() {
+        for (int type : mFloatingSheetControlButtonTypes) {
+            if (getActionButton(type).isChecked()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Deselects all floating sheet toggle buttons in the Wallpaper Control Button Group
+     */
+    public void deselectAllFloatingSheetControlButtons() {
+        for (int type : mFloatingSheetControlButtonTypes) {
+            getActionButton(type).setChecked(false);
+        }
+    }
+}
diff --git a/src/com/android/wallpaper/widget/WallpaperDownloadButton.java b/src/com/android/wallpaper/widget/WallpaperDownloadButton.java
new file mode 100644
index 0000000..c1047eb
--- /dev/null
+++ b/src/com/android/wallpaper/widget/WallpaperDownloadButton.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2022 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.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.CompoundButton;
+import android.widget.FrameLayout;
+import android.widget.ToggleButton;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.content.res.AppCompatResources;
+
+import com.android.wallpaper.R;
+
+/**
+ * Custom layout for the download button.
+ */
+public final class WallpaperDownloadButton extends FrameLayout {
+
+    ToggleButton mDownloadButton;
+    View mDownloadActionProgressBar;
+
+    /**
+     * Constructor
+     */
+    public WallpaperDownloadButton(@NonNull Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+        LayoutInflater.from(context).inflate(R.layout.button_download_wallpaper, this, true);
+        mDownloadButton = findViewById(R.id.download_button);
+        mDownloadActionProgressBar = findViewById(R.id.action_download_progress);
+    }
+
+    /**
+     * Set {@link CompoundButton.OnCheckedChangeListener }
+     */
+    public void setOnCheckedChangeListener(CompoundButton.OnCheckedChangeListener listener) {
+        mDownloadButton.setOnCheckedChangeListener(listener);
+    }
+
+    /**
+     * Show the progress bar for the download button
+     */
+    public void showDownloadActionProgress() {
+        mDownloadButton.setVisibility(GONE);
+        mDownloadActionProgressBar.setVisibility(VISIBLE);
+    }
+
+    /**
+     * Hide the progress bar for the download button
+     */
+    public void hideDownloadActionProgress() {
+        mDownloadButton.setVisibility(VISIBLE);
+        mDownloadActionProgressBar.setVisibility(GONE);
+    }
+
+    /**
+     * Update the color in case the context theme has changed.
+     */
+    public void updateColor() {
+        Context context = getContext();
+        if (context == null) {
+            return;
+        }
+        mDownloadButton.setForeground(null);
+        mDownloadButton.setForeground(AppCompatResources.getDrawable(context,
+                R.drawable.wallpaper_control_button_download));
+    }
+}
diff --git a/src/com/android/wallpaper/widget/floatingsheetcontent/FloatingSheetContent.kt b/src/com/android/wallpaper/widget/floatingsheetcontent/FloatingSheetContent.kt
new file mode 100644
index 0000000..4bdda26
--- /dev/null
+++ b/src/com/android/wallpaper/widget/floatingsheetcontent/FloatingSheetContent.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2022 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.widget.floatingsheetcontent
+
+import android.content.Context
+import android.view.LayoutInflater
+import android.view.View
+import android.widget.FrameLayout
+import androidx.annotation.LayoutRes
+
+/**
+ * Object to host content view for floating sheet to display.
+ *
+ * The view would be created in the constructor.
+ *
+ * @param <T> the floating sheet content type </T>
+ *
+ * TODO: refactoring FloatingSheetContent b/258468645
+ */
+abstract class FloatingSheetContent<T : View>(private val context: Context) {
+
+    lateinit var contentView: T
+    private var isVisible = false
+
+    /** Gets the view id to inflate. */
+    @get:LayoutRes abstract val viewId: Int
+
+    /** Gets called when the content view is created. */
+    abstract fun onViewCreated(view: T)
+
+    /** Gets called when the current content view is going to recreate. */
+    open fun onRecreateView(oldView: T) {}
+
+    fun initView() {
+        contentView = createView()
+        setVisibility(true)
+    }
+
+    fun recreateView() {
+        // Inform that the view is going to recreate.
+        onRecreateView(contentView)
+        // Create a new view with the given context.
+        contentView = createView()
+        setVisibility(isVisible)
+    }
+
+    private fun createView(): T {
+        @Suppress("UNCHECKED_CAST")
+        val contentView = LayoutInflater.from(context).inflate(viewId, null) as T
+        onViewCreated(contentView)
+        contentView.isFocusable = true
+        return contentView
+    }
+
+    open fun setVisibility(isVisible: Boolean) {
+        this.isVisible = isVisible
+        contentView.visibility = if (this.isVisible) FrameLayout.VISIBLE else FrameLayout.GONE
+    }
+}
diff --git a/src/com/android/wallpaper/widget/floatingsheetcontent/PreviewCustomizeSettingsContent.kt b/src/com/android/wallpaper/widget/floatingsheetcontent/PreviewCustomizeSettingsContent.kt
new file mode 100644
index 0000000..c836c68
--- /dev/null
+++ b/src/com/android/wallpaper/widget/floatingsheetcontent/PreviewCustomizeSettingsContent.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2022 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.widget.floatingsheetcontent
+
+import android.content.Context
+import android.net.Uri
+import android.view.View
+import androidx.lifecycle.LiveData
+import androidx.slice.Slice
+import androidx.slice.widget.SliceLiveData
+import androidx.slice.widget.SliceView
+import com.android.wallpaper.R
+
+class PreviewCustomizeSettingsContent(
+    private val context: Context,
+    private val uriSettingsSlice: Uri?
+) : FloatingSheetContent<View>(context) {
+
+    private lateinit var settingsSliceView: SliceView
+    private var settingsLiveData: LiveData<Slice>? = null
+    override val viewId: Int
+        get() = R.layout.preview_customize_settings
+
+    override fun onViewCreated(previewPage: View) {
+        settingsSliceView = previewPage.findViewById(R.id.settings_slice)
+        settingsSliceView.mode = SliceView.MODE_LARGE
+        settingsSliceView.isScrollable = false
+        if (uriSettingsSlice != null) {
+            settingsLiveData = SliceLiveData.fromUri(context, uriSettingsSlice)
+        }
+        settingsLiveData?.observeForever(settingsSliceView)
+    }
+
+    override fun onRecreateView(oldPreviewPage: View) {
+        if (settingsLiveData != null && settingsLiveData!!.hasObservers()) {
+            settingsLiveData!!.removeObserver(settingsSliceView)
+        }
+    }
+}
diff --git a/src/com/android/wallpaper/widget/floatingsheetcontent/WallpaperInfoContent.kt b/src/com/android/wallpaper/widget/floatingsheetcontent/WallpaperInfoContent.kt
new file mode 100644
index 0000000..2bc75a7
--- /dev/null
+++ b/src/com/android/wallpaper/widget/floatingsheetcontent/WallpaperInfoContent.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2022 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.widget.floatingsheetcontent
+
+import android.content.Context
+import android.content.Intent
+import com.android.wallpaper.R
+import com.android.wallpaper.model.WallpaperInfo
+import com.android.wallpaper.module.InjectorProvider
+import com.android.wallpaper.picker.WallpaperInfoHelper
+
+/** Floating Sheet Content for displaying wallpaper info */
+class WallpaperInfoContent(private var context: Context, private val wallpaper: WallpaperInfo?) :
+    FloatingSheetContent<WallpaperInfoView>(context) {
+
+    private var exploreIntent: Intent? = null
+    private var actionLabel: CharSequence? = null
+    private var wallpaperInfoView: WallpaperInfoView? = null
+    override val viewId: Int
+        get() = R.layout.floating_sheet_wallpaper_info_view
+
+    /** Gets called when the content view is created or recreated by [FloatingSheetContent] */
+    override fun onViewCreated(view: WallpaperInfoView) {
+        wallpaperInfoView = view
+        context = view.context
+        initializeWallpaperContent()
+    }
+
+    private fun initializeWallpaperContent() {
+        if (wallpaper == null) {
+            return
+        }
+        if (actionLabel == null) {
+            setUpExploreIntentAndLabel { populateWallpaperInfo(wallpaperInfoView) }
+        } else {
+            populateWallpaperInfo(wallpaperInfoView)
+        }
+    }
+
+    private fun setUpExploreIntentAndLabel(callback: Runnable?) {
+        WallpaperInfoHelper.loadExploreIntent(context, wallpaper!!) {
+            actionLabel: CharSequence?,
+            exploreIntent: Intent? ->
+            this.actionLabel = actionLabel
+            this.exploreIntent = exploreIntent
+            callback?.run()
+        }
+    }
+
+    private fun onExploreClicked() {
+        val injector = InjectorProvider.getInjector()
+        val userEventLogger = injector.getUserEventLogger(context!!.applicationContext)
+        userEventLogger.logActionClicked(
+            wallpaper!!.getCollectionId(context),
+            wallpaper.getActionLabelRes(context)
+        )
+        context.startActivity(exploreIntent)
+    }
+
+    private fun populateWallpaperInfo(view: WallpaperInfoView?) {
+        view!!.populateWallpaperInfo(
+            wallpaper!!,
+            actionLabel,
+            WallpaperInfoHelper.shouldShowExploreButton(context, exploreIntent)
+        ) { onExploreClicked() }
+    }
+}
diff --git a/src/com/android/wallpaper/widget/floatingsheetcontent/WallpaperInfoView.kt b/src/com/android/wallpaper/widget/floatingsheetcontent/WallpaperInfoView.kt
new file mode 100644
index 0000000..fe88ea4
--- /dev/null
+++ b/src/com/android/wallpaper/widget/floatingsheetcontent/WallpaperInfoView.kt
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2022 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.widget.floatingsheetcontent
+
+import android.content.Context
+import android.os.Handler
+import android.os.Looper
+import android.util.AttributeSet
+import android.widget.Button
+import android.widget.LinearLayout
+import android.widget.TextView
+import com.android.wallpaper.R
+import com.android.wallpaper.model.WallpaperInfo
+import java.util.concurrent.Executors
+
+/** A view for displaying wallpaper info. */
+class WallpaperInfoView(context: Context?, attrs: AttributeSet?) : LinearLayout(context, attrs) {
+
+    private val executorService = Executors.newCachedThreadPool()
+    private var title: TextView? = null
+    private var subtitle1: TextView? = null
+    private var subtitle2: TextView? = null
+    private var exploreButton: Button? = null
+
+    override fun onFinishInflate() {
+        super.onFinishInflate()
+        title = findViewById(R.id.wallpaper_info_title)
+        subtitle1 = findViewById(R.id.wallpaper_info_subtitle1)
+        subtitle2 = findViewById(R.id.wallpaper_info_subtitle2)
+        exploreButton = findViewById(R.id.wallpaper_info_explore_button)
+    }
+
+    /** Populates wallpaper info. */
+    fun populateWallpaperInfo(
+        wallpaperInfo: WallpaperInfo,
+        actionLabel: CharSequence?,
+        shouldShowExploreButton: Boolean,
+        exploreButtonClickListener: OnClickListener?
+    ) {
+        executorService.execute {
+            val attributions = wallpaperInfo.getAttributions(context)
+            Handler(Looper.getMainLooper()).post {
+
+                // Reset wallpaper information UI
+                title?.text = ""
+                subtitle1?.text = ""
+                subtitle1?.visibility = GONE
+                subtitle2?.text = ""
+                subtitle2?.visibility = GONE
+                exploreButton?.text = ""
+                exploreButton?.setOnClickListener(null)
+                exploreButton?.visibility = GONE
+                if (attributions.size > 0 && attributions[0] != null) {
+                    title?.text = attributions[0]
+                }
+                if (shouldShowMetadata(wallpaperInfo)) {
+                    if (attributions.size > 1 && attributions[1] != null) {
+                        subtitle1?.visibility = VISIBLE
+                        subtitle1?.text = attributions[1]
+                    }
+                    if (attributions.size > 2 && attributions[2] != null) {
+                        subtitle2?.visibility = VISIBLE
+                        subtitle2?.text = attributions[2]
+                    }
+                    if (shouldShowExploreButton) {
+                        exploreButton?.visibility = VISIBLE
+                        exploreButton?.text = actionLabel
+                        exploreButton?.setOnClickListener(exploreButtonClickListener)
+                    }
+                }
+            }
+        }
+    }
+
+    private fun shouldShowMetadata(wallpaperInfo: WallpaperInfo): Boolean {
+        val wallpaperComponent = wallpaperInfo.wallpaperComponent
+        return wallpaperComponent == null || wallpaperComponent.showMetadataInPreview
+    }
+}
diff --git a/src_override/com/android/wallpaper/module/WallpapersInjector.java b/src_override/com/android/wallpaper/module/WallpapersInjector.java
deleted file mode 100755
index 54a2533..0000000
--- a/src_override/com/android/wallpaper/module/WallpapersInjector.java
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.wallpaper.module;
-
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-
-import androidx.fragment.app.Fragment;
-
-import com.android.wallpaper.model.CategoryProvider;
-import com.android.wallpaper.model.WallpaperInfo;
-import com.android.wallpaper.monitor.PerformanceMonitor;
-import com.android.wallpaper.picker.CustomizationPickerActivity;
-import com.android.wallpaper.picker.ImagePreviewFragment;
-
-/**
- * A concrete, real implementation of the dependency provider.
- */
-public class WallpapersInjector extends BaseWallpaperInjector {
-    private CategoryProvider mCategoryProvider;
-    private UserEventLogger mUserEventLogger;
-    private WallpaperRotationRefresher mWallpaperRotationRefresher;
-    private PerformanceMonitor mPerformanceMonitor;
-
-    @Override
-    public synchronized CategoryProvider getCategoryProvider(Context context) {
-        if (mCategoryProvider == null) {
-            mCategoryProvider = new DefaultCategoryProvider(context.getApplicationContext());
-        }
-        return mCategoryProvider;
-    }
-
-    @Override
-    public synchronized UserEventLogger getUserEventLogger(Context context) {
-        if (mUserEventLogger == null) {
-            mUserEventLogger = new NoOpUserEventLogger();
-        }
-        return mUserEventLogger;
-    }
-
-    @Override
-    public synchronized WallpaperRotationRefresher getWallpaperRotationRefresher() {
-        if (mWallpaperRotationRefresher == null) {
-            mWallpaperRotationRefresher = new WallpaperRotationRefresher() {
-                @Override
-                public void refreshWallpaper(Context context, Listener listener) {
-                    // Not implemented
-                    listener.onError();
-                }
-            };
-        }
-        return mWallpaperRotationRefresher;
-    }
-
-    @Override
-    public Fragment getPreviewFragment(
-            Context context,
-            WallpaperInfo wallpaperInfo,
-            int mode,
-            boolean viewAsHome,
-            boolean viewFullScreen,
-            boolean testingModeEnabled) {
-        return ImagePreviewFragment.newInstance(wallpaperInfo, mode, viewAsHome, viewFullScreen,
-                testingModeEnabled);
-    }
-
-    @Override
-    public Intent getDeepLinkRedirectIntent(Context context, Uri uri) {
-        Intent intent = new Intent();
-        intent.setClass(context, CustomizationPickerActivity.class);
-        intent.setData(uri);
-        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
-        return intent;
-    }
-
-    @Override
-    public synchronized PerformanceMonitor getPerformanceMonitor() {
-        if (mPerformanceMonitor == null) {
-            mPerformanceMonitor = new PerformanceMonitor() {
-                @Override
-                public void recordFullResPreviewLoadedMemorySnapshot() {
-                    // No Op
-                }
-            };
-        }
-        return mPerformanceMonitor;
-    }
-
-    @Override
-    public synchronized LoggingOptInStatusProvider getLoggingOptInStatusProvider(Context context) {
-        return null;
-    }
-
-    @Override
-    public String getDownloadableIntentAction() {
-        return null;
-    }
-}
diff --git a/tests/Android.bp b/tests/Android.bp
index 54a3842..c21b526 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -20,6 +20,23 @@
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
+android_library {
+    name: "WallpaperPicker2TestLib",
+
+    defaults: ["WallpaperPicker2_defaults"],
+    srcs: [
+        "src/com/android/wallpaper/testing/**/*.java",
+        "src/com/android/wallpaper/testing/**/*.kt",
+    ],
+    static_libs: [
+        "androidx.annotation_annotation",
+        "kotlinx_coroutines_test",
+        "truth-prebuilt",
+    ],
+
+    platform_apis: true,
+}
+
 android_test {
     name: "WallpaperPicker2Tests",
 
@@ -28,8 +45,12 @@
         "src/**/*.java",
         "src/**/*.kt",
     ],
+    exclude_srcs: [
+        "src/com/android/wallpaper/testing/**/*.java",
+        "src/com/android/wallpaper/testing/**/*.kt",
+    ],
     static_libs: [
-        "androidx.annotation_annotation",
+        "WallpaperPicker2TestLib",
         "androidx.test.espresso.core",
         "androidx.test.espresso.contrib",
         "androidx.test.espresso.intents",
@@ -39,6 +60,8 @@
         "mockito-target-minus-junit4",
         "ub-uiautomator",
         "junit",
+        "kotlinx_coroutines_test",
+        "truth-prebuilt",
     ],
     libs: [
         "android.test.runner",
diff --git a/tests/robotests/Android.bp b/tests/robotests/Android.bp
new file mode 100644
index 0000000..37fb21a
--- /dev/null
+++ b/tests/robotests/Android.bp
@@ -0,0 +1,22 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_robolectric_test {
+    enabled: true,
+
+    name: "WallpaperPicker2RoboTests",
+
+    srcs: [
+        "src/**/*.java",
+    ],
+
+    java_resource_dirs: ["config"],
+
+    libs: [
+        "androidx.test.core",
+        "androidx.test.runner",
+    ],
+
+    instrumentation_for: "WallpaperPicker2",
+}
diff --git a/tests/robotests/AndroidManifest.xml b/tests/robotests/AndroidManifest.xml
new file mode 100644
index 0000000..0909593
--- /dev/null
+++ b/tests/robotests/AndroidManifest.xml
@@ -0,0 +1,6 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.wallpaper">
+
+    <application/>
+
+</manifest>
diff --git a/tests/robotests/config/robolectric.properties b/tests/robotests/config/robolectric.properties
new file mode 100644
index 0000000..fab7251
--- /dev/null
+++ b/tests/robotests/config/robolectric.properties
@@ -0,0 +1 @@
+sdk=NEWEST_SDK
diff --git a/robolectric_tests/src/com/android/wallpaper/picker/BaseActivityTest.java b/tests/robotests/src/com/android/wallpaper/picker/BaseActivityTest.java
similarity index 100%
rename from robolectric_tests/src/com/android/wallpaper/picker/BaseActivityTest.java
rename to tests/robotests/src/com/android/wallpaper/picker/BaseActivityTest.java
diff --git a/tests/src/com/android/wallpaper/picker/customization/ui/viewmodel/CustomizationPickerViewModelTest.kt b/tests/src/com/android/wallpaper/picker/customization/ui/viewmodel/CustomizationPickerViewModelTest.kt
new file mode 100644
index 0000000..f9c0640
--- /dev/null
+++ b/tests/src/com/android/wallpaper/picker/customization/ui/viewmodel/CustomizationPickerViewModelTest.kt
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2022 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.customization.ui.viewmodel
+
+import androidx.lifecycle.SavedStateHandle
+import androidx.test.filters.SmallTest
+import com.android.wallpaper.picker.undo.data.repository.UndoRepository
+import com.android.wallpaper.picker.undo.domain.interactor.UndoInteractor
+import com.android.wallpaper.testing.FAKE_RESTORERS
+import com.android.wallpaper.testing.collectLastValue
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class CustomizationPickerViewModelTest {
+
+    private lateinit var underTest: CustomizationPickerViewModel
+
+    private lateinit var savedStateHandle: SavedStateHandle
+    private lateinit var testScope: TestScope
+    private lateinit var undoInteractor: UndoInteractor
+
+    @Before
+    fun setUp() {
+        val testDispatcher = StandardTestDispatcher()
+        testScope = TestScope(testDispatcher)
+        savedStateHandle = SavedStateHandle()
+        undoInteractor =
+            UndoInteractor(
+                scope = testScope.backgroundScope,
+                repository = UndoRepository(),
+                restorerByOwnerId = FAKE_RESTORERS,
+            )
+
+        underTest =
+            CustomizationPickerViewModel(
+                undoInteractor = undoInteractor,
+                savedStateHandle = savedStateHandle,
+            )
+    }
+
+    @Test
+    fun `initial tab is lock screen`() =
+        testScope.runTest {
+            val homeScreenTab = collectLastValue(underTest.homeScreenTab)
+            val lockScreenTab = collectLastValue(underTest.lockScreenTab)
+            val isOnLockScreen = collectLastValue(underTest.isOnLockScreen)
+
+            assertThat(homeScreenTab()?.isSelected).isFalse()
+            assertThat(lockScreenTab()?.isSelected).isTrue()
+            assertThat(isOnLockScreen()).isTrue()
+        }
+
+    @Test
+    fun `switching to the home screen`() =
+        testScope.runTest {
+            val homeScreenTab = collectLastValue(underTest.homeScreenTab)
+            val lockScreenTab = collectLastValue(underTest.lockScreenTab)
+            val isOnLockScreen = collectLastValue(underTest.isOnLockScreen)
+
+            homeScreenTab()?.onClicked?.invoke()
+
+            assertThat(homeScreenTab()?.isSelected).isTrue()
+            assertThat(lockScreenTab()?.isSelected).isFalse()
+            assertThat(isOnLockScreen()).isFalse()
+        }
+
+    @Test
+    fun `switching to the home screen and back to the lock screen`() =
+        testScope.runTest {
+            val homeScreenTab = collectLastValue(underTest.homeScreenTab)
+            val lockScreenTab = collectLastValue(underTest.lockScreenTab)
+            val isOnLockScreen = collectLastValue(underTest.isOnLockScreen)
+
+            homeScreenTab()?.onClicked?.invoke()
+            lockScreenTab()?.onClicked?.invoke()
+
+            assertThat(homeScreenTab()?.isSelected).isFalse()
+            assertThat(lockScreenTab()?.isSelected).isTrue()
+            assertThat(isOnLockScreen()).isTrue()
+        }
+
+    @Test
+    fun `restores saved state`() =
+        testScope.runTest {
+            val oldHomeScreenTab = collectLastValue(underTest.homeScreenTab)
+
+            // Switch to the home screen, which is **not** the default.
+            oldHomeScreenTab()?.onClicked?.invoke()
+
+            // Instantiate a new view-model with the same saved state
+            val newUnderTest =
+                CustomizationPickerViewModel(
+                    undoInteractor = undoInteractor,
+                    savedStateHandle = savedStateHandle,
+                )
+            val newHomeScreenTab = collectLastValue(newUnderTest.homeScreenTab)
+
+            assertThat(newHomeScreenTab()?.isSelected).isTrue()
+        }
+}
diff --git a/tests/src/com/android/wallpaper/picker/undo/data/repository/UndoRepositoryTest.kt b/tests/src/com/android/wallpaper/picker/undo/data/repository/UndoRepositoryTest.kt
new file mode 100644
index 0000000..d9f8562
--- /dev/null
+++ b/tests/src/com/android/wallpaper/picker/undo/data/repository/UndoRepositoryTest.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2022 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.undo.data.repository
+
+import androidx.test.filters.SmallTest
+import com.android.wallpaper.testing.collectLastValue
+import com.android.wallpaper.testing.snapshot
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class UndoRepositoryTest {
+
+    private lateinit var underTest: UndoRepository
+
+    @Before
+    fun setUp() {
+        underTest = UndoRepository()
+    }
+
+    @Test
+    fun `put and get initial snapshot`() {
+        val ownerId1 = 1
+        val ownerId2 = 2
+
+        underTest.putSnapshot(ownerId1, snapshot(ownerId1, 1))
+        underTest.putSnapshot(ownerId2, snapshot(ownerId2, 1))
+
+        assertThat(underTest.getSnapshot(ownerId1)).isEqualTo(snapshot(ownerId1, 1))
+        assertThat(underTest.getSnapshot(ownerId2)).isEqualTo(snapshot(ownerId2, 1))
+    }
+
+    @Test
+    fun dirty() = runTest {
+        val ownerId1 = 1
+        val ownerId2 = 2
+        val isUndoable = collectLastValue(underTest.isAnythingDirty)
+
+        assertThat(isUndoable()).isFalse()
+        assertThat(underTest.getAllDirty()).isEmpty()
+
+        underTest.putDirty(ownerId1, true)
+        assertThat(isUndoable()).isTrue()
+        assertThat(underTest.getAllDirty()).isEqualTo(setOf(ownerId1))
+
+        underTest.putDirty(ownerId2, true)
+        assertThat(isUndoable()).isTrue()
+        assertThat(underTest.getAllDirty()).isEqualTo(setOf(ownerId1, ownerId2))
+
+        underTest.putDirty(ownerId1, false)
+        assertThat(isUndoable()).isTrue()
+        assertThat(underTest.getAllDirty()).isEqualTo(setOf(ownerId2))
+
+        underTest.putDirty(ownerId2, false)
+        assertThat(isUndoable()).isFalse()
+        assertThat(underTest.getAllDirty()).isEmpty()
+    }
+}
diff --git a/tests/src/com/android/wallpaper/picker/undo/domain/interactor/UndoInteractorTest.kt b/tests/src/com/android/wallpaper/picker/undo/domain/interactor/UndoInteractorTest.kt
new file mode 100644
index 0000000..5cbf67a
--- /dev/null
+++ b/tests/src/com/android/wallpaper/picker/undo/domain/interactor/UndoInteractorTest.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2022 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.undo.domain.interactor
+
+import androidx.test.filters.SmallTest
+import com.android.wallpaper.picker.undo.data.repository.UndoRepository
+import com.android.wallpaper.testing.FAKE_RESTORERS
+import com.android.wallpaper.testing.collectLastValue
+import com.android.wallpaper.testing.snapshot
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class UndoInteractorTest {
+
+    private lateinit var underTest: UndoInteractor
+
+    private lateinit var testScope: TestScope
+
+    @Before
+    fun setUp() {
+        val testDispatcher = StandardTestDispatcher()
+        testScope = TestScope(testDispatcher)
+
+        underTest =
+            UndoInteractor(
+                scope = testScope.backgroundScope,
+                repository = UndoRepository(),
+                restorerByOwnerId = FAKE_RESTORERS,
+            )
+    }
+
+    @Test
+    fun `start session - update - and undo all`() =
+        testScope.runTest {
+            val isUndoable = collectLastValue(underTest.isUndoable)
+            assertThat(isUndoable()).isFalse()
+
+            underTest.startSession()
+            assertThat(isUndoable()).isFalse()
+
+            FAKE_RESTORERS[1]?.update(2)
+            assertThat(isUndoable()).isTrue()
+
+            FAKE_RESTORERS[1]?.update(0) // This resets back to the initial snapshot
+            assertThat(isUndoable()).isFalse()
+
+            FAKE_RESTORERS[1]?.update(1)
+            FAKE_RESTORERS[2]?.update(2)
+            assertThat(isUndoable()).isTrue()
+
+            underTest.revertAll()
+            assertThat(isUndoable()).isFalse()
+            assertThat(FAKE_RESTORERS[1]?.restored).isEqualTo(snapshot(1, 0))
+            assertThat(FAKE_RESTORERS[2]?.restored).isEqualTo(snapshot(2, 0))
+            assertThat(FAKE_RESTORERS[3]?.restored).isNull()
+        }
+}
diff --git a/tests/src/com/android/wallpaper/picker/undo/ui/viewmodel/UndoViewModelTest.kt b/tests/src/com/android/wallpaper/picker/undo/ui/viewmodel/UndoViewModelTest.kt
new file mode 100644
index 0000000..0def5f2
--- /dev/null
+++ b/tests/src/com/android/wallpaper/picker/undo/ui/viewmodel/UndoViewModelTest.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2022 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.undo.ui.viewmodel
+
+import androidx.test.filters.SmallTest
+import com.android.wallpaper.picker.undo.data.repository.UndoRepository
+import com.android.wallpaper.picker.undo.domain.interactor.UndoInteractor
+import com.android.wallpaper.testing.FAKE_RESTORERS
+import com.android.wallpaper.testing.collectLastValue
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class UndoViewModelTest {
+
+    private lateinit var underTest: UndoViewModel
+
+    private lateinit var testScope: TestScope
+    private lateinit var interactor: UndoInteractor
+
+    @Before
+    fun setUp() {
+        val testDispatcher = StandardTestDispatcher()
+        testScope = TestScope(testDispatcher)
+        interactor =
+            UndoInteractor(
+                scope = testScope.backgroundScope,
+                repository = UndoRepository(),
+                restorerByOwnerId = FAKE_RESTORERS,
+            )
+
+        underTest =
+            UndoViewModel(
+                interactor = interactor,
+            )
+    }
+
+    @Test
+    fun revert() =
+        testScope.runTest {
+            val isRevertButtonVisible = collectLastValue(underTest.isRevertButtonVisible)
+            val dialog = collectLastValue(underTest.dialog)
+            assertThat(isRevertButtonVisible()).isFalse()
+            assertThat(dialog()).isNull()
+
+            // Start the session without anything to revert.
+            interactor.startSession()
+            assertThat(isRevertButtonVisible()).isFalse()
+            assertThat(dialog()).isNull()
+
+            // Record a change that can be reverted.
+            FAKE_RESTORERS[1]?.update(2)
+            assertThat(isRevertButtonVisible()).isTrue()
+            assertThat(dialog()).isNull()
+
+            // Click the revert button.
+            underTest.onRevertButtonClicked()
+            assertThat(isRevertButtonVisible()).isTrue()
+            assertThat(dialog()).isNotNull()
+
+            // Cancel the revert.
+            dialog()?.onDismissed?.invoke()
+            assertThat(isRevertButtonVisible()).isTrue()
+            assertThat(dialog()).isNull()
+
+            // Click the revert button again.
+            underTest.onRevertButtonClicked()
+            assertThat(isRevertButtonVisible()).isTrue()
+            assertThat(dialog()).isNotNull()
+
+            // Confirm the revert.
+            dialog()?.onConfirmed?.invoke()
+            assertThat(isRevertButtonVisible()).isFalse()
+        }
+}
diff --git a/tests/src/com/android/wallpaper/testing/TestCustomizationSections.kt b/tests/src/com/android/wallpaper/testing/TestCustomizationSections.kt
new file mode 100644
index 0000000..0c0ac2e
--- /dev/null
+++ b/tests/src/com/android/wallpaper/testing/TestCustomizationSections.kt
@@ -0,0 +1,48 @@
+package com.android.wallpaper.testing
+
+import android.os.Bundle
+import androidx.fragment.app.FragmentActivity
+import androidx.lifecycle.LifecycleOwner
+import com.android.wallpaper.model.CustomizationSectionController
+import com.android.wallpaper.model.PermissionRequester
+import com.android.wallpaper.model.WallpaperColorsViewModel
+import com.android.wallpaper.model.WallpaperPreviewNavigator
+import com.android.wallpaper.model.WorkspaceViewModel
+import com.android.wallpaper.module.CurrentWallpaperInfoFactory
+import com.android.wallpaper.module.CustomizationSections
+import com.android.wallpaper.util.DisplayUtils
+
+/** Test implementation of [CustomizationSections] */
+class TestCustomizationSections : CustomizationSections {
+    override fun getSectionControllersForScreen(
+        screen: CustomizationSections.Screen?,
+        activity: FragmentActivity?,
+        lifecycleOwner: LifecycleOwner?,
+        wallpaperColorsViewModel: WallpaperColorsViewModel?,
+        workspaceViewModel: WorkspaceViewModel?,
+        permissionRequester: PermissionRequester?,
+        wallpaperPreviewNavigator: WallpaperPreviewNavigator?,
+        sectionNavigationController:
+            CustomizationSectionController.CustomizationSectionNavigationController?,
+        savedInstanceState: Bundle?,
+        wallpaperInfoFactory: CurrentWallpaperInfoFactory?,
+        displayUtils: DisplayUtils?
+    ): MutableList<CustomizationSectionController<*>> {
+        return arrayListOf()
+    }
+
+    override fun getAllSectionControllers(
+        activity: FragmentActivity?,
+        lifecycleOwner: LifecycleOwner?,
+        wallpaperColorsViewModel: WallpaperColorsViewModel?,
+        workspaceViewModel: WorkspaceViewModel?,
+        permissionRequester: PermissionRequester?,
+        wallpaperPreviewNavigator: WallpaperPreviewNavigator?,
+        sectionNavigationController:
+            CustomizationSectionController.CustomizationSectionNavigationController?,
+        savedInstanceState: Bundle?,
+        displayUtils: DisplayUtils?
+    ): MutableList<CustomizationSectionController<*>> {
+        return arrayListOf()
+    }
+}
diff --git a/tests/src/com/android/wallpaper/testing/TestDrawableLayerResolver.kt b/tests/src/com/android/wallpaper/testing/TestDrawableLayerResolver.kt
new file mode 100644
index 0000000..43b3ad4
--- /dev/null
+++ b/tests/src/com/android/wallpaper/testing/TestDrawableLayerResolver.kt
@@ -0,0 +1,12 @@
+package com.android.wallpaper.testing
+
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.LayerDrawable
+import com.android.wallpaper.module.DrawableLayerResolver
+
+/** Test implementation of [DrawableLayerResolver] */
+class TestDrawableLayerResolver : DrawableLayerResolver {
+    override fun resolveLayer(layerDrawable: LayerDrawable?): Drawable {
+        return layerDrawable!!.getDrawable(0)
+    }
+}
diff --git a/tests/src/com/android/wallpaper/testing/TestInjector.java b/tests/src/com/android/wallpaper/testing/TestInjector.java
deleted file mode 100644
index ec8fb16..0000000
--- a/tests/src/com/android/wallpaper/testing/TestInjector.java
+++ /dev/null
@@ -1,284 +0,0 @@
-/*
- * Copyright (C) 2019 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.testing;
-
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-
-import androidx.annotation.Nullable;
-import androidx.fragment.app.Fragment;
-
-import com.android.wallpaper.compat.WallpaperManagerCompat;
-import com.android.wallpaper.effects.EffectsController;
-import com.android.wallpaper.model.CategoryProvider;
-import com.android.wallpaper.model.WallpaperInfo;
-import com.android.wallpaper.module.AlarmManagerWrapper;
-import com.android.wallpaper.module.BitmapCropper;
-import com.android.wallpaper.module.CurrentWallpaperInfoFactory;
-import com.android.wallpaper.module.CustomizationSections;
-import com.android.wallpaper.module.DefaultLiveWallpaperInfoFactory;
-import com.android.wallpaper.module.DrawableLayerResolver;
-import com.android.wallpaper.module.ExploreIntentChecker;
-import com.android.wallpaper.module.Injector;
-import com.android.wallpaper.module.LiveWallpaperInfoFactory;
-import com.android.wallpaper.module.LoggingOptInStatusProvider;
-import com.android.wallpaper.module.NetworkStatusNotifier;
-import com.android.wallpaper.module.PackageStatusNotifier;
-import com.android.wallpaper.module.PartnerProvider;
-import com.android.wallpaper.module.SystemFeatureChecker;
-import com.android.wallpaper.module.UserEventLogger;
-import com.android.wallpaper.module.WallpaperPersister;
-import com.android.wallpaper.module.WallpaperPreferences;
-import com.android.wallpaper.module.WallpaperRefresher;
-import com.android.wallpaper.module.WallpaperRotationRefresher;
-import com.android.wallpaper.module.WallpaperStatusChecker;
-import com.android.wallpaper.monitor.PerformanceMonitor;
-import com.android.wallpaper.network.Requester;
-import com.android.wallpaper.picker.ImagePreviewFragment;
-import com.android.wallpaper.picker.individual.IndividualPickerFragment;
-import com.android.wallpaper.util.DisplayUtils;
-
-/**
- * Test implementation of the dependency injector.
- */
-public class TestInjector implements Injector {
-
-    private BitmapCropper mBitmapCropper;
-    private CategoryProvider mCategoryProvider;
-    private PartnerProvider mPartnerProvider;
-    private WallpaperPreferences mPrefs;
-    private WallpaperPersister mWallpaperPersister;
-    private WallpaperRefresher mWallpaperRefresher;
-    private Requester mRequester;
-    private WallpaperManagerCompat mWallpaperManagerCompat;
-    private CurrentWallpaperInfoFactory mCurrentWallpaperInfoFactory;
-    private NetworkStatusNotifier mNetworkStatusNotifier;
-    private AlarmManagerWrapper mAlarmManagerWrapper;
-    private UserEventLogger mUserEventLogger;
-    private ExploreIntentChecker mExploreIntentChecker;
-    private SystemFeatureChecker mSystemFeatureChecker;
-    private WallpaperRotationRefresher mWallpaperRotationRefresher;
-    private PerformanceMonitor mPerformanceMonitor;
-    private LoggingOptInStatusProvider mLoggingOptInStatusProvider;
-
-    @Override
-    public BitmapCropper getBitmapCropper() {
-        if (mBitmapCropper == null) {
-            mBitmapCropper = new com.android.wallpaper.testing.TestBitmapCropper();
-        }
-        return mBitmapCropper;
-    }
-
-    @Override
-    public CategoryProvider getCategoryProvider(Context context) {
-        if (mCategoryProvider == null) {
-            mCategoryProvider = new TestCategoryProvider();
-        }
-        return mCategoryProvider;
-    }
-
-    @Override
-    public PartnerProvider getPartnerProvider(Context context) {
-        if (mPartnerProvider == null) {
-            mPartnerProvider = new TestPartnerProvider();
-        }
-        return mPartnerProvider;
-    }
-
-    @Override
-    public WallpaperPreferences getPreferences(Context context) {
-        if (mPrefs == null) {
-            mPrefs = new TestWallpaperPreferences();
-        }
-        return mPrefs;
-    }
-
-    @Override
-    public WallpaperPersister getWallpaperPersister(Context context) {
-        if (mWallpaperPersister == null) {
-            mWallpaperPersister = new TestWallpaperPersister(context.getApplicationContext());
-        }
-        return mWallpaperPersister;
-    }
-
-    @Override
-    public WallpaperRefresher getWallpaperRefresher(Context context) {
-        if (mWallpaperRefresher == null) {
-            mWallpaperRefresher = new TestWallpaperRefresher(context.getApplicationContext());
-        }
-        return mWallpaperRefresher;
-    }
-
-    @Override
-    public Requester getRequester(Context unused) {
-        return null;
-    }
-
-    @Override
-    public WallpaperManagerCompat getWallpaperManagerCompat(Context context) {
-        if (mWallpaperManagerCompat == null) {
-            mWallpaperManagerCompat = new com.android.wallpaper.testing.TestWallpaperManagerCompat(
-                    context.getApplicationContext());
-        }
-        return mWallpaperManagerCompat;
-    }
-
-    @Override
-    public WallpaperStatusChecker getWallpaperStatusChecker() {
-        return new WallpaperStatusChecker() {
-            @Override
-            public boolean isHomeStaticWallpaperSet(Context context) {
-                return true;
-            }
-
-            @Override
-            public boolean isLockWallpaperSet(Context context) {
-                return true;
-            }
-        };
-    }
-
-    @Override
-    public CurrentWallpaperInfoFactory getCurrentWallpaperFactory(Context context) {
-        if (mCurrentWallpaperInfoFactory == null) {
-            mCurrentWallpaperInfoFactory =
-                    new TestCurrentWallpaperInfoFactory(context.getApplicationContext());
-        }
-        return mCurrentWallpaperInfoFactory;
-    }
-
-    @Override
-    public LoggingOptInStatusProvider getLoggingOptInStatusProvider(Context context) {
-        if (mLoggingOptInStatusProvider == null) {
-            mLoggingOptInStatusProvider = new TestLoggingOptInStatusProvider();
-        }
-        return mLoggingOptInStatusProvider;
-    }
-
-    @Override
-    public NetworkStatusNotifier getNetworkStatusNotifier(Context context) {
-        if (mNetworkStatusNotifier == null) {
-            mNetworkStatusNotifier = new TestNetworkStatusNotifier();
-        }
-        return mNetworkStatusNotifier;
-    }
-
-    @Override
-    public AlarmManagerWrapper getAlarmManagerWrapper(Context unused) {
-        if (mAlarmManagerWrapper == null) {
-            mAlarmManagerWrapper = new TestAlarmManagerWrapper();
-        }
-        return mAlarmManagerWrapper;
-    }
-
-    @Override
-    public UserEventLogger getUserEventLogger(Context unused) {
-        if (mUserEventLogger == null) {
-            mUserEventLogger = new com.android.wallpaper.testing.TestUserEventLogger();
-        }
-        return mUserEventLogger;
-    }
-
-    @Override
-    public ExploreIntentChecker getExploreIntentChecker(Context unused) {
-        if (mExploreIntentChecker == null) {
-            mExploreIntentChecker = new TestExploreIntentChecker();
-        }
-        return mExploreIntentChecker;
-    }
-
-    @Override
-    public SystemFeatureChecker getSystemFeatureChecker() {
-        if (mSystemFeatureChecker == null) {
-            mSystemFeatureChecker = new com.android.wallpaper.testing.TestSystemFeatureChecker();
-        }
-        return mSystemFeatureChecker;
-    }
-
-    @Override
-    public WallpaperRotationRefresher getWallpaperRotationRefresher() {
-        if (mWallpaperRotationRefresher == null) {
-            mWallpaperRotationRefresher = (context, listener) -> {
-                // Not implemented
-                listener.onError();
-            };
-        }
-        return mWallpaperRotationRefresher;
-    }
-
-    @Override
-    public Fragment getPreviewFragment(Context context, WallpaperInfo wallpaperInfo, int mode,
-            boolean viewAsHome, boolean viewFullScreen, boolean testingModeEnabled) {
-        return ImagePreviewFragment.newInstance(wallpaperInfo, mode, viewAsHome,
-                viewFullScreen, testingModeEnabled);
-    }
-
-    @Override
-    public PackageStatusNotifier getPackageStatusNotifier(Context context) {
-        return null;
-    }
-
-    @Override
-    public IndividualPickerFragment getIndividualPickerFragment(String collectionId) {
-        return IndividualPickerFragment.newInstance(collectionId);
-    }
-
-    @Override
-    public LiveWallpaperInfoFactory getLiveWallpaperInfoFactory(Context context) {
-        return new DefaultLiveWallpaperInfoFactory();
-    }
-
-    @Override
-    public DrawableLayerResolver getDrawableLayerResolver() {
-        return null;
-    }
-
-    @Override
-    public Intent getDeepLinkRedirectIntent(Context context, Uri uri) {
-        return null;
-    }
-
-    @Override
-    public String getDownloadableIntentAction() {
-        return null;
-    }
-
-    @Override
-    public PerformanceMonitor getPerformanceMonitor() {
-        if (mPerformanceMonitor == null) {
-            mPerformanceMonitor = new TestPerformanceMonitor();
-        }
-        return mPerformanceMonitor;
-    }
-
-    @Override
-    public CustomizationSections getCustomizationSections() {
-        return null;
-    }
-
-    @Override
-    public DisplayUtils getDisplayUtils(Context context) {
-        return new DisplayUtils(context);
-    }
-
-    @Nullable
-    @Override
-    public EffectsController createEffectsController(Context context,
-            EffectsController.EffectsServiceListener listener) {
-        return null;
-    }
-}
diff --git a/tests/src/com/android/wallpaper/testing/TestInjector.kt b/tests/src/com/android/wallpaper/testing/TestInjector.kt
new file mode 100644
index 0000000..7c15540
--- /dev/null
+++ b/tests/src/com/android/wallpaper/testing/TestInjector.kt
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2019 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.testing
+
+import android.app.Activity
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
+import android.os.Bundle
+import androidx.fragment.app.Fragment
+import com.android.wallpaper.compat.WallpaperManagerCompat
+import com.android.wallpaper.config.BaseFlags
+import com.android.wallpaper.effects.EffectsController
+import com.android.wallpaper.effects.EffectsController.EffectsServiceListener
+import com.android.wallpaper.model.CategoryProvider
+import com.android.wallpaper.model.WallpaperInfo
+import com.android.wallpaper.module.AlarmManagerWrapper
+import com.android.wallpaper.module.BitmapCropper
+import com.android.wallpaper.module.CurrentWallpaperInfoFactory
+import com.android.wallpaper.module.CustomizationSections
+import com.android.wallpaper.module.DefaultLiveWallpaperInfoFactory
+import com.android.wallpaper.module.DrawableLayerResolver
+import com.android.wallpaper.module.ExploreIntentChecker
+import com.android.wallpaper.module.Injector
+import com.android.wallpaper.module.LiveWallpaperInfoFactory
+import com.android.wallpaper.module.NetworkStatusNotifier
+import com.android.wallpaper.module.PackageStatusNotifier
+import com.android.wallpaper.module.PartnerProvider
+import com.android.wallpaper.module.SystemFeatureChecker
+import com.android.wallpaper.module.UserEventLogger
+import com.android.wallpaper.module.WallpaperPersister
+import com.android.wallpaper.module.WallpaperPreferences
+import com.android.wallpaper.module.WallpaperPreviewFragmentManager
+import com.android.wallpaper.module.WallpaperRefresher
+import com.android.wallpaper.module.WallpaperRotationRefresher
+import com.android.wallpaper.module.WallpaperStatusChecker
+import com.android.wallpaper.monitor.PerformanceMonitor
+import com.android.wallpaper.network.Requester
+import com.android.wallpaper.picker.ImagePreviewFragment
+import com.android.wallpaper.picker.PreviewFragment
+import com.android.wallpaper.picker.individual.IndividualPickerFragment
+import com.android.wallpaper.picker.undo.data.repository.UndoRepository
+import com.android.wallpaper.picker.undo.domain.interactor.UndoInteractor
+import com.android.wallpaper.util.DisplayUtils
+import kotlinx.coroutines.GlobalScope
+
+/** Test implementation of [Injector] */
+open class TestInjector : Injector {
+    private var alarmManagerWrapper: AlarmManagerWrapper? = null
+    private var bitmapCropper: BitmapCropper? = null
+    private var categoryProvider: CategoryProvider? = null
+    private var currentWallpaperInfoFactory: CurrentWallpaperInfoFactory? = null
+    private var customizationSections: CustomizationSections? = null
+    private var drawableLayerResolver: DrawableLayerResolver? = null
+    private var exploreIntentChecker: ExploreIntentChecker? = null
+    private var networkStatusNotifier: NetworkStatusNotifier? = null
+    private var packageStatusNotifier: PackageStatusNotifier? = null
+    private var partnerProvider: PartnerProvider? = null
+    private var performanceMonitor: PerformanceMonitor? = null
+    private var requester: Requester? = null
+    private var systemFeatureChecker: SystemFeatureChecker? = null
+    private var userEventLogger: UserEventLogger? = null
+    private var wallpaperManagerCompat: WallpaperManagerCompat? = null
+    private var wallpaperPersister: WallpaperPersister? = null
+    private var prefs: WallpaperPreferences? = null
+    private var wallpaperPreviewFragmentManager: WallpaperPreviewFragmentManager? = null
+    private var wallpaperRefresher: WallpaperRefresher? = null
+    private var wallpaperRotationRefresher: WallpaperRotationRefresher? = null
+    private var flags: BaseFlags? = null
+    private var undoInteractor: UndoInteractor? = null
+
+    override fun getAlarmManagerWrapper(context: Context): AlarmManagerWrapper {
+        return alarmManagerWrapper ?: TestAlarmManagerWrapper().also { alarmManagerWrapper = it }
+    }
+
+    override fun getBitmapCropper(): BitmapCropper {
+        return bitmapCropper ?: TestBitmapCropper().also { bitmapCropper = it }
+    }
+
+    override fun getCategoryProvider(context: Context): CategoryProvider {
+        return categoryProvider ?: TestCategoryProvider().also { categoryProvider = it }
+    }
+
+    override fun getCurrentWallpaperInfoFactory(context: Context): CurrentWallpaperInfoFactory {
+        return currentWallpaperInfoFactory
+            ?: TestCurrentWallpaperInfoFactory(context.applicationContext).also {
+                currentWallpaperInfoFactory = it
+            }
+    }
+
+    override fun getCustomizationSections(activity: Activity): CustomizationSections {
+        return customizationSections
+            ?: TestCustomizationSections().also { customizationSections = it }
+    }
+
+    override fun getDeepLinkRedirectIntent(context: Context, uri: Uri): Intent {
+        return Intent()
+    }
+
+    override fun getDisplayUtils(context: Context): DisplayUtils {
+        return DisplayUtils(context)
+    }
+
+    override fun getDownloadableIntentAction(): String? {
+        return null
+    }
+
+    override fun getDrawableLayerResolver(): DrawableLayerResolver {
+        return drawableLayerResolver
+            ?: TestDrawableLayerResolver().also { drawableLayerResolver = it }
+    }
+
+    override fun getEffectsController(
+        context: Context,
+        listener: EffectsServiceListener
+    ): EffectsController? {
+        return null
+    }
+
+    override fun getExploreIntentChecker(context: Context): ExploreIntentChecker {
+        return exploreIntentChecker ?: TestExploreIntentChecker().also { exploreIntentChecker = it }
+    }
+
+    override fun getIndividualPickerFragment(collectionId: String): IndividualPickerFragment {
+        return IndividualPickerFragment.newInstance(collectionId)
+    }
+
+    override fun getLiveWallpaperInfoFactory(context: Context): LiveWallpaperInfoFactory {
+        return DefaultLiveWallpaperInfoFactory()
+    }
+
+    override fun getNetworkStatusNotifier(context: Context): NetworkStatusNotifier {
+        return networkStatusNotifier
+            ?: TestNetworkStatusNotifier().also { networkStatusNotifier = it }
+    }
+
+    override fun getPackageStatusNotifier(context: Context): PackageStatusNotifier {
+        return packageStatusNotifier
+            ?: TestPackageStatusNotifier().also { packageStatusNotifier = it }
+    }
+
+    override fun getPartnerProvider(context: Context): PartnerProvider {
+        return partnerProvider ?: TestPartnerProvider().also { partnerProvider = it }
+    }
+
+    override fun getPerformanceMonitor(): PerformanceMonitor? {
+        return performanceMonitor ?: TestPerformanceMonitor().also { performanceMonitor = it }
+    }
+
+    override fun getPreviewFragment(
+        context: Context,
+        wallpaperInfo: WallpaperInfo,
+        mode: Int,
+        viewAsHome: Boolean,
+        viewFullScreen: Boolean,
+        testingModeEnabled: Boolean
+    ): Fragment {
+        val args = Bundle()
+        args.putParcelable(PreviewFragment.ARG_WALLPAPER, wallpaperInfo)
+        args.putInt(PreviewFragment.ARG_PREVIEW_MODE, mode)
+        args.putBoolean(PreviewFragment.ARG_VIEW_AS_HOME, viewAsHome)
+        args.putBoolean(PreviewFragment.ARG_FULL_SCREEN, viewFullScreen)
+        args.putBoolean(PreviewFragment.ARG_TESTING_MODE_ENABLED, testingModeEnabled)
+        val fragment = ImagePreviewFragment()
+        fragment.arguments = args
+        return fragment
+    }
+
+    override fun getRequester(unused: Context): Requester {
+        return requester ?: TestRequester().also { requester = it }
+    }
+
+    override fun getSystemFeatureChecker(): SystemFeatureChecker {
+        return systemFeatureChecker ?: TestSystemFeatureChecker().also { systemFeatureChecker = it }
+    }
+
+    override fun getUserEventLogger(context: Context): UserEventLogger {
+        return userEventLogger ?: TestUserEventLogger().also { userEventLogger = it }
+    }
+
+    override fun getWallpaperManagerCompat(context: Context): WallpaperManagerCompat {
+        return wallpaperManagerCompat
+            ?: TestWallpaperManagerCompat(context.applicationContext).also {
+                wallpaperManagerCompat = it
+            }
+    }
+
+    override fun getWallpaperPersister(context: Context): WallpaperPersister {
+        return wallpaperPersister
+            ?: TestWallpaperPersister(context.applicationContext).also { wallpaperPersister = it }
+    }
+
+    override fun getPreferences(context: Context): WallpaperPreferences {
+        return prefs ?: TestWallpaperPreferences().also { prefs = it }
+    }
+
+    override fun getWallpaperPreviewFragmentManager(): WallpaperPreviewFragmentManager {
+        return wallpaperPreviewFragmentManager
+            ?: TestWallpaperPreviewFragmentManager().also { wallpaperPreviewFragmentManager = it }
+    }
+
+    override fun getWallpaperRefresher(context: Context): WallpaperRefresher {
+        return wallpaperRefresher
+            ?: TestWallpaperRefresher(context.applicationContext).also { wallpaperRefresher = it }
+    }
+
+    override fun getWallpaperRotationRefresher(): WallpaperRotationRefresher {
+        return wallpaperRotationRefresher
+            ?: WallpaperRotationRefresher {
+                    context: Context?,
+                    listener: WallpaperRotationRefresher.Listener ->
+                    // Not implemented
+                    listener.onError()
+                }
+                .also { wallpaperRotationRefresher = it }
+    }
+
+    override fun getWallpaperStatusChecker(): WallpaperStatusChecker {
+        return object : WallpaperStatusChecker {
+            override fun isHomeStaticWallpaperSet(context: Context): Boolean {
+                return true
+            }
+
+            override fun isLockWallpaperSet(context: Context): Boolean {
+                return true
+            }
+        }
+    }
+
+    override fun getFlags(): BaseFlags {
+        return flags ?: object : BaseFlags() {}.also { flags = it }
+    }
+
+    override fun getUndoInteractor(context: Context): UndoInteractor {
+        return undoInteractor
+            ?: UndoInteractor(
+                GlobalScope,
+                UndoRepository(),
+                HashMap()
+            ) // Empty because we don't support undoing in WallpaperPicker2..also{}
+    }
+}
diff --git a/tests/src/com/android/wallpaper/testing/TestLoggingOptInStatusProvider.java b/tests/src/com/android/wallpaper/testing/TestLoggingOptInStatusProvider.java
deleted file mode 100644
index f4434b1..0000000
--- a/tests/src/com/android/wallpaper/testing/TestLoggingOptInStatusProvider.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2019 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.testing;
-
-import com.android.wallpaper.module.LoggingOptInStatusProvider;
-
-/** Test implementation of {@link LoggingOptInStatusProvider}. */
-public class TestLoggingOptInStatusProvider implements LoggingOptInStatusProvider {
-
-    @Override
-    public void fetchOptInValue(OptInValueReceiver receiver) {
-        receiver.onOptInValueReady(true /* optedIn */);
-    }
-}
diff --git a/tests/src/com/android/wallpaper/testing/TestPackageStatusNotifier.kt b/tests/src/com/android/wallpaper/testing/TestPackageStatusNotifier.kt
new file mode 100644
index 0000000..fad8652
--- /dev/null
+++ b/tests/src/com/android/wallpaper/testing/TestPackageStatusNotifier.kt
@@ -0,0 +1,14 @@
+package com.android.wallpaper.testing
+
+import com.android.wallpaper.module.PackageStatusNotifier
+
+/** Test implementation of [PackageStatusNotifier] */
+class TestPackageStatusNotifier : PackageStatusNotifier {
+    override fun addListener(listener: PackageStatusNotifier.Listener?, action: String?) {
+        // Do nothing intended
+    }
+
+    override fun removeListener(listener: PackageStatusNotifier.Listener?) {
+        // Do nothing intended
+    }
+}
diff --git a/tests/src/com/android/wallpaper/testing/TestRequester.kt b/tests/src/com/android/wallpaper/testing/TestRequester.kt
new file mode 100644
index 0000000..7f2f007
--- /dev/null
+++ b/tests/src/com/android/wallpaper/testing/TestRequester.kt
@@ -0,0 +1,32 @@
+package com.android.wallpaper.testing
+
+import android.app.Activity
+import android.graphics.Bitmap
+import android.net.Uri
+import com.android.volley.Request
+import com.android.wallpaper.network.Requester
+import com.bumptech.glide.request.target.Target
+import java.io.File
+
+/** Test implementation of [Requester] */
+class TestRequester : Requester {
+    override fun <T : Any?> addToRequestQueue(request: Request<T>?) {
+        // Do nothing intended
+    }
+
+    override fun loadImageFile(imageUrl: Uri?): File {
+        return File("test_file.txt")
+    }
+
+    override fun loadImageFileWithActivity(
+        activity: Activity?,
+        imageUrl: Uri?,
+        target: Target<File>?
+    ) {
+        // Do nothing intended
+    }
+
+    override fun loadImageBitmap(imageUrl: Uri?, target: Target<Bitmap>?) {
+        // Do nothing intended
+    }
+}
diff --git a/tests/src/com/android/wallpaper/testing/TestScopeExt.kt b/tests/src/com/android/wallpaper/testing/TestScopeExt.kt
new file mode 100644
index 0000000..b5d45b0
--- /dev/null
+++ b/tests/src/com/android/wallpaper/testing/TestScopeExt.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2022 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.
+ *
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.wallpaper.testing
+
+import kotlin.coroutines.CoroutineContext
+import kotlin.coroutines.EmptyCoroutineContext
+import kotlinx.coroutines.CoroutineStart
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+
+/** Collect [flow] in a new [Job] and return a getter for the last collected value. */
+fun <T> TestScope.collectLastValue(
+    flow: Flow<T>,
+    context: CoroutineContext = EmptyCoroutineContext,
+    start: CoroutineStart = CoroutineStart.DEFAULT,
+): () -> T? {
+    var lastValue: T? = null
+    backgroundScope.launch(context, start) { flow.collect { lastValue = it } }
+    return {
+        runCurrent()
+        lastValue
+    }
+}
diff --git a/tests/src/com/android/wallpaper/testing/TestUserEventLogger.java b/tests/src/com/android/wallpaper/testing/TestUserEventLogger.java
index f439678..d99bf4b 100644
--- a/tests/src/com/android/wallpaper/testing/TestUserEventLogger.java
+++ b/tests/src/com/android/wallpaper/testing/TestUserEventLogger.java
@@ -52,6 +52,10 @@
     private String mWallpaperSetEffects;
     private String mWallpaperApplyEffect;
     private int mWallpaperApplyEffectStatus;
+    private long mTimeElapsedMillis;
+    private int mResultCode;
+    private String mWallpaperProbeEffect;
+    private int mWallpaperProbeStatus;
 
     public TestUserEventLogger() {
         mLastDailyRotationHour = -1;
@@ -240,9 +244,17 @@
     }
 
     @Override
-    public void logEffectApply(String effect, int status) {
+    public void logEffectApply(String effect, int status, long timeElapsedMillis, int resultCode) {
         mWallpaperApplyEffect = effect;
         mWallpaperApplyEffectStatus = status;
+        mTimeElapsedMillis = timeElapsedMillis;
+        mResultCode = resultCode;
+    }
+
+    @Override
+    public void logEffectProbe(String effect, @EffectStatus int status) {
+        mWallpaperProbeEffect = effect;
+        mWallpaperProbeStatus = status;
     }
 
     public int getNumWallpaperSetEvents() {
diff --git a/tests/src/com/android/wallpaper/testing/TestWallpaperPreviewFragmentManager.kt b/tests/src/com/android/wallpaper/testing/TestWallpaperPreviewFragmentManager.kt
new file mode 100644
index 0000000..a144ed3
--- /dev/null
+++ b/tests/src/com/android/wallpaper/testing/TestWallpaperPreviewFragmentManager.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2022 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.testing
+
+import android.content.Context
+import android.os.Bundle
+import androidx.fragment.app.Fragment
+import com.android.wallpaper.model.WallpaperInfo
+import com.android.wallpaper.module.WallpaperPreviewFragmentManager
+import com.android.wallpaper.picker.ImagePreviewFragment
+import com.android.wallpaper.picker.PreviewFragment
+
+class TestWallpaperPreviewFragmentManager : WallpaperPreviewFragmentManager {
+    override fun getPreviewFragment(
+        context: Context,
+        wallpaperInfo: WallpaperInfo,
+        mode: Int,
+        viewAsHome: Boolean,
+        viewFullScreen: Boolean,
+        testingModeEnabled: Boolean
+    ): Fragment {
+        val args = Bundle()
+        args.putParcelable(PreviewFragment.ARG_WALLPAPER, wallpaperInfo)
+        args.putInt(PreviewFragment.ARG_PREVIEW_MODE, mode)
+        args.putBoolean(PreviewFragment.ARG_VIEW_AS_HOME, viewAsHome)
+        args.putBoolean(PreviewFragment.ARG_FULL_SCREEN, viewFullScreen)
+        args.putBoolean(PreviewFragment.ARG_TESTING_MODE_ENABLED, testingModeEnabled)
+        val fragment = ImagePreviewFragment()
+        fragment.arguments = args
+        return fragment
+    }
+}
diff --git a/tests/src/com/android/wallpaper/testing/UndoTestUtil.kt b/tests/src/com/android/wallpaper/testing/UndoTestUtil.kt
new file mode 100644
index 0000000..0f30db2
--- /dev/null
+++ b/tests/src/com/android/wallpaper/testing/UndoTestUtil.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2022 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.testing
+
+import com.android.wallpaper.picker.undo.domain.interactor.SnapshotRestorer
+import com.android.wallpaper.picker.undo.shared.model.RestorableSnapshot
+
+val FAKE_RESTORERS =
+    mapOf(
+        1 to FakeSnapshotRestorer(1),
+        2 to FakeSnapshotRestorer(2),
+        3 to FakeSnapshotRestorer(3),
+    )
+
+fun snapshot(ownerId: Int, version: Int): RestorableSnapshot {
+    return RestorableSnapshot(
+        mapOf(
+            KEY_OWNER_ID to "$ownerId",
+            KEY_VERSION to "$version",
+        )
+    )
+}
+
+class FakeSnapshotRestorer(
+    private val ownerId: Int,
+) : SnapshotRestorer {
+    private lateinit var updater: (RestorableSnapshot) -> Unit
+    var restored: RestorableSnapshot? = null
+        private set
+
+    fun update(version: Int) {
+        updater(snapshot(ownerId, version))
+    }
+
+    override suspend fun setUpSnapshotRestorer(
+        updater: (RestorableSnapshot) -> Unit,
+    ): RestorableSnapshot {
+        this.updater = updater
+        return snapshot(
+            ownerId = ownerId,
+            version = 0,
+        )
+    }
+
+    override suspend fun restoreToSnapshot(snapshot: RestorableSnapshot) {
+        restored = snapshot
+    }
+}
+
+private const val KEY_OWNER_ID = "ownerId"
+private const val KEY_VERSION = "version"