Merge branch android10-qpr2-release

Change-Id: I1c78f211d13c7b6c21ad119650fbf597a8a74b7b
diff --git a/Android.mk b/Android.mk
index e7e051e..39eeeb6 100755
--- a/Android.mk
+++ b/Android.mk
@@ -16,24 +16,13 @@
 
 LOCAL_PATH := $(call my-dir)
 
-#
-# Prebuilt Java Libraries
-#
-include $(CLEAR_VARS)
-LOCAL_MODULE := libStyleProtos
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE_CLASS := JAVA_LIBRARIES
-LOCAL_SRC_FILES := libs/style_protos.jar
-LOCAL_UNINSTALLABLE_MODULE := true
-LOCAL_SDK_VERSION := current
-include $(BUILD_PREBUILT)
-
 include $(CLEAR_VARS)
 LOCAL_MODULE_CLASS := JAVA_LIBRARIES
 LOCAL_MODULE := wallpaper2-glide-target
 LOCAL_SDK_VERSION := current
 LOCAL_SRC_FILES := ../../../prebuilts/maven_repo/bumptech/com/github/bumptech/glide/glide/SNAPSHOT/glide-SNAPSHOT$(COMMON_JAVA_PACKAGE_SUFFIX)
 LOCAL_UNINSTALLABLE_MODULE := true
+LOCAL_JETIFIER_ENABLED := true
 include $(BUILD_PREBUILT)
 
 include $(CLEAR_VARS)
@@ -64,7 +53,7 @@
 LOCAL_PROGUARD_ENABLED := disabled
 
 LOCAL_SDK_VERSION := current
-LOCAL_MIN_SDK_VERSION := 21
+LOCAL_MIN_SDK_VERSION := 26
 LOCAL_MODULE := wallpaper-subsampling-scale-image-view
 LOCAL_MANIFEST_FILE := ../../../external/subsampling-scale-image-view/library/src/main/AndroidManifest.xml
 
@@ -82,6 +71,7 @@
     androidx.appcompat_appcompat \
     androidx.cardview_cardview \
     androidx.recyclerview_recyclerview \
+    androidx.slice_slice-view \
     androidx-constraintlayout_constraintlayout \
     com.google.android.material_material \
     androidx.exifinterface_exifinterface \
@@ -99,14 +89,7 @@
 LOCAL_PROGUARD_ENABLED := disabled
 LOCAL_MANIFEST_FILE := AndroidManifest.xml
 
-ifneq (,$(wildcard frameworks/base))
-  LOCAL_STATIC_JAVA_LIBRARIES += SystemUISharedLib styleprotosnano
-  LOCAL_PRIVATE_PLATFORM_APIS := true
-else
-  LOCAL_STATIC_JAVA_LIBRARIES += libSharedSystemUI libStyleProtos
-  LOCAL_SDK_VERSION := current
-endif
-
+LOCAL_SDK_VERSION := current
 LOCAL_MODULE := WallpaperPicker2CommonDepsLib
 LOCAL_PRIVILEGED_MODULE := true
 
@@ -131,12 +114,18 @@
 LOCAL_PROGUARD_FLAG_FILES := proguard.flags
 LOCAL_PROGUARD_ENABLED := disabled
 
+LOCAL_PRIVILEGED_MODULE := true
+
 ifneq (,$(wildcard frameworks/base))
   LOCAL_PRIVATE_PLATFORM_APIS := true
 else
   LOCAL_SDK_VERSION := system_current
+  LOCAL_STATIC_JAVA_LIBRARIES += libSharedWallpaper
 endif
+
 LOCAL_PACKAGE_NAME := WallpaperPicker2
 LOCAL_JETIFIER_ENABLED := true
 
 include $(BUILD_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 0192398..6096d10 100755
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -20,7 +20,7 @@
       android:name="com.android.wallpaper.NOTIFY_ROTATING_WALLPAPER_CHANGED" />
 
   <application
-      tools:replace="android:name"
+      tools:replace="android:icon,android:name"
       android:allowBackup="true"
       android:icon="@mipmap/product_logo_wallpapers_launcher_color_48"
       android:label="@string/app_name"
@@ -37,7 +37,7 @@
     <activity android:name="com.android.wallpaper.picker.TopLevelPickerActivity"
         android:label="@string/app_name"
         android:theme="@style/WallpaperTheme"
-        android:resizeableActivity="true">
+        android:resizeableActivity="false">
       <intent-filter>
         <action android:name="android.intent.action.SET_WALLPAPER"/>
         <category android:name="android.intent.category.DEFAULT"/>
@@ -50,14 +50,13 @@
         android:label="@string/app_name">
       <intent-filter>
         <action android:name="android.intent.action.MAIN"/>
-        <category android:name="android.intent.category.LAUNCHER"/>
       </intent-filter>
     </activity-alias>
 
     <activity android:name="com.android.wallpaper.picker.individual.IndividualPickerActivity"
         android:label="@string/app_name"
         android:theme="@style/WallpaperTheme"
-        android:resizeableActivity="true"
+        android:resizeableActivity="false"
         android:parentActivityName="com.android.wallpaper.picker.TopLevelPickerActivity">
     </activity>
 
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
new file mode 100644
index 0000000..f3db20e
--- /dev/null
+++ b/PREUPLOAD.cfg
@@ -0,0 +1,2 @@
+[Hook Scripts]
+checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
diff --git a/libs/style_protos.jar b/libs/style_protos.jar
deleted file mode 100644
index 8708555..0000000
--- a/libs/style_protos.jar
+++ /dev/null
Binary files differ
diff --git a/res/drawable/gradient_background.xml b/res/drawable/gradient_background.xml
new file mode 100644
index 0000000..78f013a
--- /dev/null
+++ b/res/drawable/gradient_background.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+
+     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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <gradient android:angle="270"
+        android:startColor="@color/translucent_black_lighter"
+
+        android:endColor="@android:color/transparent"
+        />
+</shape>
\ No newline at end of file
diff --git a/res/drawable/ic_delete_24px.xml b/res/drawable/ic_delete_24px.xml
new file mode 100644
index 0000000..bc83f36
--- /dev/null
+++ b/res/drawable/ic_delete_24px.xml
@@ -0,0 +1,30 @@
+<!--
+     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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+  <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_settings.xml b/res/drawable/ic_settings.xml
new file mode 100644
index 0000000..f24a0a8
--- /dev/null
+++ b/res/drawable/ic_settings.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+
+     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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24.0dp"
+    android:viewportHeight="24.0"
+    android:viewportWidth="24.0"
+    android:width="24.0dp" >
+
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M19.43 12.98c.04-.32.07-.64.07-.98s-.03-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46c-.12-.22-.39-.3-.61-.22l-2.49 1c-.52-.4-1.08-.73-1.69-.98l-.38-2.65C14.46 2.18 14.25 2 14 2h-4c-.25 0-.46.18-.49.42l-.38 2.65c-.61.25-1.17.59-1.69.98l-2.49-1c-.23-.09-.49 0-.61.22l-2 3.46c-.13.22-.07.49.12.64l2.11 1.65c-.04.32-.07.65-.07.98s.03.66.07.98l-2.11 1.65c-.19.15-.24.42-.12.64l2 3.46c.12.22.39.3.61.22l2.49-1c.52.4 1.08.73 1.69.98l.38 2.65c.03.24.24.42.49.42h4c.25 0 .46-.18.49-.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1c.23.09.49 0 .61-.22l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.65zM12 15.5c-1.93 0-3.5-1.57-3.5-3.5s1.57-3.5 3.5-3.5 3.5 1.57 3.5 3.5-1.57 3.5-3.5 3.5z" />
+
+</vector>
\ No newline at end of file
diff --git a/res/layout/activity_preview.xml b/res/layout/activity_preview.xml
index 3e121c5..a08260a 100755
--- a/res/layout/activity_preview.xml
+++ b/res/layout/activity_preview.xml
@@ -13,7 +13,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
@@ -22,4 +22,4 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent" />
 
-</RelativeLayout>
+</FrameLayout>
diff --git a/res/layout/fragment_image_preview.xml b/res/layout/fragment_image_preview.xml
new file mode 100755
index 0000000..a7273fb
--- /dev/null
+++ b/res/layout/fragment_image_preview.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+
+<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"
+             android:fitsSystemWindows="false">
+
+  <ImageView
+    android:id="@+id/low_res_image"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:scaleType="centerCrop"
+    android:background="@android:color/black"/>
+
+  <com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
+      android:id="@+id/full_res_image"
+      android:layout_width="match_parent"
+      android:layout_height="match_parent" />
+
+  <androidx.core.widget.ContentLoadingProgressBar
+      android:id="@+id/loading_indicator"
+      style="@android:style/Widget.DeviceDefault.ProgressBar"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:layout_gravity="center"
+      android:indeterminate="true"/>
+
+  <FrameLayout
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:fitsSystemWindows="true">
+
+      <androidx.coordinatorlayout.widget.CoordinatorLayout
+          android:id="@+id/coordinator_layout"
+          android:layout_width="match_parent"
+          android:layout_height="wrap_content"
+          android:layout_gravity="bottom">
+
+        <FrameLayout
+            android:id="@+id/bottom_sheet"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:background="@drawable/preview_bottom_sheet_background"
+            android:theme="@style/WallpaperPicker.BottomPaneStyle"
+            app:behavior_peekHeight="@dimen/preview_attribution_pane_collapsed_height"
+            app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
+          <include
+              layout="@layout/preview_page_info"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content"/>
+        </FrameLayout>
+
+    </androidx.coordinatorlayout.widget.CoordinatorLayout>
+
+    <View
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/preview_gradient_background_height"
+        android:layout_gravity="top"
+        android:background="@drawable/gradient_background"/>
+    <androidx.appcompat.widget.Toolbar
+        android:id="@+id/toolbar"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="top"
+        style="@style/TranslucentToolbarStyle"/>
+
+  </FrameLayout>
+
+</FrameLayout>
diff --git a/res/layout/fragment_live_preview.xml b/res/layout/fragment_live_preview.xml
new file mode 100755
index 0000000..0185272
--- /dev/null
+++ b/res/layout/fragment_live_preview.xml
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+
+<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">
+
+      <FrameLayout
+          android:id="@+id/loading"
+          android:layout_width="match_parent"
+          android:layout_height="match_parent"
+          android:background="@android:color/black"
+          android:forceHasOverlappingRendering="false">
+
+          <androidx.core.widget.ContentLoadingProgressBar
+              android:id="@+id/loading_indicator"
+              style="@android:style/Widget.DeviceDefault.ProgressBar"
+              android:layout_width="wrap_content"
+              android:layout_height="wrap_content"
+              android:layout_gravity="center"
+              android:indeterminate="true"/>
+
+    </FrameLayout>
+
+    <FrameLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:fitsSystemWindows="true">
+
+        <androidx.coordinatorlayout.widget.CoordinatorLayout
+            android:id="@+id/coordinator_layout"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="bottom">
+
+            <LinearLayout
+                android:id="@+id/bottom_sheet"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="vertical"
+                android:background="@drawable/preview_bottom_sheet_background"
+                android:theme="@style/WallpaperPicker.BottomPaneStyle"
+                app:behavior_peekHeight="@dimen/preview_attribution_pane_collapsed_height"
+                app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
+
+                <com.google.android.material.tabs.TabLayout
+                    android:id="@+id/tablayout"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    app:tabTextAppearance="@style/WallpaperPicker.Preview.TextAppearance.NoAllCaps"
+                    app:tabIndicatorColor="?android:attr/textColorPrimary"
+                    android:visibility="gone"/>
+
+                <com.android.wallpaper.widget.ConstraintViewPager
+                    android:id="@+id/viewpager"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content" />
+
+            </LinearLayout>
+
+        </androidx.coordinatorlayout.widget.CoordinatorLayout>
+
+        <View
+            android:layout_width="match_parent"
+            android:layout_height="@dimen/preview_gradient_background_height"
+            android:layout_gravity="top"
+            android:background="@drawable/gradient_background"/>
+
+        <androidx.appcompat.widget.Toolbar
+            android:id="@+id/toolbar"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="top"
+            style="@style/TranslucentToolbarStyle"/>
+
+    </FrameLayout>
+
+</FrameLayout>
diff --git a/res/layout/fragment_preview.xml b/res/layout/fragment_preview.xml
deleted file mode 100755
index 82c481f..0000000
--- a/res/layout/fragment_preview.xml
+++ /dev/null
@@ -1,59 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<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"
-             android:fitsSystemWindows="false">
-
-  <ImageView
-    android:id="@+id/low_res_image"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:scaleType="centerCrop"
-    android:background="@android:color/black"/>
-
-  <com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
-      android:id="@+id/full_res_image"
-      android:layout_width="match_parent"
-      android:layout_height="match_parent" />
-
-  <ImageView
-    android:id="@+id/loading_indicator"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:layout_gravity="center"
-    android:visibility="invisible"
-    android:fitsSystemWindows="false"/>
-
-  <FrameLayout
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:fitsSystemWindows="true">
-
-      <androidx.coordinatorlayout.widget.CoordinatorLayout
-          android:id="@+id/coordinator_layout"
-          android:layout_width="match_parent"
-          android:layout_height="wrap_content"
-          android:layout_gravity="bottom">
-
-        <include
-            layout="@layout/preview_page_info"
-            android:id="@+id/bottom_sheet"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            app:behavior_peekHeight="@dimen/preview_attribution_pane_collapsed_height"
-            app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"/>
-
-    </androidx.coordinatorlayout.widget.CoordinatorLayout>
-
-    <androidx.appcompat.widget.Toolbar
-      android:id="@+id/toolbar"
-      android:layout_width="match_parent"
-      android:layout_height="wrap_content"
-      android:layout_gravity="top"
-      style="@style/TranslucentToolbarStyle"/>
-
-  </FrameLayout>
-
-</FrameLayout>
diff --git a/res/layout/preview_page_info.xml b/res/layout/preview_page_info.xml
index 9299426..2489561 100644
--- a/res/layout/preview_page_info.xml
+++ b/res/layout/preview_page_info.xml
@@ -21,8 +21,7 @@
     android:layout_width="match_parent"
     android:orientation="vertical"
     android:paddingHorizontal="@dimen/preview_attribution_pane_horizontal_padding"
-    android:background="@drawable/preview_bottom_sheet_background"
-    android:theme="@android:style/Theme.DeviceDefault.Settings">
+    android:theme="@style/WallpaperPicker.BottomPaneStyle">
 
     <Space
         android:id="@+id/preview_attribution_pane_title_spacer"
diff --git a/res/layout/preview_page_settings.xml b/res/layout/preview_page_settings.xml
new file mode 100644
index 0000000..4faebb6
--- /dev/null
+++ b/res/layout/preview_page_settings.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- 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.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:gravity="center_horizontal"
+    android:layout_height="wrap_content"
+    android:layout_width="match_parent"
+    android:orientation="vertical"
+    android:paddingHorizontal="@dimen/preview_attribution_pane_horizontal_padding"
+    android:theme="@style/WallpaperPicker.BottomPaneStyle">
+
+    <androidx.slice.widget.SliceView
+        android:id="@+id/settings_slice"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"/>
+
+    <Space
+        android:id="@+id/preview_attribution_pane_spacer"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:layout_weight="1"/>
+
+    <Button
+        style="@style/ButtonStyle"
+        android:id="@+id/preview_settings_pane_set_wallpaper_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/set_wallpaper_button_text"/>
+
+</LinearLayout>
diff --git a/res/menu/preview_menu.xml b/res/menu/preview_menu.xml
index 7c9d610..f83560e 100755
--- a/res/menu/preview_menu.xml
+++ b/res/menu/preview_menu.xml
@@ -20,4 +20,18 @@
           app:actionLayout="@layout/preview_action"
           app:showAsAction="always|withText"
           android:title="@string/preview"/>
+
+    <item
+        android:id="@+id/configure"
+        android:icon="@drawable/ic_settings"
+        app:showAsAction="ifRoom"
+        android:title="@string/configure_wallpaper"
+        android:visible="false"/>
+
+    <item
+        android:id="@+id/delete_wallpaper"
+        android:icon="@drawable/ic_delete_24px"
+        app:showAsAction="ifRoom"
+        android:title="@string/delete_live_wallpaper"
+        android:visible="false"/>
 </menu>
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index ea86de2..da7170e 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Gesentreerde snoei"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Rek"</string>
     <string name="preview" msgid="1774602101743861071">"Voorskou"</string>
+    <string name="tab_info" msgid="818614080690111416">"Inligting"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Pasmaak"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Instellings …"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Vee uit"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Vee hierdie muurpapier op jou foon uit?"</string>
 </resources>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index bca3e97..11a3912 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"መሃል ላይ ከርክም"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"ወጥር"</string>
     <string name="preview" msgid="1774602101743861071">"ቅድመ-እይታ"</string>
+    <string name="tab_info" msgid="818614080690111416">"መረጃ"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"ብጁ አድርግ"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"ቅንብሮች…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"ሰርዝ"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"ይህ ልጣፍ ከስልክዎ ይሰረዝ?"</string>
 </resources>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 2a6c183..cdbd47a 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -30,11 +30,11 @@
     <string name="rotating_wallpaper_presentation_mode_message" msgid="3361676041605733288">"خلفية يومية"</string>
     <string name="wallpaper_destination_both" msgid="1124197176741944063">"الشاشة الرئيسية وشاشة التأمين"</string>
     <string name="home_screen_message" msgid="106444102822522813">"الشاشة الرئيسية"</string>
-    <string name="lock_screen_message" msgid="1534506081955058013">"شاشة التأمين"</string>
+    <string name="lock_screen_message" msgid="1534506081955058013">"شاشة القفل"</string>
     <string name="home_and_lock_short_label" msgid="2937922943541927983">"الشاشة الرئيسية وشاشة التأمين"</string>
     <string name="set_wallpaper_dialog_message" msgid="6114951028768599417">"تعيين كخلفية"</string>
     <string name="set_wallpaper_home_screen_destination" msgid="7315594722013109354">"الشاشة الرئيسية"</string>
-    <string name="set_wallpaper_lock_screen_destination" msgid="6224685559375417945">"شاشة التأمين"</string>
+    <string name="set_wallpaper_lock_screen_destination" msgid="6224685559375417945">"شاشة القفل"</string>
     <string name="set_wallpaper_both_destination" msgid="6967226064958263939">"الشاشة الرئيسية وشاشة التأمين"</string>
     <string name="no_backup_image_wallpaper_label" msgid="6316627676107284851">"خلفية الصور التي يتم عرضها بالتناوب"</string>
     <string name="permission_needed_explanation" msgid="139166837541426823">"لعرض الخلفية الحالية هنا، يحتاج تطبيق <xliff:g id="APP_NAME">%1$s</xliff:g> إلى الوصول إلى مساحة تخزين الجهاز."</string>
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"توسيط مع اقتصاص المساحة الزائدة"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"توسيع للعرض بملء الشاشة"</string>
     <string name="preview" msgid="1774602101743861071">"معاينة"</string>
+    <string name="tab_info" msgid="818614080690111416">"معلومات"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"تخصيص"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"الإعدادات…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"حذف"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"هل تريد حذف هذه الخلفية من هاتفك؟"</string>
 </resources>
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index 95a66fd..0d89701 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"মধ্য অংশৰ পৰা কৰা ক্ৰপ"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"প্ৰসাৰিত"</string>
     <string name="preview" msgid="1774602101743861071">"পূৰ্বদৰ্শন"</string>
+    <string name="tab_info" msgid="818614080690111416">"তথ্য"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"কাষ্টমাইজ কৰক"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"ছেটিংসমূহ…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"মচক"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"এই ৱালপেপাৰখন আপোনাৰ ফ’নটোৰ পৰা মচিবনে?"</string>
 </resources>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index 162d0c9..412dca2 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Mərkəzi kəsim"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Dartın"</string>
     <string name="preview" msgid="1774602101743861071">"Önizləmə"</string>
+    <string name="tab_info" msgid="818614080690111416">"Məlumat"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Fərdiləşdirin"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Ayarlar…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Silin"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Bu divar kağızı telefondan silinsin?"</string>
 </resources>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index b005851..ada913d 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Opseci u centru"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Razvuci"</string>
     <string name="preview" msgid="1774602101743861071">"Pregled"</string>
+    <string name="tab_info" msgid="818614080690111416">"Informacije"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Prilagodite"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Podešavanja…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Izbriši"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Želite da izbrišete ovu pozadinu sa telefona?"</string>
 </resources>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index bfb434f..b8f7fd9 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Запоўніць"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Расцягнуць"</string>
     <string name="preview" msgid="1774602101743861071">"Перадпрагляд"</string>
+    <string name="tab_info" msgid="818614080690111416">"Інфармацыя"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Наладка"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Налады…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Выдаліць"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Выдаліць гэтыя шпалеры з тэлефона?"</string>
 </resources>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 4953524..2e6d874 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Центриране с подрязване"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Разтегляне"</string>
     <string name="preview" msgid="1774602101743861071">"Визуализация"</string>
+    <string name="tab_info" msgid="818614080690111416">"Информация"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Персонализиране"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Настройки…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Изтриване"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Да се изтрие ли този тапет от телефона ви?"</string>
 </resources>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index d51734f..38818ba 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"সীমানার দিকে কাটছাঁট করা"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"প্রসারিত"</string>
     <string name="preview" msgid="1774602101743861071">"প্রিভিউ"</string>
+    <string name="tab_info" msgid="818614080690111416">"তথ্য"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"কাস্টমাইজ করুন"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"সেটিংস…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"মুছে দিন"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"আপনার ফোন থেকে এই ওয়ালপেপার মুছে ফেলতে চান?"</string>
 </resources>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index 167b77f..bd8682f 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Sredina s odrezanim rubovima"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Razvučeno"</string>
     <string name="preview" msgid="1774602101743861071">"Pregled"</string>
+    <string name="tab_info" msgid="818614080690111416">"Informacije"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Prilagođavanje"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Postavke…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Izbriši"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Izbrisati ovu pozadinsku sliku s telefona?"</string>
 </resources>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index b2fd98d..df8eec4 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Centra i retalla"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Amplia"</string>
     <string name="preview" msgid="1774602101743861071">"Previsualitza"</string>
+    <string name="tab_info" msgid="818614080690111416">"Informació"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Personalitza"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Configuració…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Suprimeix"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Vols suprimir aquest fons de pantalla del telèfon?"</string>
 </resources>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index cebf392..c93f9a8 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Vycentrovat a oříznout"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Roztáhnout"</string>
     <string name="preview" msgid="1774602101743861071">"Náhled"</string>
+    <string name="tab_info" msgid="818614080690111416">"Informace"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Přizpůsobení"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Nastavení…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Smazat"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Smazat tapetu z telefonu?"</string>
 </resources>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 049affe..c4d4ca1 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Centrer og beskær"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Stræk"</string>
     <string name="preview" msgid="1774602101743861071">"Forhåndsvisning"</string>
+    <string name="tab_info" msgid="818614080690111416">"Oplysninger"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Tilpas"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Indstillinger…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Slet"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Vil du slette denne baggrund på din telefon?"</string>
 </resources>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 48bb948..4f0296b 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Zentriert anpassen"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Strecken"</string>
     <string name="preview" msgid="1774602101743861071">"Vorschau"</string>
+    <string name="tab_info" msgid="818614080690111416">"Informationen"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Anpassen"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Einstellungen…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Löschen"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Diesen Hintergrund von deinem Smartphone löschen?"</string>
 </resources>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index d14cc2b..78007b5 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Περικοπή στο κέντρο"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Τέντωμα"</string>
     <string name="preview" msgid="1774602101743861071">"Προεπισκόπηση"</string>
+    <string name="tab_info" msgid="818614080690111416">"Πληροφορίες"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Προσαρμογή"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Ρυθμίσεις…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Διαγραφή"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Διαγραφή αυτής της ταπετσαρίας από το τηλέφωνό σας;"</string>
 </resources>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index 158ce10..638dda6 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Centre crop"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Stretch"</string>
     <string name="preview" msgid="1774602101743861071">"Preview"</string>
+    <string name="tab_info" msgid="818614080690111416">"Info"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Customise"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Settings…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Delete"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Delete this wallpaper from your phone?"</string>
 </resources>
diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml
new file mode 100644
index 0000000..638dda6
--- /dev/null
+++ b/res/values-en-rCA/strings.xml
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+     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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wallpaper_app_name" msgid="1719889291772891695">"Wallpapers"</string>
+    <!-- no translation found for app_name (8773648973927541493) -->
+    <skip />
+    <string name="select_wallpaper_label" msgid="6989581259339646085">"Wallpaper categories"</string>
+    <string name="set_wallpaper_button_text" msgid="4426286890442731310">"Set Wallpaper"</string>
+    <string name="set_wallpaper_progress_message" msgid="7986528287618716715">"Setting wallpaper…"</string>
+    <string name="try_again" msgid="8278874823700921234">"Try again"</string>
+    <string name="set_wallpaper_error_message" msgid="6819986999041085130">"Unable to set wallpaper."</string>
+    <string name="load_wallpaper_error_message" msgid="7913278480467707374">"Unable to load wallpaper. The image is either corrupted or unavailable."</string>
+    <string name="static_wallpaper_presentation_mode_message" msgid="417940227049360906">"Currently set"</string>
+    <string name="rotating_wallpaper_presentation_mode_message" msgid="3361676041605733288">"Daily wallpaper"</string>
+    <string name="wallpaper_destination_both" msgid="1124197176741944063">"Home &amp; Lock screen"</string>
+    <string name="home_screen_message" msgid="106444102822522813">"Home screen"</string>
+    <string name="lock_screen_message" msgid="1534506081955058013">"Lock screen"</string>
+    <string name="home_and_lock_short_label" msgid="2937922943541927983">"Home &amp; Lock"</string>
+    <string name="set_wallpaper_dialog_message" msgid="6114951028768599417">"Set wallpaper"</string>
+    <string name="set_wallpaper_home_screen_destination" msgid="7315594722013109354">"Home screen"</string>
+    <string name="set_wallpaper_lock_screen_destination" msgid="6224685559375417945">"Lock screen"</string>
+    <string name="set_wallpaper_both_destination" msgid="6967226064958263939">"Home screen and lock screen"</string>
+    <string name="no_backup_image_wallpaper_label" msgid="6316627676107284851">"Rotating Image Wallpaper"</string>
+    <string name="permission_needed_explanation" msgid="139166837541426823">"To display the current wallpaper here, <xliff:g id="APP_NAME">%1$s</xliff:g> needs access to your device\'s storage."</string>
+    <string name="permission_needed_explanation_go_to_settings" msgid="3923551582092599609">"To display the current wallpaper here, Wallpapers needs access to your device’s storage.\n\nTo change this setting, go to the Permissions area of the Wallpapers’ app info."</string>
+    <string name="permission_needed_allow_access_button_label" msgid="1943133660612924306">"Allow access"</string>
+    <string name="no_backup_image_wallpaper_description" msgid="8303268619408738057">"Live wallpaper service for rotating wallpapers"</string>
+    <string name="daily_refresh_tile_title" msgid="3270456074558525091">"Daily wallpaper"</string>
+    <string name="daily_refresh_tile_subtitle" msgid="3976682014885446443">"Tap to turn on"</string>
+    <string name="start_rotation_dialog_body_live_wallpaper_needed" msgid="5132580257563846082">"Wallpaper will change automatically every day. To finish setup, tap &lt;strong&gt;Set wallpaper&lt;/strong&gt; on the next screen."</string>
+    <string name="start_rotation_dialog_wifi_only_option_message" msgid="3126269859713666225">"Download future wallpapers on Wi-Fi only"</string>
+    <string name="start_rotation_dialog_continue" msgid="276678987852274872">"Continue"</string>
+    <string name="start_rotation_progress_message" msgid="7872623873682262083">"Downloading first wallpaper…"</string>
+    <string name="start_rotation_error_message" msgid="3053799836719618972">"Unable to download first wallpaper. Please check your network settings and try again."</string>
+    <string name="start_rotation_dialog_body" msgid="7903554799046364916">"Wallpaper will change automatically every day"</string>
+    <string name="settings_button_label" msgid="8724734130079207955">"Settings"</string>
+    <string name="explore" msgid="7468719504199497281">"Explore"</string>
+    <string name="next_wallpaper" msgid="3911873152952596232">"Next wallpaper"</string>
+    <string name="wallpaper_disabled_message" msgid="7309484130562148185">"Setting a wallpaper is disabled on this device"</string>
+    <string name="wallpaper_disabled_by_administrator_message" msgid="1551430406714747884">"Setting a wallpaper is disabled by your device administrator"</string>
+    <string name="wallpaper_set_successfully_message" msgid="2958998799111688578">"Wallpaper set successfully"</string>
+    <string name="wallpapers_unavailable_offline_message" msgid="8136405438621689532">"You need an Internet connection to view wallpapers. Please connect and try again."</string>
+    <string name="currently_set_home_wallpaper_thumbnail" msgid="4022381436821898917">"Currently set home screen wallpaper thumbnail"</string>
+    <string name="currently_set_lock_wallpaper_thumbnail" msgid="2094209303934569997">"Currently set lock screen wallpaper thumbnail"</string>
+    <string name="currently_set_wallpaper_thumbnail" msgid="8651887838745545107">"Currently set wallpaper thumbnail"</string>
+    <string name="wallpaper_thumbnail" msgid="569931475923605974">"Wallpaper thumbnail"</string>
+    <string name="explore_home_screen" msgid="8756346794535765482">"Explore home screen wallpaper"</string>
+    <string name="explore_lock_screen" msgid="268938342103703665">"Explore lock screen wallpaper"</string>
+    <string name="refresh_daily_wallpaper_home_content_description" msgid="2770445044556164259">"Refresh daily home screen wallpaper"</string>
+    <string name="refresh_daily_wallpaper_content_description" msgid="4362142658237147583">"Refresh daily wallpaper"</string>
+    <string name="refreshing_daily_wallpaper_dialog_message" msgid="1975910873362855761">"Refreshing daily wallpaper…"</string>
+    <string name="refresh_daily_wallpaper_failed_message" msgid="4749879993812557166">"Failed to refresh daily wallpaper. Please check your network connection and try again."</string>
+    <string name="collapse_attribution_panel" msgid="4367971404848122275">"Collapse wallpaper info panel"</string>
+    <string name="expand_attribution_panel" msgid="6975094181456095915">"Expand wallpaper info panel"</string>
+    <string name="on_device_wallpapers_category_title" msgid="805819102071369004">"On-device wallpapers"</string>
+    <string name="on_device_wallpapers_category_title_desktop" msgid="316919420410065369">"On device"</string>
+    <string name="on_device_wallpaper_title" msgid="5262564748034629524">"Android wallpaper"</string>
+    <string name="live_wallpapers_category_title" msgid="1814374812192366349">"Live wallpapers"</string>
+    <string name="my_photos_category_title" msgid="4294567122144565273">"My photos"</string>
+    <string name="my_photos_generic_wallpaper_title" msgid="7002867526154631172">"My photo"</string>
+    <string name="fallback_wallpaper_title" msgid="6154655421012506001">"Wallpaper"</string>
+    <string name="app_not_found" msgid="4431461707854088231">"App isn\'t installed."</string>
+    <string name="center_wallpaper_position" msgid="4166894762352288883">"Centre"</string>
+    <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Centre crop"</string>
+    <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Stretch"</string>
+    <string name="preview" msgid="1774602101743861071">"Preview"</string>
+    <string name="tab_info" msgid="818614080690111416">"Info"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Customise"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Settings…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Delete"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Delete this wallpaper from your phone?"</string>
+</resources>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 158ce10..638dda6 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Centre crop"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Stretch"</string>
     <string name="preview" msgid="1774602101743861071">"Preview"</string>
+    <string name="tab_info" msgid="818614080690111416">"Info"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Customise"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Settings…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Delete"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Delete this wallpaper from your phone?"</string>
 </resources>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index 158ce10..638dda6 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Centre crop"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Stretch"</string>
     <string name="preview" msgid="1774602101743861071">"Preview"</string>
+    <string name="tab_info" msgid="818614080690111416">"Info"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Customise"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Settings…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Delete"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Delete this wallpaper from your phone?"</string>
 </resources>
diff --git a/res/values-en-rXC/strings.xml b/res/values-en-rXC/strings.xml
new file mode 100644
index 0000000..81420be
--- /dev/null
+++ b/res/values-en-rXC/strings.xml
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+     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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="wallpaper_app_name" msgid="1719889291772891695">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‎‏‏‎‏‏‏‏‏‎‏‏‏‏‎‎‏‎‎‎‏‏‎‎‎‏‏‎‎‏‎‏‏‎‏‎‎‎‏‎‎‎‏‏‏‏‏‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎Wallpapers‎‏‎‎‏‎"</string>
+    <!-- no translation found for app_name (8773648973927541493) -->
+    <skip />
+    <string name="select_wallpaper_label" msgid="6989581259339646085">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‏‏‏‏‎‏‏‏‏‎‏‏‏‎‏‎‏‎‏‎‏‏‎‎‎‏‎‎‏‎‎‎‎‏‎‏‎Wallpaper categories‎‏‎‎‏‎"</string>
+    <string name="set_wallpaper_button_text" msgid="4426286890442731310">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‎‏‏‏‏‎‏‎‏‏‎‏‏‎‏‎‏‎‏‎‏‎‎‎‏‏‏‏‎‎‏‏‎‏‏‎‎‏‎‏‏‎‎‏‏‏‎‏‎‏‎‏‏‏‏‎‎‏‎‏‏‏‎‎Set Wallpaper‎‏‎‎‏‎"</string>
+    <string name="set_wallpaper_progress_message" msgid="7986528287618716715">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‏‎‏‎‏‎‏‏‏‎‏‏‎‎‏‎‎‏‎‏‎‎‏‏‏‎‎‎‏‎‎‎‎‎‏‏‏‏‏‎‏‎‏‎‏‎‎‎‎‏‎‏‎‏‏‎Setting wallpaper…‎‏‎‎‏‎"</string>
+    <string name="try_again" msgid="8278874823700921234">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‎‎‏‎‎‎‏‏‏‏‎‎‎‏‏‎‎‏‎‎‏‏‏‏‎‏‏‏‎‏‎‎‏‏‏‏‎‎‎‏‏‏‎‏‏‏‎‎‏‎‎‏‎‎Try again‎‏‎‎‏‎"</string>
+    <string name="set_wallpaper_error_message" msgid="6819986999041085130">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‎‏‎‎‏‎‏‎‏‏‏‎‏‏‎‎‎‎‎‏‎‏‎‏‎‏‏‎‎‏‎‏‎‏‎‎‎‎‎‎‏‎‎‏‏‏‎‏‏‎‎‏‎‏‎‎Unable to set wallpaper.‎‏‎‎‏‎"</string>
+    <string name="load_wallpaper_error_message" msgid="7913278480467707374">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‏‏‎‏‎‎‎‏‏‎‎‏‏‏‎‎‏‏‎‏‏‎‏‎‎‎‎‎‎‏‎‏‎‎‎‎‎‏‎‏‎‏‎‎‎‏‎‏‏‏‏‎‏‏‏‎‎Unable to load wallpaper. The image is either corrupted or unavailable.‎‏‎‎‏‎"</string>
+    <string name="static_wallpaper_presentation_mode_message" msgid="417940227049360906">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‎‏‏‏‎‏‏‏‎‎‏‏‎‎‏‏‎‏‎‎‏‎‎‏‏‎‏‏‎‎‎‎‎‏‎‎‏‎‏‎‎‏‏‏‏‏‏‎‎‎‏‏‏‎‎‎‎‎‏‎‏‎‎Currently set‎‏‎‎‏‎"</string>
+    <string name="rotating_wallpaper_presentation_mode_message" msgid="3361676041605733288">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‎‏‎‏‏‏‎‏‎‏‎‎‏‏‏‎‎‎‏‎‎‏‎‏‎‎‎‏‏‎‎‏‎‎‎‎‏‎‏‏‏‎‏‏‎‎‏‏‏‎‏‏‎‏‏‏‎‏‎‏‎‎‎‎Daily wallpaper‎‏‎‎‏‎"</string>
+    <string name="wallpaper_destination_both" msgid="1124197176741944063">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‎‎‏‏‏‏‏‎‎‏‏‎‎‏‏‏‏‏‎‎‏‏‎‏‏‎‎‎‎‎‎‏‎‎‎‎‎‏‎‎‏‏‎‎‏‏‎‏‎‏‏‎‏‎‏‏‏‏‏‏‏‏‎Home &amp; Lock screen‎‏‎‎‏‎"</string>
+    <string name="home_screen_message" msgid="106444102822522813">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‎‎‏‏‎‏‏‏‏‎‏‎‎‎‏‎‏‎‏‎‎‏‎‏‏‎‎‎‏‏‏‏‏‎‎‏‎‎‏‎‏‎‏‏‏‏‏‎‏‏‏‏‏‎‏‏‏‏‎‏‎Home screen‎‏‎‎‏‎"</string>
+    <string name="lock_screen_message" msgid="1534506081955058013">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‎‏‏‎‏‎‏‎‏‎‎‏‎‏‏‏‎‏‎‏‎‎‏‎‎‏‎‎‏‏‎‏‏‎‎‎‎‎‎‏‎‎‏‏‎‏‏‏‎‎‏‎‎‎‏‎‏‎‏‏‏‎‏‎Lock screen‎‏‎‎‏‎"</string>
+    <string name="home_and_lock_short_label" msgid="2937922943541927983">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‎‏‎‏‎‎‎‏‏‎‎‎‏‎‏‏‎‎‏‏‎‎‏‎‏‎‏‏‎‎‏‏‏‏‏‎‎‏‏‎‏‎‎‏‎‎‏‎‏‏‎‎‎‎‎‎‎‏‎‏‏‏‏‎Home &amp; Lock‎‏‎‎‏‎"</string>
+    <string name="set_wallpaper_dialog_message" msgid="6114951028768599417">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‏‎‏‏‏‎‎‏‎‏‎‏‎‏‏‏‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‎‏‎‏‎‎‏‎‏‏‎‏‏‎‏‎‏‏‏‏‎‎‏‎Set wallpaper‎‏‎‎‏‎"</string>
+    <string name="set_wallpaper_home_screen_destination" msgid="7315594722013109354">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‏‎‎‏‎‏‏‎‎‎‎‏‏‎‎‎‏‏‎‏‏‎‏‎‏‎‎‎‏‏‏‏‎‏‎‏‏‎‎‏‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‏‏‎‏‎‏‎‎Home screen‎‏‎‎‏‎"</string>
+    <string name="set_wallpaper_lock_screen_destination" msgid="6224685559375417945">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‎‏‏‎‎‎‏‎‏‎‎‎‎‏‏‎‏‎‎‎‏‎‎‏‎‏‎‏‎‏‎‏‎‎‎‏‎‎‏‏‎‏‎‏‎‎‏‎‎‏‎‏‏‎‎‏‎Lock screen‎‏‎‎‏‎"</string>
+    <string name="set_wallpaper_both_destination" msgid="6967226064958263939">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‏‏‎‎‎‎‏‎‎‎‏‏‏‏‎‎‏‏‎‎‎‏‎‎‏‎‏‏‏‏‏‎‏‎‏‏‏‎‎‎‎‏‎‎‏‎‏‎‎‎‎‎‏‏‎Home screen and lock screen‎‏‎‎‏‎"</string>
+    <string name="no_backup_image_wallpaper_label" msgid="6316627676107284851">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‏‎‏‎‏‎‎‏‎‎‏‎‏‎‏‏‎‏‏‎‎‏‏‏‏‎‎‎‏‏‎‎‏‏‎‎‎‏‏‎‏‎‏‎‎‎‎‏‎‏‏‏‎‎‏‏‎Rotating Image Wallpaper‎‏‎‎‏‎"</string>
+    <string name="permission_needed_explanation" msgid="139166837541426823">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‎‎‏‏‏‏‏‎‏‏‏‎‎‏‏‎‏‎‏‏‏‎‎‎‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‎‏‏‏‏‎‏‎‏‏‎‏‎‎‎‎‏‏‏‎To display the current wallpaper here, ‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ needs access to your device\'s storage.‎‏‎‎‏‎"</string>
+    <string name="permission_needed_explanation_go_to_settings" msgid="3923551582092599609">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‎‏‏‎‏‏‎‎‏‏‏‎‎‏‏‎‏‎‎‎‎‎‏‎‏‏‎‎‎‎‎‎‏‎‎‎‏‎‎‏‎‎‎‏‏‎‏‎‎‏‎‎‎‎‏‎‎‏‏‏‎‎‏‎To display the current wallpaper here, Wallpapers needs access to your device’s storage.‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎To change this setting, go to the Permissions area of Wallpapers’ app info.‎‏‎‎‏‎"</string>
+    <string name="permission_needed_allow_access_button_label" msgid="1943133660612924306">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‎‏‏‏‎‏‎‏‏‏‏‎‏‏‏‎‏‏‎‎‏‎‏‏‏‎‎‎‏‎‎‏‏‏‎‎‏‏‏‏‏‏‏‎‎‎‎‎‎‎‏‎‎‏‏‏‎‎‏‎‎‏‎‎Allow access‎‏‎‎‏‎"</string>
+    <string name="no_backup_image_wallpaper_description" msgid="8303268619408738057">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‏‏‎‎‏‏‎‎‏‏‏‎‏‏‎‎‏‎‎‎‏‎‏‏‎‏‎‎‎‏‎‎‎‏‎‏‏‎‏‎‎‎‎‎‎‏‏‎‎‎‎‏‏‏‎‎‎‎‏‎‎‏‎Live wallpaper service for rotating wallpapers‎‏‎‎‏‎"</string>
+    <string name="daily_refresh_tile_title" msgid="3270456074558525091">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‏‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‏‏‏‏‎‎‎‏‏‏‎‎‎‎‏‏‎‎‏‎‏‎‎‎‏‎‎‏‏‏‎‏‎‏‎‎‎‏‏‎Daily wallpaper‎‏‎‎‏‎"</string>
+    <string name="daily_refresh_tile_subtitle" msgid="3976682014885446443">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‎‏‏‎‏‏‏‎‎‏‏‎‎‎‎‎‎‎‎‎‎‏‏‎‎‏‏‏‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‎‎‎‎‎‏‏‎‎‏‎‏‎‏‏‎Tap to turn on‎‏‎‎‏‎"</string>
+    <string name="start_rotation_dialog_body_live_wallpaper_needed" msgid="5132580257563846082">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‎‎‏‏‏‎‏‎‏‎‎‏‎‏‏‎‏‎‎‎‏‏‎‎‏‏‏‏‏‎‎‏‎‏‎‎‎‏‏‎‏‎‏‎‎‏‎‏‏‏‎‎‎‎‏‎‎Wallpaper will change automatically every day. To finish setup, tap &lt;strong&gt;Set wallpaper&lt;/strong&gt; on the next screen.‎‏‎‎‏‎"</string>
+    <string name="start_rotation_dialog_wifi_only_option_message" msgid="3126269859713666225">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‎‏‎‏‎‏‏‎‏‏‎‎‎‏‎‏‎‏‏‏‏‎‏‏‏‎‏‏‏‏‏‏‎‏‎‏‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‏‏‎‎‏‎‏‏‎‎‎‏‎Download future wallpapers on Wi-Fi only‎‏‎‎‏‎"</string>
+    <string name="start_rotation_dialog_continue" msgid="276678987852274872">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‎‏‎‏‏‏‏‎‏‎‏‏‎‏‏‏‏‎‏‏‎‎‎‎‏‎‎‏‎‏‏‎‏‎‎‏‏‏‎‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‎‏‏‏‎‎‎‎Continue‎‏‎‎‏‎"</string>
+    <string name="start_rotation_progress_message" msgid="7872623873682262083">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‎‎‎‎‎‏‎‎‏‎‏‏‎‏‏‎‏‏‎‎‏‏‏‏‎‎‎‎‎‏‎‎‎‏‎‏‏‏‎‏‎‎‏‎‎‎‎‏‎‎‎‎‏‏‎Downloading first wallpaper…‎‏‎‎‏‎"</string>
+    <string name="start_rotation_error_message" msgid="3053799836719618972">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‎‏‎‏‎‏‎‎‏‏‎‎‎‎‏‎‏‎‎‎‏‏‎‏‏‎‎‎‏‎‏‏‎‎‏‎‎‏‎‏‏‎‏‎‎‎‏‏‎‎‏‏‎‏‏‏‎‎‏‏‏‎‎‎Unable to download first wallpaper. Please check your network settings and try again.‎‏‎‎‏‎"</string>
+    <string name="start_rotation_dialog_body" msgid="7903554799046364916">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‏‎‏‏‏‏‎‎‎‏‎‎‎‏‎‎‏‏‎‏‏‏‎‎‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‏‎‏‏‏‏‎‏‎‎‎Wallpaper will change automatically every day‎‏‎‎‏‎"</string>
+    <string name="settings_button_label" msgid="8724734130079207955">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‎‏‎‎‎‏‏‏‏‎‏‏‎‏‏‏‎‏‏‏‎‎‏‏‎‏‏‏‎‎‎‏‏‎‎‏‎‎‎‏‎‎‏‎‎‎‎‏‎‎‏‏‎Settings‎‏‎‎‏‎"</string>
+    <string name="explore" msgid="7468719504199497281">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‏‎‎‏‏‎‎‎‏‏‏‎‎‎‏‏‎‏‎‎‎‏‎‏‏‎‏‏‏‎‏‏‎‏‎‏‏‏‏‎‏‏‎‏‏‎‎‏‎‎‎‎‎‏‎Explore‎‏‎‎‏‎"</string>
+    <string name="next_wallpaper" msgid="3911873152952596232">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‎‏‏‎‏‏‎‎‏‎‎‏‎‎‏‏‏‎‎‎‎‏‏‏‏‏‎‏‎‎‎‎‎‎‏‏‎‎‎‎‎‏‎‎‎‎‎‎‏‏‏‎‏‏‏‎‎‎‎‏‎‎‎‎Next wallpaper‎‏‎‎‏‎"</string>
+    <string name="wallpaper_disabled_message" msgid="7309484130562148185">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‏‏‏‎‎‎‎‏‎‎‎‎‎‎‏‎‎‎‏‎‏‏‎‏‏‏‎‎‏‎‏‏‎‎‎‎‎‏‏‎‎‎‎‎‏‏‏‎‏‎‏‏‎‎‏‎Setting a wallpaper is disabled on this device‎‏‎‎‏‎"</string>
+    <string name="wallpaper_disabled_by_administrator_message" msgid="1551430406714747884">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‎‏‏‎‏‎‏‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‏‏‎‏‏‏‏‎‎‎‎‏‎‏‏‎‏‏‏‏‏‏‎‎‎‏‏‏‎‏‎‏‏‏‏‏‎‏‏‎‎‎Setting a wallpaper is disabled by your device administrator‎‏‎‎‏‎"</string>
+    <string name="wallpaper_set_successfully_message" msgid="2958998799111688578">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‎‏‎‏‎‎‏‎‎‎‏‎‎‎‎‎‏‏‏‏‎‎‏‏‎‏‏‏‎‏‎‏‏‏‏‎‏‏‏‏‏‎‏‏‏‎‎‎‎‎‏‎‎‎‏‏‎‎‎‎‎‏‎‎Wallpaper set successfully‎‏‎‎‏‎"</string>
+    <string name="wallpapers_unavailable_offline_message" msgid="8136405438621689532">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‎‏‎‏‎‎‏‎‏‎‎‎‏‏‎‏‎‎‎‏‏‎‏‎‎‏‏‎‏‏‎‎‏‏‎‏‎‎‎‏‎‎‏‏‎‏‎‏‏‏‏‎‎‎You need an Internet connection to view wallpapers. Please connect and try again.‎‏‎‎‏‎"</string>
+    <string name="currently_set_home_wallpaper_thumbnail" msgid="4022381436821898917">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‎‏‏‎‏‏‏‏‏‎‏‎‎‏‎‎‏‎‏‏‏‏‎‏‎‎‏‏‎‏‎‏‏‏‏‎‏‎‎‏‏‏‎‎‏‎‏‎‎‎‎‎‏‏‎‏‎‏‎‎‏‎‏‎Currently set home screen wallpaper thumbnail‎‏‎‎‏‎"</string>
+    <string name="currently_set_lock_wallpaper_thumbnail" msgid="2094209303934569997">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‎‏‏‏‏‎‏‎‎‎‏‎‎‎‎‎‎‏‎‎‎‎‎‎‏‎‎‎‏‎‎‎‎‏‎‏‏‏‎‎‏‎‏‎‏‏‎‏‎‎‎‏‏‏‎‎‎‎‎‏‏‎‏‎Currently set lock screen wallpaper thumbnail‎‏‎‎‏‎"</string>
+    <string name="currently_set_wallpaper_thumbnail" msgid="8651887838745545107">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‏‎‏‎‎‎‎‏‏‏‏‎‎‏‏‎‏‎‎‏‎‎‏‏‎‎‏‎‎‏‏‎Currently set wallpaper thumbnail‎‏‎‎‏‎"</string>
+    <string name="wallpaper_thumbnail" msgid="569931475923605974">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‎‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‎‎‏‏‎‏‏‎‏‎‏‎‎‎‎‏‏‎‎‎‎‎‏‎‏‏‏‏‎‏‏‎‎‏‏‎‎‏‏‏‎‏‎‏‏‎‎Wallpaper thumbnail‎‏‎‎‏‎"</string>
+    <string name="explore_home_screen" msgid="8756346794535765482">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‏‎‎‎‎‏‎‎‏‏‎‎‏‎‏‏‎‎‎‎‎‏‎‎‎‎‏‏‏‎‏‎‎‏‏‎‏‏‏‏‎‏‎‎‎‏‎‏‏‏‏‎‏‎‏‎‎Explore home screen wallpaper‎‏‎‎‏‎"</string>
+    <string name="explore_lock_screen" msgid="268938342103703665">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‎‏‎‏‏‏‎‏‏‏‎‏‏‎‏‏‏‎‏‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‏‏‎‎‏‎‎‏‎‏‏‏‎‎‎‎‎‎‏‏‏‎‎‎‏‎Explore lock screen wallpaper‎‏‎‎‏‎"</string>
+    <string name="refresh_daily_wallpaper_home_content_description" msgid="2770445044556164259">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‎‏‎‎‏‏‎‎‏‏‏‎‎‏‎‏‎‎‏‏‎‎‏‎‎‎‏‎‏‏‎‏‏‏‎‏‏‏‎‏‎‎‎‎‎‏‎‏‏‏‎‏‏‎‎‏‎‏‎‎‎‏‏‎Refresh daily home screen wallpaper‎‏‎‎‏‎"</string>
+    <string name="refresh_daily_wallpaper_content_description" msgid="4362142658237147583">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‎‏‏‏‏‎‎‏‎‎‎‏‎‎‏‎‏‏‏‎‎‎‏‏‎‏‎‎‎‏‎‏‏‎‏‎‎‏‏‏‏‎‎‏‏‎‏‏‎‏‎‏‎‎‏‏‎‏‏‏‏‏‏‎Refresh daily wallpaper‎‏‎‎‏‎"</string>
+    <string name="refreshing_daily_wallpaper_dialog_message" msgid="1975910873362855761">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‎‏‏‏‎‏‏‎‏‏‎‏‎‏‏‏‏‎‏‏‎‎‎‎‏‏‏‏‎‎‎‎‏‏‏‎‎‏‎‏‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‏‎‎‎‏‎Refreshing daily wallpaper…‎‏‎‎‏‎"</string>
+    <string name="refresh_daily_wallpaper_failed_message" msgid="4749879993812557166">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‏‏‎‏‎‏‎‏‏‏‏‎‏‏‎‏‎‏‏‎‎‎‎‎‎‏‏‏‏‎‎‏‏‏‎‏‏‏‎‏‎‎‏‏‏‎‏‎‏‏‎‏‏‏‎‎Failed to refresh daily wallpaper. Please check your network connection and try again.‎‏‎‎‏‎"</string>
+    <string name="collapse_attribution_panel" msgid="4367971404848122275">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‎‏‏‏‏‎‎‏‎‎‏‏‏‏‎‎‎‏‎‎‏‏‎‏‏‎‏‏‎‎‏‏‎‏‎‎‏‏‏‎‎‎‎‏‎‎‏‎‏‎‎‏‎‎‏‏‎‏‎‎‎‏‏‎Collapse wallpaper info panel‎‏‎‎‏‎"</string>
+    <string name="expand_attribution_panel" msgid="6975094181456095915">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‎‎‏‏‎‎‏‎‎‎‎‎‏‏‎‎‏‏‎‎‏‏‏‏‎‏‎‎‎‎‏‎‎‏‎‏‎‏‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‏‎Expand wallpaper info panel‎‏‎‎‏‎"</string>
+    <string name="on_device_wallpapers_category_title" msgid="805819102071369004">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‎‎‏‎‏‏‎‎‏‎‏‏‏‎‏‏‎‏‏‎‎‎‎‎‏‏‎‏‎‎‎‎‏‏‎‏‎‎‎‎‏‏‎‎‎‎‎‎‏‎‏‏‎‏‎‎‏‎‏‏‎‎‎On-device wallpapers‎‏‎‎‏‎"</string>
+    <string name="on_device_wallpapers_category_title_desktop" msgid="316919420410065369">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‎‏‏‏‎‎‎‏‏‎‎‏‎‏‏‏‏‎‏‏‎‎‏‎‎‎‏‎‎‎‏‎‏‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‏‏‎‎‎‏‏‏‎‏‏‎‎‏‎On device‎‏‎‎‏‎"</string>
+    <string name="on_device_wallpaper_title" msgid="5262564748034629524">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‎‎‏‎‎‏‎‎‎‎‏‎‎‎‎‏‏‎‎‎‏‎‏‏‎‎‎‎‎‏‏‎‎‎‏‏‏‎‎‎‎‏‏‎‎‎‏‏‎‏‏‏‏‏‏‎‎‏‎‏‎‎‎Android wallpaper‎‏‎‎‏‎"</string>
+    <string name="live_wallpapers_category_title" msgid="1814374812192366349">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‎‏‏‏‎‎‏‎‎‏‎‏‏‎‏‏‏‏‏‎‏‎‎‎‏‎‎‎‏‏‏‎‏‎‏‏‎‏‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‎‎‎‏‏‎‏‎Live wallpapers‎‏‎‎‏‎"</string>
+    <string name="my_photos_category_title" msgid="4294567122144565273">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‎‏‏‏‎‏‏‏‎‎‏‏‎‎‏‎‏‎‏‏‏‏‎‎‎‎‎‏‎‏‏‎‏‎‎‎‎‏‏‎‏‏‏‏‏‏‏‏‎‎‏‏‎‎‎‎‎‎‏‏‎‎‏‎My photos‎‏‎‎‏‎"</string>
+    <string name="my_photos_generic_wallpaper_title" msgid="7002867526154631172">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‏‎‏‏‏‏‎‎‏‎‏‏‏‎‏‏‏‎‏‎‎‏‏‎‎‎‏‎‎‏‏‎‎‏‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‎‎‏‎‎‎My photo‎‏‎‎‏‎"</string>
+    <string name="fallback_wallpaper_title" msgid="6154655421012506001">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‏‏‎‏‎‎‏‏‎‏‏‏‎‏‎‎‏‏‏‏‏‏‏‎‎‏‎‎‏‎‎‎‏‏‎‎‎‎‏‏‏‏‏‎‏‎‏‏‎‎‏‎‎‎‏‎Wallpaper‎‏‎‎‏‎"</string>
+    <string name="app_not_found" msgid="4431461707854088231">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‎‏‏‏‏‎‏‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‏‏‏‏‎‎‎‏‏‏‎‎‎‏‎‎‎‏‏‎‎‎‏‏‏‎‎‎‏‎‎‎‎‎‏‎‎‏‏‏‎App isn\'t installed.‎‏‎‎‏‎"</string>
+    <string name="center_wallpaper_position" msgid="4166894762352288883">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‎‏‏‏‎‎‏‏‏‎‏‎‎‏‏‏‏‎‎‏‎‎‎‏‎‏‏‎‏‎‏‏‎‎‎‏‏‏‎‎‏‏‏‎‎‎‏‏‎‏‎‎‏‎‎‎‏‏‏‎‎‏‏‎Center‎‏‎‎‏‎"</string>
+    <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‎‏‏‎‏‏‏‎‏‎‏‎‏‏‏‏‎‎‏‎‏‏‏‏‏‏‎‏‎‏‎‏‎‏‎‎‎‎‎‎‏‏‎‎‏‎‏‏‏‏‎‎‏‎‎‏‏‏‏‎‏‎‎‎Center crop‎‏‎‎‏‎"</string>
+    <string name="stretch_wallpaper_position" msgid="5002680983147456935">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‏‏‎‏‏‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‎‎‏‎‏‎‎‏‏‎‏‎‎‎‏‏‎‏‏‎‎‏‎‏‎‎‏‏‎‏‎‎‏‏‏‎Stretch‎‏‎‎‏‎"</string>
+    <string name="preview" msgid="1774602101743861071">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‎‏‏‏‎‎‎‏‎‏‎‎‎‎‎‏‎‏‎‎‏‏‏‎‎‏‏‎‏‎‏‏‎‎‏‎‏‎‎‎‎‎‏‏‎‏‎‏‎‏‏‏‏‎‏‎‏‎‎‏‏‏‏‎Preview‎‏‎‎‏‎"</string>
+    <string name="tab_info" msgid="818614080690111416">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‎‎‏‎‏‏‎‏‎‏‏‏‎‎‎‏‎‎‏‏‎‏‎‎‏‎‏‎‏‏‎‏‎‎‏‏‏‏‏‎‏‎‏‎‏‎‏‏‎‎‎‎‏‏‏‎‏‏‏‎‎‎‎Info‎‏‎‎‏‎"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‎‏‎‎‎‏‏‎‎‏‎‏‎‎‏‏‎‏‎‏‏‎‎‎‎‎‎‎‏‏‎‎‏‏‏‏‎‎‏‎‎‏‎‎‏‏‎‎‏‏‏‎‏‏‏‎‏‎‏‏‎‎‎‎Customize‎‏‎‎‏‎"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‎‎‏‎‏‏‏‏‎‎‏‎‏‏‎‏‏‎‎‎‏‏‎‏‎‏‎‏‏‎‏‏‎‎‏‏‏‏‎‎‏‎‎‎‏‎‏‏‎‏‏‏‏‎‎‏‏‎‏‏‎‏‎Settings…‎‏‎‎‏‎"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‏‏‎‎‏‎‎‎‎‎‏‎‏‏‎‏‎‏‎‎‏‏‎‏‏‏‎‏‎‎‏‏‎‏‎‎‏‎‎‎‎‏‎‏‏‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‎‎‏‎Delete‎‏‎‎‏‎"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‏‏‏‎‏‏‏‏‏‏‏‎‎‎‎‎‎‎‏‏‎‎‎‎‎‏‏‏‎‏‏‎‎‏‎‎‏‎‏‏‎‎‏‏‏‎‎‏‏‎‎‏‏‎‎‏‏‎‎‎‏‎‎Delete this wallpaper from your phone?‎‏‎‎‏‎"</string>
+</resources>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index d96971c..00e1822 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Recortar y centrar"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Estirar"</string>
     <string name="preview" msgid="1774602101743861071">"Vista previa"</string>
+    <string name="tab_info" msgid="818614080690111416">"Información"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Personalizar"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Configuración…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Borrar"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"¿Quieres borrar este fondo de pantalla del teléfono?"</string>
 </resources>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index f28956d..f2bfa6e 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Recortar el centro"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Expandir"</string>
     <string name="preview" msgid="1774602101743861071">"Vista previa"</string>
+    <string name="tab_info" msgid="818614080690111416">"Información"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Personalizar"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Ajustes…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Eliminar"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"¿Quieres eliminar este fondo de pantalla de tu teléfono?"</string>
 </resources>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index 8216c44..9b3ad6a 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Keskele kärpimine"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Venitamine"</string>
     <string name="preview" msgid="1774602101743861071">"Eelvaade"</string>
+    <string name="tab_info" msgid="818614080690111416">"Teave"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Kohandamine"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Seaded …"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Kustuta"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Kas soovite taustapildi telefonist kustutada?"</string>
 </resources>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index ef4d945..f76681b 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Erdian, moztuta"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Pantailara egokituta"</string>
     <string name="preview" msgid="1774602101743861071">"Aurreikusi"</string>
+    <string name="tab_info" msgid="818614080690111416">"Informazioa"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Pertsonalizatu"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Ezarpenak…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Ezabatu"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Telefonotik ezabatu nahi duzu horma-papera?"</string>
 </resources>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 898c514..105ce8c 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"برش از مرکز برای پر کردن صفحه."</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"کشیدن برای پر کردن صفحه"</string>
     <string name="preview" msgid="1774602101743861071">"پیش‌نمایش"</string>
+    <string name="tab_info" msgid="818614080690111416">"اطلاعات"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"سفارشی کردن"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"تنظیمات…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"حذف"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"این کاغذدیواری از تلفن حذف شود؟"</string>
 </resources>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 2a5d754..43980b4 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Keskirajaus"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Venytä"</string>
     <string name="preview" msgid="1774602101743861071">"Esikatselu"</string>
+    <string name="tab_info" msgid="818614080690111416">"Tiedot"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Personoi"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Asetukset…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Poista"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Poistetaanko tämä taustakuva puhelimelta?"</string>
 </resources>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index 539256a..0a263cb 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Rogné au centre"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Étiré"</string>
     <string name="preview" msgid="1774602101743861071">"Aperçu"</string>
+    <string name="tab_info" msgid="818614080690111416">"Info"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Personnaliser"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Paramètres…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Supprimer"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Supprimer ce fond d\'écran de votre téléphone?"</string>
 </resources>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index f075e19..d4699bb 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Recadrer et centrer"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Étirer"</string>
     <string name="preview" msgid="1774602101743861071">"Prévisualiser"</string>
+    <string name="tab_info" msgid="818614080690111416">"Informations"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Personnaliser"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Paramètres…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Supprimer"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Supprimer ce fond d\'écran de votre téléphone ?"</string>
 </resources>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index fc49812..30870fe 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Recortar no centro"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Estirar"</string>
     <string name="preview" msgid="1774602101743861071">"Mostrar vista previa"</string>
+    <string name="tab_info" msgid="818614080690111416">"Información"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Personalizar"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Configuración…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Eliminar"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Queres eliminar este fondo de pantalla do teu teléfono?"</string>
 </resources>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index e9fcf97..66c9ec5 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"મધ્યમાંથી કાપેલ"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"ખેંચેલ"</string>
     <string name="preview" msgid="1774602101743861071">"પ્રીવ્યૂ કરો"</string>
+    <string name="tab_info" msgid="818614080690111416">"માહિતી"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"કસ્ટમાઇઝ કરો"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"સેટિંગ…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"ડિલીટ કરો"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"તમારા ફોનમાંથી આ વૉલપેપર ડિલીટ કરીએ?"</string>
 </resources>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 453ecb0..c1b55ed 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"बीच का हिस्सा रखें"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"स्क्रीन के हिसाब से खींचें"</string>
     <string name="preview" msgid="1774602101743861071">"झलक"</string>
+    <string name="tab_info" msgid="818614080690111416">"जानकारी"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"पसंद के मुताबिक बनाएं"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"सेटिंग…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"मिटाएं"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"क्या आप अपने फ़ोन से यह वॉलपेपर मिटाना चाहते हैं?"</string>
 </resources>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index baa985b..b3f96b4 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Centriranje s obrezivanjem"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Rastezanje"</string>
     <string name="preview" msgid="1774602101743861071">"Pregled"</string>
+    <string name="tab_info" msgid="818614080690111416">"Informacije"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Prilagodba"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Postavke…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Izbriši"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Želite li izbrisati tu pozadinu s telefona?"</string>
 </resources>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index de05be5..df08a11 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Középre igazítás körülvágással"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Nyújtás"</string>
     <string name="preview" msgid="1774602101743861071">"Előnézet"</string>
+    <string name="tab_info" msgid="818614080690111416">"Információ"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Személyre szabás"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Beállítások…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Törlés"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Törli ezt a háttérképet a telefonjáról?"</string>
 </resources>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index 9ea0696..cf12757 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Կենտրոնում, կտրած"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Ձգել"</string>
     <string name="preview" msgid="1774602101743861071">"Նախադիտել"</string>
+    <string name="tab_info" msgid="818614080690111416">"Տեղեկություն"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Կարգավորել"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Կարգավորումներ"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Ջնջել"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Ջնջե՞լ այս պաստառը հեռախոսից։"</string>
 </resources>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index afe8069..acef669 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Crop tengah"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Regang"</string>
     <string name="preview" msgid="1774602101743861071">"Pratinjau"</string>
+    <string name="tab_info" msgid="818614080690111416">"Info"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Sesuaikan"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Setelan…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Hapus"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Hapus wallpaper ini dari ponsel Anda?"</string>
 </resources>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index 7a1d53f..a80fc19 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Miðjuskurður"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Teygja"</string>
     <string name="preview" msgid="1774602101743861071">"Forskoða"</string>
+    <string name="tab_info" msgid="818614080690111416">"Upplýsingar"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Sérsníða"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Stillingar…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Eyða"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Viltu eyða þessu veggfóðri úr símanum?"</string>
 </resources>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 2d8cc15..cbf5531 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Ritaglia al centro"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Allunga"</string>
     <string name="preview" msgid="1774602101743861071">"Anteprima"</string>
+    <string name="tab_info" msgid="818614080690111416">"Informazioni"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Personalizza"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Impostazioni…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Elimina"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Vuoi eliminare questo sfondo dal telefono?"</string>
 </resources>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index d8a697f..e35a08e 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"ממורכז וחתוך"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"מתוח"</string>
     <string name="preview" msgid="1774602101743861071">"תצוגה מקדימה"</string>
+    <string name="tab_info" msgid="818614080690111416">"מידע"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"התאמה אישית"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"הגדרות…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"מחיקה"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"למחוק את הטפט הזה מהטלפון?"</string>
 </resources>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index bc84a91..d88d496 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"中央で切り抜き"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"拡大"</string>
     <string name="preview" msgid="1774602101743861071">"プレビュー"</string>
+    <string name="tab_info" msgid="818614080690111416">"情報"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"カスタマイズ"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"設定…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"削除"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"スマートフォンからこの壁紙を削除しますか?"</string>
 </resources>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index 9a41fc9..907b5af 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"ცენტრში ჩამოჭრა"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"გადაჭიმვა"</string>
     <string name="preview" msgid="1774602101743861071">"გადახედვა"</string>
+    <string name="tab_info" msgid="818614080690111416">"ინფორმაცია"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"მორგება"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"პარამეტრები..."</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"წაშლა"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"წაიშალოს ეს ფონი თქვენი ტელეფონიდან?"</string>
 </resources>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index 03094c0..9384fbe 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Ортасынан қию"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Созу"</string>
     <string name="preview" msgid="1774602101743861071">"Алдын ала қарау"</string>
+    <string name="tab_info" msgid="818614080690111416">"Ақпарат"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Реттеу"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Параметрлер…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Жою"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Бұл тұсқағаз телефоннан жойылсын ба?"</string>
 </resources>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index dba3d35..bf89c50 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"ច្រឹប​កណ្តាល"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"ទាញ"</string>
     <string name="preview" msgid="1774602101743861071">"មើលសាកល្បង"</string>
+    <string name="tab_info" msgid="818614080690111416">"ព័ត៌មាន"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"ប្ដូរ​តាម​បំណង"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"ការកំណត់…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"លុប"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"លុប​ផ្ទាំងរូបភាពនេះ​ពី​ទូរសព្ទរបស់អ្នកដែរទេ?"</string>
 </resources>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index c6cf1d4..f2c5564 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"ಮಧ್ಯಕ್ಕೆ ಕ್ರಾಪ್ ಮಾಡಿ"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"ವಿಸ್ತರಿಸಿ"</string>
     <string name="preview" msgid="1774602101743861071">"ಪೂರ್ವವೀಕ್ಷಣೆ"</string>
+    <string name="tab_info" msgid="818614080690111416">"ಮಾಹಿತಿ"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"ಕಸ್ಟಮೈಜ್‌ ಮಾಡಿ"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"ಸೆಟ್ಟಿಂಗ್‌ಗಳು..."</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"ಅಳಿಸಿ"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"ನಿಮ್ಮ ಫೋನ್‌ನಿಂದ ಈ ವಾಲ್‌ಪೇಪರ್ ಅನ್ನು ಅಳಿಸುವುದೇ?"</string>
 </resources>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index 6f10f4f..9e9736f 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"중앙 자르기"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"확대"</string>
     <string name="preview" msgid="1774602101743861071">"미리보기"</string>
+    <string name="tab_info" msgid="818614080690111416">"정보"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"맞춤설정"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"설정…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"삭제"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"휴대전화에서 이 배경화면을 삭제하시겠습니까?"</string>
 </resources>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index b56b372..8c95a09 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Ортосуна тууралап кесүү"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Чоюу"</string>
     <string name="preview" msgid="1774602101743861071">"Алдын ала көрүү"</string>
+    <string name="tab_info" msgid="818614080690111416">"Маалымат"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Ыңгайлаштыруу"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Жөндөөлөр…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Жок кылуу"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Бул тушкагаз телефонуңуздан өчүрүлсүнбү?"</string>
 </resources>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index 1f5bc34..d0d533f 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"ຕັດເຄິ່ງກາງ"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"ຍືດ"</string>
     <string name="preview" msgid="1774602101743861071">"ຕົວຢ່າງ"</string>
+    <string name="tab_info" msgid="818614080690111416">"ຂໍ້ມູນ"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"ປັບແຕ່ງ"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"ການຕັ້ງຄ່າ…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"ລຶບອອກ"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"ລຶບຮູບພື້ນຫຼັງນີ້ອອກຈາກໂທລະສັບຂອງທ່ານບໍ?"</string>
 </resources>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 80fa37e..ddba93e 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Apkirpti centre"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Ištempti"</string>
     <string name="preview" msgid="1774602101743861071">"Peržiūra"</string>
+    <string name="tab_info" msgid="818614080690111416">"Informacija"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Tinkinimas"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Nustatymai…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Ištrinti"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Ištrinti šį ekrano foną iš telefono?"</string>
 </resources>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 318c0f8..0aeed74 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Apgriezt centrā"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Pielāgot"</string>
     <string name="preview" msgid="1774602101743861071">"Priekšskatīt"</string>
+    <string name="tab_info" msgid="818614080690111416">"Informācija"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Pielāgošana"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Iestatījumi…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Dzēst"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Vai dzēst šo fona tapeti no tālruņa?"</string>
 </resources>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index 9f2901f..315becc 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Исечи централно"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Растегни"</string>
     <string name="preview" msgid="1774602101743861071">"Преглед"</string>
+    <string name="tab_info" msgid="818614080690111416">"Информации"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Приспособување"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Поставки…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Избриши"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Да се избрише тапетов од телефонот?"</string>
 </resources>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index f70fbf0..09af34c 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"നടുവിൽ വലുപ്പം മാറ്റുക"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"വലിച്ചുനീട്ടുക"</string>
     <string name="preview" msgid="1774602101743861071">"പ്രിവ്യൂ"</string>
+    <string name="tab_info" msgid="818614080690111416">"വിവരം"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"ഇഷ്‌ടാനുസൃതമാക്കുക"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"ക്രമീകരണം…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"ഇല്ലാതാക്കുക"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"നിങ്ങളുടെ ഫോണിൽ നിന്ന് ഈ വാൾപേപ്പർ ഇല്ലാതാക്കണോ?"</string>
 </resources>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index 7352b7c..1009b28 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Голлуулж тайрах"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Сунгах"</string>
     <string name="preview" msgid="1774602101743861071">"Урьдчилан үзэх"</string>
+    <string name="tab_info" msgid="818614080690111416">"Мэдээлэл"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Өөрчлөх"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Тохиргоо…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Устгах"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Энэ ханын зургийг утаснаасаа устгах уу?"</string>
 </resources>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index bc7de36..a45a507 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -37,9 +37,9 @@
     <string name="set_wallpaper_lock_screen_destination" msgid="6224685559375417945">"लॉक स्क्रीन"</string>
     <string name="set_wallpaper_both_destination" msgid="6967226064958263939">"होम स्क्रीन आणि लॉक स्क्रीन"</string>
     <string name="no_backup_image_wallpaper_label" msgid="6316627676107284851">"फिरती इमेज वॉलपेपर"</string>
-    <string name="permission_needed_explanation" msgid="139166837541426823">"येथे सध्याचा वॉलपेपर दाखवण्यासाठी, <xliff:g id="APP_NAME">%1$s</xliff:g> ला तुमच्या डिव्हाइसच्या स्टोरेजचा अॅक्सेस हवा आहे."</string>
-    <string name="permission_needed_explanation_go_to_settings" msgid="3923551582092599609">"येथे सध्याचा वॉलपेपर दाखवण्यासाठी, वॉलपेपरला तुमच्या डिव्हाइसच्या स्टोरेजचा अॅक्सेस हवा आहे.\n\nहे सेटिंग बदलण्यासाठी, वॉलपेपरच्या ॲप माहितीच्या परवानग्या भागावर जा."</string>
-    <string name="permission_needed_allow_access_button_label" msgid="1943133660612924306">"अॅक्सेस द्या"</string>
+    <string name="permission_needed_explanation" msgid="139166837541426823">"येथे सध्याचा वॉलपेपर दाखवण्यासाठी, <xliff:g id="APP_NAME">%1$s</xliff:g> ला तुमच्या डिव्हाइसच्या स्टोरेजचा ॲक्सेस हवा आहे."</string>
+    <string name="permission_needed_explanation_go_to_settings" msgid="3923551582092599609">"येथे सध्याचा वॉलपेपर दाखवण्यासाठी, वॉलपेपरला तुमच्या डिव्हाइसच्या स्टोरेजचा ॲक्सेस हवा आहे.\n\nहे सेटिंग बदलण्यासाठी, वॉलपेपरच्या ॲप माहितीच्या परवानग्या भागावर जा."</string>
+    <string name="permission_needed_allow_access_button_label" msgid="1943133660612924306">"ॲक्सेस द्या"</string>
     <string name="no_backup_image_wallpaper_description" msgid="8303268619408738057">"फिरत्या वॉलपेपरसाठी लाइव्ह वॉलपेपर सेवा"</string>
     <string name="daily_refresh_tile_title" msgid="3270456074558525091">"दैनिक वॉलपेपर"</string>
     <string name="daily_refresh_tile_subtitle" msgid="3976682014885446443">"चालू करण्यासाठी टॅप करा"</string>
@@ -53,7 +53,7 @@
     <string name="explore" msgid="7468719504199497281">"एक्सप्लोर करा"</string>
     <string name="next_wallpaper" msgid="3911873152952596232">"पुढील वॉलपेपर"</string>
     <string name="wallpaper_disabled_message" msgid="7309484130562148185">"या डिव्हाइसवर वॉलपेपर सेट करणे बंद केलेले आहे"</string>
-    <string name="wallpaper_disabled_by_administrator_message" msgid="1551430406714747884">"तुमच्या डिव्हाइस अॅडमिनिस्ट्रेटरने वॉलपेपर सेट करणे बंद केले आहे"</string>
+    <string name="wallpaper_disabled_by_administrator_message" msgid="1551430406714747884">"तुमच्या डिव्हाइस ॲडमिनिस्ट्रेटरने वॉलपेपर सेट करणे बंद केले आहे"</string>
     <string name="wallpaper_set_successfully_message" msgid="2958998799111688578">"वॉलपेपर यशस्वीरीत्या सेट झाला"</string>
     <string name="wallpapers_unavailable_offline_message" msgid="8136405438621689532">"वॉलपेपर पाहण्यासाठी तुमच्याकडे इंटरनेट कनेक्शन असणे गरजेचे आहे. कृपया कनेक्ट करा आणि पुन्हा प्रयत्न करा."</string>
     <string name="currently_set_home_wallpaper_thumbnail" msgid="4022381436821898917">"सध्या होम स्क्रीन वॉलपेपर थंबनेल सेट केले आहे"</string>
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"मध्यभागी क्रॉप केलेले"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"ताणा"</string>
     <string name="preview" msgid="1774602101743861071">"पूर्वावलोकन करा"</string>
+    <string name="tab_info" msgid="818614080690111416">"माहिती"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"कस्टमाइझ करा"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"सेटिंग्ज…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"हटवा"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"तुमच्या फोनवरून हा वॉलपेपर हटवायचा आहे का?"</string>
 </resources>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index d10b20d..84bad3d 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Pangkas ke tengah"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Regang"</string>
     <string name="preview" msgid="1774602101743861071">"Pratonton"</string>
+    <string name="tab_info" msgid="818614080690111416">"Maklumat"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Sesuaikan"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Tetapan..."</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Padam"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Padamkan kertas dinding ini daripada telefon anda?"</string>
 </resources>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index b5d4855..6b8ae70 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"အလယ်တွင် ဖြတ်ညှိရန်"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"ဆွဲဆန့်ရန်"</string>
     <string name="preview" msgid="1774602101743861071">"အစမ်းကြည့်ရန်"</string>
+    <string name="tab_info" msgid="818614080690111416">"အချက်အလက်"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"စိတ်ကြိုက်ပြုလုပ်ရန်"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"ဆက်တင်များ…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"ဖျက်ရန်"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"ဤနောက်ခံပုံကို သင့်ပင်မစာမျက်နှာမှ ဖျက်ပစ်မလား။"</string>
 </resources>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 2028c9d..14da40b 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Midtstilt beskjæring"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Strekk"</string>
     <string name="preview" msgid="1774602101743861071">"Ta en forhåndskikk"</string>
+    <string name="tab_info" msgid="818614080690111416">"Informasjon"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Tilpass"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Innstillinger"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Slett"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Vil du slette denne bakgrunnen fra telefonen din?"</string>
 </resources>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index 28a1052..e3ec544 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"मध्यभागमा क्रप गरिएको"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"तन्काइएको"</string>
     <string name="preview" msgid="1774602101743861071">"पूर्वावलोकन"</string>
+    <string name="tab_info" msgid="818614080690111416">"जानकारी"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"आफू अनुकूल पार्नुहोस्"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"सेटिङ…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"मेट्नुहोस्"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"तपाईंको फोनबाट यो वालपेपर मेटाउने हो?"</string>
 </resources>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 543e654..8c259e2 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Gecentreerd bijsnijden"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Uitrekken"</string>
     <string name="preview" msgid="1774602101743861071">"Voorbeeld"</string>
+    <string name="tab_info" msgid="818614080690111416">"Informatie"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Aanpassen"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Instellingen…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Verwijderen"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Wil je deze achtergrond verwijderen van je telefoon?"</string>
 </resources>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index 32e791e..6bbea17 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"ସେଣ୍ଟର୍ କ୍ରପ୍"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"ପ୍ରସାରିତ କରନ୍ତୁ"</string>
     <string name="preview" msgid="1774602101743861071">"ପ୍ରିଭ୍ୟୁ"</string>
+    <string name="tab_info" msgid="818614080690111416">"ସୂଚନା"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"କଷ୍ଟମାଇଜ୍ କରନ୍ତୁ"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"ସେଟିଂସ୍..."</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"ଡିଲିଟ୍ କରନ୍ତୁ"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"ଆପଣଙ୍କର ଫୋନ୍‌ରୁ ଏହି ୱାଲ୍‌ପେପର୍‌କୁ ଡିଲିଟ୍ କରିବେ କି?"</string>
 </resources>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index 9d8f5f7..ebeed97 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"ਵਿਚਕਾਰੋਂ ਕਾਂਟ-ਛਾਂਟ ਕਰੋ"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"ਫੈਲਾਓ"</string>
     <string name="preview" msgid="1774602101743861071">"ਪੂਰਵ-ਝਲਕ"</string>
+    <string name="tab_info" msgid="818614080690111416">"ਜਾਣਕਾਰੀ"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"ਵਿਉਂਤਬੱਧ ਕਰੋ"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"ਸੈਟਿੰਗਾਂ…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"ਮਿਟਾਓ"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"ਕੀ ਇਹ ਵਾਲਪੇਪਰ ਤੁਹਾਡੇ ਫ਼ੋਨ ਤੋਂ ਮਿਟਾਉਣਾ ਹੈ?"</string>
 </resources>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 82d7408..9801576 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Przycięta na środku"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Rozciągnięcie"</string>
     <string name="preview" msgid="1774602101743861071">"Podgląd"</string>
+    <string name="tab_info" msgid="818614080690111416">"Informacje"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Dostosuj"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Ustawienia…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Usuń"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Usunąć tę tapetę z telefonu?"</string>
 </resources>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 3ba961d..59b2054 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Recortar e centrar"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Esticar"</string>
     <string name="preview" msgid="1774602101743861071">"Pré-visualizar"</string>
+    <string name="tab_info" msgid="818614080690111416">"Informações"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Personalizar"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Definições…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Eliminar"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Pretende eliminar esta imagem de fundo do telemóvel?"</string>
 </resources>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index f26164b..1589064 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Corte central"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Expandir"</string>
     <string name="preview" msgid="1774602101743861071">"Visualizar"</string>
+    <string name="tab_info" msgid="818614080690111416">"Informações"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Personalizar"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Configurações…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Excluir"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Excluir esse plano de fundo do seu smartphone?"</string>
 </resources>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 0766d1a..4ec0ea1 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Decupare în centru"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Extindeți"</string>
     <string name="preview" msgid="1774602101743861071">"Previzualizați"</string>
+    <string name="tab_info" msgid="818614080690111416">"Informații"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Personalizați"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Setări…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Ștergeți"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Ștergeți această imagine de fundal de pe telefon?"</string>
 </resources>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 1421a1b..264f88a 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Заполнить"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Растянуть"</string>
     <string name="preview" msgid="1774602101743861071">"Предварительный просмотр"</string>
+    <string name="tab_info" msgid="818614080690111416">"Информация"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Настройка"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Настройки…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Удалить"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Удалить эти обои с телефона?"</string>
 </resources>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index 8022ce9..b7d767e 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"මැද කප්පාදු කිරීම"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"අදින්න"</string>
     <string name="preview" msgid="1774602101743861071">"පෙරදසුන"</string>
+    <string name="tab_info" msgid="818614080690111416">"තතු"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"අභිරුචිකරණය කරන්න"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"සැකසීම්…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"මකන්න"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"මෙම බිතුපත ඔබේ දුරකථනයෙන් මකන්නද?"</string>
 </resources>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index d12ddd0..60a2bdb 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Vycentrovať a orezať"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Roztiahnuť"</string>
     <string name="preview" msgid="1774602101743861071">"Zobraziť ukážku"</string>
+    <string name="tab_info" msgid="818614080690111416">"Informácie"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Prispôsobenie"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Nastavenia…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Odstrániť"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Chcete túto tapetu odstrániť z telefónu?"</string>
 </resources>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index dae44d7..b2e0434 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Na sredini obrezano"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Raztegnjeno"</string>
     <string name="preview" msgid="1774602101743861071">"Predogled"</string>
+    <string name="tab_info" msgid="818614080690111416">"Informacije"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Prilagajanje"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Nastavitve …"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Izbriši"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Ali želite to ozadje izbrisati iz telefona?"</string>
 </resources>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index e35ad48..6b4b090 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Prerje qendrore"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"E tendosur"</string>
     <string name="preview" msgid="1774602101743861071">"Shiko paraprakisht"</string>
+    <string name="tab_info" msgid="818614080690111416">"Informacione"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Personalizo"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Cilësimet…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Fshi"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Do ta fshish këtë imazh sfondi nga telefoni?"</string>
 </resources>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index 6c26b0a..0cc71af 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Опсеци у центру"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Развуци"</string>
     <string name="preview" msgid="1774602101743861071">"Преглед"</string>
+    <string name="tab_info" msgid="818614080690111416">"Информације"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Прилагодите"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Подешавања…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Избриши"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Желите да избришете ову позадину са телефона?"</string>
 </resources>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 54bc2cb..7fcbd57 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Centrera och beskär"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Dra ut"</string>
     <string name="preview" msgid="1774602101743861071">"Förhandsgranska"</string>
+    <string name="tab_info" msgid="818614080690111416">"Information"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Anpassa"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Inställningar…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Radera"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Vill du radera den här bakgrunden från mobilen?"</string>
 </resources>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 17734e1..7221e1f 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Punguza katikati"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Panua"</string>
     <string name="preview" msgid="1774602101743861071">"Kagua kwanza"</string>
+    <string name="tab_info" msgid="818614080690111416">"Maelezo"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Weka mapendeleo"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Mipangilio…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Futa"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Je, ungependa kufuta mandhari haya kwenye simu yako?"</string>
 </resources>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index 5be8064..75248b4 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"மையமாகச் செதுக்கு"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"திரையில் பொருந்தும்படி விரி"</string>
     <string name="preview" msgid="1774602101743861071">"மாதிரிக்காட்சி"</string>
+    <string name="tab_info" msgid="818614080690111416">"தகவல்"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"பிரத்தியேகமாக்கு"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"அமைப்புகள்…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"நீக்கு"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"மொபைலில் இருந்து இந்த வால்பேப்பரை நீக்கவா?"</string>
 </resources>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 3db68b3..a5beb79 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"మధ్యన కత్తిరించు"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"విస్తరించు"</string>
     <string name="preview" msgid="1774602101743861071">"ప్రివ్యూ చూపు"</string>
+    <string name="tab_info" msgid="818614080690111416">"సమాచారం"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"అనుకూలీకరించండి"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"సెట్టింగ్‌లు…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"తొలగించు"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"మీ ఫోన్ నుండి ఈ వాల్‌పేపర్‌ను తొలగించాలా?"</string>
 </resources>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 0aaa891..c86eb72 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"ครอบตัดกึ่งกลาง"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"ยืด"</string>
     <string name="preview" msgid="1774602101743861071">"แสดงพรีวิว"</string>
+    <string name="tab_info" msgid="818614080690111416">"ข้อมูล"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"ปรับแต่ง"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"การตั้งค่า…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"ลบ"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"ลบวอลเปเปอร์นี้ออกจากโทรศัพท์ไหม"</string>
 </resources>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 7232a7e..6988601 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"I-crop sa gitna"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"I-stretch"</string>
     <string name="preview" msgid="1774602101743861071">"I-preview"</string>
+    <string name="tab_info" msgid="818614080690111416">"Impormasyon"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"I-customize"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Mga Setting…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"I-delete"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"I-delete ang wallpaper na ito sa iyong telepono?"</string>
 </resources>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 1600f69..1d30e7e 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Ortalayarak kırp"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Genişlet"</string>
     <string name="preview" msgid="1774602101743861071">"Önizle"</string>
+    <string name="tab_info" msgid="818614080690111416">"Bilgi"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Özelleştir"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Ayarlar…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Sil"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Bu duvar kağıdı telefonunuzdan silinsin mi?"</string>
 </resources>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 0d4fd12..1df810a 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Обрізати відносно центру"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Розтягнути"</string>
     <string name="preview" msgid="1774602101743861071">"Переглянути"</string>
+    <string name="tab_info" msgid="818614080690111416">"Інформація"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Налаштування"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Налаштування…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Видалити"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Видалити цей фоновий малюнок із телефона?"</string>
 </resources>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index b4a5043..9d87add 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"مرکزی تراش"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"پھیلائیں"</string>
     <string name="preview" msgid="1774602101743861071">"پیش منظر"</string>
+    <string name="tab_info" msgid="818614080690111416">"معلومات"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"حسب ضرورت بنائیں"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"ترتیبات…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"حذف کریں"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"اس وال پیپر کو آپ کے فون سے حذف کریں؟"</string>
 </resources>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index ede76e6..d0d316a 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Markazlab qirqish"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Yoyish"</string>
     <string name="preview" msgid="1774602101743861071">"Razm solish"</string>
+    <string name="tab_info" msgid="818614080690111416">"Axborot"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Sozlash"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Sozlamalar…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Oʻchirish"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Bu fon rasmi telefoningizdan oʻchirib tashlansinmi?"</string>
 </resources>
diff --git a/res/values-v29/styles.xml b/res/values-v29/styles.xml
index e7f27d4..6cae735 100644
--- a/res/values-v29/styles.xml
+++ b/res/values-v29/styles.xml
@@ -18,5 +18,6 @@
 <resources>
     <style name="PreviewCheckboxDeviceTheme" parent="@android:style/ThemeOverlay.DeviceDefault.Accent.DayNight">
         <item name="android:colorControlActivated">?android:attr/colorAccent</item>
+        <item name="android:colorControlNormal">?android:attr/colorPrimary</item>
     </style>
 </resources>
\ No newline at end of file
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index f0c0176..b11245e 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Cắt giữa"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Kéo dài"</string>
     <string name="preview" msgid="1774602101743861071">"Xem trước"</string>
+    <string name="tab_info" msgid="818614080690111416">"Thông tin"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Tùy chỉnh"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Cài đặt…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Xóa"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Bạn muốn xóa hình nền này khỏi điện thoại của mình?"</string>
 </resources>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 3795c39..5db994c 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"居中裁剪"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"拉伸"</string>
     <string name="preview" msgid="1774602101743861071">"预览"</string>
+    <string name="tab_info" msgid="818614080690111416">"信息"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"自定义"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"设置…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"删除"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"要从您的手机上删除此壁纸吗?"</string>
 </resources>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index f9ecc62..507758b 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"裁剪中間部分"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"延展"</string>
     <string name="preview" msgid="1774602101743861071">"預覽"</string>
+    <string name="tab_info" msgid="818614080690111416">"資訊"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"自訂"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"設定…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"刪除"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"要從手機中刪除此桌布嗎?"</string>
 </resources>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index f6e67cf..ac79125 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"置中裁剪"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"延伸"</string>
     <string name="preview" msgid="1774602101743861071">"預覽"</string>
+    <string name="tab_info" msgid="818614080690111416">"資訊"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"自訂"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"設定…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"刪除"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"要從手機中刪除這張桌布嗎?"</string>
 </resources>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 2db2a33..98625b6 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -80,4 +80,9 @@
     <string name="center_crop_wallpaper_position" msgid="1681980019815343348">"Nqampuna phakathi kwendawo"</string>
     <string name="stretch_wallpaper_position" msgid="5002680983147456935">"Nweba"</string>
     <string name="preview" msgid="1774602101743861071">"Buka kuqala"</string>
+    <string name="tab_info" msgid="818614080690111416">"Ulwazi"</string>
+    <string name="tab_customize" msgid="2533745409174959960">"Enza ngendlela oyifisayo"</string>
+    <string name="configure_wallpaper" msgid="849882179182976621">"Izilungiselelo…"</string>
+    <string name="delete_live_wallpaper" msgid="589212696102662329">"Susa"</string>
+    <string name="delete_wallpaper_confirmation" msgid="33790318361863778">"Landa lesi sithombe esingemuva kusukela kufoni yakho?"</string>
 </resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 53c95c0..843ff79 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -126,6 +126,7 @@
     <dimen name="preview_attribution_pane_description_height">34dp</dimen>
     <dimen name="preview_attribution_pane_button_bottom_margin">8dp</dimen>
 
+    <dimen name="preview_gradient_background_height">256dp</dimen>
 
     <!-- Dimensions for the "start rotation" dialog. -->
     <dimen name="start_rotation_dialog_subhead_margin_top">19dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index fe99050..0201f96 100755
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -300,4 +300,19 @@
 
     <!-- Label for a checkbox which lets user preview the displayed image as wallpaper. [CHAR LIMIT=30] -->
     <string name="preview">Preview</string>
+
+    <!-- Label for the 'Info' tab of view pager in wallpaper preview activity. [CHAR_LIMIT=25] -->
+    <string name="tab_info">Info</string>
+    <!-- Label for the 'Customize' tab of view pager in wallpaper preview activity.
+         [CHAR_LIMIT=25] -->
+    <string name="tab_customize">Customize</string>
+
+    <!-- List item for configuring the current wallpaper [CHAR LIMIT=30] -->
+    <string name="configure_wallpaper">Settings…</string>
+
+    <!-- List item for deleting the current wallpaper [CHAR LIMIT=30] -->
+    <string name="delete_live_wallpaper">Delete</string>
+
+    <!-- Confirmation dialog. Shown after user selects to delete one wallpaper. [CHAR LIMIT=NONE] -->
+    <string name="delete_wallpaper_confirmation">Delete this wallpaper from your phone?</string>
 </resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 6259281..3ea8c33 100755
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -40,7 +40,10 @@
         <item name="android:windowDrawsSystemBarBackgrounds">true</item>
     </style>
 
-    <style name="WallpaperTheme.Preview" parent="@style/Theme.AppCompat.NoActionBar">
+    <style name="WallpaperTheme.Preview" parent="@android:style/Theme.DeviceDefault.Settings">
+        <item name="android:colorPrimary">@color/material_white_100</item>
+        <item name="colorPrimary">@color/material_white_100</item>
+        <item name="colorControlActivated">?attr/colorPrimary</item>
         <item name="android:windowBackground">@android:color/transparent</item>
         <item name="android:windowContentOverlay">@null</item>
         <item name="android:windowDisablePreview">true</item>
@@ -51,10 +54,18 @@
         <item name="android:windowActionBar">false</item>
         <item name="android:windowNoTitle">true</item>
 
+        <item name="actionBarSize">?android:attr/actionBarSize</item>
+        <item name="homeAsUpIndicator">@drawable/material_ic_arrow_back_black_24</item>
+
+        <item name="selectableItemBackground">?android:attr/selectableItemBackground</item>
+        <item name="dialogPreferredPadding">24dp</item>
+        <item name="colorControlHighlight">@color/ripple_material_dark</item>
+        <item name="toolbarNavigationButtonStyle">@style/Widget.AppCompat.Toolbar.Button.Navigation</item>
+
         <!-- Set status bar and navigation buttons to be translucent. -->
         <item name="android:colorPrimaryDark">@color/translucent_black</item>
-        <item name="android:statusBarColor">@color/translucent_black</item>
-        <item name="android:navigationBarColor">@color/translucent_black</item>
+        <item name="android:statusBarColor">@android:color/transparent</item>
+        <item name="android:navigationBarColor">@android:color/transparent</item>
 
         <!-- Apply borderless button style for the right ripple effect. -->
         <item name="borderlessButtonStyle">@style/Widget.AppCompat.Button.Borderless</item>
@@ -64,10 +75,20 @@
         </item>
     </style>
 
+    <style name="WallpaperTheme.DarkActionBar" parent="@style/WallpaperTheme.Preview">
+        <item name="colorControlNormal">?attr/colorPrimary</item>
+        <item name="colorButtonNormal">?attr/colorPrimary</item>
+        <item name="android:colorControlActivated">?attr/colorPrimary</item>
+        <item name="android:colorControlHighlight">?attr/colorPrimary</item>
+        <item name="actionMenuTextColor">?attr/colorPrimary</item>
+        <item name="android:actionMenuTextColor">?attr/colorPrimary</item>
+        <item name="android:actionMenuTextAppearance">@style/WallpaperPicker.Preview.TextAppearance.ActionBar</item>
+    </style>
+
     <!-- Toolbar -->
     <style name="TranslucentToolbarStyle" parent="@style/Widget.AppCompat.Toolbar">
         <item name="android:background">@android:color/transparent</item>
-        <item name="android:theme">@style/ThemeOverlay.AppCompat.Dark.ActionBar</item>
+        <item name="android:theme">@style/WallpaperTheme.DarkActionBar</item>
     </style>
 
     <style name="RegularToolbarStyle" parent="@style/Widget.AppCompat.Toolbar">
@@ -100,9 +121,13 @@
         <item name="android:textColor">@android:color/white</item>
     </style>
 
-    <style name="ActionBarCheckboxStyle" parent="@android:style/Widget.DeviceDefault.CompoundButton.CheckBox"/>
+    <style name="ActionBarCheckboxStyle" parent="@android:style/Widget.DeviceDefault.CompoundButton.CheckBox">
+        <item name="android:textColor">?android:attr/colorPrimary</item>
+    </style>
 
-    <style name="PreviewCheckboxDeviceTheme" parent="@android:style/Theme.DeviceDefault"/>
+    <style name="PreviewCheckboxDeviceTheme" parent="@android:style/Theme.DeviceDefault">
+        <item name="android:colorControlNormal">?android:attr/colorPrimary</item>
+    </style>
 
     <style name="select_wallpaper_header">
         <item name="android:textColor">@color/translucent_black_60_alpha</item>
@@ -111,6 +136,15 @@
     </style>
 
     <!-- Preview attribution pane styles -->
+    <style name="WallpaperPicker.BottomPaneStyle" parent="@android:style/Theme.DeviceDefault.Settings">
+        <item name="android:textColorPrimary">@color/material_white_100</item>
+        <item name="android:textColorSecondary">@color/white_70_alpha</item>
+        <item name="tabTextAppearance">@style/WallpaperPicker.Preview.TextAppearance.NoAllCaps</item>
+        <item name="tabIndicatorColor">?android:attr/textColorPrimary</item>
+        <item name="tabGravity">fill</item>
+        <item name="tabMaxWidth">0dp</item>
+    </style>
+
     <style name="preview_attribution_pane_title">
         <item name="android:textColor">@color/material_white_text</item>
         <item name="android:textSize">@dimen/abc_text_size_subhead_material</item>
@@ -121,6 +155,17 @@
         <item name="android:textSize">@dimen/abc_text_size_body_2_material</item>
     </style>
 
+    <style name="WallpaperPicker.Preview.TextAppearance.NoAllCaps"
+        parent="@android:style/TextAppearance.DeviceDefault.Widget.TabWidget">
+        <item name="android:textAllCaps">false</item>
+    </style>
+
+    <style name="WallpaperPicker.Preview.TextAppearance.ActionBar"
+        parent="@android:style/TextAppearance.DeviceDefault.Widget.ActionBar.Menu">
+        <item name="android:textAllCaps">false</item>
+        <item name="android:textColor">@color/material_white_100</item>
+    </style>
+
     <!-- Set wallpaper destination item -->
     <style name="set_wallpaper_destination_item">
         <item name="android:minHeight">@dimen/set_wallpaper_dialog_item_min_height</item>
diff --git a/src/com/android/wallpaper/model/CurrentWallpaperInfoV16.java b/src/com/android/wallpaper/model/CurrentWallpaperInfoV16.java
deleted file mode 100755
index 3db46e7..0000000
--- a/src/com/android/wallpaper/model/CurrentWallpaperInfoV16.java
+++ /dev/null
@@ -1,139 +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.model;
-
-import android.app.Activity;
-import android.content.Context;
-import android.os.Parcel;
-
-import com.android.wallpaper.asset.Asset;
-import com.android.wallpaper.asset.CurrentWallpaperAssetV16;
-import com.android.wallpaper.asset.FileAsset;
-import com.android.wallpaper.module.InjectorProvider;
-import com.android.wallpaper.module.NoBackupImageWallpaper;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-
-import androidx.annotation.DrawableRes;
-import androidx.annotation.StringRes;
-
-/**
- * Represents the wallpaper currently set to the device for API 16 through 23. Should not be used
- * to set a new wallpaper.
- */
-public class CurrentWallpaperInfoV16 extends WallpaperInfo {
-
-    public static final Creator<CurrentWallpaperInfoV16> CREATOR =
-            new Creator<CurrentWallpaperInfoV16>() {
-                @Override
-                public CurrentWallpaperInfoV16 createFromParcel(Parcel source) {
-                    return new CurrentWallpaperInfoV16(source);
-                }
-
-                @Override
-                public CurrentWallpaperInfoV16[] newArray(int size) {
-                    return new CurrentWallpaperInfoV16[size];
-                }
-            };
-    private List<String> mAttributions;
-    private Asset mAsset;
-    private String mActionUrl;
-    @StringRes
-    private int mActionLabelRes;
-    @DrawableRes
-    private int mActionIconRes;
-    private String mCollectionId;
-
-    public CurrentWallpaperInfoV16(List<String> attributions, String actionUrl,
-                                   @StringRes int actionLabelRes, @DrawableRes int actionIconRes,
-                                   String collectionId) {
-        mAttributions = attributions;
-        mActionUrl = actionUrl;
-        mActionLabelRes = actionLabelRes;
-        mActionIconRes = actionIconRes;
-        mCollectionId = collectionId;
-    }
-
-    private CurrentWallpaperInfoV16(Parcel in) {
-        mAttributions = new ArrayList<>();
-        in.readStringList(mAttributions);
-        mActionUrl = in.readString();
-        mCollectionId = in.readString();
-        mActionLabelRes = in.readInt();
-        mActionIconRes = in.readInt();
-    }
-
-    @Override
-    public List<String> getAttributions(Context context) {
-        return mAttributions;
-    }
-
-    @Override
-    public Asset getAsset(Context context) {
-        if (mAsset == null) {
-            boolean isNoBackupImageWallpaperSet = InjectorProvider.getInjector()
-                    .getLiveWallpaperStatusChecker(context).isNoBackupImageWallpaperSet();
-
-            mAsset = isNoBackupImageWallpaperSet
-                    ? new FileAsset(new File(context.getApplicationContext().getFilesDir(),
-                    NoBackupImageWallpaper.ROTATING_WALLPAPER_FILE_PATH))
-                    : new CurrentWallpaperAssetV16(context);
-        }
-        return mAsset;
-    }
-
-    @Override
-    public Asset getThumbAsset(Context context) {
-        return getAsset(context);
-    }
-
-    @Override
-    public String getActionUrl(Context unused) {
-        return mActionUrl;
-    }
-
-    @Override
-    public int getActionIconRes(Context unused) {
-        return mActionIconRes != 0 ? mActionIconRes : WallpaperInfo.getDefaultActionIcon();
-    }
-
-    @Override
-    public int getActionLabelRes(Context unused) {
-        return mActionLabelRes != 0 ? mActionLabelRes : WallpaperInfo.getDefaultActionLabel();
-    }
-
-    @Override
-    public String getCollectionId(Context unused) {
-        return mCollectionId;
-    }
-
-    @Override
-    public void writeToParcel(Parcel parcel, int flags) {
-        parcel.writeStringList(mAttributions);
-        parcel.writeString(mActionUrl);
-        parcel.writeString(mCollectionId);
-        parcel.writeInt(mActionLabelRes);
-        parcel.writeInt(mActionIconRes);
-    }
-
-    @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/CurrentWallpaperInfoVN.java b/src/com/android/wallpaper/model/CurrentWallpaperInfoVN.java
index e44e717..bf54bfd 100755
--- a/src/com/android/wallpaper/model/CurrentWallpaperInfoVN.java
+++ b/src/com/android/wallpaper/model/CurrentWallpaperInfoVN.java
@@ -27,13 +27,10 @@
 import com.android.wallpaper.asset.Asset;
 import com.android.wallpaper.asset.BuiltInWallpaperAsset;
 import com.android.wallpaper.asset.CurrentWallpaperAssetVN;
-import com.android.wallpaper.asset.FileAsset;
 import com.android.wallpaper.compat.WallpaperManagerCompat;
 import com.android.wallpaper.compat.WallpaperManagerCompat.WallpaperLocation;
 import com.android.wallpaper.module.InjectorProvider;
-import com.android.wallpaper.module.NoBackupImageWallpaper;
 
-import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
@@ -142,17 +139,6 @@
      * Constructs and returns an Asset instance representing the currently-set wallpaper asset.
      */
     private Asset createCurrentWallpaperAssetVN(Context context) {
-        // If the live wallpaper for rotating wallpapers is set, then provide a file asset
-        // representing that wallpaper.
-        boolean isNoBackupImageWallpaperSet = InjectorProvider.getInjector()
-                .getLiveWallpaperStatusChecker(context).isNoBackupImageWallpaperSet();
-        if (mWallpaperManagerFlag == WallpaperManagerCompat.FLAG_SYSTEM
-                && isNoBackupImageWallpaperSet) {
-            Context deviceProtectedContext = context.createDeviceProtectedStorageContext();
-            return new FileAsset(new File(deviceProtectedContext.getFilesDir(),
-                    NoBackupImageWallpaper.ROTATING_WALLPAPER_FILE_PATH));
-        }
-
         WallpaperManagerCompat wallpaperManagerCompat = InjectorProvider.getInjector()
                 .getWallpaperManagerCompat(context);
 
diff --git a/src/com/android/wallpaper/model/InlinePreviewIntentFactory.java b/src/com/android/wallpaper/model/InlinePreviewIntentFactory.java
index ce29761..787c76a 100755
--- a/src/com/android/wallpaper/model/InlinePreviewIntentFactory.java
+++ b/src/com/android/wallpaper/model/InlinePreviewIntentFactory.java
@@ -23,6 +23,16 @@
  * wallpaper, if appropriate for that wallpaper.
  */
 public interface InlinePreviewIntentFactory {
+
+    /**
+     * @return whether it's possible to use the internal live picker, or {@code false} to use the
+     * the Framework Activity.
+     */
+    default boolean shouldUseInternalLivePicker(Context context) {
+        return false; // Disable always for now
+            //ContextCompat.checkSelfPermission(context, BIND_WALLPAPER) == PERMISSION_GRANTED;
+    }
+
     /**
      * Gets an intent to show the preview activity for the given wallpaper.
      *
@@ -30,5 +40,5 @@
      * @param wallpaper
      * @return Intent to show the inline preview activity.
      */
-    public Intent newIntent(Context ctx, WallpaperInfo wallpaper);
+    Intent newIntent(Context ctx, WallpaperInfo wallpaper);
 }
diff --git a/src/com/android/wallpaper/model/LiveWallpaperInfo.java b/src/com/android/wallpaper/model/LiveWallpaperInfo.java
index a408aed..4337230 100755
--- a/src/com/android/wallpaper/model/LiveWallpaperInfo.java
+++ b/src/com/android/wallpaper/model/LiveWallpaperInfo.java
@@ -245,7 +245,7 @@
         return wallpaperInfos;
     }
 
-    static boolean isSystemApp(ApplicationInfo appInfo) {
+    private static boolean isSystemApp(ApplicationInfo appInfo) {
         return (appInfo.flags & (ApplicationInfo.FLAG_SYSTEM
                 | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) != 0;
     }
@@ -262,11 +262,12 @@
     @Override
     public List<String> getAttributions(Context context) {
         List<String> attributions = new ArrayList<>();
-        CharSequence labelCharSeq = mInfo.loadLabel(context.getPackageManager());
+        PackageManager packageManager = context.getPackageManager();
+        CharSequence labelCharSeq = mInfo.loadLabel(packageManager);
         attributions.add(labelCharSeq == null ? null : labelCharSeq.toString());
 
         try {
-            CharSequence authorCharSeq = mInfo.loadAuthor(context.getPackageManager());
+            CharSequence authorCharSeq = mInfo.loadAuthor(packageManager);
             if (authorCharSeq != null) {
                 String author = authorCharSeq.toString();
                 attributions.add(author);
@@ -275,6 +276,16 @@
             // No author specified, so no other attribution to add.
         }
 
+        try {
+            CharSequence descCharSeq = mInfo.loadDescription(packageManager);
+            if (descCharSeq != null) {
+                String desc = descCharSeq.toString();
+                attributions.add(desc);
+            }
+        } catch (Resources.NotFoundException e) {
+            // No description specified, so no other attribution to add.
+        }
+
         return attributions;
     }
 
@@ -294,6 +305,18 @@
         return null;
     }
 
+    /**
+     * Get an optional description for the action button if provided by this LiveWallpaper.
+     */
+    @Nullable
+    public CharSequence getActionDescription(Context context) {
+        try {
+            return mInfo.loadContextDescription(context.getPackageManager());
+        } catch (Resources.NotFoundException e) {
+            return null;
+        }
+    }
+
     @Override
     public Asset getAsset(Context context) {
         return null;
@@ -310,9 +333,14 @@
     @Override
     public void showPreview(Activity srcActivity, InlinePreviewIntentFactory factory,
                             int requestCode) {
-        Intent preview = new Intent(WallpaperManager.ACTION_CHANGE_LIVE_WALLPAPER);
-        preview.putExtra(WallpaperManager.EXTRA_LIVE_WALLPAPER_COMPONENT, mInfo.getComponent());
-        ActivityUtils.startActivityForResultSafely(srcActivity, preview, requestCode);
+        //Only use internal live picker if available, otherwise, default to the Framework one
+        if (factory.shouldUseInternalLivePicker(srcActivity)) {
+            srcActivity.startActivityForResult(factory.newIntent(srcActivity, this), requestCode);
+        } else {
+            Intent preview = new Intent(WallpaperManager.ACTION_CHANGE_LIVE_WALLPAPER);
+            preview.putExtra(WallpaperManager.EXTRA_LIVE_WALLPAPER_COMPONENT, mInfo.getComponent());
+            ActivityUtils.startActivityForResultSafely(srcActivity, preview, requestCode);
+        }
     }
 
     @Override
diff --git a/src/com/android/wallpaper/model/WallpaperRotationInitializer.java b/src/com/android/wallpaper/model/WallpaperRotationInitializer.java
index ad9eef9..5797650 100755
--- a/src/com/android/wallpaper/model/WallpaperRotationInitializer.java
+++ b/src/com/android/wallpaper/model/WallpaperRotationInitializer.java
@@ -58,13 +58,6 @@
                                      Listener listener);
 
     /**
-     * Returns whether the live wallpaper needs to be set to the device in order to be able to start
-     * rotation or is already set but on home-only on N-MR2 or later, which means the user has the
-     * option to pick a new destination preference.
-     */
-    boolean isNoBackupImageWallpaperPreviewNeeded(Context appContext);
-
-    /**
      * Gets the current state of the possible wallpaper rotation represented by this object.
      */
     void fetchRotationInitializationState(Context context, RotationStateListener listener);
diff --git a/src/com/android/wallpaper/module/BaseWallpaperInjector.java b/src/com/android/wallpaper/module/BaseWallpaperInjector.java
index f8191fc..de9ddb1 100755
--- a/src/com/android/wallpaper/module/BaseWallpaperInjector.java
+++ b/src/com/android/wallpaper/module/BaseWallpaperInjector.java
@@ -34,12 +34,10 @@
     private Requester mRequester;
     private WallpaperManagerCompat mWallpaperManagerCompat;
     private CurrentWallpaperInfoFactory mCurrentWallpaperFactory;
-    private LiveWallpaperStatusChecker mLiveWallpaperStatusChecker;
     private NetworkStatusNotifier mNetworkStatusNotifier;
     private AlarmManagerWrapper mAlarmManagerWrapper;
     private ExploreIntentChecker mExploreIntentChecker;
     private SystemFeatureChecker mSystemFeatureChecker;
-    private RotatingWallpaperComponentChecker mRotatingWallpaperComponentChecker;
     private FormFactorChecker mFormFactorChecker;
     private PackageStatusNotifier mPackageStatusNotifier;
     private LiveWallpaperInfoFactory mLiveWallpaperInfoFactory;
@@ -111,15 +109,6 @@
     }
 
     @Override
-    public synchronized LiveWallpaperStatusChecker getLiveWallpaperStatusChecker(Context context) {
-        if (mLiveWallpaperStatusChecker == null) {
-            mLiveWallpaperStatusChecker =
-                    new DefaultLiveWallpaperStatusChecker(context.getApplicationContext());
-        }
-        return mLiveWallpaperStatusChecker;
-    }
-
-    @Override
     public synchronized NetworkStatusNotifier getNetworkStatusNotifier(Context context) {
         if (mNetworkStatusNotifier == null) {
             mNetworkStatusNotifier = new DefaultNetworkStatusNotifier(context.getApplicationContext());
@@ -161,14 +150,6 @@
     }
 
     @Override
-    public synchronized RotatingWallpaperComponentChecker getRotatingWallpaperComponentChecker() {
-        if (mRotatingWallpaperComponentChecker == null) {
-            mRotatingWallpaperComponentChecker = new DefaultRotatingWallpaperComponentChecker();
-        }
-        return mRotatingWallpaperComponentChecker;
-    }
-
-    @Override
     public synchronized FormFactorChecker getFormFactorChecker(Context context) {
         if (mFormFactorChecker == null) {
             mFormFactorChecker = new DefaultFormFactorChecker(context.getApplicationContext());
diff --git a/src/com/android/wallpaper/module/DefaultCurrentWallpaperInfoFactory.java b/src/com/android/wallpaper/module/DefaultCurrentWallpaperInfoFactory.java
index c74f9b2..dbe3722 100755
--- a/src/com/android/wallpaper/module/DefaultCurrentWallpaperInfoFactory.java
+++ b/src/com/android/wallpaper/module/DefaultCurrentWallpaperInfoFactory.java
@@ -17,16 +17,13 @@
 
 import android.content.Context;
 
-import com.android.wallpaper.compat.BuildCompat;
+import androidx.annotation.Nullable;
+
 import com.android.wallpaper.compat.WallpaperManagerCompat;
-import com.android.wallpaper.model.CurrentWallpaperInfoV16;
 import com.android.wallpaper.model.CurrentWallpaperInfoVN;
-import com.android.wallpaper.model.LiveWallpaperInfo;
 import com.android.wallpaper.model.WallpaperInfo;
 import com.android.wallpaper.module.WallpaperPreferences.PresentationMode;
 
-import androidx.annotation.Nullable;
-
 /**
  * Default implementation of {@link CurrentWallpaperInfoFactory} which actually constructs
  * {@link WallpaperInfo} instances representing the wallpapers currently set to the device.
@@ -35,7 +32,6 @@
 
     private final Context mAppContext;
     private final WallpaperRefresher mWallpaperRefresher;
-    private final LiveWallpaperStatusChecker mLiveWallpaperStatusChecker;
     private final LiveWallpaperInfoFactory mLiveWallpaperInfoFactory;
 
     // Cached copies of the currently-set WallpaperInfo(s) and presentation mode.
@@ -49,8 +45,6 @@
         mAppContext = context.getApplicationContext();
         Injector injector = InjectorProvider.getInjector();
         mWallpaperRefresher = injector.getWallpaperRefresher(mAppContext);
-        mLiveWallpaperStatusChecker =
-                injector.getLiveWallpaperStatusChecker(mAppContext);
         mLiveWallpaperInfoFactory = injector.getLiveWallpaperInfoFactory(mAppContext);
     }
 
@@ -72,29 +66,16 @@
 
         mWallpaperRefresher.refresh(
                 (homeWallpaperMetadata, lockWallpaperMetadata, presentationMode) -> {
-
                     WallpaperInfo homeWallpaper;
-
-                    if (homeWallpaperMetadata.getWallpaperComponent() == null
-                            || mLiveWallpaperStatusChecker.isNoBackupImageWallpaperSet()) {
-                        // Image wallpaper
-                        if (BuildCompat.isAtLeastN()) {
-                            homeWallpaper = new CurrentWallpaperInfoVN(
-                                    homeWallpaperMetadata.getAttributions(),
-                                    homeWallpaperMetadata.getActionUrl(),
-                                    homeWallpaperMetadata.getActionLabelRes(),
-                                    homeWallpaperMetadata.getActionIconRes(),
-                                    homeWallpaperMetadata.getCollectionId(),
-                                    WallpaperManagerCompat.FLAG_SYSTEM);
-                        } else {
-                            homeWallpaper = new CurrentWallpaperInfoV16(
-                                    homeWallpaperMetadata.getAttributions(),
-                                    homeWallpaperMetadata.getActionUrl(),
-                                    homeWallpaperMetadata.getActionLabelRes(),
-                                    homeWallpaperMetadata.getActionIconRes(),
-                                    homeWallpaperMetadata.getCollectionId());
-                        }
-                    } else { // Live wallpaper
+                    if (homeWallpaperMetadata.getWallpaperComponent() == null) {
+                        homeWallpaper = new CurrentWallpaperInfoVN(
+                                homeWallpaperMetadata.getAttributions(),
+                                homeWallpaperMetadata.getActionUrl(),
+                                homeWallpaperMetadata.getActionLabelRes(),
+                                homeWallpaperMetadata.getActionIconRes(),
+                                homeWallpaperMetadata.getCollectionId(),
+                                WallpaperManagerCompat.FLAG_SYSTEM);
+                    } else {
                         homeWallpaper = mLiveWallpaperInfoFactory.getLiveWallpaperInfo(
                                 homeWallpaperMetadata.getWallpaperComponent());
                     }
diff --git a/src/com/android/wallpaper/module/DefaultLiveWallpaperStatusChecker.java b/src/com/android/wallpaper/module/DefaultLiveWallpaperStatusChecker.java
deleted file mode 100755
index 003a06b..0000000
--- a/src/com/android/wallpaper/module/DefaultLiveWallpaperStatusChecker.java
+++ /dev/null
@@ -1,42 +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.annotation.SuppressLint;
-import android.app.WallpaperManager;
-import android.content.Context;
-
-/**
- * Default implementation of {@link LiveWallpaperStatusChecker}.
- */
-@SuppressLint("ServiceCast")
-public class DefaultLiveWallpaperStatusChecker implements LiveWallpaperStatusChecker {
-
-    private WallpaperManager mWallpaperManager;
-
-    public DefaultLiveWallpaperStatusChecker(Context context) {
-        // Retrieve WallpaperManager using Context#getSystemService instead of
-        // WallpaperManager#getInstance so it can be mocked out in test.
-        mWallpaperManager = (WallpaperManager) context.getSystemService(Context.WALLPAPER_SERVICE);
-    }
-
-    @Override
-    public boolean isNoBackupImageWallpaperSet() {
-        android.app.WallpaperInfo liveWallpaper = mWallpaperManager.getWallpaperInfo();
-        return liveWallpaper != null
-                && liveWallpaper.getServiceName().equals(NoBackupImageWallpaper.class.getName());
-    }
-}
diff --git a/src/com/android/wallpaper/module/DefaultRotatingWallpaperComponentChecker.java b/src/com/android/wallpaper/module/DefaultRotatingWallpaperComponentChecker.java
deleted file mode 100755
index eddb6bf..0000000
--- a/src/com/android/wallpaper/module/DefaultRotatingWallpaperComponentChecker.java
+++ /dev/null
@@ -1,77 +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.pm.PackageManager;
-
-/**
- * Default implementation of {@link RotatingWallpaperComponentChecker}.
- */
-public class DefaultRotatingWallpaperComponentChecker implements RotatingWallpaperComponentChecker {
-
-    private static boolean isLiveWallpaperSupported(Context context) {
-        return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LIVE_WALLPAPER);
-    }
-
-    @Override
-    @RotatingWallpaperComponent
-    public int getCurrentRotatingWallpaperComponent(Context context) {
-        if (!isLiveWallpaperSupported(context)) {
-            return ROTATING_WALLPAPER_COMPONENT_STATIC;
-        }
-
-        // If presentation mode is ROTATING but the live wallpaper is not set, then "legacy" rotation
-        // from older APKs is in effect and the current rotating wallpaper component is a static WP.
-        Injector injector = InjectorProvider.getInjector();
-        WallpaperPreferences preferences = injector.getPreferences(context);
-        LiveWallpaperStatusChecker liveWallpaperStatusChecker = injector
-                .getLiveWallpaperStatusChecker(context);
-        if (preferences.getWallpaperPresentationMode()
-                == WallpaperPreferences.PRESENTATION_MODE_ROTATING
-                && !liveWallpaperStatusChecker.isNoBackupImageWallpaperSet()) {
-            return ROTATING_WALLPAPER_COMPONENT_STATIC;
-        }
-
-        return ROTATING_WALLPAPER_COMPONENT_LIVE;
-    }
-
-    @Override
-    @RotatingWallpaperComponent
-    public int getNextRotatingWallpaperComponent(Context context) {
-        if (!isLiveWallpaperSupported(context)) {
-            return ROTATING_WALLPAPER_COMPONENT_STATIC;
-        }
-
-        return ROTATING_WALLPAPER_COMPONENT_LIVE;
-    }
-
-    @Override
-    @RotatingWallpaperSupport
-    public int getRotatingWallpaperSupport(Context context) {
-        FormFactorChecker formFactorChecker =
-                InjectorProvider.getInjector().getFormFactorChecker(context);
-
-        if (formFactorChecker.getFormFactor() == FormFactorChecker.FORM_FACTOR_DESKTOP) {
-            return ROTATING_WALLPAPER_SUPPORT_SUPPORTED;
-        }
-
-        // While static daily rotation is supported on desktops, it isn't (yet?) supported on phones.
-        // For phones which don't support live wallpapers thus we disallow daily rotation altogether.
-        return isLiveWallpaperSupported(context) ? ROTATING_WALLPAPER_SUPPORT_SUPPORTED
-                : ROTATING_WALLPAPER_SUPPORT_NOT_SUPPORTED;
-    }
-}
diff --git a/src/com/android/wallpaper/module/DefaultWallpaperPersister.java b/src/com/android/wallpaper/module/DefaultWallpaperPersister.java
index e2c968c..27e4491 100755
--- a/src/com/android/wallpaper/module/DefaultWallpaperPersister.java
+++ b/src/com/android/wallpaper/module/DefaultWallpaperPersister.java
@@ -19,7 +19,6 @@
 import android.app.Activity;
 import android.app.WallpaperManager;
 import android.content.Context;
-import android.content.Intent;
 import android.graphics.Bitmap;
 import android.graphics.Bitmap.CompressFormat;
 import android.graphics.BitmapFactory;
@@ -32,6 +31,8 @@
 import android.view.Display;
 import android.view.WindowManager;
 
+import androidx.annotation.Nullable;
+
 import com.android.wallpaper.asset.Asset;
 import com.android.wallpaper.asset.Asset.BitmapReceiver;
 import com.android.wallpaper.asset.Asset.DimensionsReceiver;
@@ -42,24 +43,16 @@
 import com.android.wallpaper.compat.WallpaperManagerCompat;
 import com.android.wallpaper.model.WallpaperInfo;
 import com.android.wallpaper.module.BitmapCropper.Callback;
-import com.android.wallpaper.module.RotatingWallpaperComponentChecker.RotatingWallpaperComponent;
 import com.android.wallpaper.util.BitmapTransformer;
-import com.android.wallpaper.util.DiskBasedLogger;
-import com.android.wallpaper.util.FileMover;
 import com.android.wallpaper.util.ScreenSizeCalculator;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
-import java.io.File;
 import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.List;
 
-import androidx.annotation.Nullable;
-
 /**
  * Concrete implementation of WallpaperPersister which actually sets wallpapers to the system via
  * the WallpaperManager.
@@ -71,11 +64,9 @@
 
     private final Context mAppContext; // The application's context.
     // Context that accesses files in device protected storage
-    private final Context mDeviceProtectedContext;
     private final WallpaperManager mWallpaperManager;
     private final WallpaperManagerCompat mWallpaperManagerCompat;
     private final WallpaperPreferences mWallpaperPreferences;
-    private final RotatingWallpaperComponentChecker mRotatingWallpaperComponentChecker;
     private final WallpaperChangedNotifier mWallpaperChangedNotifier;
 
     private WallpaperInfo mWallpaperInfoInPreview;
@@ -83,21 +74,19 @@
     @SuppressLint("ServiceCast")
     public DefaultWallpaperPersister(Context context) {
         mAppContext = context.getApplicationContext();
-        mDeviceProtectedContext = mAppContext.createDeviceProtectedStorageContext();
         // Retrieve WallpaperManager using Context#getSystemService instead of
         // WallpaperManager#getInstance so it can be mocked out in test.
         Injector injector = InjectorProvider.getInjector();
         mWallpaperManager = (WallpaperManager) context.getSystemService(Context.WALLPAPER_SERVICE);
         mWallpaperManagerCompat = injector.getWallpaperManagerCompat(context);
         mWallpaperPreferences = injector.getPreferences(context);
-        mRotatingWallpaperComponentChecker = injector.getRotatingWallpaperComponentChecker();
         mWallpaperChangedNotifier = WallpaperChangedNotifier.getInstance();
     }
 
     @Override
     public void setIndividualWallpaper(final WallpaperInfo wallpaper, Asset asset,
-                                       @Nullable Rect cropRect, float scale, @Destination final int destination,
-                                       final SetWallpaperCallback callback) {
+            @Nullable Rect cropRect, float scale, @Destination final int destination,
+            final SetWallpaperCallback callback) {
         // Set wallpaper without downscaling directly from an input stream if there's no crop rect
         // specified by the caller and the asset is streamable.
         if (cropRect == null && asset instanceof StreamableAsset) {
@@ -149,7 +138,7 @@
 
     @Override
     public void setIndividualWallpaperWithPosition(Activity activity, WallpaperInfo wallpaper,
-                                                   @WallpaperPosition int wallpaperPosition, SetWallpaperCallback callback) {
+            @WallpaperPosition int wallpaperPosition, SetWallpaperCallback callback) {
         Display display = ((WindowManager) mAppContext.getSystemService(Context.WINDOW_SERVICE))
                 .getDefaultDisplay();
         Point screenSize = ScreenSizeCalculator.getInstance().getScreenSize(display);
@@ -164,9 +153,10 @@
                 }
 
                 switch (wallpaperPosition) {
-                    // Crop out screen-sized center portion of the source image if it's larger than the screen
-                    // in both dimensions. Otherwise, decode the entire bitmap and fill the space around it to
-                    // fill a new screen-sized bitmap with plain black pixels.
+                    // Crop out screen-sized center portion of the source image if it's larger
+                    // than the screen
+                    // in both dimensions. Otherwise, decode the entire bitmap and fill the space
+                    // around it to fill a new screen-sized bitmap with plain black pixels.
                     case WALLPAPER_POSITION_CENTER:
                         setIndividualWallpaperWithCenterPosition(
                                 wallpaper, asset, dimensions, screenSize, callback);
@@ -178,19 +168,22 @@
                                 wallpaper, asset, dimensions, screenSize, callback);
                         break;
 
-                    // Decode full bitmap sized for screen and stretch it to fill the screen dimensions.
+                    // Decode full bitmap sized for screen and stretch it to fill the screen
+                    // dimensions.
                     case WALLPAPER_POSITION_STRETCH:
                         asset.decodeBitmap(screenSize.x, screenSize.y, new BitmapReceiver() {
                             @Override
                             public void onBitmapDecoded(@Nullable Bitmap bitmap) {
-                                setIndividualWallpaperStretch(wallpaper, bitmap, screenSize /* stretchSize */,
+                                setIndividualWallpaperStretch(wallpaper, bitmap,
+                                        screenSize /* stretchSize */,
                                         WallpaperPersister.DEST_BOTH, callback);
                             }
                         });
                         break;
 
                     default:
-                        Log.e(TAG, "Unsupported wallpaper position option specified: " + wallpaperPosition);
+                        Log.e(TAG, "Unsupported wallpaper position option specified: "
+                                + wallpaperPosition);
                         callback.onError(null);
                 }
             }
@@ -208,7 +201,7 @@
      * @param callback   Callback used to notify original caller of wallpaper set operation result.
      */
     private void setIndividualWallpaperWithCenterPosition(WallpaperInfo wallpaper, Asset asset,
-                                                          Point dimensions, Point screenSize, SetWallpaperCallback callback) {
+            Point dimensions, Point screenSize, SetWallpaperCallback callback) {
         if (dimensions.x >= screenSize.x && dimensions.y >= screenSize.y) {
             Rect cropRect = new Rect(
                     (dimensions.x - screenSize.x) / 2,
@@ -218,7 +211,8 @@
             asset.decodeBitmapRegion(cropRect, screenSize.x, screenSize.y, new BitmapReceiver() {
                 @Override
                 public void onBitmapDecoded(@Nullable Bitmap bitmap) {
-                    setIndividualWallpaper(wallpaper, bitmap, WallpaperPersister.DEST_BOTH, callback);
+                    setIndividualWallpaper(wallpaper, bitmap, WallpaperPersister.DEST_BOTH,
+                            callback);
                 }
             });
         } else {
@@ -249,7 +243,7 @@
      * @param callback   Callback used to notify original caller of wallpaper set operation result.
      */
     private void setIndividualWallpaperWithCenterCropPosition(WallpaperInfo wallpaper, Asset asset,
-                                                              Point dimensions, Point screenSize, SetWallpaperCallback callback) {
+            Point dimensions, Point screenSize, SetWallpaperCallback callback) {
         float scale = Math.max((float) screenSize.x / dimensions.x,
                 (float) screenSize.y / dimensions.y);
 
@@ -276,7 +270,7 @@
      * @param callback      Called once the wallpaper was set or if an error occurred.
      */
     private void setIndividualWallpaper(WallpaperInfo wallpaper, Bitmap croppedBitmap,
-                                        @Destination int destination, SetWallpaperCallback callback) {
+            @Destination int destination, SetWallpaperCallback callback) {
         SetWallpaperTask setWallpaperTask =
                 new SetWallpaperTask(wallpaper, croppedBitmap, destination, callback);
         setWallpaperTask.execute();
@@ -287,15 +281,16 @@
      *
      * @param wallpaper     Wallpaper model object.
      * @param croppedBitmap Bitmap representing the individual wallpaper image.
-     * @param fillSize      Specifies the final bitmap size that should be set to WallpaperManager. This
-     *                      final bitmap will show the visible area of the provided bitmap after applying a mask with
-     *                      black background the source bitmap and centering. There may be black borders around the
-     *                      original bitmap if it's smaller than the fillSize in one or both dimensions.
+     * @param fillSize      Specifies the final bitmap size that should be set to WallpaperManager.
+     *                      This final bitmap will show the visible area of the provided bitmap
+     *                      after applying a mask with black background the source bitmap and
+     *                      centering. There may be black borders around the original bitmap if
+     *                      it's smaller than the fillSize in one or both dimensions.
      * @param destination   The destination - where to set the wallpaper to.
      * @param callback      Called once the wallpaper was set or if an error occurred.
      */
     private void setIndividualWallpaperFill(WallpaperInfo wallpaper, Bitmap croppedBitmap,
-                                            Point fillSize, @Destination int destination, SetWallpaperCallback callback) {
+            Point fillSize, @Destination int destination, SetWallpaperCallback callback) {
         SetWallpaperTask setWallpaperTask =
                 new SetWallpaperTask(wallpaper, croppedBitmap, destination, callback);
         setWallpaperTask.setFillSize(fillSize);
@@ -308,13 +303,14 @@
      *
      * @param wallpaper     Wallpaper model object.
      * @param croppedBitmap Bitmap representing the individual wallpaper image.
-     * @param stretchSize   Specifies the final size to which the the bitmap should be stretched prior
+     * @param stretchSize   Specifies the final size to which the bitmap should be stretched
+     *                      prior
      *                      to being set to the device.
      * @param destination   The destination - where to set the wallpaper to.
      * @param callback      Called once the wallpaper was set or if an error occurred.
      */
     private void setIndividualWallpaperStretch(WallpaperInfo wallpaper, Bitmap croppedBitmap,
-                                               Point stretchSize, @Destination int destination, SetWallpaperCallback callback) {
+            Point stretchSize, @Destination int destination, SetWallpaperCallback callback) {
         SetWallpaperTask setWallpaperTask =
                 new SetWallpaperTask(wallpaper, croppedBitmap, destination, callback);
         setWallpaperTask.setStretchSize(stretchSize);
@@ -330,7 +326,7 @@
      * @param callback    Called once the wallpaper was set or if an error occurred.
      */
     private void setIndividualWallpaper(WallpaperInfo wallpaper, InputStream inputStream,
-                                        @Destination int destination, SetWallpaperCallback callback) {
+            @Destination int destination, SetWallpaperCallback callback) {
         SetWallpaperTask setWallpaperTask =
                 new SetWallpaperTask(wallpaper, inputStream, destination, callback);
         setWallpaperTask.execute();
@@ -338,58 +334,31 @@
 
     @Override
     public boolean setWallpaperInRotation(Bitmap wallpaperBitmap, List<String> attributions,
-                                          int actionLabelRes, int actionIconRes,
-                                          String actionUrl, String collectionId) {
-        @RotatingWallpaperComponent int rotatingWallpaperComponent = mRotatingWallpaperComponentChecker
-                .getCurrentRotatingWallpaperComponent(mAppContext);
+            int actionLabelRes, int actionIconRes, String actionUrl, String collectionId) {
 
-        switch (rotatingWallpaperComponent) {
-            case RotatingWallpaperComponentChecker.ROTATING_WALLPAPER_COMPONENT_STATIC:
-                return setWallpaperInRotationStatic(wallpaperBitmap, attributions, actionUrl,
-                        actionLabelRes, actionIconRes, collectionId);
-            case RotatingWallpaperComponentChecker.ROTATING_WALLPAPER_COMPONENT_LIVE:
-                return setWallpaperInRotationLive(wallpaperBitmap, attributions, actionUrl,
-                        actionLabelRes, actionIconRes, collectionId);
-            default:
-                Log.e(TAG, "Unknown rotating wallpaper component: " + rotatingWallpaperComponent);
-                return false;
-        }
+        return setWallpaperInRotationStatic(wallpaperBitmap, attributions, actionUrl,
+                actionLabelRes, actionIconRes, collectionId);
     }
 
     @Override
     public int setWallpaperBitmapInNextRotation(Bitmap wallpaperBitmap) {
-        @RotatingWallpaperComponent int rotatingWallpaperComponent = mRotatingWallpaperComponentChecker
-                .getNextRotatingWallpaperComponent(mAppContext);
-
-        switch (rotatingWallpaperComponent) {
-            case RotatingWallpaperComponentChecker.ROTATING_WALLPAPER_COMPONENT_STATIC:
-                return setWallpaperBitmapInRotationStatic(wallpaperBitmap);
-            case RotatingWallpaperComponentChecker.ROTATING_WALLPAPER_COMPONENT_LIVE:
-                boolean isSuccess = setWallpaperBitmapInRotationLive(wallpaperBitmap, true /* isPreview */);
-                return isSuccess ? 1 : 0;
-            default:
-                Log.e(TAG, "Unknown rotating wallpaper component: " + rotatingWallpaperComponent);
-                return 0;
-        }
+        return setWallpaperBitmapInRotationStatic(wallpaperBitmap);
     }
 
     @Override
     public boolean finalizeWallpaperForNextRotation(List<String> attributions, String actionUrl,
-                                                    int actionLabelRes, int actionIconRes,
-                                                    String collectionId, int wallpaperId) {
-        @RotatingWallpaperComponent int rotatingWallpaperComponent =
-                mRotatingWallpaperComponentChecker.getNextRotatingWallpaperComponent(mAppContext);
+            int actionLabelRes, int actionIconRes, String collectionId, int wallpaperId) {
         return finalizeWallpaperForRotatingComponent(attributions, actionUrl, actionLabelRes,
-                actionIconRes, collectionId, wallpaperId, rotatingWallpaperComponent);
+                actionIconRes, collectionId, wallpaperId);
     }
 
     /**
-     * Sets wallpaper image and attributions when a static wallpaper is responsible for presenting the
+     * Sets wallpaper image and attributions when a static wallpaper is responsible for presenting
+     * the
      * current "daily wallpaper".
      */
     private boolean setWallpaperInRotationStatic(Bitmap wallpaperBitmap, List<String> attributions,
-                                                 String actionUrl, int actionLabelRes,
-                                                 int actionIconRes, String collectionId) {
+            String actionUrl, int actionLabelRes, int actionIconRes, String collectionId) {
         final int wallpaperId = setWallpaperBitmapInRotationStatic(wallpaperBitmap);
 
         if (wallpaperId == 0) {
@@ -397,8 +366,7 @@
         }
 
         return finalizeWallpaperForRotatingComponent(attributions, actionUrl, actionLabelRes,
-                actionIconRes, collectionId, wallpaperId,
-                RotatingWallpaperComponentChecker.ROTATING_WALLPAPER_COMPONENT_STATIC);
+                actionIconRes, collectionId, wallpaperId);
     }
 
     /**
@@ -413,83 +381,21 @@
             int actionLabelRes,
             int actionIconRes,
             String collectionId,
-            int wallpaperId,
-            @RotatingWallpaperComponent int rotatingWallpaperComponent) {
+            int wallpaperId) {
         mWallpaperPreferences.clearHomeWallpaperMetadata();
 
         boolean isLockWallpaperSet = isSeparateLockScreenWallpaperSet();
 
-        // Persist wallpaper IDs if the rotating wallpaper component is static and this device is
-        // running Android N or later.
-        if (rotatingWallpaperComponent
-                == RotatingWallpaperComponentChecker.ROTATING_WALLPAPER_COMPONENT_STATIC) {
-            if (BuildCompat.isAtLeastN()) {
-                mWallpaperPreferences.setHomeWallpaperManagerId(wallpaperId);
+        // Persist wallpaper IDs if the rotating wallpaper component
+        mWallpaperPreferences.setHomeWallpaperManagerId(wallpaperId);
 
-                // Only copy over wallpaper ID to lock wallpaper if no explicit lock wallpaper is set (so
-                // metadata isn't lost if a user explicitly sets a home-only wallpaper).
-                if (!isLockWallpaperSet) {
-                    mWallpaperPreferences.setLockWallpaperId(wallpaperId);
-                }
-            } else { // Pre-N but using static component
-                // Compute bitmap hash code after setting the wallpaper because JPEG compression has likely
-                // changed many pixels' color values. Forget the previously loaded wallpaper bitmap so that
-                // WallpaperManager doesn't return the old wallpaper drawable.
-                mWallpaperManager.forgetLoadedWallpaper();
-                Bitmap bitmap = ((BitmapDrawable) mWallpaperManagerCompat.getDrawable()).getBitmap();
-                long bitmapHash = BitmapUtils.generateHashCode(bitmap);
-
-                mWallpaperPreferences.setHomeWallpaperHashCode(bitmapHash);
-            }
-        } else { // Live wallpaper rotating component.
-
-            // Copy "preview" JPEG to "rotating" JPEG if the preview file exists.
-           File rotatingWallpaper;
-            try {
-                rotatingWallpaper = moveToDeviceProtectedStorage(
-                        NoBackupImageWallpaper.PREVIEW_WALLPAPER_FILE_PATH,
-                        NoBackupImageWallpaper.ROTATING_WALLPAPER_FILE_PATH);
-            } catch (Exception e) {
-                DiskBasedLogger.e(
-                        TAG,
-                        "Unable to move preview to final file for rotating wallpaper " +
-                                "file (exception)" + e.toString(),
-                        mAppContext);
-                return false;
-            }
-            if (rotatingWallpaper == null) {
-                rotatingWallpaper = mDeviceProtectedContext.getFileStreamPath(
-                        NoBackupImageWallpaper.ROTATING_WALLPAPER_FILE_PATH);
-            }
-            try {
-                FileInputStream fis = new FileInputStream(rotatingWallpaper.getAbsolutePath());
-                Bitmap bitmap = BitmapFactory.decodeStream(fis);
-                fis.close();
-
-                if (bitmap != null) {
-                    long bitmapHash = BitmapUtils.generateHashCode(bitmap);
-                    mWallpaperPreferences.setHomeWallpaperHashCode(bitmapHash);
-                } else {
-                    Log.e(TAG, "Unable to decode rotating wallpaper file");
-                    return false;
-                }
-            } catch (FileNotFoundException e) {
-                Log.e(TAG, "Rotating wallpaper file not found at path: "
-                        + rotatingWallpaper.getAbsolutePath());
-                e.printStackTrace();
-                return false;
-            } catch (IOException e) {
-                Log.e(TAG, "IOException when closing FileInputStream " + e);
-                return false;
-            }
-
-            mWallpaperChangedNotifier.notifyWallpaperChanged();
-
-            // Send a broadcast to {@link RotatingWallpaperChangedReceiver} in the :live_wallpaper
-            // process so the currently displayed wallpaper updates.
-            notifyLiveWallpaperBitmapChanged();
+        // Only copy over wallpaper ID to lock wallpaper if no explicit lock wallpaper is set
+        // (so metadata isn't lost if a user explicitly sets a home-only wallpaper).
+        if (!isLockWallpaperSet) {
+            mWallpaperPreferences.setLockWallpaperId(wallpaperId);
         }
 
+
         mWallpaperPreferences.setHomeWallpaperAttributions(attributions);
         mWallpaperPreferences.setHomeWallpaperActionUrl(actionUrl);
         mWallpaperPreferences.setHomeWallpaperActionLabelRes(actionLabelRes);
@@ -498,11 +404,9 @@
         mWallpaperPreferences.setHomeWallpaperBaseImageUrl(null);
         mWallpaperPreferences.setHomeWallpaperCollectionId(collectionId);
 
-        // Set metadata to lock screen also when the rotating wallpaper is a static one so if user sets
-        // a home screen-only wallpaper later, these attributions will still be available.
-        if (rotatingWallpaperComponent
-                == RotatingWallpaperComponentChecker.ROTATING_WALLPAPER_COMPONENT_STATIC
-                && !isLockWallpaperSet) {
+        // Set metadata to lock screen also when the rotating wallpaper so if user sets a home
+        // screen-only wallpaper later, these attributions will still be available.
+        if (!isLockWallpaperSet) {
             mWallpaperPreferences.setLockWallpaperAttributions(attributions);
             mWallpaperPreferences.setLockWallpaperActionUrl(actionUrl);
             mWallpaperPreferences.setLockWallpaperActionLabelRes(actionLabelRes);
@@ -514,26 +418,6 @@
     }
 
     /**
-     * Sets wallpaper image and attributions when a live wallpaper is responsible for presenting the
-     * current "daily wallpaper".
-     */
-    private boolean setWallpaperInRotationLive(Bitmap wallpaperBitmap, List<String> attributions,
-                                               String actionUrl, int actionLabelRes,
-                                               int actionIconRes, String collectionId) {
-
-        synchronized (RotatingWallpaperLockProvider.getInstance()) {
-            if (!setWallpaperBitmapInRotationLive(wallpaperBitmap, false /* isPreview */)) {
-                return false;
-            }
-
-            return finalizeWallpaperForRotatingComponent(attributions, actionUrl, actionLabelRes,
-                    actionIconRes, collectionId,
-                    0 /* wallpaperId */,
-                    RotatingWallpaperComponentChecker.ROTATING_WALLPAPER_COMPONENT_LIVE);
-        }
-    }
-
-    /**
      * Sets a wallpaper in rotation as a static wallpaper to the {@link WallpaperManager} with the
      * option allowBackup=false to save user data.
      *
@@ -553,84 +437,6 @@
     }
 
     /**
-     * Sets a wallpaper in rotation as a live wallpaper. Writes wallpaper bitmap to a file in internal
-     * storage and sends a broadcast to the live wallpaper notifying it that rotating wallpaper image
-     * data changed.
-     *
-     * @return whether the set wallpaper operation was successful.
-     */
-    private boolean setWallpaperBitmapInRotationLive(Bitmap wallpaperBitmap, boolean isPreview) {
-        File pendingFile;
-        try {
-            pendingFile = File.createTempFile("rotating_pending", ".jpg", mAppContext.getFilesDir());
-        } catch (IOException e) {
-            Log.e(TAG, "Unable to create temp file for rotating wallpaper");
-            return false;
-        }
-
-        FileOutputStream fos;
-        try {
-            fos = mAppContext.openFileOutput(pendingFile.getName(), Context.MODE_PRIVATE);
-        } catch (FileNotFoundException e) {
-            Log.e(TAG, "Unable to open file output stream for pending rotating wallpaper file");
-            return false;
-        }
-
-        boolean compressedSuccessfully =
-                wallpaperBitmap.compress(CompressFormat.JPEG, DEFAULT_COMPRESS_QUALITY, fos);
-
-        // Close the file stream.
-        try {
-            fos.flush();
-            fos.close();
-        } catch (IOException e) {
-            Log.e(TAG, "Unable to close FileOutputStream for pending rotating wallpaper file"
-                    + " (compress succeeded");
-            return false;
-        }
-
-        if (compressedSuccessfully) {
-            // Compressing/writing to disk succeeded, so move the pending file to the final location.
-            try {
-                if (isPreview) {
-                    if (!pendingFile.renameTo(mAppContext.getFileStreamPath(
-                            NoBackupImageWallpaper.PREVIEW_WALLPAPER_FILE_PATH))) {
-                        return false;
-                    }
-                } else {
-                    moveToDeviceProtectedStorage(pendingFile.getName(),
-                            NoBackupImageWallpaper.ROTATING_WALLPAPER_FILE_PATH);
-                }
-            } catch (Exception e) {
-                Log.e(TAG, "Unable to rename pending to final file for rotating wallpaper file"
-                        + " (exception)" + e.toString());
-                return false;
-            }
-        } else {
-            Log.e(TAG, "Unable to compress the wallpaper bitmap");
-
-            // Delete the pending file since compressing/writing the image to disk failed.
-            try {
-                pendingFile.delete();
-            } catch (SecurityException e) {
-                Log.e(TAG, "Unable to delete pending rotating wallpaper file");
-                return false;
-            }
-
-            return false;
-        }
-
-        mWallpaperChangedNotifier.notifyWallpaperChanged();
-
-        // Send a broadcast to {@link RotatingWallpaperChangedReceiver} in the :live_wallpaper
-        // process so the currently displayed wallpaper updates if the live wallpaper is set to the
-        // device.
-        notifyLiveWallpaperBitmapChanged();
-
-        return true;
-    }
-
-    /**
      * Sets a wallpaper bitmap to the {@link WallpaperManagerCompat}.
      *
      * @return an integer wallpaper ID. This is an actual wallpaper ID on N and later versions of
@@ -638,7 +444,7 @@
      * operation was successful and zero if the operation encountered an error.
      */
     private int setBitmapToWallpaperManagerCompat(Bitmap wallpaperBitmap, boolean allowBackup,
-                                                  int whichWallpaper) {
+            int whichWallpaper) {
         ByteArrayOutputStream tmpOut = new ByteArrayOutputStream();
         if (wallpaperBitmap.compress(CompressFormat.JPEG, DEFAULT_COMPRESS_QUALITY, tmpOut)) {
             try {
@@ -668,9 +474,10 @@
     }
 
     private int setStreamToWallpaperManagerCompat(InputStream inputStream, boolean allowBackup,
-                                                  int whichWallpaper) {
+            int whichWallpaper) {
         try {
-            return mWallpaperManagerCompat.setStream(inputStream, null, allowBackup, whichWallpaper);
+            return mWallpaperManagerCompat.setStream(inputStream, null, allowBackup,
+                    whichWallpaper);
         } catch (IOException e) {
             return 0;
         }
@@ -688,7 +495,8 @@
                 mWallpaperInfoInPreview.getWallpaperComponent();
 
         // If there is no live wallpaper set on the WallpaperManager or it doesn't match the
-        // WallpaperInfo which was last previewed, then do nothing and nullify last previewed wallpaper.
+        // WallpaperInfo which was last previewed, then do nothing and nullify last previewed
+        // wallpaper.
         if (currentWallpaperComponent == null || previewedWallpaperComponent == null
                 || !currentWallpaperComponent.getPackageName()
                 .equals(previewedWallpaperComponent.getPackageName())) {
@@ -729,9 +537,9 @@
                 mWallpaperInfoInPreview.getWallpaperComponent();
 
         mWallpaperPreferences.clearHomeWallpaperMetadata();
-        // NOTE: We explicitly do not also clear the lock wallpaper metadata. Since the user may have
-        // set the live wallpaper on the home screen only, we leave the lock wallpaper metadata intact.
-        // If the user has set the live wallpaper for both home and lock screens, then the
+        // NOTE: We explicitly do not also clear the lock wallpaper metadata. Since the user may
+        // have set the live wallpaper on the home screen only, we leave the lock wallpaper metadata
+        // intact. If the user has set the live wallpaper for both home and lock screens, then the
         // WallpaperRefresher will pick up on that and update the preferences later.
         mWallpaperPreferences
                 .setHomeWallpaperAttributions(mWallpaperInfoInPreview.getAttributions(mAppContext));
@@ -744,36 +552,6 @@
         mWallpaperPreferences.clearDailyRotations();
     }
 
-    /**
-     * Notifies the :live_wallpaper process that the contents of the rotating live wallpaper bitmap
-     * changed.
-     */
-    private void notifyLiveWallpaperBitmapChanged() {
-        Intent intent = new Intent(mAppContext.getPackageName()
-                + NoBackupImageWallpaper.ACTION_ROTATING_WALLPAPER_CHANGED);
-        // Handled by a runtime-registered receiver in NoBackupImageWallpaper.
-        intent.setPackage(mAppContext.getPackageName());
-        mAppContext.sendBroadcast(intent);
-    }
-
-    /**
-     * Moves a file from the app's files directory to the device content protected storage
-     * directory.
-     * @param srcFileName Name of the source file (just the name, no path). It's expected to be
-     *                    located in {@link Context#getFilesDir()} for {@link #mAppContext}
-     * @param dstFileName Name of the destination file (just the name, no path), which will be
-     *                    located in {@link Context#getFilesDir()}
-     *                    for {@link #mDeviceProtectedContext}
-     * @return a {@link File} corresponding to the moved file in its new location, or null if
-     *      nothing was moved (because srcFileName didn't exist).
-     */
-    @Nullable
-    private File moveToDeviceProtectedStorage(String srcFileName, String dstFileName)
-            throws IOException {
-        return FileMover.moveFileBetweenContexts(mAppContext, srcFileName, mDeviceProtectedContext,
-                dstFileName);
-    }
-
     private class SetWallpaperTask extends AsyncTask<Void, Void, Boolean> {
 
         private final WallpaperInfo mWallpaper;
@@ -793,7 +571,7 @@
         private Point mStretchSize;
 
         SetWallpaperTask(WallpaperInfo wallpaper, Bitmap bitmap, @Destination int destination,
-                         WallpaperPersister.SetWallpaperCallback callback) {
+                WallpaperPersister.SetWallpaperCallback callback) {
             super();
             mWallpaper = wallpaper;
             mBitmap = bitmap;
@@ -806,7 +584,7 @@
          * will close the InputStream once it is done with it.
          */
         SetWallpaperTask(WallpaperInfo wallpaper, InputStream stream,
-                         @Destination int destination, WallpaperPersister.SetWallpaperCallback callback) {
+                @Destination int destination, WallpaperPersister.SetWallpaperCallback callback) {
             mWallpaper = wallpaper;
             mInputStream = stream;
             mDestination = destination;
@@ -815,16 +593,18 @@
 
         void setFillSize(Point fillSize) {
             if (mStretchSize != null) {
-                throw new IllegalArgumentException("Can't pass a fill size option if a stretch size is "
-                        + "already set.");
+                throw new IllegalArgumentException(
+                        "Can't pass a fill size option if a stretch size is "
+                                + "already set.");
             }
             mFillSize = fillSize;
         }
 
         void setStretchSize(Point stretchSize) {
             if (mFillSize != null) {
-                throw new IllegalArgumentException("Can't pass a stretch size option if a fill size is "
-                        + "already set.");
+                throw new IllegalArgumentException(
+                        "Can't pass a stretch size option if a fill size is "
+                                + "already set.");
             }
             mStretchSize = stretchSize;
         }
@@ -841,16 +621,9 @@
                         | WallpaperManagerCompat.FLAG_LOCK;
             }
 
-            // NOTE: The rotating wallpaper component must be determined here, _before_ actually setting
-            // the bitmap/stream on WallpaperManagerCompat, to ensure that the
-            // RotatingWallpaperComponentChecker is doing its check while rotation is still enabled.
-            // E.g., if "live wallpaper" is the component, then it needs to check while live wallpaper is
-            // still set as the active wallpaper on the device. Otherwise, the checker would see a static
-            // wallpaper is currently set and it would return the wrong value.
-            @RotatingWallpaperComponent int currentRotatingWallpaperComponent =
-                    mRotatingWallpaperComponentChecker.getCurrentRotatingWallpaperComponent(mAppContext);
 
-            boolean wasLockWallpaperSet = LockWallpaperStatusChecker.isLockWallpaperSet(mAppContext);
+            boolean wasLockWallpaperSet = LockWallpaperStatusChecker.isLockWallpaperSet(
+                    mAppContext);
 
             boolean allowBackup = mWallpaper.getBackupPermission() == WallpaperInfo.BACKUP_ALLOWED;
             final int wallpaperId;
@@ -860,15 +633,20 @@
                     mBitmap = BitmapTransformer.applyFillTransformation(mBitmap, mFillSize);
                 }
                 if (mStretchSize != null) {
-                    mBitmap = Bitmap.createScaledBitmap(mBitmap, mStretchSize.x, mStretchSize.y, true);
+                    mBitmap = Bitmap.createScaledBitmap(mBitmap, mStretchSize.x, mStretchSize.y,
+                            true);
                 }
 
-                wallpaperId = setBitmapToWallpaperManagerCompat(mBitmap, allowBackup, whichWallpaper);
+                wallpaperId = setBitmapToWallpaperManagerCompat(mBitmap, allowBackup,
+                        whichWallpaper);
             } else if (mInputStream != null) {
-                wallpaperId = setStreamToWallpaperManagerCompat(mInputStream, allowBackup, whichWallpaper);
+                wallpaperId = setStreamToWallpaperManagerCompat(mInputStream, allowBackup,
+                        whichWallpaper);
             } else {
-                Log.e(TAG, "Both the wallpaper bitmap and input stream are null so we're unable to set any "
-                        + "kind of wallpaper here.");
+                Log.e(TAG,
+                        "Both the wallpaper bitmap and input stream are null so we're unable to "
+                                + "set any "
+                                + "kind of wallpaper here.");
                 wallpaperId = 0;
             }
 
@@ -878,7 +656,7 @@
                         == WallpaperPreferences.PRESENTATION_MODE_ROTATING
                         && !wasLockWallpaperSet
                         && BuildCompat.isAtLeastN()) {
-                    copyRotatingWallpaperToLock(currentRotatingWallpaperComponent);
+                    copyRotatingWallpaperToLock();
                 }
                 setImageWallpaperMetadata(mDestination, wallpaperId);
                 return true;
@@ -914,12 +692,8 @@
          * Used to accommodate the case where a user had gone from a home+lock daily rotation to
          * selecting a static wallpaper on home-only. The image and metadata that was previously
          * rotating is now copied to the lock screen.
-         *
-         * @param currentRotatingWallpaperComponent The component in which rotating wallpapers were
-         *                                          presented.
          */
-        private void copyRotatingWallpaperToLock(
-                @RotatingWallpaperComponent int currentRotatingWallpaperComponent) {
+        private void copyRotatingWallpaperToLock() {
 
             mWallpaperPreferences.setLockWallpaperAttributions(
                     mWallpaperPreferences.getHomeWallpaperAttributions());
@@ -935,41 +709,28 @@
             // Set the lock wallpaper ID to what Android set it to, following its having
             // copied the system wallpaper over to the lock screen when we changed from
             // "both" to distinct system and lock screen wallpapers.
-            if (currentRotatingWallpaperComponent
-                    == RotatingWallpaperComponentChecker.ROTATING_WALLPAPER_COMPONENT_STATIC) {
-                mWallpaperPreferences.setLockWallpaperId(
-                        mWallpaperManagerCompat.getWallpaperId(WallpaperManagerCompat.FLAG_LOCK));
-            } else {
-                try {
-                    FileInputStream fileInputStream = mDeviceProtectedContext.openFileInput(
-                            NoBackupImageWallpaper.ROTATING_WALLPAPER_FILE_PATH);
-                    int lockWallpaperId = setStreamToWallpaperManagerCompat(
-                            fileInputStream, false /* allowBackup */, WallpaperManagerCompat.FLAG_LOCK);
-                    fileInputStream.close();
-                    mWallpaperPreferences.setLockWallpaperId(lockWallpaperId);
-                } catch (FileNotFoundException e) {
-                    Log.e(TAG, "Couldn't copy over previously rotating wallpaper to lock screen.");
-                } catch (IOException e) {
-                    Log.e(TAG, "IOException when closing the file input stream " + e);
-                }
-            }
+            mWallpaperPreferences.setLockWallpaperId(
+                    mWallpaperManagerCompat.getWallpaperId(WallpaperManagerCompat.FLAG_LOCK));
+
         }
 
         /**
-         * Sets the image wallpaper's metadata on SharedPreferences. This method is called after the set
-         * wallpaper operation is successful.
+         * Sets the image wallpaper's metadata on SharedPreferences. This method is called after the
+         * set wallpaper operation is successful.
          *
-         * @param destination Which destination of wallpaper the metadata corresponds to (home screen,
-         *                    lock screen, or both).
-         * @param wallpaperId The ID of the static wallpaper returned by WallpaperManager, which on N
-         *                    and later versions of Android uniquely identifies a wallpaper image.
+         * @param destination Which destination of wallpaper the metadata corresponds to (home
+         *                    screen, lock screen, or both).
+         * @param wallpaperId The ID of the static wallpaper returned by WallpaperManager, which
+         *                    on N and later versions of Android uniquely identifies a wallpaper
+         *                    image.
          */
         private void setImageWallpaperMetadata(@Destination int destination, int wallpaperId) {
             if (destination == DEST_HOME_SCREEN || destination == DEST_BOTH) {
                 mWallpaperPreferences.clearHomeWallpaperMetadata();
                 setImageWallpaperHomeMetadata(wallpaperId);
 
-                // Reset presentation mode to STATIC if an individual wallpaper is set to the home screen
+                // Reset presentation mode to STATIC if an individual wallpaper is set to the
+                // home screen
                 // because rotation always affects at least the home screen.
                 mWallpaperPreferences.setWallpaperPresentationMode(
                         WallpaperPreferences.PRESENTATION_MODE_STATIC);
@@ -988,10 +749,11 @@
                 mWallpaperPreferences.setHomeWallpaperManagerId(homeWallpaperId);
             }
 
-            // Compute bitmap hash code after setting the wallpaper because JPEG compression has likely
-            // changed many pixels' color values. Forget the previously loaded wallpaper bitmap so that
-            // WallpaperManager doesn't return the old wallpaper drawable. Do this on N+ devices in
-            // addition to saving the wallpaper ID for the purpose of backup & restore.
+            // Compute bitmap hash code after setting the wallpaper because JPEG compression has
+            // likely changed many pixels' color values. Forget the previously loaded wallpaper
+            // bitmap so that WallpaperManager doesn't return the old wallpaper drawable. Do this
+            // on N+ devices in addition to saving the wallpaper ID for the purpose of backup &
+            // restore.
             mWallpaperManager.forgetLoadedWallpaper();
             mBitmap = ((BitmapDrawable) mWallpaperManagerCompat.getDrawable()).getBitmap();
             long bitmapHash = BitmapUtils.generateHashCode(mBitmap);
@@ -1023,9 +785,10 @@
             mWallpaperPreferences.setLockWallpaperCollectionId(
                     mWallpaper.getCollectionId(mAppContext));
 
-            // Save the lock wallpaper image's hash code as well for the sake of backup & restore because
-            // WallpaperManager-generated IDs are specific to a physical device and cannot be used to
-            // identify a wallpaper image on another device after restore is complete.
+            // Save the lock wallpaper image's hash code as well for the sake of backup & restore
+            // because WallpaperManager-generated IDs are specific to a physical device and
+            // cannot be  used to identify a wallpaper image on another device after restore is
+            // complete.
             saveLockWallpaperHashCode();
         }
 
@@ -1051,7 +814,9 @@
                     try {
                         fileStream.close();
                     } catch (IOException e) {
-                        Log.e(TAG, "IO exception when closing the input stream for the lock screen WP.");
+                        Log.e(TAG,
+                                "IO exception when closing the input stream for the lock screen "
+                                        + "WP.");
                     }
                 }
             }
diff --git a/src/com/android/wallpaper/module/DefaultWallpaperPreferences.java b/src/com/android/wallpaper/module/DefaultWallpaperPreferences.java
index 319276b..b040ebb 100755
--- a/src/com/android/wallpaper/module/DefaultWallpaperPreferences.java
+++ b/src/com/android/wallpaper/module/DefaultWallpaperPreferences.java
@@ -24,6 +24,8 @@
 
 import androidx.annotation.Nullable;
 
+import com.android.wallpaper.module.WallpaperPreferenceKeys.NoBackupKeys;
+
 import org.json.JSONArray;
 import org.json.JSONException;
 
@@ -39,10 +41,12 @@
  */
 public class DefaultWallpaperPreferences implements WallpaperPreferences {
     public static final String PREFS_NAME = "wallpaper";
+    public static final String NO_BACKUP_PREFS_NAME = "wallpaper-nobackup";
 
     private static final String TAG = "DefaultWPPreferences";
 
     protected SharedPreferences mSharedPrefs;
+    protected SharedPreferences mNoBackupPrefs;
     protected Context mContext;
 
     // Keep a strong reference to this OnSharedPreferenceChangeListener to prevent the listener from
@@ -51,19 +55,106 @@
 
     public DefaultWallpaperPreferences(Context context) {
         mSharedPrefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
+        mNoBackupPrefs = context.getSharedPreferences(NO_BACKUP_PREFS_NAME, Context.MODE_PRIVATE);
+        if (mNoBackupPrefs.getAll().isEmpty() && !mSharedPrefs.getAll().isEmpty()) {
+            upgradePrefs();
+        }
         mContext = context.getApplicationContext();
 
         // Register a prefs changed listener so that all prefs changes trigger a backup event.
         final BackupManager backupManager = new BackupManager(context);
-        mSharedPrefsChangedListener = new OnSharedPreferenceChangeListener() {
-            @Override
-            public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
-                backupManager.dataChanged();
-            }
-        };
+        mSharedPrefsChangedListener = (sharedPreferences, key) -> backupManager.dataChanged();
         mSharedPrefs.registerOnSharedPreferenceChangeListener(mSharedPrefsChangedListener);
     }
 
+    /**
+     * Move {@link NoBackupKeys} preferences that might have been in mSharedPrefs from previous
+     * versions of the app into mNoBackupPrefs.
+     */
+    private void upgradePrefs() {
+        SharedPreferences.Editor editor = mNoBackupPrefs.edit();
+        if (mSharedPrefs.contains(
+                NoBackupKeys.KEY_HOME_WALLPAPER_BASE_IMAGE_URL)) {
+            editor.putString(NoBackupKeys.KEY_HOME_WALLPAPER_BASE_IMAGE_URL,
+                    mSharedPrefs.getString(NoBackupKeys.KEY_HOME_WALLPAPER_BASE_IMAGE_URL, null));
+        }
+        if (mSharedPrefs.contains(NoBackupKeys.KEY_HOME_WALLPAPER_MANAGER_ID)) {
+            editor.putInt(NoBackupKeys.KEY_HOME_WALLPAPER_MANAGER_ID,
+                    mSharedPrefs.getInt(NoBackupKeys.KEY_HOME_WALLPAPER_MANAGER_ID, 0));
+        }
+        if (mSharedPrefs.contains(NoBackupKeys.KEY_HOME_WALLPAPER_REMOTE_ID)) {
+            editor.putString(NoBackupKeys.KEY_HOME_WALLPAPER_REMOTE_ID,
+                    mSharedPrefs.getString(NoBackupKeys.KEY_HOME_WALLPAPER_REMOTE_ID, null));
+        }
+        if (mSharedPrefs.contains(
+                NoBackupKeys.KEY_HOME_WALLPAPER_BACKING_FILE)) {
+            editor.putString(NoBackupKeys.KEY_HOME_WALLPAPER_BACKING_FILE,
+                    mSharedPrefs.getString(NoBackupKeys.KEY_HOME_WALLPAPER_BACKING_FILE, null));
+        }
+        if (mSharedPrefs.contains(NoBackupKeys.KEY_LOCK_WALLPAPER_MANAGER_ID)) {
+            editor.putInt(NoBackupKeys.KEY_LOCK_WALLPAPER_MANAGER_ID,
+                    mSharedPrefs.getInt(NoBackupKeys.KEY_LOCK_WALLPAPER_MANAGER_ID, 0));
+        }
+        if (mSharedPrefs.contains(
+                NoBackupKeys.KEY_LOCK_WALLPAPER_BACKING_FILE)) {
+            editor.putString(NoBackupKeys.KEY_LOCK_WALLPAPER_BACKING_FILE,
+                    mSharedPrefs.getString(NoBackupKeys.KEY_LOCK_WALLPAPER_BACKING_FILE, null));
+        }
+        if (mSharedPrefs.contains(NoBackupKeys.KEY_DAILY_ROTATION_TIMESTAMPS)) {
+            editor.putString(NoBackupKeys.KEY_DAILY_ROTATION_TIMESTAMPS,
+                    mSharedPrefs.getString(NoBackupKeys.KEY_DAILY_ROTATION_TIMESTAMPS, null));
+        }
+        if (mSharedPrefs.contains(
+                NoBackupKeys.KEY_DAILY_WALLPAPER_ENABLED_TIMESTAMP)) {
+            editor.putLong(NoBackupKeys.KEY_DAILY_WALLPAPER_ENABLED_TIMESTAMP,
+                    mSharedPrefs.getLong(NoBackupKeys.KEY_DAILY_WALLPAPER_ENABLED_TIMESTAMP, -1));
+        }
+        if (mSharedPrefs.contains(NoBackupKeys.KEY_LAST_DAILY_LOG_TIMESTAMP)) {
+            editor.putLong(NoBackupKeys.KEY_LAST_DAILY_LOG_TIMESTAMP,
+                    mSharedPrefs.getLong(NoBackupKeys.KEY_LAST_DAILY_LOG_TIMESTAMP, 0));
+        }
+        if (mSharedPrefs.contains(NoBackupKeys.KEY_LAST_APP_ACTIVE_TIMESTAMP)) {
+            editor.putLong(NoBackupKeys.KEY_LAST_APP_ACTIVE_TIMESTAMP,
+                    mSharedPrefs.getLong(NoBackupKeys.KEY_LAST_APP_ACTIVE_TIMESTAMP, 0));
+        }
+        if (mSharedPrefs.contains(NoBackupKeys.KEY_LAST_ROTATION_STATUS)) {
+            editor.putInt(NoBackupKeys.KEY_LAST_ROTATION_STATUS,
+                    mSharedPrefs.getInt(NoBackupKeys.KEY_LAST_ROTATION_STATUS, -1));
+        }
+        if (mSharedPrefs.contains(NoBackupKeys.KEY_LAST_ROTATION_STATUS_TIMESTAMP)) {
+            editor.putLong(NoBackupKeys.KEY_LAST_ROTATION_STATUS_TIMESTAMP,
+                    mSharedPrefs.getLong(NoBackupKeys.KEY_LAST_ROTATION_STATUS_TIMESTAMP, 0));
+        }
+        if (mSharedPrefs.contains(NoBackupKeys.KEY_LAST_SYNC_TIMESTAMP)) {
+            editor.putLong(NoBackupKeys.KEY_LAST_SYNC_TIMESTAMP,
+                    mSharedPrefs.getLong(NoBackupKeys.KEY_LAST_SYNC_TIMESTAMP, 0));
+        }
+        if (mSharedPrefs.contains(NoBackupKeys.KEY_PENDING_WALLPAPER_SET_STATUS)) {
+            editor.putInt(NoBackupKeys.KEY_PENDING_WALLPAPER_SET_STATUS,
+                    mSharedPrefs.getInt(NoBackupKeys.KEY_PENDING_WALLPAPER_SET_STATUS,
+                            WALLPAPER_SET_NOT_PENDING));
+        }
+        if (mSharedPrefs.contains(NoBackupKeys.KEY_PENDING_DAILY_WALLPAPER_UPDATE_STATUS)) {
+            editor.putInt(NoBackupKeys.KEY_PENDING_DAILY_WALLPAPER_UPDATE_STATUS,
+                    mSharedPrefs.getInt(NoBackupKeys.KEY_PENDING_DAILY_WALLPAPER_UPDATE_STATUS,
+                            DAILY_WALLPAPER_UPDATE_NOT_PENDING));
+        }
+        if (mSharedPrefs.contains(NoBackupKeys.KEY_NUM_DAYS_DAILY_ROTATION_FAILED)) {
+            editor.putInt(NoBackupKeys.KEY_NUM_DAYS_DAILY_ROTATION_FAILED,
+                    mSharedPrefs.getInt(NoBackupKeys.KEY_NUM_DAYS_DAILY_ROTATION_FAILED, 0));
+        }
+        if (mSharedPrefs.contains(NoBackupKeys.KEY_NUM_DAYS_DAILY_ROTATION_NOT_ATTEMPTED)) {
+            editor.putInt(NoBackupKeys.KEY_NUM_DAYS_DAILY_ROTATION_NOT_ATTEMPTED,
+                    mSharedPrefs.getInt(NoBackupKeys.KEY_NUM_DAYS_DAILY_ROTATION_NOT_ATTEMPTED, 0));
+        }
+        if (mSharedPrefs.contains(NoBackupKeys.KEY_HOME_WALLPAPER_PACKAGE_NAME)) {
+            editor.putString(NoBackupKeys.KEY_HOME_WALLPAPER_PACKAGE_NAME,
+                    mSharedPrefs.getString(NoBackupKeys.KEY_HOME_WALLPAPER_PACKAGE_NAME, null));
+        }
+
+        editor.apply();
+    }
+
     private int getResIdPersistedByName(String key, String type) {
         String resName = mSharedPrefs.getString(key, null);
         if (resName == null) {
@@ -106,13 +197,16 @@
     public void setHomeWallpaperAttributions(List<String> attributions) {
         SharedPreferences.Editor editor = mSharedPrefs.edit();
         if (attributions.size() > 0) {
-            editor.putString(WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_ATTRIB_1, attributions.get(0));
+            editor.putString(WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_ATTRIB_1,
+                    attributions.get(0));
         }
         if (attributions.size() > 1) {
-            editor.putString(WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_ATTRIB_2, attributions.get(1));
+            editor.putString(WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_ATTRIB_2,
+                    attributions.get(1));
         }
         if (attributions.size() > 2) {
-            editor.putString(WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_ATTRIB_3, attributions.get(2));
+            editor.putString(WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_ATTRIB_3,
+                    attributions.get(2));
         }
         editor.apply();
     }
@@ -155,19 +249,22 @@
 
     @Override
     public String getHomeWallpaperBaseImageUrl() {
-        return mSharedPrefs.getString(WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_BASE_IMAGE_URL, null);
+        return mNoBackupPrefs.getString(
+                NoBackupKeys.KEY_HOME_WALLPAPER_BASE_IMAGE_URL, null);
     }
 
     @Override
     public void setHomeWallpaperBaseImageUrl(String baseImageUrl) {
-        mSharedPrefs.edit().putString(
-                WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_BASE_IMAGE_URL, baseImageUrl).apply();
+        mNoBackupPrefs.edit().putString(
+                NoBackupKeys.KEY_HOME_WALLPAPER_BASE_IMAGE_URL, baseImageUrl)
+                .apply();
     }
 
     @Override
     @Nullable
     public String getHomeWallpaperCollectionId() {
-        return mSharedPrefs.getString(WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_COLLECTION_ID, null);
+        return mSharedPrefs.getString(WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_COLLECTION_ID,
+                null);
     }
 
     @Override
@@ -179,14 +276,14 @@
     @Override
     @Nullable
     public String getHomeWallpaperBackingFileName() {
-        return mSharedPrefs.getString(WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_BACKING_FILE,
-                null);
+        return mNoBackupPrefs.getString(
+                NoBackupKeys.KEY_HOME_WALLPAPER_BACKING_FILE, null);
     }
 
     @Override
     public void setHomeWallpaperBackingFileName(String fileName) {
-        mSharedPrefs.edit().putString(
-                WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_BACKING_FILE, fileName).apply();
+        mNoBackupPrefs.edit().putString(
+                NoBackupKeys.KEY_HOME_WALLPAPER_BACKING_FILE, fileName).apply();
     }
 
     @Override
@@ -213,47 +310,56 @@
                 .remove(WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_ACTION_URL)
                 .remove(WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_ACTION_LABEL_RES)
                 .remove(WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_ACTION_ICON_RES)
-                .remove(WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_BASE_IMAGE_URL)
                 .remove(WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_HASH_CODE)
-                .remove(WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_MANAGER_ID)
-                .remove(WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_PACKAGE_NAME)
-                .remove(WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_REMOTE_ID)
-                .remove(WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_BACKING_FILE)
+                .apply();
+
+        mNoBackupPrefs.edit()
+                .remove(NoBackupKeys.KEY_HOME_WALLPAPER_PACKAGE_NAME)
+                .remove(NoBackupKeys.KEY_HOME_WALLPAPER_MANAGER_ID)
+                .remove(NoBackupKeys.KEY_HOME_WALLPAPER_REMOTE_ID)
+                .remove(NoBackupKeys.KEY_HOME_WALLPAPER_BASE_IMAGE_URL)
+                .remove(NoBackupKeys.KEY_HOME_WALLPAPER_BACKING_FILE)
                 .apply();
     }
 
     @Override
     public String getHomeWallpaperPackageName() {
-        return mSharedPrefs.getString(WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_PACKAGE_NAME, null);
+        return mNoBackupPrefs.getString(
+                NoBackupKeys.KEY_HOME_WALLPAPER_PACKAGE_NAME, null);
     }
 
     @Override
     public void setHomeWallpaperPackageName(String packageName) {
-        mSharedPrefs.edit().putString(
-                WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_PACKAGE_NAME, packageName).apply();
+        mNoBackupPrefs.edit().putString(
+                NoBackupKeys.KEY_HOME_WALLPAPER_PACKAGE_NAME, packageName)
+                .apply();
     }
 
     @Override
     public int getHomeWallpaperManagerId() {
-        return mSharedPrefs.getInt(WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_MANAGER_ID, 0);
+        return mNoBackupPrefs.getInt(
+                NoBackupKeys.KEY_HOME_WALLPAPER_MANAGER_ID, 0);
     }
 
     @Override
     public void setHomeWallpaperManagerId(int homeWallpaperId) {
-        mSharedPrefs.edit().putInt(
-                WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_MANAGER_ID, homeWallpaperId).apply();
+        mNoBackupPrefs.edit().putInt(
+                NoBackupKeys.KEY_HOME_WALLPAPER_MANAGER_ID, homeWallpaperId)
+                .apply();
     }
 
     @Nullable
     @Override
     public String getHomeWallpaperRemoteId() {
-        return mSharedPrefs.getString(WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_REMOTE_ID, null);
+        return mNoBackupPrefs.getString(
+                NoBackupKeys.KEY_HOME_WALLPAPER_REMOTE_ID, null);
     }
 
     @Override
     public void setHomeWallpaperRemoteId(@Nullable String wallpaperRemoteId) {
-        mSharedPrefs.edit().putString(
-                WallpaperPreferenceKeys.KEY_HOME_WALLPAPER_REMOTE_ID, wallpaperRemoteId).apply();
+        mNoBackupPrefs.edit().putString(
+                NoBackupKeys.KEY_HOME_WALLPAPER_REMOTE_ID, wallpaperRemoteId)
+                .apply();
     }
 
     @Override
@@ -269,13 +375,16 @@
     public void setLockWallpaperAttributions(List<String> attributions) {
         SharedPreferences.Editor editor = mSharedPrefs.edit();
         if (attributions.size() > 0) {
-            editor.putString(WallpaperPreferenceKeys.KEY_LOCK_WALLPAPER_ATTRIB_1, attributions.get(0));
+            editor.putString(WallpaperPreferenceKeys.KEY_LOCK_WALLPAPER_ATTRIB_1,
+                    attributions.get(0));
         }
         if (attributions.size() > 1) {
-            editor.putString(WallpaperPreferenceKeys.KEY_LOCK_WALLPAPER_ATTRIB_2, attributions.get(1));
+            editor.putString(WallpaperPreferenceKeys.KEY_LOCK_WALLPAPER_ATTRIB_2,
+                    attributions.get(1));
         }
         if (attributions.size() > 2) {
-            editor.putString(WallpaperPreferenceKeys.KEY_LOCK_WALLPAPER_ATTRIB_3, attributions.get(2));
+            editor.putString(WallpaperPreferenceKeys.KEY_LOCK_WALLPAPER_ATTRIB_3,
+                    attributions.get(2));
         }
         editor.apply();
     }
@@ -319,7 +428,8 @@
     @Override
     @Nullable
     public String getLockWallpaperCollectionId() {
-        return mSharedPrefs.getString(WallpaperPreferenceKeys.KEY_LOCK_WALLPAPER_COLLECTION_ID, null);
+        return mSharedPrefs.getString(WallpaperPreferenceKeys.KEY_LOCK_WALLPAPER_COLLECTION_ID,
+                null);
     }
 
     @Override
@@ -331,25 +441,27 @@
     @Override
     @Nullable
     public String getLockWallpaperBackingFileName() {
-        return mSharedPrefs.getString(WallpaperPreferenceKeys.KEY_LOCK_WALLPAPER_BACKING_FILE,
-                null);
+        return mNoBackupPrefs.getString(
+                NoBackupKeys.KEY_LOCK_WALLPAPER_BACKING_FILE, null);
     }
 
     @Override
     public void setLockWallpaperBackingFileName(String fileName) {
-        mSharedPrefs.edit().putString(
-                WallpaperPreferenceKeys.KEY_LOCK_WALLPAPER_BACKING_FILE, fileName).apply();
+        mNoBackupPrefs.edit().putString(
+                NoBackupKeys.KEY_LOCK_WALLPAPER_BACKING_FILE, fileName).apply();
     }
 
     @Override
     public int getLockWallpaperId() {
-        return mSharedPrefs.getInt(WallpaperPreferenceKeys.KEY_LOCK_WALLPAPER_MANAGER_ID, 0);
+        return mNoBackupPrefs.getInt(
+                NoBackupKeys.KEY_LOCK_WALLPAPER_MANAGER_ID, 0);
     }
 
     @Override
     public void setLockWallpaperId(int lockWallpaperId) {
-        mSharedPrefs.edit().putInt(
-                WallpaperPreferenceKeys.KEY_LOCK_WALLPAPER_MANAGER_ID, lockWallpaperId).apply();
+        mNoBackupPrefs.edit().putInt(
+                NoBackupKeys.KEY_LOCK_WALLPAPER_MANAGER_ID, lockWallpaperId)
+                .apply();
     }
 
     @Override
@@ -378,21 +490,25 @@
                 .remove(WallpaperPreferenceKeys.KEY_LOCK_WALLPAPER_ACTION_LABEL_RES)
                 .remove(WallpaperPreferenceKeys.KEY_LOCK_WALLPAPER_ACTION_ICON_RES)
                 .remove(WallpaperPreferenceKeys.KEY_LOCK_WALLPAPER_HASH_CODE)
-                .remove(WallpaperPreferenceKeys.KEY_LOCK_WALLPAPER_MANAGER_ID)
-                .remove(WallpaperPreferenceKeys.KEY_LOCK_WALLPAPER_BACKING_FILE)
+                .apply();
+
+        mNoBackupPrefs.edit()
+                .remove(NoBackupKeys.KEY_LOCK_WALLPAPER_MANAGER_ID)
+                .remove(NoBackupKeys.KEY_LOCK_WALLPAPER_BACKING_FILE)
                 .apply();
     }
 
     @Override
     public void addDailyRotation(long timestamp) {
-        String jsonString = mSharedPrefs.getString(
-                WallpaperPreferenceKeys.KEY_DAILY_ROTATION_TIMESTAMPS, "[]");
+        String jsonString = mNoBackupPrefs.getString(
+                NoBackupKeys.KEY_DAILY_ROTATION_TIMESTAMPS, "[]");
         try {
             JSONArray jsonArray = new JSONArray(jsonString);
             jsonArray.put(timestamp);
 
-            mSharedPrefs.edit()
-                    .putString(WallpaperPreferenceKeys.KEY_DAILY_ROTATION_TIMESTAMPS, jsonArray.toString())
+            mNoBackupPrefs.edit()
+                    .putString(NoBackupKeys.KEY_DAILY_ROTATION_TIMESTAMPS,
+                            jsonArray.toString())
                     .apply();
         } catch (JSONException e) {
             Log.e(TAG, "Failed to add a daily rotation timestamp due to a JSON parse exception");
@@ -401,8 +517,8 @@
 
     @Override
     public long getLastDailyRotationTimestamp() {
-        String jsonString = mSharedPrefs.getString(
-                WallpaperPreferenceKeys.KEY_DAILY_ROTATION_TIMESTAMPS, "[]");
+        String jsonString = mNoBackupPrefs.getString(
+                NoBackupKeys.KEY_DAILY_ROTATION_TIMESTAMPS, "[]");
 
         try {
             JSONArray jsonArray = new JSONArray(jsonString);
@@ -428,15 +544,15 @@
         oneWeekAgo.add(Calendar.WEEK_OF_YEAR, -1);
         long oneWeekAgoTimestamp = oneWeekAgo.getTimeInMillis();
 
-        // Return null if daily rotation wasn't enabled (timestamp value of -1) or was enabled earlier
-        // than one week ago.
+        // Return null if daily rotation wasn't enabled (timestamp value of -1) or was enabled
+        // less than one week ago.
         if (enabledTimestamp == -1 || enabledTimestamp > oneWeekAgoTimestamp) {
             return null;
         }
 
         List<Long> timestamps = new ArrayList<>();
-        String jsonString = mSharedPrefs.getString(
-                WallpaperPreferenceKeys.KEY_DAILY_ROTATION_TIMESTAMPS, "[]");
+        String jsonString = mNoBackupPrefs.getString(
+                NoBackupKeys.KEY_DAILY_ROTATION_TIMESTAMPS, "[]");
 
         try {
             JSONArray jsonArray = new JSONArray(jsonString);
@@ -451,8 +567,9 @@
             }
 
             jsonArray = new JSONArray(timestamps);
-            mSharedPrefs.edit()
-                    .putString(WallpaperPreferenceKeys.KEY_DAILY_ROTATION_TIMESTAMPS, jsonArray.toString())
+            mNoBackupPrefs.edit()
+                    .putString(NoBackupKeys.KEY_DAILY_ROTATION_TIMESTAMPS,
+                            jsonArray.toString())
                     .apply();
         } catch (JSONException e) {
             Log.e(TAG, "Failed to get daily rotation timestamps due to a JSON parse exception");
@@ -479,21 +596,21 @@
         midnightToday.set(Calendar.MINUTE, 0);
         long midnightTodayTimestamp = midnightToday.getTimeInMillis();
 
-        // Return null if daily rotation wasn't enabled (timestamp value of -1) or was enabled earlier
-        // than midnight yesterday.
+        // Return null if daily rotation wasn't enabled (timestamp value of -1) or was enabled
+        // less than midnight yesterday.
         if (enabledTimestamp == -1 || enabledTimestamp > midnightYesterdayTimestamp) {
             return null;
         }
 
         List<Long> timestamps = new ArrayList<>();
-        String jsonString = mSharedPrefs.getString(
-                WallpaperPreferenceKeys.KEY_DAILY_ROTATION_TIMESTAMPS, "[]");
+        String jsonString = mNoBackupPrefs.getString(
+                NoBackupKeys.KEY_DAILY_ROTATION_TIMESTAMPS, "[]");
 
         try {
             JSONArray jsonArray = new JSONArray(jsonString);
 
-            // Filter the timestamps (which cover up to one week of data) to only include those between
-            // midnight yesterday and midnight today.
+            // Filter the timestamps (which cover up to one week of data) to only include those
+            // between midnight yesterday and midnight today.
             for (int i = 0; i < jsonArray.length(); i++) {
                 long timestamp = jsonArray.getLong(i);
                 if (timestamp >= midnightYesterdayTimestamp && timestamp < midnightTodayTimestamp) {
@@ -510,160 +627,175 @@
 
     @Override
     public long getDailyWallpaperEnabledTimestamp() {
-        return mSharedPrefs.getLong(WallpaperPreferenceKeys.KEY_DAILY_WALLPAPER_ENABLED_TIMESTAMP, -1);
+        return mNoBackupPrefs.getLong(
+                NoBackupKeys.KEY_DAILY_WALLPAPER_ENABLED_TIMESTAMP, -1);
     }
 
     @Override
     public void setDailyWallpaperEnabledTimestamp(long timestamp) {
-        mSharedPrefs.edit()
-                .putLong(WallpaperPreferenceKeys.KEY_DAILY_WALLPAPER_ENABLED_TIMESTAMP, timestamp)
+        mNoBackupPrefs.edit()
+                .putLong(NoBackupKeys.KEY_DAILY_WALLPAPER_ENABLED_TIMESTAMP,
+                        timestamp)
                 .apply();
     }
 
     @Override
     public void clearDailyRotations() {
-        mSharedPrefs.edit()
-                .remove(WallpaperPreferenceKeys.KEY_DAILY_ROTATION_TIMESTAMPS)
-                .remove(WallpaperPreferenceKeys.KEY_DAILY_WALLPAPER_ENABLED_TIMESTAMP)
+        mNoBackupPrefs.edit()
+                .remove(NoBackupKeys.KEY_DAILY_ROTATION_TIMESTAMPS)
+                .remove(NoBackupKeys.KEY_DAILY_WALLPAPER_ENABLED_TIMESTAMP)
                 .apply();
     }
 
     @Override
     public long getLastDailyLogTimestamp() {
-        return mSharedPrefs.getLong(WallpaperPreferenceKeys.KEY_LAST_DAILY_LOG_TIMESTAMP, 0);
+        return mNoBackupPrefs.getLong(
+                NoBackupKeys.KEY_LAST_DAILY_LOG_TIMESTAMP, 0);
     }
 
     @Override
     public void setLastDailyLogTimestamp(long timestamp) {
-        mSharedPrefs.edit()
-                .putLong(WallpaperPreferenceKeys.KEY_LAST_DAILY_LOG_TIMESTAMP, timestamp)
+        mNoBackupPrefs.edit()
+                .putLong(NoBackupKeys.KEY_LAST_DAILY_LOG_TIMESTAMP, timestamp)
                 .apply();
     }
 
     @Override
     public long getLastAppActiveTimestamp() {
-        return mSharedPrefs.getLong(WallpaperPreferenceKeys.KEY_LAST_APP_ACTIVE_TIMESTAMP, 0);
+        return mNoBackupPrefs.getLong(
+                NoBackupKeys.KEY_LAST_APP_ACTIVE_TIMESTAMP, 0);
     }
 
     @Override
     public void setLastAppActiveTimestamp(long timestamp) {
-        mSharedPrefs.edit()
-                .putLong(WallpaperPreferenceKeys.KEY_LAST_APP_ACTIVE_TIMESTAMP, timestamp)
+        mNoBackupPrefs.edit()
+                .putLong(NoBackupKeys.KEY_LAST_APP_ACTIVE_TIMESTAMP, timestamp)
                 .apply();
     }
 
     @Override
     public void setDailyWallpaperRotationStatus(int status, long timestamp) {
-        mSharedPrefs.edit()
-                .putInt(WallpaperPreferenceKeys.KEY_LAST_ROTATION_STATUS, status)
-                .putLong(WallpaperPreferenceKeys.KEY_LAST_ROTATION_STATUS_TIMESTAMP, timestamp)
+        mNoBackupPrefs.edit()
+                .putInt(NoBackupKeys.KEY_LAST_ROTATION_STATUS, status)
+                .putLong(NoBackupKeys.KEY_LAST_ROTATION_STATUS_TIMESTAMP,
+                        timestamp)
                 .apply();
     }
 
     @Override
     public int getDailyWallpaperLastRotationStatus() {
-        return mSharedPrefs.getInt(WallpaperPreferenceKeys.KEY_LAST_ROTATION_STATUS, -1);
+        return mNoBackupPrefs.getInt(NoBackupKeys.KEY_LAST_ROTATION_STATUS, -1);
     }
 
     @Override
     public long getDailyWallpaperLastRotationStatusTimestamp() {
-        return mSharedPrefs.getLong(WallpaperPreferenceKeys.KEY_LAST_ROTATION_STATUS_TIMESTAMP, 0);
+        return mNoBackupPrefs.getLong(
+                NoBackupKeys.KEY_LAST_ROTATION_STATUS_TIMESTAMP, 0);
     }
 
     @Override
     public long getLastSyncTimestamp() {
-        return mSharedPrefs.getLong(WallpaperPreferenceKeys.KEY_LAST_SYNC_TIMESTAMP, 0);
+        return mNoBackupPrefs.getLong(NoBackupKeys.KEY_LAST_SYNC_TIMESTAMP, 0);
     }
 
     @Override
     public void setLastSyncTimestamp(long timestamp) {
-        // Write synchronously via commit() to ensure this timetsamp gets written to disk immediately.
-        mSharedPrefs.edit()
-                .putLong(WallpaperPreferenceKeys.KEY_LAST_SYNC_TIMESTAMP, timestamp)
+        // Write synchronously via commit() to ensure this timetsamp gets written to disk
+        // immediately.
+        mNoBackupPrefs.edit()
+                .putLong(NoBackupKeys.KEY_LAST_SYNC_TIMESTAMP, timestamp)
                 .commit();
     }
 
     @Override
     public void setPendingWallpaperSetStatusSync(@PendingWallpaperSetStatus int setStatus) {
-        mSharedPrefs.edit()
-                .putInt(WallpaperPreferenceKeys.KEY_PENDING_WALLPAPER_SET_STATUS, setStatus)
+        mNoBackupPrefs.edit()
+                .putInt(NoBackupKeys.KEY_PENDING_WALLPAPER_SET_STATUS,
+                        setStatus)
                 .commit();
     }
 
     @Override
     public int getPendingWallpaperSetStatus() {
         //noinspection ResourceType
-        return mSharedPrefs.getInt(
-                WallpaperPreferenceKeys.KEY_PENDING_WALLPAPER_SET_STATUS, WALLPAPER_SET_NOT_PENDING);
+        return mNoBackupPrefs.getInt(
+                NoBackupKeys.KEY_PENDING_WALLPAPER_SET_STATUS,
+                WALLPAPER_SET_NOT_PENDING);
     }
 
     @Override
     public void setPendingWallpaperSetStatus(@PendingWallpaperSetStatus int setStatus) {
-        mSharedPrefs.edit()
-                .putInt(WallpaperPreferenceKeys.KEY_PENDING_WALLPAPER_SET_STATUS, setStatus)
+        mNoBackupPrefs.edit()
+                .putInt(NoBackupKeys.KEY_PENDING_WALLPAPER_SET_STATUS,
+                        setStatus)
                 .apply();
     }
 
     @Override
     public void setPendingDailyWallpaperUpdateStatusSync(
             @PendingDailyWallpaperUpdateStatus int updateStatus) {
-        mSharedPrefs.edit()
-                .putInt(WallpaperPreferenceKeys.KEY_PENDING_DAILY_WALLPAPER_UPDATE_STATUS, updateStatus)
+        mNoBackupPrefs.edit()
+                .putInt(NoBackupKeys.KEY_PENDING_DAILY_WALLPAPER_UPDATE_STATUS,
+                        updateStatus)
                 .commit();
     }
 
     @Override
     public int getPendingDailyWallpaperUpdateStatus() {
         //noinspection ResourceType
-        return mSharedPrefs.getInt(WallpaperPreferenceKeys.KEY_PENDING_DAILY_WALLPAPER_UPDATE_STATUS,
+        return mNoBackupPrefs.getInt(
+                NoBackupKeys.KEY_PENDING_DAILY_WALLPAPER_UPDATE_STATUS,
                 DAILY_WALLPAPER_UPDATE_NOT_PENDING);
     }
 
     @Override
     public void setPendingDailyWallpaperUpdateStatus(
             @PendingDailyWallpaperUpdateStatus int updateStatus) {
-        mSharedPrefs.edit()
-                .putInt(WallpaperPreferenceKeys.KEY_PENDING_DAILY_WALLPAPER_UPDATE_STATUS, updateStatus)
+        mNoBackupPrefs.edit()
+                .putInt(NoBackupKeys.KEY_PENDING_DAILY_WALLPAPER_UPDATE_STATUS,
+                        updateStatus)
                 .apply();
     }
 
     @Override
     public void incrementNumDaysDailyRotationFailed() {
-        mSharedPrefs.edit()
-                .putInt(WallpaperPreferenceKeys.KEY_NUM_DAYS_DAILY_ROTATION_FAILED,
+        mNoBackupPrefs.edit()
+                .putInt(NoBackupKeys.KEY_NUM_DAYS_DAILY_ROTATION_FAILED,
                         getNumDaysDailyRotationFailed() + 1)
                 .apply();
     }
 
     @Override
     public int getNumDaysDailyRotationFailed() {
-        return mSharedPrefs.getInt(WallpaperPreferenceKeys.KEY_NUM_DAYS_DAILY_ROTATION_FAILED, 0);
+        return mNoBackupPrefs.getInt(
+                NoBackupKeys.KEY_NUM_DAYS_DAILY_ROTATION_FAILED, 0);
     }
 
     @Override
     public void resetNumDaysDailyRotationFailed() {
-        mSharedPrefs.edit()
-                .putInt(WallpaperPreferenceKeys.KEY_NUM_DAYS_DAILY_ROTATION_FAILED, 0)
+        mNoBackupPrefs.edit()
+                .putInt(NoBackupKeys.KEY_NUM_DAYS_DAILY_ROTATION_FAILED, 0)
                 .apply();
     }
 
     @Override
     public void incrementNumDaysDailyRotationNotAttempted() {
-        mSharedPrefs.edit()
-                .putInt(WallpaperPreferenceKeys.KEY_NUM_DAYS_DAILY_ROTATION_NOT_ATTEMPTED,
+        mNoBackupPrefs.edit()
+                .putInt(NoBackupKeys.KEY_NUM_DAYS_DAILY_ROTATION_NOT_ATTEMPTED,
                         getNumDaysDailyRotationNotAttempted() + 1)
                 .apply();
     }
 
     @Override
     public int getNumDaysDailyRotationNotAttempted() {
-        return mSharedPrefs.getInt(WallpaperPreferenceKeys.KEY_NUM_DAYS_DAILY_ROTATION_NOT_ATTEMPTED, 0);
+        return mNoBackupPrefs.getInt(
+                NoBackupKeys.KEY_NUM_DAYS_DAILY_ROTATION_NOT_ATTEMPTED, 0);
     }
 
     @Override
     public void resetNumDaysDailyRotationNotAttempted() {
-        mSharedPrefs.edit()
-                .putInt(WallpaperPreferenceKeys.KEY_NUM_DAYS_DAILY_ROTATION_NOT_ATTEMPTED, 0)
+        mNoBackupPrefs.edit()
+                .putInt(NoBackupKeys.KEY_NUM_DAYS_DAILY_ROTATION_NOT_ATTEMPTED, 0)
                 .apply();
     }
 }
diff --git a/src/com/android/wallpaper/module/DefaultWallpaperRefresher.java b/src/com/android/wallpaper/module/DefaultWallpaperRefresher.java
index c97d2b9..d85e04e 100755
--- a/src/com/android/wallpaper/module/DefaultWallpaperRefresher.java
+++ b/src/com/android/wallpaper/module/DefaultWallpaperRefresher.java
@@ -32,7 +32,6 @@
 import com.android.wallpaper.model.WallpaperMetadata;
 
 import java.io.FileInputStream;
-import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
@@ -50,9 +49,6 @@
     private final Context mAppContext;
     private final WallpaperPreferences mWallpaperPreferences;
     private final WallpaperManager mWallpaperManager;
-    private final LiveWallpaperStatusChecker mLiveWallpaperStatusChecker;
-    private final UserEventLogger mUserEventLogger;
-    private final Context mDeviceProtectedContext;
 
     /**
      * @param context The application's context.
@@ -62,13 +58,10 @@
 
         Injector injector = InjectorProvider.getInjector();
         mWallpaperPreferences = injector.getPreferences(mAppContext);
-        mLiveWallpaperStatusChecker = injector.getLiveWallpaperStatusChecker(mAppContext);
-        mUserEventLogger = injector.getUserEventLogger(mAppContext);
 
         // Retrieve WallpaperManager using Context#getSystemService instead of
         // WallpaperManager#getInstance so it can be mocked out in test.
         mWallpaperManager = (WallpaperManager) context.getSystemService(Context.WALLPAPER_SERVICE);
-        mDeviceProtectedContext = mAppContext.createDeviceProtectedStorageContext();
     }
 
     @Override
@@ -203,8 +196,7 @@
          * current system wallpaper.
          */
         private boolean isHomeScreenMetadataCurrent() {
-            return (mWallpaperManager.getWallpaperInfo() == null
-                    || mLiveWallpaperStatusChecker.isNoBackupImageWallpaperSet())
+            return (mWallpaperManager.getWallpaperInfo() == null)
                     ? isHomeScreenImageWallpaperCurrent()
                     : isHomeScreenLiveWallpaperCurrent();
         }
@@ -221,36 +213,6 @@
 
         private long getCurrentHomeWallpaperHashCode() {
             if (mCurrentHomeWallpaperHashCode == 0) {
-                if (mLiveWallpaperStatusChecker.isNoBackupImageWallpaperSet()) {
-
-                    synchronized (RotatingWallpaperLockProvider.getInstance()) {
-                        Bitmap bitmap = null;
-                        try {
-                            FileInputStream fis =
-                                    mDeviceProtectedContext.openFileInput(
-                                            NoBackupImageWallpaper.ROTATING_WALLPAPER_FILE_PATH);
-                            bitmap = BitmapFactory.decodeStream(fis);
-                            fis.close();
-                        } catch (FileNotFoundException e) {
-                            Log.e(TAG, "Rotating wallpaper file not found at path: "
-                                    + mDeviceProtectedContext.getFileStreamPath(
-                                            NoBackupImageWallpaper.ROTATING_WALLPAPER_FILE_PATH),
-                                    e);
-                        } catch (IOException e) {
-                            Log.e(TAG, "IOException when closing FileInputStream " + e);
-                        }
-
-                        if (bitmap != null) {
-                            mCurrentHomeWallpaperHashCode = BitmapUtils.generateHashCode(bitmap);
-                            mUserEventLogger.logDailyWallpaperDecodes(true);
-                        } else {
-                            // If an error occurred decoding the stream then we should just assume the current
-                            // home wallpaper remained intact.
-                            mCurrentHomeWallpaperHashCode = mWallpaperPreferences.getHomeWallpaperHashCode();
-                            mUserEventLogger.logDailyWallpaperDecodes(false);
-                        }
-                    }
-                } else {
                     BitmapDrawable wallpaperDrawable = (BitmapDrawable) mWallpaperManagerCompat.getDrawable();
                     Bitmap wallpaperBitmap = wallpaperDrawable.getBitmap();
                     mCurrentHomeWallpaperHashCode = BitmapUtils.generateHashCode(wallpaperBitmap);
@@ -258,7 +220,6 @@
                     // Manually request that WallpaperManager loses its reference to the current wallpaper
                     // bitmap, which can occupy a large memory allocation for the lifetime of the app.
                     mWallpaperManager.forgetLoadedWallpaper();
-                }
             }
             return mCurrentHomeWallpaperHashCode;
         }
diff --git a/src/com/android/wallpaper/module/Injector.java b/src/com/android/wallpaper/module/Injector.java
index 3afaeb4..ca64b9d 100755
--- a/src/com/android/wallpaper/module/Injector.java
+++ b/src/com/android/wallpaper/module/Injector.java
@@ -44,8 +44,6 @@
 
     FormFactorChecker getFormFactorChecker(Context context);
 
-    LiveWallpaperStatusChecker getLiveWallpaperStatusChecker(Context context);
-
     LoggingOptInStatusProvider getLoggingOptInStatusProvider(Context context);
 
     NetworkStatusNotifier getNetworkStatusNotifier(Context context);
@@ -56,8 +54,6 @@
 
     Requester getRequester(Context context);
 
-    RotatingWallpaperComponentChecker getRotatingWallpaperComponentChecker();
-
     SystemFeatureChecker getSystemFeatureChecker();
 
     UserEventLogger getUserEventLogger(Context context);
diff --git a/src/com/android/wallpaper/module/NoBackupImageWallpaper.java b/src/com/android/wallpaper/module/NoBackupImageWallpaper.java
deleted file mode 100755
index a76d881..0000000
--- a/src/com/android/wallpaper/module/NoBackupImageWallpaper.java
+++ /dev/null
@@ -1,1018 +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 static javax.microedition.khronos.egl.EGL10.EGL_NO_CONTEXT;
-import static javax.microedition.khronos.egl.EGL10.EGL_NO_SURFACE;
-
-import android.annotation.SuppressLint;
-import android.app.WallpaperColors;
-import android.app.WallpaperManager;
-import android.content.BroadcastReceiver;
-import android.content.ComponentCallbacks2;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.opengl.GLES20;
-import android.opengl.GLUtils;
-import android.os.AsyncTask;
-import android.os.Build.VERSION;
-import android.os.Build.VERSION_CODES;
-import android.os.Handler;
-import android.renderscript.Matrix4f;
-import android.service.wallpaper.WallpaperService;
-import android.util.Log;
-import android.view.Display;
-import android.view.MotionEvent;
-import android.view.SurfaceHolder;
-import android.view.WindowManager;
-
-import com.android.wallpaper.util.ScreenSizeCalculator;
-
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.FloatBuffer;
-
-import javax.microedition.khronos.egl.EGL10;
-import javax.microedition.khronos.egl.EGLConfig;
-import javax.microedition.khronos.egl.EGLContext;
-import javax.microedition.khronos.egl.EGLDisplay;
-import javax.microedition.khronos.egl.EGLSurface;
-
-import androidx.annotation.RequiresApi;
-
-/**
- * Live wallpaper service which simply renders a wallpaper from internal storage. Designed as a
- * workaround to WallpaperManager not having an allowBackup=false option on pre-N builds of Android.
- * <p>
- * Adapted from {@code com.android.systemui.ImageWallpaper}.
- */
-@SuppressLint("ServiceCast")
-public class NoBackupImageWallpaper extends WallpaperService {
-
-    public static final String ACTION_ROTATING_WALLPAPER_CHANGED =
-            ".ACTION_ROTATING_WALLPAPER_CHANGED";
-    public static final String PERMISSION_NOTIFY_ROTATING_WALLPAPER_CHANGED =
-            ".NOTIFY_ROTATING_WALLPAPER_CHANGED";
-    public static final String PREVIEW_WALLPAPER_FILE_PATH = "preview_wallpaper.jpg";
-    public static final String ROTATING_WALLPAPER_FILE_PATH = "rotating_wallpaper.jpg";
-
-    private static final String TAG = "NoBackupImageWallpaper";
-    private static final String GL_LOG_TAG = "ImageWallpaperGL";
-    private static final boolean DEBUG = false;
-    private static final boolean FIXED_SIZED_SURFACE = false;
-
-    private final Handler mHandler = new Handler();
-
-    private int mOpenGlContextCounter;
-    private WallpaperManager mWallpaperManager;
-    private DrawableEngine mEngine;
-    private boolean mIsHardwareAccelerated;
-
-    @Override
-    public void onCreate() {
-        super.onCreate();
-
-        mOpenGlContextCounter = 0;
-        mWallpaperManager = (WallpaperManager) getSystemService(WALLPAPER_SERVICE);
-
-        // By default, use OpenGL for drawing the static wallpaper image.
-        mIsHardwareAccelerated = true;
-    }
-
-    @Override
-    public void onTrimMemory(int level) {
-        if (mEngine != null) {
-            mEngine.trimMemory(level);
-        }
-    }
-
-    @Override
-    public Engine onCreateEngine() {
-        mEngine = new DrawableEngine();
-        return mEngine;
-    }
-
-    private class DrawableEngine extends Engine {
-        static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
-        static final int EGL_OPENGL_ES2_BIT = 4;
-        private static final String S_SIMPLE_VS =
-                "attribute vec4 position;\n"
-                        + "attribute vec2 texCoords;\n"
-                        + "varying vec2 outTexCoords;\n"
-                        + "uniform mat4 projection;\n"
-                        + "\nvoid main(void) {\n"
-                        + "    outTexCoords = texCoords;\n"
-                        + "    gl_Position = projection * position;\n"
-                        + "}\n\n";
-        private static final String S_SIMPLE_FS =
-                "precision mediump float;\n\n"
-                        + "varying vec2 outTexCoords;\n"
-                        + "uniform sampler2D texture;\n"
-                        + "\nvoid main(void) {\n"
-                        + "    gl_FragColor = texture2D(texture, outTexCoords);\n"
-                        + "}\n\n";
-        private static final int FLOAT_SIZE_BYTES = 4;
-        private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES;
-        private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0;
-        private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3;
-        Bitmap mBackground;
-        WallpaperColors mCachedWallpaperColors;
-        int mBackgroundWidth = -1, mBackgroundHeight = -1;
-        int mLastSurfaceWidth = -1, mLastSurfaceHeight = -1;
-        int mLastRotation = -1;
-        float mXOffset = 0.5f;
-        float mYOffset = 0.5f;
-        float mScale = 1f;
-        boolean mVisible = true;
-        boolean mOffsetsChanged;
-        int mLastXTranslation;
-        int mLastYTranslation;
-        private Display mDefaultDisplay;
-        private EGL10 mEgl;
-        private EGLDisplay mEglDisplay;
-        private EGLConfig mEglConfig;
-        private EGLContext mEglContext;
-        private EGLSurface mEglSurface;
-        private int mTexture;
-        private int mProgram;
-        private boolean mIsOpenGlTextureLoaded;
-        private int mRotationAtLastSurfaceSizeUpdate = -1;
-        private int mDisplayWidthAtLastSurfaceSizeUpdate = -1;
-        private int mDisplayHeightAtLastSurfaceSizeUpdate = -1;
-
-        private int mLastRequestedWidth = -1;
-        private int mLastRequestedHeight = -1;
-        private AsyncTask<Void, Void, Bitmap> mLoader;
-        private boolean mNeedsDrawAfterLoadingWallpaper;
-        private boolean mSurfaceValid;
-
-        private BroadcastReceiver mReceiver;
-
-        public DrawableEngine() {
-            super();
-        }
-
-        public void trimMemory(int level) {
-            if (level >= ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW
-                    && mBackground != null) {
-                if (DEBUG) {
-                    Log.d(TAG, "trimMemory");
-                }
-                mBackground.recycle();
-                mBackground = null;
-                mBackgroundWidth = -1;
-                mBackgroundHeight = -1;
-            }
-        }
-
-        @Override
-        public void onCreate(SurfaceHolder surfaceHolder) {
-            if (DEBUG) {
-                Log.d(TAG, "onCreate");
-            }
-
-            super.onCreate(surfaceHolder);
-
-            mIsOpenGlTextureLoaded = false;
-
-            mDefaultDisplay = ((WindowManager) getSystemService(Context.WINDOW_SERVICE))
-                    .getDefaultDisplay();
-
-            updateSurfaceSize(surfaceHolder, mDefaultDisplay, false /* forDraw */);
-
-            // Enable offset notifications to pan wallpaper for parallax effect.
-            setOffsetNotificationsEnabled(true);
-
-            // If not a preview, then register a local broadcast receiver for listening to changes in the
-            // rotating wallpaper file.
-            if (!isPreview()) {
-                IntentFilter filter = new IntentFilter();
-                filter.addAction(getPackageName() + ACTION_ROTATING_WALLPAPER_CHANGED);
-
-                mReceiver = new BroadcastReceiver() {
-                    @Override
-                    public void onReceive(Context context, Intent intent) {
-                        if (DEBUG) {
-                            Log.i(TAG, "Broadcast received with intent: " + intent);
-                        }
-
-                        String action = intent.getAction();
-                        if (action.equals(getPackageName() + ACTION_ROTATING_WALLPAPER_CHANGED)) {
-                            DrawableEngine.this.invalidateAndRedrawWallpaper();
-                        }
-                    }
-                };
-
-                registerReceiver(mReceiver, filter, getPackageName()
-                        + PERMISSION_NOTIFY_ROTATING_WALLPAPER_CHANGED, null /* handler */);
-            }
-        }
-
-        @Override
-        public void onDestroy() {
-            super.onDestroy();
-            mBackground = null;
-            mWallpaperManager.forgetLoadedWallpaper();
-
-            if (!isPreview() && mReceiver != null) {
-                unregisterReceiver(mReceiver);
-            }
-        }
-
-        boolean updateSurfaceSize(SurfaceHolder surfaceHolder, Display display, boolean forDraw) {
-            boolean hasWallpaper = true;
-            Point displaySize = ScreenSizeCalculator.getInstance().getScreenSize(display);
-
-            // Load background image dimensions, if we haven't saved them yet
-            if (mBackgroundWidth <= 0 || mBackgroundHeight <= 0) {
-                // Need to load the image to get dimensions
-                loadWallpaper(forDraw);
-                if (DEBUG) {
-                    Log.d(TAG, "Reloading, redoing updateSurfaceSize later.");
-                }
-                hasWallpaper = false;
-            }
-
-            // Force the wallpaper to cover the screen in both dimensions
-            int surfaceWidth = Math.max(displaySize.x, mBackgroundWidth);
-            int surfaceHeight = Math.max(displaySize.y, mBackgroundHeight);
-
-            if (FIXED_SIZED_SURFACE) {
-                // Used a fixed size surface, because we are special.  We can do
-                // this because we know the current design of window animations doesn't
-                // cause this to break.
-                surfaceHolder.setFixedSize(surfaceWidth, surfaceHeight);
-                mLastRequestedWidth = surfaceWidth;
-                mLastRequestedHeight = surfaceHeight;
-            } else {
-                surfaceHolder.setSizeFromLayout();
-            }
-            return hasWallpaper;
-        }
-
-        @Override
-        public void onVisibilityChanged(boolean visible) {
-            if (DEBUG) {
-                Log.d(TAG, "onVisibilityChanged: mVisible, visible=" + mVisible + ", " + visible);
-            }
-
-            if (mVisible != visible) {
-                if (DEBUG) {
-                    Log.d(TAG, "Visibility changed to visible=" + visible);
-                }
-                mVisible = visible;
-                drawFrame(false /* forceRedraw */);
-            }
-        }
-
-        @Override
-        public void onTouchEvent(MotionEvent event) {
-            super.onTouchEvent(event);
-        }
-
-        @Override
-        public void onOffsetsChanged(float xOffset, float yOffset,
-                                     float xOffsetStep, float yOffsetStep,
-                                     int xPixels, int yPixels) {
-            if (DEBUG) {
-                Log.d(TAG, "onOffsetsChanged: xOffset=" + xOffset + ", yOffset=" + yOffset
-                        + ", xOffsetStep=" + xOffsetStep + ", yOffsetStep=" + yOffsetStep
-                        + ", xPixels=" + xPixels + ", yPixels=" + yPixels);
-            }
-
-            if (mXOffset != xOffset || mYOffset != yOffset) {
-                if (DEBUG) {
-                    Log.d(TAG, "Offsets changed to (" + xOffset + "," + yOffset + ").");
-                }
-                mXOffset = xOffset;
-                mYOffset = yOffset;
-                mOffsetsChanged = true;
-            }
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    drawFrame(false /* forceRedraw */);
-                }
-            });
-        }
-
-        @Override
-        public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
-            if (DEBUG) {
-                Log.d(TAG, "onSurfaceChanged: width=" + width + ", height=" + height);
-            }
-
-            super.onSurfaceChanged(holder, format, width, height);
-
-            // Retrieve buffer in new size.
-            if (mEgl != null) {
-                mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
-            }
-            drawFrame(false /* forceRedraw */);
-        }
-
-        @Override
-        public void onSurfaceDestroyed(SurfaceHolder holder) {
-            super.onSurfaceDestroyed(holder);
-            if (DEBUG) {
-                Log.d(TAG, "onSurfaceDestroyed");
-            }
-            mLastSurfaceWidth = mLastSurfaceHeight = -1;
-            mSurfaceValid = false;
-
-            if (mIsHardwareAccelerated) {
-                finishGL(mTexture, mProgram);
-            }
-        }
-
-        @Override
-        public void onSurfaceCreated(SurfaceHolder holder) {
-            super.onSurfaceCreated(holder);
-            if (DEBUG) {
-                Log.d(TAG, "onSurfaceCreated");
-            }
-            mLastSurfaceWidth = mLastSurfaceHeight = -1;
-            mSurfaceValid = true;
-
-            if (mIsHardwareAccelerated) {
-                if (!initGL(holder)) {
-                    // Fall back to canvas drawing if initializing OpenGL failed.
-                    mIsHardwareAccelerated = false;
-                    mEgl = null;
-                }
-            }
-        }
-
-        @Override
-        public void onSurfaceRedrawNeeded(SurfaceHolder holder) {
-            if (DEBUG) {
-                Log.d(TAG, "onSurfaceRedrawNeeded");
-            }
-            super.onSurfaceRedrawNeeded(holder);
-
-            drawFrame(true /* forceRedraw */);
-        }
-
-        @RequiresApi(VERSION_CODES.O_MR1)
-        @Override
-        public WallpaperColors onComputeColors() {
-            // It's OK to return null here.
-            return mCachedWallpaperColors;
-        }
-
-        /**
-         * Invalidates the currently-drawn wallpaper image, causing the engine to reload the image from
-         * disk and draw the new wallpaper image.
-         */
-        public void invalidateAndRedrawWallpaper() {
-            // If a wallpaper load was already in flight, cancel it and restart a load in order to decode
-            // the new image.
-            if (mLoader != null) {
-                mLoader.cancel(true /* mayInterruptIfRunning */);
-                mLoader = null;
-            }
-
-            loadWallpaper(true /* needsDraw */);
-        }
-
-        void drawFrame(boolean forceRedraw) {
-            if (!mSurfaceValid) {
-                return;
-            }
-
-            Point screenSize = ScreenSizeCalculator.getInstance().getScreenSize(mDefaultDisplay);
-            int newRotation = mDefaultDisplay.getRotation();
-
-            // Sometimes a wallpaper is not large enough to cover the screen in one dimension.
-            // Call updateSurfaceSize -- it will only actually do the update if the dimensions
-            // should change
-            if (newRotation != mLastRotation) {
-                // Update surface size (if necessary)
-                if (!updateSurfaceSize(getSurfaceHolder(), mDefaultDisplay, true /* forDraw */)) {
-                    return;
-                }
-                mRotationAtLastSurfaceSizeUpdate = newRotation;
-                mDisplayWidthAtLastSurfaceSizeUpdate = screenSize.x;
-                mDisplayHeightAtLastSurfaceSizeUpdate = screenSize.y;
-            }
-            SurfaceHolder sh = getSurfaceHolder();
-            final Rect frame = sh.getSurfaceFrame();
-            final int dw = frame.width();
-            final int dh = frame.height();
-            boolean surfaceDimensionsChanged = dw != mLastSurfaceWidth
-                    || dh != mLastSurfaceHeight;
-
-            boolean redrawNeeded = surfaceDimensionsChanged || newRotation != mLastRotation
-                    || forceRedraw;
-            if (!redrawNeeded && !mOffsetsChanged) {
-                if (DEBUG) {
-                    Log.d(TAG, "Suppressed drawFrame since redraw is not needed "
-                            + "and offsets have not changed.");
-                }
-                return;
-            }
-            mLastRotation = newRotation;
-
-            // Load bitmap if its null and we're not using hardware acceleration.
-            if ((mIsHardwareAccelerated && !mIsOpenGlTextureLoaded) // Using OpenGL but texture not loaded
-                    || (!mIsHardwareAccelerated && mBackground == null)) { // Draw with Canvas but no bitmap
-                if (DEBUG) {
-                    Log.d(TAG, "Reloading bitmap: mBackground, bgw, bgh, dw, dh = "
-                            + mBackground + ", " + ((mBackground == null) ? 0 : mBackground.getWidth()) + ", "
-                            + ((mBackground == null) ? 0 : mBackground.getHeight()) + ", " + dw + ", " + dh);
-                }
-                loadWallpaper(true /* needDraw */);
-                if (DEBUG) {
-                    Log.d(TAG, "Reloading, resuming draw later");
-                }
-                return;
-            }
-
-            // Center the scaled image
-            mScale = Math.max(1f, Math.max(dw / (float) mBackgroundWidth,
-                    dh / (float) mBackgroundHeight));
-            final int availw = dw - (int) (mBackgroundWidth * mScale);
-            final int availh = dh - (int) (mBackgroundHeight * mScale);
-            int xPixels = availw / 2;
-            int yPixels = availh / 2;
-
-            // Adjust the image for xOffset/yOffset values. If window manager is handling offsets,
-            // mXOffset and mYOffset are set to 0.5f by default and therefore xPixels and yPixels
-            // will remain unchanged
-            final int availwUnscaled = dw - mBackgroundWidth;
-            final int availhUnscaled = dh - mBackgroundHeight;
-            if (availwUnscaled < 0) {
-                xPixels += (int) (availwUnscaled * (mXOffset - .5f) + .5f);
-            }
-            if (availhUnscaled < 0) {
-                yPixels += (int) (availhUnscaled * (mYOffset - .5f) + .5f);
-            }
-
-            mOffsetsChanged = false;
-            if (surfaceDimensionsChanged) {
-                mLastSurfaceWidth = dw;
-                mLastSurfaceHeight = dh;
-            }
-            if (!redrawNeeded && xPixels == mLastXTranslation && yPixels == mLastYTranslation) {
-                if (DEBUG) {
-                    Log.d(TAG, "Suppressed drawFrame since the image has not "
-                            + "actually moved an integral number of pixels.");
-                }
-                return;
-            }
-            mLastXTranslation = xPixels;
-            mLastYTranslation = yPixels;
-
-            if (DEBUG) {
-                Log.d(TAG, "Redrawing wallpaper");
-            }
-
-            if (mIsHardwareAccelerated) {
-                if (!drawWallpaperWithOpenGL(sh, availw, availh, xPixels, yPixels)) {
-                    drawWallpaperWithCanvas(sh, availw, availh, xPixels, yPixels);
-                } else {
-                    // If OpenGL drawing was successful, then we can safely discard a reference to the
-                    // wallpaper bitmap to save memory (since a copy has already been loaded into an OpenGL
-                    // texture).
-                    mBackground = null;
-                }
-            } else {
-                drawWallpaperWithCanvas(sh, availw, availh, xPixels, yPixels);
-            }
-        }
-
-        /**
-         * Loads the wallpaper on background thread and schedules updating the surface frame,
-         * and if {@param needsDraw} is set also draws a frame.
-         * <p>
-         * If loading is already in-flight, subsequent loads are ignored (but needDraw is or-ed to
-         * the active request).
-         * <p>
-         * If {@param needsReset} is set also clears the cache in WallpaperManager first.
-         */
-        private void loadWallpaper(boolean needsDraw) {
-            mNeedsDrawAfterLoadingWallpaper |= needsDraw;
-            if (mLoader != null) {
-                if (DEBUG) {
-                    Log.d(TAG, "Skipping loadWallpaper, already in flight ");
-                }
-                return;
-            }
-            mLoader = new AsyncTask<Void, Void, Bitmap>() {
-                @Override
-                protected Bitmap doInBackground(Void... params) {
-                    Throwable exception = null;
-                    try {
-                        // Decode bitmap of rotating image wallpaper.
-                        String wallpaperFilePath = isPreview()
-                                ? PREVIEW_WALLPAPER_FILE_PATH : ROTATING_WALLPAPER_FILE_PATH;
-                        Context context = isPreview() ? getApplicationContext()
-                                : getApplicationContext().createDeviceProtectedStorageContext();
-                        FileInputStream fileInputStream = context.openFileInput(wallpaperFilePath);
-                        Bitmap bitmap = BitmapFactory.decodeStream(fileInputStream);
-                        fileInputStream.close();
-                        return bitmap;
-                    } catch (RuntimeException | FileNotFoundException | OutOfMemoryError e) {
-                        Log.i(TAG, "couldn't decode stream: ", e);
-                        exception = e;
-                    } catch (IOException e) {
-                        Log.i(TAG, "couldn't close stream: ", e);
-                        exception = e;
-                    }
-
-                    if (isCancelled()) {
-                        return null;
-                    }
-
-                    if (exception != null) {
-                        // Note that if we do fail at this, and the default wallpaper can't
-                        // be loaded, we will go into a cycle.  Don't do a build where the
-                        // default wallpaper can't be loaded.
-                        Log.w(TAG, "Unable to load wallpaper!", exception);
-                        try {
-                            return ((BitmapDrawable) getFallbackDrawable()).getBitmap();
-                        } catch (OutOfMemoryError ex) {
-                            // now we're really screwed.
-                            Log.w(TAG, "Unable reset to default wallpaper!", ex);
-                        }
-
-                        if (isCancelled()) {
-                            return null;
-                        }
-                    }
-                    return null;
-                }
-
-                @Override
-                protected void onPostExecute(Bitmap b) {
-                    mBackground = null;
-                    mBackgroundWidth = -1;
-                    mBackgroundHeight = -1;
-
-                    if (b != null) {
-                        mBackground = b;
-                        mBackgroundWidth = mBackground.getWidth();
-                        mBackgroundHeight = mBackground.getHeight();
-
-                        if (VERSION.SDK_INT >= VERSION_CODES.O_MR1) {
-                            mCachedWallpaperColors = WallpaperColors.fromBitmap(mBackground);
-                            notifyColorsChanged();
-                        }
-                    }
-
-                    if (DEBUG) {
-                        Log.d(TAG, "Wallpaper loaded: " + mBackground);
-                    }
-                    updateSurfaceSize(getSurfaceHolder(), mDefaultDisplay,
-                            false /* forDraw */);
-                    if (mTexture != 0 && mEgl != null) {
-                        deleteTexture(mTexture);
-                    }
-                    // If background is absent (due to an error decoding the bitmap) then don't try to load
-                    // a texture.
-                    if (mEgl != null && mBackground != null) {
-                        mTexture = loadTexture(mBackground);
-                    }
-                    if (mNeedsDrawAfterLoadingWallpaper) {
-                        drawFrame(true /* forceRedraw */);
-                    }
-
-                    mLoader = null;
-                    mNeedsDrawAfterLoadingWallpaper = false;
-                }
-            }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
-        }
-
-        private Drawable getFallbackDrawable() {
-            Drawable drawable;
-            try {
-                drawable = mWallpaperManager.getDrawable();
-            } catch (java.lang.Exception e) {
-                // Work around Samsung bug where SecurityException is thrown if device is still using its
-                // default wallpaper, and around Android 7.0 bug where SELinux issues can cause a perfectly
-                // valid access of the current wallpaper to cause a failed Binder transaction manifest here
-                // as a RuntimeException.
-                drawable = mWallpaperManager.getBuiltInDrawable();
-            }
-            return drawable;
-        }
-
-        @Override
-        protected void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) {
-            super.dump(prefix, fd, out, args);
-
-            out.print(prefix);
-            out.println("ImageWallpaper.DrawableEngine:");
-            out.print(prefix);
-            out.print(" mBackground=");
-            out.print(mBackground);
-            out.print(" mBackgroundWidth=");
-            out.print(mBackgroundWidth);
-            out.print(" mBackgroundHeight=");
-            out.println(mBackgroundHeight);
-
-            out.print(prefix);
-            out.print(" mLastRotation=");
-            out.print(mLastRotation);
-            out.print(" mLastSurfaceWidth=");
-            out.print(mLastSurfaceWidth);
-            out.print(" mLastSurfaceHeight=");
-            out.println(mLastSurfaceHeight);
-
-            out.print(prefix);
-            out.print(" mXOffset=");
-            out.print(mXOffset);
-            out.print(" mYOffset=");
-            out.println(mYOffset);
-
-            out.print(prefix);
-            out.print(" mVisible=");
-            out.print(mVisible);
-            out.print(" mOffsetsChanged=");
-            out.println(mOffsetsChanged);
-
-            out.print(prefix);
-            out.print(" mLastXTranslation=");
-            out.print(mLastXTranslation);
-            out.print(" mLastYTranslation=");
-            out.print(mLastYTranslation);
-            out.print(" mScale=");
-            out.println(mScale);
-
-            out.print(prefix);
-            out.print(" mLastRequestedWidth=");
-            out.print(mLastRequestedWidth);
-            out.print(" mLastRequestedHeight=");
-            out.println(mLastRequestedHeight);
-
-            out.print(prefix);
-            out.println(" DisplayInfo at last updateSurfaceSize:");
-            out.print(prefix);
-            out.print("  rotation=");
-            out.print(mRotationAtLastSurfaceSizeUpdate);
-            out.print("  width=");
-            out.print(mDisplayWidthAtLastSurfaceSizeUpdate);
-            out.print("  height=");
-            out.println(mDisplayHeightAtLastSurfaceSizeUpdate);
-        }
-
-        private void drawWallpaperWithCanvas(SurfaceHolder sh, int w, int h, int left, int top) {
-            Canvas c = sh.lockCanvas();
-            if (c != null) {
-                try {
-                    if (DEBUG) {
-                        Log.d(TAG, "Redrawing: left=" + left + ", top=" + top);
-                    }
-
-                    final float right = left + mBackgroundWidth * mScale;
-                    final float bottom = top + mBackgroundHeight * mScale;
-                    if (w < 0 || h < 0) {
-                        c.save();
-                        c.clipOutRect(left, top, right, bottom);
-                        c.drawColor(0xff000000);
-                        c.restore();
-                    }
-                    if (mBackground != null) {
-                        RectF dest = new RectF(left, top, right, bottom);
-                        // add a filter bitmap?
-                        c.drawBitmap(mBackground, null, dest, null);
-                    }
-                } finally {
-                    sh.unlockCanvasAndPost(c);
-                }
-            }
-        }
-
-        private boolean drawWallpaperWithOpenGL(SurfaceHolder sh, int w, int h, int left, int top) {
-
-            mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext);
-
-            final float right = left + mBackgroundWidth * mScale;
-            final float bottom = top + mBackgroundHeight * mScale;
-
-            final Rect frame = sh.getSurfaceFrame();
-            final Matrix4f ortho = new Matrix4f();
-            ortho.loadOrtho(0.0f, frame.width(), frame.height(), 0.0f, -1.0f, 1.0f);
-
-            final FloatBuffer triangleVertices = createMesh(left, top, right, bottom);
-
-            final int attribPosition = GLES20.glGetAttribLocation(mProgram, "position");
-            final int attribTexCoords = GLES20.glGetAttribLocation(mProgram, "texCoords");
-            final int uniformTexture = GLES20.glGetUniformLocation(mProgram, "texture");
-            final int uniformProjection = GLES20.glGetUniformLocation(mProgram, "projection");
-
-            checkGlError();
-
-            GLES20.glViewport(0, 0, frame.width(), frame.height());
-            GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTexture);
-
-            GLES20.glUseProgram(mProgram);
-            GLES20.glEnableVertexAttribArray(attribPosition);
-            GLES20.glEnableVertexAttribArray(attribTexCoords);
-            GLES20.glUniform1i(uniformTexture, 0);
-            GLES20.glUniformMatrix4fv(uniformProjection, 1, false, ortho.getArray(), 0);
-
-            checkGlError();
-
-            if (w > 0 || h > 0) {
-                GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
-                GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
-            }
-
-            // drawQuad
-            triangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
-            GLES20.glVertexAttribPointer(attribPosition, 3, GLES20.GL_FLOAT, false,
-                    TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
-
-            triangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
-            GLES20.glVertexAttribPointer(attribTexCoords, 3, GLES20.GL_FLOAT, false,
-                    TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
-
-            GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
-
-            boolean status = mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
-            checkEglError();
-
-            return status;
-        }
-
-        private FloatBuffer createMesh(int left, int top, float right, float bottom) {
-            final float[] verticesData = {
-                    // X, Y, Z, U, V
-                    left, bottom, 0.0f, 0.0f, 1.0f,
-                    right, bottom, 0.0f, 1.0f, 1.0f,
-                    left, top, 0.0f, 0.0f, 0.0f,
-                    right, top, 0.0f, 1.0f, 0.0f,
-            };
-
-            final int bytes = verticesData.length * FLOAT_SIZE_BYTES;
-            final FloatBuffer triangleVertices = ByteBuffer.allocateDirect(bytes).order(
-                    ByteOrder.nativeOrder()).asFloatBuffer();
-            triangleVertices.put(verticesData).position(0);
-            return triangleVertices;
-        }
-
-        private int loadTexture(Bitmap bitmap) {
-            mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext);
-
-            int[] textures = new int[1];
-
-            GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
-            GLES20.glGenTextures(1, textures, 0);
-            checkGlError();
-
-            int texture = textures[0];
-            GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture);
-            checkGlError();
-
-            GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
-            GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
-
-            GLES20.glTexParameteri(
-                    GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
-            GLES20.glTexParameteri(
-                    GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
-
-            GLUtils.texImage2D(
-                    GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, bitmap, GLES20.GL_UNSIGNED_BYTE, 0);
-            checkGlError();
-
-            mIsOpenGlTextureLoaded = true;
-
-            return texture;
-        }
-
-        private int buildProgram(String vertex, String fragment) {
-            int vertexShader = buildShader(vertex, GLES20.GL_VERTEX_SHADER);
-            if (vertexShader == 0) {
-                return 0;
-            }
-
-            int fragmentShader = buildShader(fragment, GLES20.GL_FRAGMENT_SHADER);
-            if (fragmentShader == 0) {
-                return 0;
-            }
-
-            int program = GLES20.glCreateProgram();
-            GLES20.glAttachShader(program, vertexShader);
-            GLES20.glAttachShader(program, fragmentShader);
-            GLES20.glLinkProgram(program);
-            checkGlError();
-
-            GLES20.glDeleteShader(vertexShader);
-            GLES20.glDeleteShader(fragmentShader);
-
-            int[] status = new int[1];
-            GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, status, 0);
-            if (status[0] != GLES20.GL_TRUE) {
-                String error = GLES20.glGetProgramInfoLog(program);
-                Log.d(GL_LOG_TAG, "Error while linking program:\n" + error);
-                GLES20.glDeleteProgram(program);
-                return 0;
-            }
-
-            return program;
-        }
-
-        private int buildShader(String source, int type) {
-            int shader = GLES20.glCreateShader(type);
-
-            GLES20.glShaderSource(shader, source);
-            checkGlError();
-
-            GLES20.glCompileShader(shader);
-            checkGlError();
-
-            int[] status = new int[1];
-            GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, status, 0);
-            if (status[0] != GLES20.GL_TRUE) {
-                String error = GLES20.glGetShaderInfoLog(shader);
-                Log.d(GL_LOG_TAG, "Error while compiling shader:\n" + error);
-                GLES20.glDeleteShader(shader);
-                return 0;
-            }
-
-            return shader;
-        }
-
-        private void checkEglError() {
-            int error = mEgl.eglGetError();
-            if (error != EGL10.EGL_SUCCESS) {
-                Log.w(GL_LOG_TAG, "EGL error = " + GLUtils.getEGLErrorString(error));
-            }
-        }
-
-        private void checkGlError() {
-            int error = GLES20.glGetError();
-            if (error != GLES20.GL_NO_ERROR) {
-                Log.w(GL_LOG_TAG, "GL error = 0x" + Integer.toHexString(error), new Throwable());
-            }
-        }
-
-        private void deleteTexture(int texture) {
-            int[] textures = new int[1];
-            textures[0] = texture;
-
-            mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext);
-            GLES20.glDeleteTextures(1, textures, 0);
-            mTexture = 0;
-        }
-
-        private void finishGL(int texture, int program) {
-            if (mEgl == null) {
-                return;
-            }
-
-            mOpenGlContextCounter--;
-
-            mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext);
-            deleteTexture(mTexture);
-            GLES20.glDeleteProgram(program);
-            mEgl.eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
-            mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
-            mEgl.eglDestroyContext(mEglDisplay, mEglContext);
-            if (mOpenGlContextCounter == 0) {
-                mEgl.eglTerminate(mEglDisplay);
-            }
-
-            mEgl = null;
-        }
-
-        private boolean initGL(SurfaceHolder surfaceHolder) {
-            mEgl = (EGL10) EGLContext.getEGL();
-
-            mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
-            if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
-                throw new RuntimeException("eglGetDisplay failed "
-                        + GLUtils.getEGLErrorString(mEgl.eglGetError()));
-            }
-
-            int[] version = new int[2];
-            if (!mEgl.eglInitialize(mEglDisplay, version)) {
-                throw new RuntimeException("eglInitialize failed "
-                        + GLUtils.getEGLErrorString(mEgl.eglGetError()));
-            }
-
-            mOpenGlContextCounter++;
-
-            mEglConfig = chooseEglConfig();
-            if (mEglConfig == null) {
-                throw new RuntimeException("eglConfig not initialized");
-            }
-
-            mEglContext = createContext(mEgl, mEglDisplay, mEglConfig);
-            if (mEglContext == EGL_NO_CONTEXT) {
-                throw new RuntimeException("createContext failed "
-                        + GLUtils.getEGLErrorString(mEgl.eglGetError()));
-            }
-
-            int attribs[] = {
-                    EGL10.EGL_WIDTH, 1,
-                    EGL10.EGL_HEIGHT, 1,
-                    EGL10.EGL_NONE
-            };
-            EGLSurface tmpSurface = mEgl.eglCreatePbufferSurface(mEglDisplay, mEglConfig, attribs);
-            mEgl.eglMakeCurrent(mEglDisplay, tmpSurface, tmpSurface, mEglContext);
-
-            int[] maxSize = new int[1];
-            Rect frame = surfaceHolder.getSurfaceFrame();
-            GLES20.glGetIntegerv(GLES20.GL_MAX_TEXTURE_SIZE, maxSize, 0);
-
-            mEgl.eglMakeCurrent(
-                    mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
-            mEgl.eglDestroySurface(mEglDisplay, tmpSurface);
-
-            if (frame.width() > maxSize[0] || frame.height() > maxSize[0]) {
-                mEgl.eglDestroyContext(mEglDisplay, mEglContext);
-                mEgl.eglTerminate(mEglDisplay);
-                Log.e(GL_LOG_TAG, "requested  texture size " + frame.width() + "x" + frame.height()
-                        + " exceeds the support maximum of " + maxSize[0] + "x" + maxSize[0]);
-                return false;
-            }
-
-            mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, surfaceHolder, null);
-            if (mEglSurface == null || mEglSurface == EGL_NO_SURFACE) {
-                int error = mEgl.eglGetError();
-                if (error == EGL10.EGL_BAD_NATIVE_WINDOW || error == EGL10.EGL_BAD_ALLOC) {
-                    Log.e(GL_LOG_TAG, "createWindowSurface returned " + GLUtils.getEGLErrorString(error)
-                            + ".");
-                    return false;
-                }
-                throw new RuntimeException("createWindowSurface failed "
-                        + GLUtils.getEGLErrorString(error));
-            }
-
-            if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
-                throw new RuntimeException("eglMakeCurrent failed "
-                        + GLUtils.getEGLErrorString(mEgl.eglGetError()));
-            }
-
-            mProgram = buildProgram(S_SIMPLE_VS, S_SIMPLE_FS);
-            if (mBackground != null) {
-                mTexture = loadTexture(mBackground);
-            }
-
-            return true;
-        }
-
-
-        EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
-            int[] attribList = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE};
-            return egl.eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, attribList);
-        }
-
-        private EGLConfig chooseEglConfig() {
-            int[] configsCount = new int[1];
-            EGLConfig[] configs = new EGLConfig[1];
-            int[] configSpec = getConfig();
-            if (!mEgl.eglChooseConfig(mEglDisplay, configSpec, configs, 1, configsCount)) {
-                throw new IllegalArgumentException("eglChooseConfig failed "
-                        + GLUtils.getEGLErrorString(mEgl.eglGetError()));
-            } else if (configsCount[0] > 0) {
-                return configs[0];
-            }
-            return null;
-        }
-
-        private int[] getConfig() {
-            return new int[]{
-                    EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
-                    EGL10.EGL_RED_SIZE, 8,
-                    EGL10.EGL_GREEN_SIZE, 8,
-                    EGL10.EGL_BLUE_SIZE, 8,
-                    EGL10.EGL_ALPHA_SIZE, 0,
-                    EGL10.EGL_DEPTH_SIZE, 0,
-                    EGL10.EGL_STENCIL_SIZE, 0,
-                    EGL10.EGL_CONFIG_CAVEAT, EGL10.EGL_NONE,
-                    EGL10.EGL_NONE
-            };
-        }
-    }
-}
diff --git a/src/com/android/wallpaper/module/RotatingWallpaperComponentChecker.java b/src/com/android/wallpaper/module/RotatingWallpaperComponentChecker.java
deleted file mode 100755
index 8a2908a..0000000
--- a/src/com/android/wallpaper/module/RotatingWallpaperComponentChecker.java
+++ /dev/null
@@ -1,69 +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 androidx.annotation.IntDef;
-
-/**
- * Checks what component is responsible for presenting the rotating wallpaper image under the
- * device's launcher or keyguard for current and future rotations.
- */
-public interface RotatingWallpaperComponentChecker {
-
-    int ROTATING_WALLPAPER_COMPONENT_LIVE = 1;
-    int ROTATING_WALLPAPER_COMPONENT_STATIC = 2;
-
-    int ROTATING_WALLPAPER_SUPPORT_NOT_SUPPORTED = 0;
-    int ROTATING_WALLPAPER_SUPPORT_SUPPORTED = 1;
-
-    /**
-     * Returns the rotating wallpaper component for the current wallpaper rotation.
-     */
-    @RotatingWallpaperComponent
-    int getCurrentRotatingWallpaperComponent(Context context);
-
-    /**
-     * Returns the rotating wallpaper component for future wallpaper rotations.
-     */
-    @RotatingWallpaperComponent
-    int getNextRotatingWallpaperComponent(Context context);
-
-    /**
-     * Returns the support level for rotating wallpaper.
-     */
-    @RotatingWallpaperSupport
-    int getRotatingWallpaperSupport(Context context);
-
-    /**
-     * Possible components for presenting the rotating wallpaper image.
-     */
-    @IntDef({
-            ROTATING_WALLPAPER_COMPONENT_LIVE,
-            ROTATING_WALLPAPER_COMPONENT_STATIC})
-    @interface RotatingWallpaperComponent {
-    }
-
-    /**
-     * Possible support levels for rotating wallpaper.
-     */
-    @IntDef({
-            ROTATING_WALLPAPER_SUPPORT_NOT_SUPPORTED,
-            ROTATING_WALLPAPER_SUPPORT_SUPPORTED})
-    @interface RotatingWallpaperSupport {
-    }
-}
diff --git a/src/com/android/wallpaper/module/RotationWallpaperMoveReceiver.java b/src/com/android/wallpaper/module/RotationWallpaperMoveReceiver.java
deleted file mode 100644
index 975b0db..0000000
--- a/src/com/android/wallpaper/module/RotationWallpaperMoveReceiver.java
+++ /dev/null
@@ -1,77 +0,0 @@
-package com.android.wallpaper.module;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-
-import com.android.wallpaper.compat.BuildCompat;
-import com.android.wallpaper.util.DiskBasedLogger;
-import com.android.wallpaper.util.FileMover;
-
-import java.io.File;
-import java.io.IOException;
-
-/**
- * Receiver to run when the app was updated or on first boot to migrate previously existing rotating
- * wallpaper file to device protected storage so it can be read in direct-boot.
- * This is basically a no-op if there's no file in
- * {@link NoBackupImageWallpaper#ROTATING_WALLPAPER_FILE_PATH}.
- */
-public class RotationWallpaperMoveReceiver extends BroadcastReceiver {
-
-    private static final String TAG = "RotationWallpaperMoveReceiver";
-
-    @Override
-    public void onReceive(Context context, Intent intent) {
-        // This receiver is a no-op on pre-N Android and should only respond to a
-        // MY_PACKAGE_REPLACED intent.
-        if (intent.getAction() == null
-                || !(intent.getAction().equals(Intent.ACTION_MY_PACKAGE_REPLACED)
-                    || intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED))
-                || !BuildCompat.isAtLeastN()) {
-            DiskBasedLogger.e(
-                    TAG,
-                    "Unexpected action or Android version!",
-                    context);
-            throw new IllegalStateException(
-                    "Unexpected broadcast action or unsupported Android version");
-        }
-
-        // This is a no-op if there is no rotating wallpaper file in the files directory.
-        if (!context.getFileStreamPath(
-                NoBackupImageWallpaper.ROTATING_WALLPAPER_FILE_PATH).exists()) {
-            return;
-        }
-
-        PendingResult broadcastResult = goAsync();
-        new Thread(() -> {
-            Context appContext = context.getApplicationContext();
-            Context deviceProtectedContext = appContext.createDeviceProtectedStorageContext();
-            try {
-                File movedFile = FileMover.moveFileBetweenContexts(appContext,
-                        NoBackupImageWallpaper.ROTATING_WALLPAPER_FILE_PATH,
-                        deviceProtectedContext,
-                        NoBackupImageWallpaper.ROTATING_WALLPAPER_FILE_PATH);
-
-                if (movedFile != null) {
-                    // Notify NoBackupImageWallpaper of the change in case that's the currently
-                    // set wallpaper
-                    Intent intent1 = new Intent(appContext.getPackageName()
-                            + NoBackupImageWallpaper.ACTION_ROTATING_WALLPAPER_CHANGED);
-                    // Handled by a runtime-registered receiver in NoBackupImageWallpaper.
-                    intent1.setPackage(appContext.getPackageName());
-                    appContext.sendBroadcast(intent1);
-                }
-            } catch (IOException e) {
-                DiskBasedLogger.e(
-                        TAG,
-                        "Failed to move rotating wallpaper file to device protected storage: "
-                                + e.getMessage(),
-                        appContext);
-            } finally {
-                broadcastResult.finish();
-            }
-        }).start();
-
-    }
-}
diff --git a/src/com/android/wallpaper/module/RotationWallpaperUpdateReceiver.java b/src/com/android/wallpaper/module/RotationWallpaperUpdateReceiver.java
new file mode 100644
index 0000000..4fbc20d
--- /dev/null
+++ b/src/com/android/wallpaper/module/RotationWallpaperUpdateReceiver.java
@@ -0,0 +1,111 @@
+/*
+ * 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.module;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+
+import com.android.wallpaper.compat.BuildCompat;
+import com.android.wallpaper.util.DiskBasedLogger;
+import com.android.wallpaper.util.FileMover;
+
+import java.io.File;
+
+/**
+ * Receiver to run when the app was updated or on first boot to switch from live rotating wallpaper
+ * to static rotating wallpaper.
+ */
+public class RotationWallpaperUpdateReceiver extends BroadcastReceiver {
+
+
+    // Earlier versions of rotating wallpaper save the current rotation image as a file.
+    // We can infer from the extistance of this file whether or not user had rotating live wallpaper
+    private static final String ROTATING_WALLPAPER_FILE_PATH = "rotating_wallpaper.jpg";
+    private static final String TAG = "RotationWallpaperUpdateReceiver";
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        // This receiver is a no-op on pre-N Android and should only respond to a
+        // MY_PACKAGE_REPLACED intent.
+        if (intent.getAction() == null
+                || !(intent.getAction().equals(Intent.ACTION_MY_PACKAGE_REPLACED)
+                || intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED))
+                || !BuildCompat.isAtLeastN()) {
+            DiskBasedLogger.e(
+                    TAG,
+                    "Unexpected action or Android version!",
+                    context);
+            throw new IllegalStateException(
+                    "Unexpected broadcast action or unsupported Android version");
+        }
+
+        PendingResult broadcastResult = goAsync();
+        new Thread(() -> {
+            Context appContext = context.getApplicationContext();
+            Context deviceProtectedContext = appContext.createDeviceProtectedStorageContext();
+
+            if (context.getFileStreamPath(ROTATING_WALLPAPER_FILE_PATH).exists()) {
+                moveFileToProtectedStorage(appContext, deviceProtectedContext);
+            }
+
+            File wallpaperFile = deviceProtectedContext.getFileStreamPath(
+                    ROTATING_WALLPAPER_FILE_PATH);
+            if (wallpaperFile.exists()) {
+                switchToStaticWallpaper(appContext, wallpaperFile);
+            }
+            broadcastResult.finish();
+        }).start();
+    }
+
+    private void moveFileToProtectedStorage(Context context, Context deviceProtectedContext) {
+        try {
+            FileMover.moveFileBetweenContexts(context, ROTATING_WALLPAPER_FILE_PATH,
+                    deviceProtectedContext, ROTATING_WALLPAPER_FILE_PATH);
+        } catch (Exception ex) {
+            DiskBasedLogger.e(
+                    TAG,
+                    "Failed to move rotating wallpaper file to device protected storage: "
+                            + ex.getMessage(),
+                    context);
+        }
+    }
+
+    private void switchToStaticWallpaper(Context appContext, File wallpaperFile) {
+        try {
+            Injector injector = InjectorProvider.getInjector();
+            WallpaperPreferences wallpaperPreferences = injector.getPreferences(appContext);
+            if (wallpaperPreferences.getWallpaperPresentationMode()
+                    != WallpaperPreferences.PRESENTATION_MODE_ROTATING) {
+                return;
+            }
+            Bitmap bitmap = BitmapFactory.decodeFile(wallpaperFile.getAbsolutePath());
+
+            injector.getWallpaperPersister(appContext).setWallpaperInRotation(bitmap,
+                    wallpaperPreferences.getHomeWallpaperAttributions(),
+                    wallpaperPreferences.getHomeWallpaperActionLabelRes(),
+                    wallpaperPreferences.getHomeWallpaperActionIconRes(),
+                    wallpaperPreferences.getHomeWallpaperActionUrl(),
+                    wallpaperPreferences.getHomeWallpaperCollectionId());
+            wallpaperFile.delete();
+
+        } catch (Exception ex) {
+            DiskBasedLogger.e(TAG, "Unable to set static wallpaper", appContext);
+        }
+    }
+}
diff --git a/src/com/android/wallpaper/module/WallpaperPreferenceKeys.java b/src/com/android/wallpaper/module/WallpaperPreferenceKeys.java
index f63506f..e4c6588 100755
--- a/src/com/android/wallpaper/module/WallpaperPreferenceKeys.java
+++ b/src/com/android/wallpaper/module/WallpaperPreferenceKeys.java
@@ -28,12 +28,7 @@
     public static final String KEY_HOME_WALLPAPER_ACTION_LABEL_RES = "home_wallpaper_action_label";
     public static final String KEY_HOME_WALLPAPER_ACTION_ICON_RES = "home_wallpaper_action_icon";
     public static final String KEY_HOME_WALLPAPER_COLLECTION_ID = "home_wallpaper_collection_id";
-    public static final String KEY_HOME_WALLPAPER_BASE_IMAGE_URL = "home_wallpaper_base_image_url";
     public static final String KEY_HOME_WALLPAPER_HASH_CODE = "home_wallpaper_hash_code";
-    public static final String KEY_HOME_WALLPAPER_MANAGER_ID = "home_wallpaper_id";
-    public static final String KEY_HOME_WALLPAPER_PACKAGE_NAME = "home_wallpaper_package_name";
-    public static final String KEY_HOME_WALLPAPER_REMOTE_ID = "home_wallpaper_remote_id";
-    public static final String KEY_HOME_WALLPAPER_BACKING_FILE = "home_wallpaper_backing_file";
 
     public static final String KEY_LOCK_WALLPAPER_ATTRIB_1 = "lock_wallpaper_attribution_line_1";
     public static final String KEY_LOCK_WALLPAPER_ATTRIB_2 = "lock_wallpaper_attribution_line_2";
@@ -43,24 +38,35 @@
     public static final String KEY_LOCK_WALLPAPER_ACTION_ICON_RES = "lock_wallpaper_action_icon";
     public static final String KEY_LOCK_WALLPAPER_HASH_CODE = "lock_wallpaper_hash_code";
     public static final String KEY_LOCK_WALLPAPER_COLLECTION_ID = "lock_wallpaper_collection_id";
-    public static final String KEY_LOCK_WALLPAPER_MANAGER_ID = "lock_wallpaper_id";
-    public static final String KEY_LOCK_WALLPAPER_BACKING_FILE = "lock_wallpaper_backing_file";
 
-    public static final String KEY_DAILY_ROTATION_TIMESTAMPS = "daily_rotation_timestamps";
-    public static final String KEY_DAILY_WALLPAPER_ENABLED_TIMESTAMP =
-            "daily_wallpaper_enabled_timestamp";
-
-    public static final String KEY_LAST_DAILY_LOG_TIMESTAMP = "last_daily_log_timestamp";
-    public static final String KEY_LAST_APP_ACTIVE_TIMESTAMP = "last_app_active_timestamp";
-    public static final String KEY_LAST_ROTATION_STATUS = "last_rotation_status";
-    public static final String KEY_LAST_ROTATION_STATUS_TIMESTAMP = "last_rotation_status_timestamp";
-    public static final String KEY_LAST_SYNC_TIMESTAMP = "last_sync_timestamp";
-
-    public static final String KEY_PENDING_WALLPAPER_SET_STATUS = "pending_wallpaper_set_status";
-    public static final String KEY_PENDING_DAILY_WALLPAPER_UPDATE_STATUS =
-            "pending_daily_wallpaper_update_status";
-
-    public static final String KEY_NUM_DAYS_DAILY_ROTATION_FAILED = "num_days_daily_rotation_failed";
-    public static final String KEY_NUM_DAYS_DAILY_ROTATION_NOT_ATTEMPTED =
-            "num_days_daily_rotation_not_attempted";
+    /**
+     * Preferences with these keys should not be backed up
+     */
+    public interface NoBackupKeys {
+        public static final String KEY_HOME_WALLPAPER_BASE_IMAGE_URL =
+                "home_wallpaper_base_image_url";
+        public static final String KEY_HOME_WALLPAPER_MANAGER_ID = "home_wallpaper_id";
+        public static final String KEY_HOME_WALLPAPER_REMOTE_ID = "home_wallpaper_remote_id";
+        public static final String KEY_HOME_WALLPAPER_BACKING_FILE = "home_wallpaper_backing_file";
+        public static final String KEY_LOCK_WALLPAPER_MANAGER_ID = "lock_wallpaper_id";
+        public static final String KEY_LOCK_WALLPAPER_BACKING_FILE = "lock_wallpaper_backing_file";
+        public static final String KEY_DAILY_ROTATION_TIMESTAMPS = "daily_rotation_timestamps";
+        public static final String KEY_DAILY_WALLPAPER_ENABLED_TIMESTAMP =
+                "daily_wallpaper_enabled_timestamp";
+        public static final String KEY_LAST_DAILY_LOG_TIMESTAMP = "last_daily_log_timestamp";
+        public static final String KEY_LAST_APP_ACTIVE_TIMESTAMP = "last_app_active_timestamp";
+        public static final String KEY_LAST_ROTATION_STATUS = "last_rotation_status";
+        public static final String KEY_LAST_ROTATION_STATUS_TIMESTAMP =
+                "last_rotation_status_timestamp";
+        public static final String KEY_LAST_SYNC_TIMESTAMP = "last_sync_timestamp";
+        public static final String KEY_PENDING_WALLPAPER_SET_STATUS =
+                "pending_wallpaper_set_status";
+        public static final String KEY_PENDING_DAILY_WALLPAPER_UPDATE_STATUS =
+                "pending_daily_wallpaper_update_status";
+        public static final String KEY_NUM_DAYS_DAILY_ROTATION_FAILED =
+                "num_days_daily_rotation_failed";
+        public static final String KEY_NUM_DAYS_DAILY_ROTATION_NOT_ATTEMPTED =
+                "num_days_daily_rotation_not_attempted";
+        public static final String KEY_HOME_WALLPAPER_PACKAGE_NAME = "home_wallpaper_package_name";
+    }
 }
diff --git a/src/com/android/wallpaper/module/WallpaperSetter.java b/src/com/android/wallpaper/module/WallpaperSetter.java
index 830d914..a33a5f1 100644
--- a/src/com/android/wallpaper/module/WallpaperSetter.java
+++ b/src/com/android/wallpaper/module/WallpaperSetter.java
@@ -72,8 +72,9 @@
      * @param callback optional callback to be notified when the wallpaper is set.
      */
     public void setCurrentWallpaper(Activity containerActivity, WallpaperInfo wallpaper,
-            Asset wallpaperAsset, @Destination final int destination, float wallpaperScale,
-            @Nullable Rect cropRect, @Nullable SetWallpaperCallback callback) {
+            @Nullable Asset wallpaperAsset, @Destination final int destination,
+            float wallpaperScale, @Nullable Rect cropRect,
+            @Nullable SetWallpaperCallback callback) {
         if (wallpaper instanceof LiveWallpaperInfo) {
             setCurrentLiveWallpaper(containerActivity, (LiveWallpaperInfo) wallpaper, destination,
                     callback);
diff --git a/src/com/android/wallpaper/picker/ImagePreviewFragment.java b/src/com/android/wallpaper/picker/ImagePreviewFragment.java
new file mode 100755
index 0000000..a6babe3
--- /dev/null
+++ b/src/com/android/wallpaper/picker/ImagePreviewFragment.java
@@ -0,0 +1,370 @@
+/*
+ * 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.picker;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.Color;
+import android.graphics.Point;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.view.Display;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import androidx.annotation.Nullable;
+import androidx.fragment.app.FragmentActivity;
+
+import com.android.wallpaper.R;
+import com.android.wallpaper.asset.Asset;
+import com.android.wallpaper.module.WallpaperPersister.Destination;
+import com.android.wallpaper.module.WallpaperPersister.SetWallpaperCallback;
+import com.android.wallpaper.util.ScreenSizeCalculator;
+import com.android.wallpaper.util.WallpaperCropUtils;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.MemoryCategory;
+import com.davemorrissey.labs.subscaleview.ImageSource;
+import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView;
+import com.google.android.material.bottomsheet.BottomSheetBehavior;
+
+/**
+ * Fragment which displays the UI for previewing an individual static wallpaper and its attribution
+ * information.
+ */
+public class ImagePreviewFragment extends PreviewFragment {
+
+    private static final float DEFAULT_WALLPAPER_MAX_ZOOM = 8f;
+
+    private SubsamplingScaleImageView mFullResImageView;
+    private Asset mWallpaperAsset;
+    private Point mDefaultCropSurfaceSize;
+    private Point mScreenSize;
+    private Point mRawWallpaperSize; // Native size of wallpaper image.
+    private ImageView mLowResImageView;
+
+    private InfoPageController mInfoPageController;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mWallpaperAsset = mWallpaper.getAsset(requireContext().getApplicationContext());
+    }
+
+    @Override
+    protected int getLayoutResId() {
+        return R.layout.fragment_image_preview;
+    }
+
+
+    protected int getBottomSheetResId() {
+        return R.id.bottom_sheet;
+    }
+
+    @Override
+    protected int getLoadingIndicatorResId() {
+        return R.id.loading_indicator;
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                             Bundle savedInstanceState) {
+        View view = super.onCreateView(inflater, container, savedInstanceState);
+
+        Activity activity = requireActivity();
+
+        mFullResImageView = view.findViewById(R.id.full_res_image);
+
+        mInfoPageController = new InfoPageController(view.findViewById(R.id.page_info),
+                mPreviewMode);
+
+        mLowResImageView = view.findViewById(R.id.low_res_image);
+
+        // Trim some memory from Glide to make room for the full-size image in this fragment.
+        Glide.get(activity).setMemoryCategory(MemoryCategory.LOW);
+
+        mDefaultCropSurfaceSize = WallpaperCropUtils.getDefaultCropSurfaceSize(
+                getResources(), activity.getWindowManager().getDefaultDisplay());
+        mScreenSize = ScreenSizeCalculator.getInstance().getScreenSize(
+                activity.getWindowManager().getDefaultDisplay());
+
+        // Load a low-res placeholder image if there's a thumbnail available from the asset that can
+        // be shown to the user more quickly than the full-sized image.
+        if (mWallpaperAsset.hasLowResDataSource()) {
+            mWallpaperAsset.loadLowResDrawable(activity, mLowResImageView, Color.BLACK,
+                    new WallpaperPreviewBitmapTransformation(activity.getApplicationContext(),
+                            isRtl()));
+        }
+
+        mWallpaperAsset.decodeRawDimensions(getActivity(), dimensions -> {
+            // Don't continue loading the wallpaper if the Fragment is detached.
+            if (getActivity() == null) {
+                return;
+            }
+
+            // Return early and show a dialog if dimensions are null (signaling a decoding error).
+            if (dimensions == null) {
+                showLoadWallpaperErrorDialog();
+                return;
+            }
+
+            mRawWallpaperSize = dimensions;
+            setUpExploreIntent(ImagePreviewFragment.this::initFullResView);
+        });
+
+        setUpLoadingIndicator();
+
+        return view;
+    }
+
+    @Override
+    protected void setUpBottomSheetView(ViewGroup bottomSheet) {
+        // Nothing needed here.
+    }
+
+    @Override
+    protected boolean isLoaded() {
+        return mFullResImageView != null && mFullResImageView.hasImage();
+    }
+
+    @Override
+    public void onClickOk() {
+        FragmentActivity activity = getActivity();
+        if (activity != null) {
+            activity.finish();
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        if (mLoadingProgressBar != null) {
+            mLoadingProgressBar.hide();
+        }
+        mFullResImageView.recycle();
+    }
+
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+
+        final BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(mBottomSheet);
+        outState.putInt(KEY_BOTTOM_SHEET_STATE, bottomSheetBehavior.getState());
+    }
+
+    @Override
+    protected void setBottomSheetContentAlpha(float alpha) {
+        mInfoPageController.setContentAlpha(alpha);
+    }
+
+    @Override
+    protected CharSequence getExploreButtonLabel(Context context) {
+        return context.getString(mWallpaper.getActionLabelRes(context));
+    }
+
+    /**
+     * Initializes MosaicView by initializing tiling, setting a fallback page bitmap, and
+     * initializing a zoom-scroll observer and click listener.
+     */
+    private void initFullResView() {
+        mFullResImageView.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_CROP);
+
+        // Set a solid black "page bitmap" so MosaicView draws a black background while waiting
+        // for the image to load or a transparent one if a thumbnail already loaded.
+        Bitmap blackBitmap = Bitmap.createBitmap(1, 1, Config.ARGB_8888);
+        int color = (mLowResImageView.getDrawable() == null) ? Color.BLACK : Color.TRANSPARENT;
+        blackBitmap.setPixel(0, 0, color);
+        mFullResImageView.setImage(ImageSource.bitmap(blackBitmap));
+
+        // Then set a fallback "page bitmap" to cover the whole MosaicView, which is an actual
+        // (lower res) version of the image to be displayed.
+        Point targetPageBitmapSize = new Point(mRawWallpaperSize);
+        mWallpaperAsset.decodeBitmap(targetPageBitmapSize.x, targetPageBitmapSize.y,
+                pageBitmap -> {
+                    // Check that the activity is still around since the decoding task started.
+                    if (getActivity() == null) {
+                        return;
+                    }
+
+                    // Some of these may be null depending on if the Fragment is paused, stopped,
+                    // or destroyed.
+                    if (mLoadingProgressBar != null) {
+                        mLoadingProgressBar.hide();
+                    }
+                    // The page bitmap may be null if there was a decoding error, so show an
+                    // error dialog.
+                    if (pageBitmap == null) {
+                        showLoadWallpaperErrorDialog();
+                        return;
+                    }
+                    if (mFullResImageView != null) {
+                        // Set page bitmap.
+                        mFullResImageView.setImage(ImageSource.bitmap(pageBitmap));
+
+                        setDefaultWallpaperZoomAndScroll();
+                        crossFadeInMosaicView();
+                    }
+                    getActivity().invalidateOptionsMenu();
+
+                    populateInfoPage(mInfoPageController);
+                });
+    }
+
+    /**
+     * Makes the MosaicView visible with an alpha fade-in animation while fading out the loading
+     * indicator.
+     */
+    private void crossFadeInMosaicView() {
+        long shortAnimationDuration = getResources().getInteger(
+                android.R.integer.config_shortAnimTime);
+
+        mFullResImageView.setAlpha(0f);
+        mFullResImageView.animate()
+                .alpha(1f)
+                .setDuration(shortAnimationDuration)
+                .setListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        // Clear the thumbnail bitmap reference to save memory since it's no longer
+                        // visible.
+                        if (mLowResImageView != null) {
+                            mLowResImageView.setImageBitmap(null);
+                        }
+                    }
+                });
+
+        mLoadingProgressBar.animate()
+                .alpha(0f)
+                .setDuration(shortAnimationDuration)
+                .setListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        if (mLoadingProgressBar != null) {
+                            mLoadingProgressBar.hide();
+                        }
+                    }
+                });
+    }
+
+    /**
+     * Sets the default wallpaper zoom and scroll position based on a "crop surface"
+     * (with extra width to account for parallax) superimposed on the screen. Shows as much of the
+     * wallpaper as possible on the crop surface and align screen to crop surface such that the
+     * default preview matches what would be seen by the user in the left-most home screen.
+     *
+     * <p>This method is called once in the Fragment lifecycle after the wallpaper asset has loaded
+     * and rendered to the layout.
+     */
+    private void setDefaultWallpaperZoomAndScroll() {
+        // Determine minimum zoom to fit maximum visible area of wallpaper on crop surface.
+        float defaultWallpaperZoom =
+                WallpaperCropUtils.calculateMinZoom(mRawWallpaperSize, mDefaultCropSurfaceSize);
+        float minWallpaperZoom =
+                WallpaperCropUtils.calculateMinZoom(mRawWallpaperSize, mScreenSize);
+
+        Point screenToCropSurfacePosition = WallpaperCropUtils.calculateCenterPosition(
+                mDefaultCropSurfaceSize, mScreenSize, true /* alignStart */, isRtl());
+        Point zoomedWallpaperSize = new Point(
+                Math.round(mRawWallpaperSize.x * defaultWallpaperZoom),
+                Math.round(mRawWallpaperSize.y * defaultWallpaperZoom));
+        Point cropSurfaceToWallpaperPosition = WallpaperCropUtils.calculateCenterPosition(
+                zoomedWallpaperSize, mDefaultCropSurfaceSize, false /* alignStart */, isRtl());
+
+        // Set min wallpaper zoom and max zoom on MosaicView widget.
+        mFullResImageView.setMaxScale(Math.max(DEFAULT_WALLPAPER_MAX_ZOOM, defaultWallpaperZoom));
+        mFullResImageView.setMinScale(minWallpaperZoom);
+
+        // Set center to composite positioning between scaled wallpaper and screen.
+        PointF centerPosition = new PointF(
+                mRawWallpaperSize.x / 2f,
+                mRawWallpaperSize.y / 2f);
+        centerPosition.offset(-(screenToCropSurfacePosition.x + cropSurfaceToWallpaperPosition.x),
+                -(screenToCropSurfacePosition.y + cropSurfaceToWallpaperPosition.y));
+
+        mFullResImageView.setScaleAndCenter(minWallpaperZoom, centerPosition);
+    }
+
+    private Rect calculateCropRect() {
+        // Calculate Rect of wallpaper in physical pixel terms (i.e., scaled to current zoom).
+        float wallpaperZoom = mFullResImageView.getScale();
+        int scaledWallpaperWidth = (int) (mRawWallpaperSize.x * wallpaperZoom);
+        int scaledWallpaperHeight = (int) (mRawWallpaperSize.y * wallpaperZoom);
+        Rect rect = new Rect();
+        mFullResImageView.visibleFileRect(rect);
+        int scrollX = (int) (rect.left * wallpaperZoom);
+        int scrollY = (int) (rect.top * wallpaperZoom);
+
+        rect.set(0, 0, scaledWallpaperWidth, scaledWallpaperHeight);
+
+        Display defaultDisplay =  requireActivity().getWindowManager().getDefaultDisplay();
+        Point screenSize = ScreenSizeCalculator.getInstance().getScreenSize(defaultDisplay);
+        // Crop rect should start off as the visible screen and then include extra width and height
+        // if available within wallpaper at the current zoom.
+        Rect cropRect = new Rect(scrollX, scrollY, scrollX + screenSize.x, scrollY + screenSize.y);
+
+        Point defaultCropSurfaceSize = WallpaperCropUtils.getDefaultCropSurfaceSize(
+                getResources(), defaultDisplay);
+        int extraWidth = defaultCropSurfaceSize.x - screenSize.x;
+        int extraHeightTopAndBottom = (int) ((defaultCropSurfaceSize.y - screenSize.y) / 2f);
+
+        // Try to increase size of screenRect to include extra width depending on the layout
+        // direction.
+        if (isRtl()) {
+            cropRect.left = Math.max(cropRect.left - extraWidth, rect.left);
+        } else {
+            cropRect.right = Math.min(cropRect.right + extraWidth, rect.right);
+        }
+
+        // Try to increase the size of the cropRect to to include extra height.
+        int availableExtraHeightTop = cropRect.top - Math.max(
+                rect.top,
+                cropRect.top - extraHeightTopAndBottom);
+        int availableExtraHeightBottom = Math.min(
+                rect.bottom,
+                cropRect.bottom + extraHeightTopAndBottom) - cropRect.bottom;
+
+        int availableExtraHeightTopAndBottom =
+                Math.min(availableExtraHeightTop, availableExtraHeightBottom);
+        cropRect.top -= availableExtraHeightTopAndBottom;
+        cropRect.bottom += availableExtraHeightTopAndBottom;
+
+        return cropRect;
+    }
+
+    @Override
+    protected void setCurrentWallpaper(@Destination int destination) {
+        mWallpaperSetter.setCurrentWallpaper(getActivity(), mWallpaper, mWallpaperAsset,
+                destination, mFullResImageView.getScale(), calculateCropRect(),
+                new SetWallpaperCallback() {
+                    @Override
+                    public void onSuccess() {
+                        finishActivityWithResultOk();
+                    }
+
+                    @Override
+                    public void onError(@Nullable Throwable throwable) {
+                        showSetWallpaperErrorDialog(destination);
+                    }
+                });
+    }
+}
diff --git a/src/com/android/wallpaper/picker/LivePreviewFragment.java b/src/com/android/wallpaper/picker/LivePreviewFragment.java
new file mode 100644
index 0000000..d577fa7
--- /dev/null
+++ b/src/com/android/wallpaper/picker/LivePreviewFragment.java
@@ -0,0 +1,597 @@
+/*
+ * 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.picker;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.WallpaperColors;
+import android.app.WallpaperInfo;
+import android.app.WallpaperManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.graphics.Rect;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.service.wallpaper.IWallpaperConnection;
+import android.service.wallpaper.IWallpaperEngine;
+import android.service.wallpaper.IWallpaperService;
+import android.service.wallpaper.WallpaperService;
+import android.service.wallpaper.WallpaperSettingsActivity;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Pair;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager.LayoutParams;
+import android.view.animation.AnimationUtils;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.lifecycle.LiveData;
+import androidx.slice.Slice;
+import androidx.slice.widget.SliceLiveData;
+import androidx.slice.widget.SliceView;
+import androidx.viewpager.widget.PagerAdapter;
+import androidx.viewpager.widget.ViewPager;
+
+import com.android.wallpaper.R;
+import com.android.wallpaper.compat.BuildCompat;
+import com.android.wallpaper.model.LiveWallpaperInfo;
+import com.android.wallpaper.module.WallpaperPersister.SetWallpaperCallback;
+
+import com.google.android.material.tabs.TabLayout;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Fragment which displays the UI for previewing an individual live wallpaper, its attribution
+ * information and settings slices if available.
+ */
+public class LivePreviewFragment extends PreviewFragment {
+
+    private static final String TAG = "LivePreviewFragment";
+
+    private static final String KEY_ACTION_DELETE_LIVE_WALLPAPER = "action_delete_live_wallpaper";
+    private static final String EXTRA_LIVE_WALLPAPER_INFO = "android.live_wallpaper.info";
+
+    /**
+     * Instance of {@link WallpaperConnection} used to bind to the live wallpaper service to show
+     * it in this preview fragment.
+     * @see IWallpaperConnection
+     */
+    protected WallpaperConnection mWallpaperConnection;
+
+    private Intent mWallpaperIntent;
+    private Intent mDeleteIntent;
+    private Intent mSettingsIntent;
+
+    private List<Pair<String, View>> mPages;
+    private ViewPager mViewPager;
+    private TabLayout mTabLayout;
+    private SliceView mSettingsSliceView;
+    private LiveData<Slice> mSettingsLiveData;
+    private View mLoadingScrim;
+    private InfoPageController mInfoPageController;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        android.app.WallpaperInfo info = mWallpaper.getWallpaperComponent();
+        mWallpaperIntent = getWallpaperIntent(info);
+        setUpExploreIntent(null);
+
+        android.app.WallpaperInfo currentWallpaper =
+                WallpaperManager.getInstance(requireContext()).getWallpaperInfo();
+        String deleteAction = getDeleteAction(info, currentWallpaper);
+
+        if (!TextUtils.isEmpty(deleteAction)) {
+            mDeleteIntent = new Intent(deleteAction);
+            mDeleteIntent.setPackage(info.getPackageName());
+            mDeleteIntent.putExtra(EXTRA_LIVE_WALLPAPER_INFO, info);
+        }
+
+        String settingsActivity = getSettingsActivity(info);
+        if (settingsActivity != null) {
+            mSettingsIntent = new Intent();
+            mSettingsIntent.setComponent(new ComponentName(info.getPackageName(),
+                    settingsActivity));
+            mSettingsIntent.putExtra(WallpaperSettingsActivity.EXTRA_PREVIEW_MODE, true);
+            PackageManager pm = requireContext().getPackageManager();
+            ActivityInfo activityInfo = mSettingsIntent.resolveActivityInfo(pm, 0);
+            if (activityInfo == null) {
+                Log.i(TAG, "Couldn't find wallpaper settings activity: " + settingsActivity);
+                mSettingsIntent = null;
+            }
+        }
+    }
+
+    @Nullable
+    protected String getSettingsActivity(WallpaperInfo info) {
+        return info.getSettingsActivity();
+    }
+
+    protected Intent getWallpaperIntent(WallpaperInfo info) {
+        return new Intent(WallpaperService.SERVICE_INTERFACE)
+                .setClassName(info.getPackageName(), info.getServiceName());
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        mPages = new ArrayList<>();
+        View view = super.onCreateView(inflater, container, savedInstanceState);
+        if (view == null) {
+            return null;
+        }
+
+        Activity activity = requireActivity();
+
+        mLoadingScrim = view.findViewById(R.id.loading);
+        setUpLoadingIndicator();
+
+        mWallpaperConnection = new WallpaperConnection(mWallpaperIntent, activity,
+                getWallpaperConnectionListener());
+        container.post(() -> {
+            if (!mWallpaperConnection.connect()) {
+                mWallpaperConnection = null;
+            }
+        });
+
+        return view;
+    }
+
+    @Override
+    public void onDestroyView() {
+        super.onDestroyView();
+        if (mSettingsLiveData != null && mSettingsLiveData.hasObservers()) {
+            mSettingsLiveData.removeObserver(mSettingsSliceView);
+            mSettingsLiveData = null;
+        }
+        if (mWallpaperConnection != null) {
+            mWallpaperConnection.disconnect();
+        }
+        mWallpaperConnection = null;
+        super.onDestroy();
+    }
+
+    @Override
+    protected void setUpBottomSheetView(ViewGroup bottomSheet) {
+
+        initInfoPage();
+        initSettingsPage();
+
+        mViewPager = bottomSheet.findViewById(R.id.viewpager);
+        mTabLayout = bottomSheet.findViewById(R.id.tablayout);
+
+        // Create PagerAdapter
+        final PagerAdapter pagerAdapter = new PagerAdapter() {
+            @Override
+            public Object instantiateItem(ViewGroup container, int position) {
+                final View page = mPages.get(position).second;
+                container.addView(page);
+                return page;
+            }
+
+            @Override
+            public void destroyItem(@NonNull ViewGroup container, int position,
+                    @NonNull Object object) {
+                if (object instanceof View) {
+                    container.removeView((View) object);
+                }
+            }
+
+            @Override
+            public int getCount() {
+                return mPages.size();
+            }
+
+            @Override
+            public CharSequence getPageTitle(int position) {
+                try {
+                    return mPages.get(position).first;
+                } catch (IndexOutOfBoundsException e) {
+                    return null;
+                }
+            }
+
+            @Override
+            public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
+                return (view == object);
+            }
+        };
+
+        // Add OnPageChangeListener to re-measure ViewPager's height
+        mViewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
+            @Override
+            public void onPageSelected(int position) {
+                mViewPager.requestLayout();
+            }
+        });
+
+        // Set PagerAdapter
+        mViewPager.setAdapter(pagerAdapter);
+
+        // Make TabLayout visible if there are more than one page
+        if (mPages.size() > 1) {
+            mTabLayout.setVisibility(View.VISIBLE);
+            mTabLayout.setupWithViewPager(mViewPager);
+        }
+        mViewPager.setCurrentItem(0);
+    }
+
+    protected WallpaperConnectionListener getWallpaperConnectionListener() {
+        return null;
+    }
+
+    @Override
+    protected boolean isLoaded() {
+        return mWallpaperConnection != null && mWallpaperConnection.isEngineReady();
+    }
+
+    private void initInfoPage() {
+        View pageInfo = InfoPageController.createView(getLayoutInflater());
+        mInfoPageController = new InfoPageController(pageInfo, mPreviewMode);
+        mPages.add(Pair.create(getString(R.string.tab_info), pageInfo));
+    }
+
+    private void initSettingsPage() {
+        final Uri uriSettingsSlice = getSettingsSliceUri(mWallpaper.getWallpaperComponent());
+        if (uriSettingsSlice == null) {
+            return;
+        }
+
+        final View pageSettings = getLayoutInflater().inflate(R.layout.preview_page_settings,
+                null /* root */);
+
+        mSettingsSliceView = pageSettings.findViewById(R.id.settings_slice);
+        mSettingsSliceView.setMode(SliceView.MODE_LARGE);
+        mSettingsSliceView.setScrollable(false);
+
+        // Set LiveData for SliceView
+        mSettingsLiveData = SliceLiveData.fromUri(requireContext() /* context */, uriSettingsSlice);
+        mSettingsLiveData.observeForever(mSettingsSliceView);
+
+        pageSettings.findViewById(R.id.preview_settings_pane_set_wallpaper_button)
+                .setOnClickListener(this::onSetWallpaperClicked);
+
+        mPages.add(Pair.create(getResources().getString(R.string.tab_customize), pageSettings));
+    }
+
+    @Override
+    protected CharSequence getExploreButtonLabel(Context context) {
+        CharSequence exploreLabel = ((LiveWallpaperInfo) mWallpaper).getActionDescription(context);
+        if (TextUtils.isEmpty(exploreLabel)) {
+            exploreLabel = context.getString(mWallpaper.getActionLabelRes(context));
+        }
+        return exploreLabel;
+    }
+
+    @SuppressLint("NewApi") //Already checking with isAtLeastQ
+    protected Uri getSettingsSliceUri(android.app.WallpaperInfo info) {
+        if (BuildCompat.isAtLeastQ()) {
+            return info.getSettingsSliceUri();
+        }
+        return null;
+    }
+
+    @Override
+    protected int getLayoutResId() {
+        return R.layout.fragment_live_preview;
+    }
+
+    @Override
+    protected int getBottomSheetResId() {
+        return R.id.bottom_sheet;
+    }
+
+    @Override
+    protected int getLoadingIndicatorResId() {
+        return R.id.loading_indicator;
+    }
+
+    @Override
+    protected void setCurrentWallpaper(int destination) {
+        mWallpaperSetter.setCurrentWallpaper(getActivity(), mWallpaper, null,
+                destination, 0, null, new SetWallpaperCallback() {
+                    @Override
+                    public void onSuccess() {
+                        finishActivityWithResultOk();
+                    }
+
+                    @Override
+                    public void onError(@Nullable Throwable throwable) {
+                        showSetWallpaperErrorDialog(destination);
+                    }
+                });
+    }
+
+    @Override
+    protected void setBottomSheetContentAlpha(float alpha) {
+        mInfoPageController.setContentAlpha(alpha);
+    }
+
+
+    @Nullable
+    protected String getDeleteAction(android.app.WallpaperInfo wallpaperInfo,
+            @Nullable android.app.WallpaperInfo currentInfo) {
+        ServiceInfo serviceInfo = wallpaperInfo.getServiceInfo();
+        if (!isPackagePreInstalled(serviceInfo.applicationInfo)) {
+            Log.d(TAG, "This wallpaper is not pre-installed: " + serviceInfo.name);
+            return null;
+        }
+
+        ServiceInfo currentService = currentInfo == null ? null : currentInfo.getServiceInfo();
+        // A currently set Live wallpaper should not be deleted.
+        if (currentService != null && TextUtils.equals(serviceInfo.name, currentService.name)) {
+            return null;
+        }
+
+        final Bundle metaData = serviceInfo.metaData;
+        if (metaData != null) {
+            return metaData.getString(KEY_ACTION_DELETE_LIVE_WALLPAPER);
+        }
+        return null;
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        if (mWallpaperConnection != null) {
+            mWallpaperConnection.setVisibility(true);
+        }
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        if (mWallpaperConnection != null) {
+            mWallpaperConnection.setVisibility(false);
+        }
+    }
+
+    @Override
+    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        super.onCreateOptionsMenu(menu, inflater);
+        menu.findItem(R.id.configure).setVisible(mSettingsIntent != null);
+        menu.findItem(R.id.delete_wallpaper).setVisible(mDeleteIntent != null);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        int id = item.getItemId();
+        if (id == R.id.configure) {
+            if (getActivity() != null) {
+                startActivity(mSettingsIntent);
+                return true;
+            }
+        } else if (id == R.id.delete_wallpaper) {
+            showDeleteConfirmDialog();
+            return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+
+    private void showDeleteConfirmDialog() {
+        final AlertDialog alertDialog = new AlertDialog.Builder(
+                new ContextThemeWrapper(getContext(), getDeviceDefaultTheme()))
+                .setMessage(R.string.delete_wallpaper_confirmation)
+                .setPositiveButton(R.string.delete_live_wallpaper,
+                        (dialog, which) -> deleteLiveWallpaper())
+                .setNegativeButton(android.R.string.cancel, null /* listener */)
+                .create();
+        alertDialog.show();
+    }
+
+    private void deleteLiveWallpaper() {
+        if (mDeleteIntent != null) {
+            requireContext().startService(mDeleteIntent);
+            finishActivityWithResultOk();
+        }
+    }
+
+    private boolean isPackagePreInstalled(ApplicationInfo info) {
+        if (info != null && (info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Interface to be notified of connect/disconnect events from {@link WallpaperConnection}
+     */
+    public interface WallpaperConnectionListener {
+        /**
+         * Called after the Wallpaper service has been bound.
+         */
+        void onConnected();
+
+        /**
+         * Called after the Wallpaper engine has been terminated and the service has been unbound.
+         */
+        void onDisconnected();
+    }
+
+    protected class WallpaperConnection extends IWallpaperConnection.Stub
+            implements ServiceConnection {
+
+        private final Activity mActivity;
+        private final Intent mIntent;
+        private final WallpaperConnectionListener mListener;
+        private IWallpaperService mService;
+        private IWallpaperEngine mEngine;
+        private boolean mConnected;
+        private boolean mIsVisible;
+        private boolean mIsEngineVisible;
+        private boolean mEngineReady;
+
+        WallpaperConnection(Intent intent, Activity activity,
+                @Nullable WallpaperConnectionListener listener) {
+            mActivity = activity;
+            mIntent = intent;
+            mListener = listener;
+        }
+
+        public boolean connect() {
+            synchronized (this) {
+                if (!mActivity.bindService(mIntent, this,
+                        Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT)) {
+                    return false;
+                }
+
+                mConnected = true;
+            }
+            if (mListener != null) {
+                mListener.onConnected();
+            }
+            return true;
+        }
+
+        public void disconnect() {
+            synchronized (this) {
+                mConnected = false;
+                if (mEngine != null) {
+                    try {
+                        mEngine.destroy();
+                    } catch (RemoteException e) {
+                        // Ignore
+                    }
+                    mEngine = null;
+                }
+                try {
+                    mActivity.unbindService(this);
+                } catch (IllegalArgumentException e) {
+                    Log.w(TAG, "Can't unbind wallpaper service. "
+                            + "It might have crashed, just ignoring.", e);
+                }
+                mService = null;
+            }
+            if (mListener != null) {
+                mListener.onDisconnected();
+            }
+        }
+
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            if (mWallpaperConnection == this) {
+                mService = IWallpaperService.Stub.asInterface(service);
+                try {
+                    View root = mActivity.getWindow().getDecorView();
+                    int displayId = root.getDisplay().getDisplayId();
+                    mService.attach(this, root.getWindowToken(),
+                            LayoutParams.TYPE_APPLICATION_MEDIA,
+                            true, root.getWidth(), root.getHeight(),
+                            new Rect(0, 0, 0, 0), displayId);
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Failed attaching wallpaper; clearing", e);
+                }
+            }
+        }
+
+        public void onServiceDisconnected(ComponentName name) {
+            mService = null;
+            mEngine = null;
+            if (mWallpaperConnection == this) {
+                Log.w(TAG, "Wallpaper service gone: " + name);
+            }
+        }
+
+        public void attachEngine(IWallpaperEngine engine, int displayId) {
+            synchronized (this) {
+                if (mConnected) {
+                    mEngine = engine;
+                    if (mIsVisible) {
+                        setEngineVisibility(true);
+                    }
+                } else {
+                    try {
+                        engine.destroy();
+                    } catch (RemoteException e) {
+                        // Ignore
+                    }
+                }
+            }
+        }
+
+        public IWallpaperEngine getEngine() {
+            return mEngine;
+        }
+
+        public ParcelFileDescriptor setWallpaper(String name) {
+            return null;
+        }
+
+        @Override
+        public void onWallpaperColorsChanged(WallpaperColors colors, int displayId)
+                throws RemoteException {
+
+        }
+
+        @Override
+        public void engineShown(IWallpaperEngine engine)  {
+            mLoadingScrim.post(() -> {
+                mLoadingScrim.animate()
+                        .alpha(0f)
+                        .setDuration(220)
+                        .setStartDelay(300)
+                        .setInterpolator(AnimationUtils.loadInterpolator(mActivity,
+                                android.R.interpolator.fast_out_linear_in))
+                        .withEndAction(() -> {
+                            if (mLoadingProgressBar != null) {
+                                mLoadingProgressBar.hide();
+                            }
+                            mLoadingScrim.setVisibility(View.INVISIBLE);
+                            populateInfoPage(mInfoPageController);
+                        });
+            });
+            mEngineReady = true;
+        }
+
+        public boolean isEngineReady() {
+            return mEngineReady;
+        }
+
+        public void setVisibility(boolean visible) {
+            mIsVisible = visible;
+            setEngineVisibility(visible);
+        }
+
+        private void setEngineVisibility(boolean visible) {
+            if (mEngine != null && visible != mIsEngineVisible) {
+                try {
+                    mEngine.setVisibility(visible);
+                    mIsEngineVisible = visible;
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Failure setting wallpaper visibility ", e);
+                }
+            }
+        }
+    }
+}
diff --git a/src/com/android/wallpaper/picker/PreviewActivity.java b/src/com/android/wallpaper/picker/PreviewActivity.java
index 457de69..6785afe 100755
--- a/src/com/android/wallpaper/picker/PreviewActivity.java
+++ b/src/com/android/wallpaper/picker/PreviewActivity.java
@@ -18,15 +18,16 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.Bundle;
+import android.view.View;
+
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
 
 import com.android.wallpaper.R;
 import com.android.wallpaper.model.InlinePreviewIntentFactory;
 import com.android.wallpaper.model.WallpaperInfo;
 import com.android.wallpaper.module.InjectorProvider;
 
-import androidx.fragment.app.Fragment;
-import androidx.fragment.app.FragmentManager;
-
 /**
  * Activity that displays a preview of a specific wallpaper and provides the ability to set the
  * wallpaper as the user's current wallpaper.
@@ -46,11 +47,10 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_preview);
-    }
-
-    @Override
-    public void onAttachedToWindow() {
-        super.onAttachedToWindow();
+        getWindow().getDecorView().setSystemUiVisibility(
+                View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+                        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+                        | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
 
         FragmentManager fm = getSupportFragmentManager();
         Fragment fragment = fm.findFragmentById(R.id.fragment_container);
@@ -70,6 +70,7 @@
         }
     }
 
+
     /**
      * Implementation that provides an intent to start a PreviewActivity.
      */
diff --git a/src/com/android/wallpaper/picker/PreviewFragment.java b/src/com/android/wallpaper/picker/PreviewFragment.java
index c698c8c..d0d8202 100755
--- a/src/com/android/wallpaper/picker/PreviewFragment.java
+++ b/src/com/android/wallpaper/picker/PreviewFragment.java
@@ -18,77 +18,54 @@
 import static com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_COLLAPSED;
 import static com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.annotation.TargetApi;
 import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.res.Configuration;
+import android.content.res.ColorStateList;
 import android.content.res.Resources.NotFoundException;
 import android.content.res.TypedArray;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
-import android.graphics.Color;
-import android.graphics.Point;
-import android.graphics.PointF;
-import android.graphics.PorterDuff.Mode;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
+import android.graphics.Insets;
 import android.graphics.drawable.GradientDrawable;
 import android.net.Uri;
 import android.os.Bundle;
 import android.util.Log;
 import android.view.ContextThemeWrapper;
-import android.view.Display;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
-import android.view.Surface;
 import android.view.View;
+import android.view.View.OnClickListener;
 import android.view.ViewGroup;
-import android.view.Window;
+import android.view.WindowInsets;
 import android.widget.Button;
 import android.widget.CheckBox;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
 import android.widget.TextView;
 import android.widget.Toast;
 
+import androidx.annotation.CallSuper;
+import androidx.annotation.IdRes;
 import androidx.annotation.IntDef;
+import androidx.annotation.LayoutRes;
 import androidx.annotation.Nullable;
 import androidx.appcompat.app.AppCompatActivity;
 import androidx.appcompat.widget.Toolbar;
 import androidx.core.view.ViewCompat;
+import androidx.core.widget.ContentLoadingProgressBar;
 import androidx.fragment.app.Fragment;
 import androidx.fragment.app.FragmentActivity;
 
 import com.android.wallpaper.R;
-import com.android.wallpaper.asset.Asset;
-import com.android.wallpaper.asset.Asset.BitmapReceiver;
-import com.android.wallpaper.asset.Asset.DimensionsReceiver;
-import com.android.wallpaper.compat.BuildCompat;
 import com.android.wallpaper.model.LiveWallpaperInfo;
 import com.android.wallpaper.model.WallpaperInfo;
 import com.android.wallpaper.module.ExploreIntentChecker;
 import com.android.wallpaper.module.Injector;
 import com.android.wallpaper.module.InjectorProvider;
 import com.android.wallpaper.module.UserEventLogger;
-import com.android.wallpaper.module.WallpaperPersister;
 import com.android.wallpaper.module.WallpaperPersister.Destination;
-import com.android.wallpaper.module.WallpaperPersister.SetWallpaperCallback;
 import com.android.wallpaper.module.WallpaperPreferences;
 import com.android.wallpaper.module.WallpaperSetter;
-import com.android.wallpaper.util.ScreenSizeCalculator;
-import com.android.wallpaper.util.WallpaperCropUtils;
-import com.android.wallpaper.widget.MaterialProgressDrawable;
 
-import com.bumptech.glide.Glide;
-import com.bumptech.glide.MemoryCategory;
-import com.davemorrissey.labs.subscaleview.ImageSource;
-import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView;
 import com.google.android.material.bottomsheet.BottomSheetBehavior;
 import com.google.android.material.bottomsheet.BottomSheetBehavior.State;
 
@@ -96,23 +73,23 @@
 import java.util.List;
 
 /**
- * Fragment which displays the UI for previewing an individual wallpaper and its attribution
- * information.
+ * Base Fragment to display the UI for previewing an individual wallpaper
  */
-public class PreviewFragment extends Fragment implements
+public abstract class PreviewFragment extends Fragment implements
         SetWallpaperDialogFragment.Listener, SetWallpaperErrorDialogFragment.Listener,
         LoadWallpaperErrorDialogFragment.Listener {
 
     /**
-     * User can view wallpaper and attributions in full screen, but "Set wallpaper" button is hidden.
+     * User can view wallpaper and attributions in full screen, but "Set wallpaper" button is
+     * hidden.
      */
-    public static final int MODE_VIEW_ONLY = 0;
+    static final int MODE_VIEW_ONLY = 0;
 
     /**
      * User can view wallpaper and attributions in full screen and click "Set wallpaper" to set the
      * wallpaper with pan and crop position to the device.
      */
-    public static final int MODE_CROP_AND_SET_WALLPAPER = 1;
+    static final int MODE_CROP_AND_SET_WALLPAPER = 1;
 
     /**
      * Possible preview modes for the fragment.
@@ -126,52 +103,57 @@
     public static final String ARG_WALLPAPER = "wallpaper";
     public static final String ARG_PREVIEW_MODE = "preview_mode";
     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 testingModeEnabled) {
+
+        boolean isLive = wallpaperInfo instanceof LiveWallpaperInfo;
+
+        Bundle args = new Bundle();
+        args.putParcelable(ARG_WALLPAPER, wallpaperInfo);
+        args.putInt(ARG_PREVIEW_MODE, mode);
+        args.putBoolean(ARG_TESTING_MODE_ENABLED, testingModeEnabled);
+
+        PreviewFragment fragment = isLive ? 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 =
             "set_wallpaper_error_dialog";
     private static final int UNUSED_REQUEST_CODE = 1;
-    private static final float DEFAULT_WALLPAPER_MAX_ZOOM = 8f;
     private static final String TAG = "PreviewFragment";
-    private static final float PAGE_BITMAP_MAX_HEAP_RATIO = 0.25f;
-    private static final String KEY_BOTTOM_SHEET_STATE = "key_bottom_sheet_state";
+    static final String KEY_BOTTOM_SHEET_STATE = "key_bottom_sheet_state";
 
     @PreviewMode
-    private int mPreviewMode;
+    protected int mPreviewMode;
 
     /**
      * When true, enables a test mode of operation -- in which certain UI features are disabled to
      * allow for UI tests to run correctly. Works around issue in ProgressDialog currently where the
      * dialog constantly keeps the UI thread alive and blocks a test forever.
      */
-    private boolean mTestingModeEnabled;
+    protected boolean mTestingModeEnabled;
 
-    protected SubsamplingScaleImageView mFullResImageView;
     protected WallpaperInfo mWallpaper;
-    private Asset mWallpaperAsset;
-    private WallpaperSetter mWallpaperSetter;
-    private UserEventLogger mUserEventLogger;
-    private LinearLayout mBottomSheet;
-    private TextView mAttributionTitle;
-    private TextView mAttributionSubtitle1;
-    private TextView mAttributionSubtitle2;
-    private Button mAttributionExploreButton;
-    private int mCurrentScreenOrientation;
-    private Point mDefaultCropSurfaceSize;
-    private Point mScreenSize;
-    private Point mRawWallpaperSize; // Native size of wallpaper image.
-    private ImageView mLoadingIndicator;
-    private MaterialProgressDrawable mProgressDrawable;
-    private ImageView mLowResImageView;
-    private Button mSetWallpaperButton;
-    private View mSpacer;
-    private CheckBox mPreview;
+    protected WallpaperSetter mWallpaperSetter;
+    protected UserEventLogger mUserEventLogger;
+    protected ViewGroup mBottomSheet;
+    protected ContentLoadingProgressBar mLoadingProgressBar;
+
+    protected CheckBox mPreview;
 
     @SuppressWarnings("RestrictTo")
     @State
-    private int mBottomSheetInitialState;
+    protected int mBottomSheetInitialState;
 
-    private Intent mExploreIntent;
+    protected Intent mExploreIntent;
 
     /**
      * Staged error dialog fragments that were unable to be shown when the hosting activity didn't
@@ -180,23 +162,7 @@
     private SetWallpaperErrorDialogFragment mStagedSetWallpaperErrorDialogFragment;
     private LoadWallpaperErrorDialogFragment mStagedLoadWallpaperErrorDialogFragment;
 
-    /**
-     * Creates and returns new instance of {@link PreviewFragment} with the provided wallpaper set as
-     * an argument.
-     */
-    public static PreviewFragment newInstance(
-            WallpaperInfo wallpaperInfo, @PreviewMode int mode, boolean testingModeEnabled) {
-        Bundle args = new Bundle();
-        args.putParcelable(ARG_WALLPAPER, wallpaperInfo);
-        args.putInt(ARG_PREVIEW_MODE, mode);
-        args.putBoolean(ARG_TESTING_MODE_ENABLED, testingModeEnabled);
-
-        PreviewFragment fragment = new PreviewFragment();
-        fragment.setArguments(args);
-        return fragment;
-    }
-
-    private static int getAttrColor(Context context, int attr) {
+    protected static int getAttrColor(Context context, int attr) {
         TypedArray ta = context.obtainStyledAttributes(new int[]{attr});
         int colorAccent = ta.getColor(0, 0);
         ta.recycle();
@@ -208,12 +174,12 @@
         super.onCreate(savedInstanceState);
 
         Activity activity = getActivity();
-        Context appContext = activity.getApplicationContext();
+        Context appContext = getContext().getApplicationContext();
         Injector injector = InjectorProvider.getInjector();
 
         mUserEventLogger = injector.getUserEventLogger(appContext);
         mWallpaper = getArguments().getParcelable(ARG_WALLPAPER);
-        mWallpaperAsset = mWallpaper.getAsset(appContext);
+
         //noinspection ResourceType
         mPreviewMode = getArguments().getInt(ARG_PREVIEW_MODE);
         mTestingModeEnabled = getArguments().getBoolean(ARG_TESTING_MODE_ENABLED);
@@ -222,45 +188,27 @@
 
         setHasOptionsMenu(true);
 
-        // Allow the layout to draw fullscreen even behind the status bar, so we can set as the status
-        // bar color a color that has a custom translucency in the theme.
-        Window window = activity.getWindow();
-        window.getDecorView().setSystemUiVisibility(
-                View.SYSTEM_UI_FLAG_LAYOUT_STABLE
-                        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
-                        | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
-
-        List<String> attributions = mWallpaper.getAttributions(activity);
+        List<String> attributions = getAttributions(activity);
         if (attributions.size() > 0 && attributions.get(0) != null) {
             activity.setTitle(attributions.get(0));
         }
     }
 
     @Override
+    @CallSuper
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
                              Bundle savedInstanceState) {
-        View view = inflater.inflate(R.layout.fragment_preview, container, false);
+        View view = inflater.inflate(getLayoutResId(), container, false);
 
         // Set toolbar as the action bar.
         Toolbar toolbar = view.findViewById(R.id.toolbar);
-        AppCompatActivity activity = (AppCompatActivity) getActivity();
+        AppCompatActivity activity = (AppCompatActivity) requireActivity();
         activity.setSupportActionBar(toolbar);
         activity.getSupportActionBar().setDisplayHomeAsUpEnabled(true);
         activity.getSupportActionBar().setDisplayShowTitleEnabled(false);
 
-        // Use updated fancy arrow icon for O+.
-        if (BuildCompat.isAtLeastO()) {
-            Drawable navigationIcon = getResources().getDrawable(
-                    R.drawable.material_ic_arrow_back_black_24);
-
-            // This Drawable's state is shared across the app, so make a copy of it before applying a
-            // color tint as not to affect other clients elsewhere in the app.
-            navigationIcon = navigationIcon.getConstantState().newDrawable().mutate();
-            navigationIcon.setColorFilter(
-                    getResources().getColor(R.color.material_white_100), Mode.SRC_IN);
-            navigationIcon.setAutoMirrored(true);
-            toolbar.setNavigationIcon(navigationIcon);
-        }
+        toolbar.getNavigationIcon().setTint(getAttrColor(activity, android.R.attr.colorPrimary));
+        toolbar.getNavigationIcon().setAutoMirrored(true);
 
         ViewCompat.setPaddingRelative(toolbar,
         /* start */ getResources().getDimensionPixelSize(
@@ -270,18 +218,11 @@
                         R.dimen.preview_toolbar_set_wallpaper_button_end_padding),
         /* bottom */ 0);
 
-        mFullResImageView = view.findViewById(R.id.full_res_image);
-        mLoadingIndicator = view.findViewById(R.id.loading_indicator);
+        mLoadingProgressBar = view.findViewById(getLoadingIndicatorResId());
+        mLoadingProgressBar.show();
 
-        mBottomSheet = view.findViewById(R.id.bottom_sheet);
-        mAttributionTitle = view.findViewById(R.id.preview_attribution_pane_title);
-        mAttributionSubtitle1 = view.findViewById(R.id.preview_attribution_pane_subtitle1);
-        mAttributionSubtitle2 = view.findViewById(R.id.preview_attribution_pane_subtitle2);
-        mAttributionExploreButton = view.findViewById(
-                R.id.preview_attribution_pane_explore_button);
-        mLowResImageView = view.findViewById(R.id.low_res_image);
-        mSetWallpaperButton = view.findViewById(R.id.preview_attribution_pane_set_wallpaper_button);
-        mSpacer = view.findViewById(R.id.spacer);
+        mBottomSheet = view.findViewById(getBottomSheetResId());
+        setUpBottomSheetView(mBottomSheet);
 
         // Workaround as we don't have access to bottomDialogCornerRadius, mBottomSheet radii are
         // set to dialogCornerRadius by default.
@@ -294,93 +235,78 @@
         bottomSheetBackground.setCornerRadii(radii);
         mBottomSheet.setBackground(bottomSheetBackground);
 
-        // Trim some memory from Glide to make room for the full-size image in this fragment.
-        Glide.get(getActivity()).setMemoryCategory(MemoryCategory.LOW);
-
-        mDefaultCropSurfaceSize = WallpaperCropUtils.getDefaultCropSurfaceSize(
-                getResources(), getActivity().getWindowManager().getDefaultDisplay());
-        mScreenSize = ScreenSizeCalculator.getInstance().getScreenSize(
-                getActivity().getWindowManager().getDefaultDisplay());
-
-        // Load a low-res placeholder image if there's a thumbnail available from the asset that can be
-        // shown to the user more quickly than the full-sized image.
-        if (mWallpaperAsset.hasLowResDataSource()) {
-            mWallpaperAsset.loadLowResDrawable(getActivity(), mLowResImageView, Color.BLACK,
-                    new WallpaperPreviewBitmapTransformation(getActivity().getApplicationContext(),
-                            isRtl()));
-        }
-
-        mWallpaperAsset.decodeRawDimensions(getActivity(), new DimensionsReceiver() {
-            @Override
-            public void onDimensionsDecoded(Point dimensions) {
-                // Don't continue loading the wallpaper if the Fragment is detached.
-                Activity activity = getActivity();
-                if (activity == null) {
-                    return;
-                }
-
-                // Return early and show a dialog if dimensions are null (signaling a decoding error).
-                if (dimensions == null) {
-                    showLoadWallpaperErrorDialog();
-                    return;
-                }
-
-                mRawWallpaperSize = dimensions;
-                ExploreIntentChecker intentChecker =
-                        InjectorProvider.getInjector().getExploreIntentChecker(activity);
-                String actionUrl = mWallpaper.getActionUrl(activity);
-                if (actionUrl != null && !actionUrl.isEmpty()) {
-                    Uri exploreUri = Uri.parse(mWallpaper.getActionUrl(activity));
-
-                    intentChecker.fetchValidActionViewIntent(exploreUri, exploreIntent -> {
-                        if (getActivity() == null) {
-                            return;
-                        }
-
-                        mExploreIntent = exploreIntent;
-                        initFullResView();
-                    });
-                } else {
-                    initFullResView();
-                }
-            }
-        });
-
-        // Configure loading indicator with a MaterialProgressDrawable.
-        mProgressDrawable = new MaterialProgressDrawable(getActivity().getApplicationContext(),
-                mLoadingIndicator);
-        mProgressDrawable.setAlpha(255);
-        mProgressDrawable.setBackgroundColor(getResources().getColor(R.color.material_white_100,
-                getContext().getTheme()));
-        mProgressDrawable.setColorSchemeColors(getAttrColor(
-                new ContextThemeWrapper(getContext(), getDeviceDefaultTheme()),
-                android.R.attr.colorAccent));
-        mProgressDrawable.updateSizes(MaterialProgressDrawable.LARGE);
-        mLoadingIndicator.setImageDrawable(mProgressDrawable);
-
-        // We don't want to show the spinner every time we load an image if it loads quickly; instead,
-        // only start showing the spinner if loading the image has taken longer than half of a second.
-        mLoadingIndicator.postDelayed(() -> {
-            if (mFullResImageView != null && !mFullResImageView.hasImage()
-                    && !mTestingModeEnabled) {
-                mLoadingIndicator.setVisibility(View.VISIBLE);
-                mLoadingIndicator.setAlpha(1f);
-                if (mProgressDrawable != null) {
-                    mProgressDrawable.start();
-                }
-            }
-        }, 500);
-
-
-        mBottomSheetInitialState = (savedInstanceState == null)
-                ? STATE_EXPANDED
-                : savedInstanceState.getInt(KEY_BOTTOM_SHEET_STATE,
-                        STATE_EXPANDED);
+        mBottomSheetInitialState = (savedInstanceState == null) ? STATE_EXPANDED
+                : savedInstanceState.getInt(KEY_BOTTOM_SHEET_STATE, STATE_EXPANDED);
         setUpBottomSheetListeners();
 
+        view.setOnApplyWindowInsetsListener((v, windowInsets) -> {
+            toolbar.setPadding(toolbar.getPaddingLeft(),
+                    toolbar.getPaddingTop() + windowInsets.getSystemWindowInsetTop(),
+                    toolbar.getPaddingRight(), toolbar.getBottom());
+            mBottomSheet.setPadding(mBottomSheet.getPaddingLeft(),
+                    mBottomSheet.getPaddingTop(), mBottomSheet.getPaddingRight(),
+                    mBottomSheet.getPaddingBottom() + windowInsets.getSystemWindowInsetBottom());
+            WindowInsets.Builder builder = new WindowInsets.Builder(windowInsets);
+            builder.setSystemWindowInsets(Insets.of(windowInsets.getSystemWindowInsetLeft(),
+                    0, windowInsets.getStableInsetRight(), 0));
+            return builder.build();
+        });
+
         return view;
     }
 
+    protected void populateInfoPage(InfoPageController infoPage) {
+        Context context = requireContext();
+
+        BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(mBottomSheet);
+
+        List<String> attributions = getAttributions(context);
+        boolean showMetadata = shouldShowMetadataInPreview();
+        CharSequence exploreLabel = getExploreButtonLabel(context);
+
+        infoPage.populate(attributions, showMetadata, this::onSetWallpaperClicked,
+                exploreLabel,
+                (showMetadata && mExploreIntent != null) ? this::onExploreClicked : null);
+
+        mBottomSheet.setVisibility(View.VISIBLE);
+
+        // Initialize the state of the BottomSheet based on the current state because if the initial
+        // and current state are the same, the state change listener won't fire and set the correct
+        // arrow asset and text alpha.
+        if (mBottomSheetInitialState == STATE_EXPANDED) {
+            setPreviewChecked(false);
+            infoPage.setContentAlpha(1f);
+        } else {
+            setPreviewChecked(true);
+            infoPage.setContentAlpha(0f);
+        }
+
+        bottomSheetBehavior.setState(mBottomSheetInitialState);
+    }
+
+    protected List<String> getAttributions(Context context) {
+        return mWallpaper.getAttributions(context);
+    }
+
+    protected boolean shouldShowMetadataInPreview() {
+        android.app.WallpaperInfo wallpaperComponent = mWallpaper.getWallpaperComponent();
+        return wallpaperComponent == null || wallpaperComponent.getShowMetadataInPreview();
+    }
+
+    @Nullable
+    protected abstract CharSequence getExploreButtonLabel(Context context);
+
+    @LayoutRes
+    protected abstract int getLayoutResId();
+
+    protected abstract void setUpBottomSheetView(ViewGroup bottomSheet);
+
+    @IdRes
+    protected abstract int getBottomSheetResId();
+
+    @IdRes
+    protected abstract int getLoadingIndicatorResId();
+
     protected int getDeviceDefaultTheme() {
         return android.R.style.Theme_DeviceDefault;
     }
@@ -398,12 +324,12 @@
         // allow committing fragment transactions.
         if (mStagedLoadWallpaperErrorDialogFragment != null) {
             mStagedLoadWallpaperErrorDialogFragment.show(
-                    getFragmentManager(), TAG_LOAD_WALLPAPER_ERROR_DIALOG_FRAGMENT);
+                    requireFragmentManager(), TAG_LOAD_WALLPAPER_ERROR_DIALOG_FRAGMENT);
             mStagedLoadWallpaperErrorDialogFragment = null;
         }
         if (mStagedSetWallpaperErrorDialogFragment != null) {
             mStagedSetWallpaperErrorDialogFragment.show(
-                    getFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT);
+                    requireFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT);
             mStagedSetWallpaperErrorDialogFragment = null;
         }
     }
@@ -428,20 +354,20 @@
             // the IndividualPreviewActivity, the "My photos" selection (by way of
             // TopLevelPickerActivity), or from a system "crop and set wallpaper" intent.
             // Therefore, handle the Up button as a global Back.
-            getActivity().onBackPressed();
+            requireActivity().onBackPressed();
             return true;
         }
 
         return false;
     }
 
-    protected void setupPreviewMenu(Menu menu) {
+    private void setupPreviewMenu(Menu menu) {
         mPreview = (CheckBox) menu.findItem(R.id.preview).getActionView();
         mPreview.setChecked(mBottomSheetInitialState == STATE_COLLAPSED);
         mPreview.setOnClickListener(this::setPreviewBehavior);
     }
 
-    private void setPreviewChecked(boolean checked) {
+    protected void setPreviewChecked(boolean checked) {
         if (mPreview != null) {
             mPreview.setChecked(checked);
             int resId = checked ? R.string.expand_attribution_panel
@@ -450,7 +376,7 @@
         }
     }
 
-    private void setPreviewBehavior(final View v) {
+    private void setPreviewBehavior(View v) {
         CheckBox checkbox = (CheckBox) v;
         BottomSheetBehavior<?> behavior = BottomSheetBehavior.from(mBottomSheet);
 
@@ -461,6 +387,46 @@
         }
     }
 
+    protected void setUpExploreIntent(@Nullable Runnable callback) {
+        Context context = getContext();
+        if (context == null) {
+            return;
+        }
+        String actionUrl = mWallpaper.getActionUrl(context);
+        if (actionUrl != null && !actionUrl.isEmpty()) {
+            Uri exploreUri = Uri.parse(mWallpaper.getActionUrl(context));
+            ExploreIntentChecker intentChecker =
+                    InjectorProvider.getInjector().getExploreIntentChecker(context);
+
+            intentChecker.fetchValidActionViewIntent(exploreUri, exploreIntent -> {
+                if (getActivity() == null) {
+                    return;
+                }
+
+                mExploreIntent = exploreIntent;
+                if (callback != null) {
+                    callback.run();
+                }
+            });
+        } else {
+            if (callback != null) {
+                callback.run();
+            }
+        }
+    }
+
+    /**
+     * Configure loading indicator with a MaterialProgressDrawable.
+     */
+    protected void setUpLoadingIndicator() {
+        mLoadingProgressBar.setProgressTintList(ColorStateList.valueOf(getAttrColor(
+                new ContextThemeWrapper(requireContext(), getDeviceDefaultTheme()),
+                android.R.attr.colorAccent)));
+        mLoadingProgressBar.show();
+    }
+
+    protected abstract boolean isLoaded();
+
     @Override
     public void onSet(int destination) {
         setCurrentWallpaper(destination);
@@ -483,10 +449,6 @@
     public void onDestroy() {
         super.onDestroy();
         mWallpaperSetter.cleanUp();
-        if (mProgressDrawable != null) {
-            mProgressDrawable.stop();
-        }
-        mFullResImageView.recycle();
     }
 
     @Override
@@ -497,42 +459,20 @@
         outState.putInt(KEY_BOTTOM_SHEET_STATE, bottomSheetBehavior.getState());
     }
 
-    private void onSetWallpaperClicked(View button) {
-        if (BuildCompat.isAtLeastN()) {
-            mWallpaperSetter.requestDestination(getContext(), getFragmentManager(), this,
-                    mWallpaper instanceof LiveWallpaperInfo);
-        } else {
-            setCurrentWallpaper(WallpaperPersister.DEST_HOME_SCREEN);
+    protected void onSetWallpaperClicked(View button) {
+        mWallpaperSetter.requestDestination(getContext(), getFragmentManager(), this,
+                mWallpaper instanceof LiveWallpaperInfo);
+    }
+
+    private void onExploreClicked(View button) {
+        if (getContext() == null) {
+            return;
         }
-    }
+        Context context = getContext();
+        mUserEventLogger.logActionClicked(mWallpaper.getCollectionId(context),
+                mWallpaper.getActionLabelRes(context));
 
-    /**
-     * Returns a zoom level that is similar to the actual zoom, but that is exactly 0.5 ** n for some
-     * integer n. This is useful for downsampling a bitmap--we want to see the bitmap at full detail,
-     * or downsampled to 1 in every 2 pixels, or 1 in 4, and so on, depending on the zoom.
-     */
-    private static float getDownsampleZoom(float actualZoom) {
-        if (actualZoom > 1) {
-            // Very zoomed in, but we can't sample more than 1 pixel per pixel.
-            return 1.0f;
-        }
-        float lower = 1.0f / roundUpToPower2((int) Math.ceil(1 / actualZoom));
-        float upper = lower * 2;
-        return nearestValue(actualZoom, lower, upper);
-    }
-
-    /**
-     * Returns the integer rounded up to the next power of 2.
-     */
-    private static int roundUpToPower2(int value) {
-        return 1 << (32 - Integer.numberOfLeadingZeros(value - 1));
-    }
-
-    /**
-     * Returns the closer of two values a and b to the given value.
-     */
-    private static float nearestValue(float value, float a, float b) {
-        return Math.abs(a - value) < Math.abs(b - value) ? a : b;
+        startActivity(mExploreIntent);
     }
 
     private void setUpBottomSheetListeners() {
@@ -554,6 +494,8 @@
                     case STATE_EXPANDED:
                         setPreviewChecked(false /* checked */);
                         break;
+                    default:
+                        Log.v(TAG, "Ignoring BottomSheet state: " + newState);
                 }
             }
 
@@ -565,264 +507,13 @@
                 } else {
                     alpha = 1f - slideOffset;
                 }
-                mAttributionTitle.setAlpha(alpha);
-                mAttributionSubtitle1.setAlpha(alpha);
-                mAttributionSubtitle2.setAlpha(alpha);
-                mAttributionExploreButton.setAlpha(alpha);
+                setBottomSheetContentAlpha(alpha);
             }
         });
     }
 
-    private boolean isWallpaperLoaded() {
-        return mFullResImageView.hasImage();
-    }
+    protected void setBottomSheetContentAlpha(float alpha) {
 
-    private void populateAttributionPane() {
-        final Context context = getContext();
-
-        final BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(mBottomSheet);
-
-        List<String> attributions = mWallpaper.getAttributions(context);
-        if (attributions.size() > 0 && attributions.get(0) != null) {
-            mAttributionTitle.setText(attributions.get(0));
-        }
-
-        if (attributions.size() > 1 && attributions.get(1) != null) {
-            mAttributionSubtitle1.setVisibility(View.VISIBLE);
-            mAttributionSubtitle1.setText(attributions.get(1));
-        }
-
-        if (attributions.size() > 2 && attributions.get(2) != null) {
-            mAttributionSubtitle2.setVisibility(View.VISIBLE);
-            mAttributionSubtitle2.setText(attributions.get(2));
-        }
-
-        if (mPreviewMode == MODE_CROP_AND_SET_WALLPAPER) {
-            mSetWallpaperButton.setVisibility(View.VISIBLE);
-            mSetWallpaperButton.setOnClickListener(this::onSetWallpaperClicked);
-        } else {
-            mSetWallpaperButton.setVisibility(View.GONE);
-        }
-
-        String actionUrl = mWallpaper.getActionUrl(context);
-
-        mAttributionExploreButton.setVisibility(View.GONE);
-        if (actionUrl != null && !actionUrl.isEmpty()) {
-            if (mExploreIntent != null) {
-                mAttributionExploreButton.setVisibility(View.VISIBLE);
-                mAttributionExploreButton.setText(context.getString(
-                        mWallpaper.getActionLabelRes(context)));
-
-                mAttributionExploreButton.setOnClickListener(view -> {
-                    mUserEventLogger.logActionClicked(mWallpaper.getCollectionId(context),
-                            mWallpaper.getActionLabelRes(context));
-
-                    startActivity(mExploreIntent);
-                });
-            }
-        }
-
-        if (mAttributionExploreButton.getVisibility() == View.VISIBLE
-                && mSetWallpaperButton.getVisibility() == View.VISIBLE) {
-            mSpacer.setVisibility(View.VISIBLE);
-        } else {
-            mSpacer.setVisibility(View.GONE);
-        }
-
-        mBottomSheet.setVisibility(View.VISIBLE);
-
-        // Initialize the state of the BottomSheet based on the current state because if the initial
-        // and current state are the same, the state change listener won't fire and set the correct
-        // arrow asset and text alpha.
-        if (bottomSheetBehavior.getState() == STATE_EXPANDED) {
-            setPreviewChecked(false);
-            mAttributionTitle.setAlpha(1f);
-            mAttributionSubtitle1.setAlpha(1f);
-            mAttributionSubtitle2.setAlpha(1f);
-        } else {
-            setPreviewChecked(true);
-            mAttributionTitle.setAlpha(0f);
-            mAttributionSubtitle1.setAlpha(0f);
-            mAttributionSubtitle2.setAlpha(0f);
-        }
-
-        // Let the state change listener take care of animating a state change to the initial state
-        // if there's a state change.
-        bottomSheetBehavior.setState(mBottomSheetInitialState);
-    }
-
-    /**
-     * Initializes MosaicView by initializing tiling, setting a fallback page bitmap, and
-     * initializing a zoom-scroll observer and click listener.
-     */
-    private void initFullResView() {
-        mFullResImageView.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_CROP);
-
-        // Set a solid black "page bitmap" so MosaicView draws a black background while waiting
-        // for the image to load or a transparent one if a thumbnail already loaded.
-        Bitmap blackBitmap = Bitmap.createBitmap(1, 1, Config.ARGB_8888);
-        int color = (mLowResImageView.getDrawable() == null) ? Color.BLACK : Color.TRANSPARENT;
-        blackBitmap.setPixel(0, 0, color);
-        mFullResImageView.setImage(ImageSource.bitmap(blackBitmap));
-
-        // Then set a fallback "page bitmap" to cover the whole MosaicView, which is an actual
-        // (lower res) version of the image to be displayed.
-        Point targetPageBitmapSize = new Point(mRawWallpaperSize);
-        mWallpaperAsset.decodeBitmap(targetPageBitmapSize.x, targetPageBitmapSize.y,
-                new BitmapReceiver() {
-                    @Override
-                    public void onBitmapDecoded(Bitmap pageBitmap) {
-                        // Check that the activity is still around since the decoding task started.
-                        if (getActivity() == null) {
-                            return;
-                        }
-
-                        // Some of these may be null depending on if the Fragment is paused, stopped,
-                        // or destroyed.
-                        if (mLoadingIndicator != null) {
-                            mLoadingIndicator.setVisibility(View.GONE);
-                        }
-                        // The page bitmap may be null if there was a decoding error, so show an
-                        // error dialog.
-                        if (pageBitmap == null) {
-                            showLoadWallpaperErrorDialog();
-                            return;
-                        }
-                        if (mFullResImageView != null) {
-                            // Set page bitmap.
-                            mFullResImageView.setImage(ImageSource.bitmap(pageBitmap));
-
-                            setDefaultWallpaperZoomAndScroll();
-                            crossFadeInMosaicView();
-                        }
-                        if (mProgressDrawable != null) {
-                            mProgressDrawable.stop();
-                        }
-                        getActivity().invalidateOptionsMenu();
-
-                        populateAttributionPane();
-                    }
-                });
-    }
-
-    /**
-     * Makes the MosaicView visible with an alpha fade-in animation while fading out the loading
-     * indicator.
-     */
-    private void crossFadeInMosaicView() {
-        long shortAnimationDuration = getResources().getInteger(
-                android.R.integer.config_shortAnimTime);
-
-        mFullResImageView.setAlpha(0f);
-        mFullResImageView.animate()
-                .alpha(1f)
-                .setDuration(shortAnimationDuration)
-                .setListener(new AnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationEnd(Animator animation) {
-                        // Clear the thumbnail bitmap reference to save memory since it's no longer
-                        // visible.
-                        if (mLowResImageView != null) {
-                            mLowResImageView.setImageBitmap(null);
-                        }
-                    }
-                });
-
-        mLoadingIndicator.animate()
-                .alpha(0f)
-                .setDuration(shortAnimationDuration)
-                .setListener(new AnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationEnd(Animator animation) {
-                        if (mLoadingIndicator != null) {
-                            mLoadingIndicator.setVisibility(View.GONE);
-                        }
-                    }
-                });
-    }
-
-    /**
-     * Sets the default wallpaper zoom and scroll position based on a "crop surface"
-     * (with extra width to account for parallax) superimposed on the screen. Shows as much of the
-     * wallpaper as possible on the crop surface and align screen to crop surface such that the
-     * default preview matches what would be seen by the user in the left-most home screen.
-     *
-     * <p>This method is called once in the Fragment lifecycle after the wallpaper asset has loaded
-     * and rendered to the layout.
-     */
-    private void setDefaultWallpaperZoomAndScroll() {
-        // Determine minimum zoom to fit maximum visible area of wallpaper on crop surface.
-        float defaultWallpaperZoom =
-                WallpaperCropUtils.calculateMinZoom(mRawWallpaperSize, mDefaultCropSurfaceSize);
-        float minWallpaperZoom =
-                WallpaperCropUtils.calculateMinZoom(mRawWallpaperSize, mScreenSize);
-
-        Point screenToCropSurfacePosition = WallpaperCropUtils.calculateCenterPosition(
-                mDefaultCropSurfaceSize, mScreenSize, true /* alignStart */, isRtl());
-        Point zoomedWallpaperSize = new Point(
-                Math.round(mRawWallpaperSize.x * defaultWallpaperZoom),
-                Math.round(mRawWallpaperSize.y * defaultWallpaperZoom));
-        Point cropSurfaceToWallpaperPosition = WallpaperCropUtils.calculateCenterPosition(
-                zoomedWallpaperSize, mDefaultCropSurfaceSize, false /* alignStart */, isRtl());
-
-        // Set min wallpaper zoom and max zoom on MosaicView widget.
-        mFullResImageView.setMaxScale(Math.max(DEFAULT_WALLPAPER_MAX_ZOOM, defaultWallpaperZoom));
-        mFullResImageView.setMinScale(minWallpaperZoom);
-
-        // Set center to composite positioning between scaled wallpaper and screen.
-        PointF centerPosition = new PointF(
-                mRawWallpaperSize.x / 2f,
-                mRawWallpaperSize.y / 2f);
-        centerPosition.offset( - (screenToCropSurfacePosition.x + cropSurfaceToWallpaperPosition.x),
-                - (screenToCropSurfacePosition.y + cropSurfaceToWallpaperPosition.y));
-
-        mFullResImageView.setScaleAndCenter(minWallpaperZoom, centerPosition);
-    }
-
-    protected Rect calculateCropRect() {
-        // Calculate Rect of wallpaper in physical pixel terms (i.e., scaled to current zoom).
-        float wallpaperZoom = mFullResImageView.getScale();
-        int scaledWallpaperWidth = (int) (mRawWallpaperSize.x * wallpaperZoom);
-        int scaledWallpaperHeight = (int) (mRawWallpaperSize.y * wallpaperZoom);
-        Rect rect = new Rect();
-        mFullResImageView.visibleFileRect(rect);
-        int scrollX = (int) (rect.left * wallpaperZoom);
-        int scrollY = (int) (rect.top * wallpaperZoom);
-
-        rect.set(0, 0, scaledWallpaperWidth, scaledWallpaperHeight);
-        Point screenSize = ScreenSizeCalculator.getInstance().getScreenSize(
-                getActivity().getWindowManager().getDefaultDisplay());
-        // Crop rect should start off as the visible screen and then include extra width and height
-        // if available within wallpaper at the current zoom.
-        Rect cropRect = new Rect(scrollX, scrollY, scrollX + screenSize.x, scrollY + screenSize.y);
-
-        Point defaultCropSurfaceSize = WallpaperCropUtils.getDefaultCropSurfaceSize(
-                getResources(), getActivity().getWindowManager().getDefaultDisplay());
-        int extraWidth = defaultCropSurfaceSize.x - screenSize.x;
-        int extraHeightTopAndBottom = (int) ((defaultCropSurfaceSize.y - screenSize.y) / 2f);
-
-        // Try to increase size of screenRect to include extra width depending on the layout
-        // direction.
-        if (isRtl()) {
-            cropRect.left = Math.max(cropRect.left - extraWidth, rect.left);
-        } else {
-            cropRect.right = Math.min(cropRect.right + extraWidth, rect.right);
-        }
-
-        // Try to increase the size of the cropRect to to include extra height.
-        int availableExtraHeightTop = cropRect.top - Math.max(
-                rect.top,
-                cropRect.top - extraHeightTopAndBottom);
-        int availableExtraHeightBottom = Math.min(
-                rect.bottom,
-                cropRect.bottom + extraHeightTopAndBottom) - cropRect.bottom;
-
-        int availableExtraHeightTopAndBottom =
-                Math.min(availableExtraHeightTop, availableExtraHeightBottom);
-        cropRect.top -= availableExtraHeightTopAndBottom;
-        cropRect.bottom += availableExtraHeightTopAndBottom;
-
-        return cropRect;
     }
 
     /**
@@ -830,35 +521,22 @@
      *
      * @param destination The wallpaper destination i.e. home vs. lockscreen vs. both.
      */
-    private void setCurrentWallpaper(@Destination final int destination) {
-        mWallpaperSetter.setCurrentWallpaper(getActivity(), mWallpaper, mWallpaperAsset,
-                destination, mFullResImageView.getScale(), calculateCropRect(),
-                new SetWallpaperCallback() {
-                    @Override
-                    public void onSuccess() {
-                        finishActivityWithResultOk();
-                    }
+    protected abstract void setCurrentWallpaper(@Destination int destination);
 
-                    @Override
-                    public void onError(@Nullable Throwable throwable) {
-                        showSetWallpaperErrorDialog(destination);
-                    }
-                });
-    }
-
-    private void finishActivityWithResultOk() {
+    protected void finishActivityWithResultOk() {
+        Activity activity = requireActivity();
         try {
-            Toast.makeText(
-                    getActivity(), R.string.wallpaper_set_successfully_message, Toast.LENGTH_SHORT).show();
+            Toast.makeText(activity,
+                    R.string.wallpaper_set_successfully_message, Toast.LENGTH_SHORT).show();
         } catch (NotFoundException e) {
             Log.e(TAG, "Could not show toast " + e);
         }
-        getActivity().overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
-        getActivity().setResult(Activity.RESULT_OK);
-        getActivity().finish();
+        activity.overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
+        activity.setResult(Activity.RESULT_OK);
+        activity.finish();
     }
 
-    private void showSetWallpaperErrorDialog(@Destination int wallpaperDestination) {
+    protected void showSetWallpaperErrorDialog(@Destination int wallpaperDestination) {
         SetWallpaperErrorDialogFragment newFragment = SetWallpaperErrorDialogFragment.newInstance(
                 R.string.set_wallpaper_error_message, wallpaperDestination);
         newFragment.setTargetFragment(this, UNUSED_REQUEST_CODE);
@@ -866,9 +544,9 @@
         // Show 'set wallpaper' error dialog now if it's safe to commit fragment transactions,
         // otherwise stage it for later when the hosting activity is in a state to commit fragment
         // transactions.
-        BasePreviewActivity activity = (BasePreviewActivity) getActivity();
+        BasePreviewActivity activity = (BasePreviewActivity) requireActivity();
         if (activity.isSafeToCommitFragmentTransaction()) {
-            newFragment.show(getFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT);
+            newFragment.show(requireFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT);
         } else {
             mStagedSetWallpaperErrorDialogFragment = newFragment;
         }
@@ -878,85 +556,117 @@
      * Shows 'load wallpaper' error dialog now or stage it to be shown when the hosting activity is
      * in a state that allows committing fragment transactions.
      */
-    private void showLoadWallpaperErrorDialog() {
+    protected void showLoadWallpaperErrorDialog() {
         LoadWallpaperErrorDialogFragment dialogFragment =
                 LoadWallpaperErrorDialogFragment.newInstance();
-        dialogFragment.setTargetFragment(PreviewFragment.this, UNUSED_REQUEST_CODE);
+        dialogFragment.setTargetFragment(this, UNUSED_REQUEST_CODE);
 
         // Show 'load wallpaper' error dialog now or stage it to be shown when the hosting
         // activity is in a state that allows committing fragment transactions.
         BasePreviewActivity activity = (BasePreviewActivity) getActivity();
         if (activity != null && activity.isSafeToCommitFragmentTransaction()) {
-            dialogFragment.show(PreviewFragment.this.getFragmentManager(),
-                    TAG_LOAD_WALLPAPER_ERROR_DIALOG_FRAGMENT);
+            dialogFragment.show(requireFragmentManager(), TAG_LOAD_WALLPAPER_ERROR_DIALOG_FRAGMENT);
         } else {
             mStagedLoadWallpaperErrorDialogFragment = dialogFragment;
         }
     }
 
-    @IntDef({
-            ActivityInfo.SCREEN_ORIENTATION_PORTRAIT,
-            ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE,
-            ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT,
-            ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE})
-    private @interface ActivityInfoScreenOrientation {
-    }
-
-    /**
-     * Gets the appropriate ActivityInfo orientation for the current configuration orientation to
-     * enable locking screen rotation at API levels lower than 18.
-     */
-    @ActivityInfoScreenOrientation
-    private int getCompatActivityInfoOrientation() {
-        int configOrientation = getResources().getConfiguration().orientation;
-        final Display display = getActivity().getWindowManager().getDefaultDisplay();
-        int naturalOrientation = Configuration.ORIENTATION_LANDSCAPE;
-        switch (display.getRotation()) {
-            case Surface.ROTATION_0:
-            case Surface.ROTATION_180:
-                // We are currently in the same basic orientation as the natural orientation.
-                naturalOrientation = configOrientation;
-                break;
-            case Surface.ROTATION_90:
-            case Surface.ROTATION_270:
-                // We are currently in the other basic orientation to the natural orientation.
-                naturalOrientation = (configOrientation == Configuration.ORIENTATION_LANDSCAPE)
-                        ? Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE;
-                break;
-            default:
-                // continue below
-        }
-
-        // Since the map starts at portrait, we need to offset if this device's natural orientation
-        // is landscape.
-        int indexOffset = 0;
-        if (naturalOrientation == Configuration.ORIENTATION_LANDSCAPE) {
-            indexOffset = 1;
-        }
-
-        switch ((display.getRotation() + indexOffset) % 4) {
-            case 0:
-                return ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
-            case 1:
-                return ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
-            case 2:
-                return ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
-            case 3:
-                return ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
-            default:
-                Log.e(TAG, "Display rotation did not correspond to a valid ActivityInfo orientation"
-                        +  "with display rotation: " + display.getRotation() + " and index offset: "
-                        + indexOffset + ".");
-                return ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
-        }
-    }
-
     /**
      * Returns whether layout direction is RTL (or false for LTR). Since native RTL layout support
      * was added in API 17, returns false for versions lower than 17.
      */
-    private boolean isRtl() {
+    protected boolean isRtl() {
         return getResources().getConfiguration().getLayoutDirection()
                     == View.LAYOUT_DIRECTION_RTL;
     }
-}
\ No newline at end of file
+
+    protected static class InfoPageController {
+
+        public static View createView(LayoutInflater inflater) {
+            return inflater.inflate(R.layout.preview_page_info, null /* root */);
+        }
+
+        private final int mPreviewMode;
+        private final View mInfoPage;
+        private final TextView mAttributionTitle;
+        private final TextView mAttributionSubtitle1;
+        private final TextView mAttributionSubtitle2;
+        private final Button mExploreButton;
+        private final Button mSetWallpaperButton;
+        private final View mSpacer;
+
+        public InfoPageController(View infoPage, int previewMode) {
+            mInfoPage = infoPage;
+            mPreviewMode = previewMode;
+
+            mAttributionTitle = mInfoPage.findViewById(R.id.preview_attribution_pane_title);
+            mAttributionSubtitle1 = mInfoPage.findViewById(R.id.preview_attribution_pane_subtitle1);
+            mAttributionSubtitle2 = mInfoPage.findViewById(R.id.preview_attribution_pane_subtitle2);
+            mSpacer = mInfoPage.findViewById(R.id.spacer);
+
+            mExploreButton = mInfoPage.findViewById(R.id.preview_attribution_pane_explore_button);
+            mSetWallpaperButton = mInfoPage.findViewById(
+                    R.id.preview_attribution_pane_set_wallpaper_button);
+        }
+
+        public void populate(List<String> attributions, boolean showMetadata,
+                OnClickListener setWallpaperOnClickListener,
+                CharSequence exploreButtonLabel,
+                @Nullable OnClickListener exploreOnClickListener) {
+            if (attributions.size() > 0 && attributions.get(0) != null) {
+                mAttributionTitle.setText(attributions.get(0));
+            }
+
+            if (showMetadata) {
+                if (attributions.size() > 1 && attributions.get(1) != null) {
+                    mAttributionSubtitle1.setVisibility(View.VISIBLE);
+                    mAttributionSubtitle1.setText(attributions.get(1));
+                }
+
+                if (attributions.size() > 2 && attributions.get(2) != null) {
+                    mAttributionSubtitle2.setVisibility(View.VISIBLE);
+                    mAttributionSubtitle2.setText(attributions.get(2));
+                }
+            }
+            setUpSetWallpaperButton(setWallpaperOnClickListener);
+
+            setUpExploreButton(exploreButtonLabel, exploreOnClickListener);
+
+            if (mExploreButton.getVisibility() == View.VISIBLE
+                    && mSetWallpaperButton.getVisibility() == View.VISIBLE) {
+                mSpacer.setVisibility(View.VISIBLE);
+            } else {
+                mSpacer.setVisibility(View.GONE);
+            }
+        }
+
+        public void setContentAlpha(float alpha) {
+            mSetWallpaperButton.setAlpha(alpha);
+            mExploreButton.setAlpha(alpha);
+            mAttributionTitle.setAlpha(alpha);
+            mAttributionSubtitle1.setAlpha(alpha);
+            mAttributionSubtitle2.setAlpha(alpha);
+        }
+
+        private void setUpSetWallpaperButton(OnClickListener setWallpaperOnClickListener) {
+            if (mPreviewMode == MODE_VIEW_ONLY) {
+                mSetWallpaperButton.setVisibility(View.GONE);
+            } else {
+                mSetWallpaperButton.setVisibility(View.VISIBLE);
+                mSetWallpaperButton.setOnClickListener(setWallpaperOnClickListener);
+            }
+        }
+
+        private void setUpExploreButton(CharSequence label,
+                @Nullable OnClickListener exploreOnClickListener) {
+            mExploreButton.setVisibility(View.GONE);
+            if (exploreOnClickListener == null) {
+                return;
+            }
+            mExploreButton.setVisibility(View.VISIBLE);
+            mExploreButton.setText(label);
+
+            mExploreButton.setOnClickListener(exploreOnClickListener);
+        }
+    }
+}
diff --git a/src/com/android/wallpaper/picker/StartRotationDialogFragment.java b/src/com/android/wallpaper/picker/StartRotationDialogFragment.java
index d5fd017..f0e4dcd 100755
--- a/src/com/android/wallpaper/picker/StartRotationDialogFragment.java
+++ b/src/com/android/wallpaper/picker/StartRotationDialogFragment.java
@@ -29,8 +29,6 @@
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-
-import androidx.appcompat.view.ContextThemeWrapper;
 import androidx.fragment.app.DialogFragment;
 
 import com.android.wallpaper.R;
@@ -42,29 +40,14 @@
  */
 public class StartRotationDialogFragment extends DialogFragment {
     private static final String KEY_IS_WIFI_ONLY_CHECKED = "key_is_wifi_only_checked";
-    private static final String KEY_IS_LIVE_WALLPAPER_PREVIEW_NEEDED = "key_is_live_wallpaper_needed";
     private static final boolean DEFAULT_IS_WIFI_ONLY = true;
 
     private boolean mIsWifiOnlyChecked;
-    private boolean mIsLiveWallpaperPreviewNeeded;
-
-    public static StartRotationDialogFragment newInstance(boolean isLiveWallpaperPreviewNeeded) {
-        StartRotationDialogFragment dialogFragment = new StartRotationDialogFragment();
-        Bundle args = new Bundle();
-        args.putBoolean(KEY_IS_LIVE_WALLPAPER_PREVIEW_NEEDED, isLiveWallpaperPreviewNeeded);
-        dialogFragment.setArguments(args);
-        return dialogFragment;
-    }
 
     @Override
     public void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        Bundle args = getArguments();
-        if (args != null) {
-            mIsLiveWallpaperPreviewNeeded = args.getBoolean(KEY_IS_LIVE_WALLPAPER_PREVIEW_NEEDED);
-        }
-
         if (savedInstanceState == null) {
             mIsWifiOnlyChecked = DEFAULT_IS_WIFI_ONLY;
         } else {
@@ -117,14 +100,10 @@
     }
 
     private int getBodyTextResourceId() {
-        return mIsLiveWallpaperPreviewNeeded
-                ? R.string.start_rotation_dialog_body_live_wallpaper_needed
-                : R.string.start_rotation_dialog_body;
+        return R.string.start_rotation_dialog_body;
     }
 
     private int getPositiveButtonTextResourceId() {
-        return mIsLiveWallpaperPreviewNeeded
-                ? R.string.start_rotation_dialog_continue
-                : android.R.string.ok;
+        return android.R.string.ok;
     }
 }
diff --git a/src/com/android/wallpaper/picker/individual/IndividualPickerActivity.java b/src/com/android/wallpaper/picker/individual/IndividualPickerActivity.java
index 367181a..de5ba84 100755
--- a/src/com/android/wallpaper/picker/individual/IndividualPickerActivity.java
+++ b/src/com/android/wallpaper/picker/individual/IndividualPickerActivity.java
@@ -16,8 +16,6 @@
 package com.android.wallpaper.picker.individual;
 
 import android.app.Activity;
-import android.app.WallpaperManager;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources.NotFoundException;
@@ -44,12 +42,9 @@
 import com.android.wallpaper.model.WallpaperInfo;
 import com.android.wallpaper.module.Injector;
 import com.android.wallpaper.module.InjectorProvider;
-import com.android.wallpaper.module.LiveWallpaperStatusChecker;
-import com.android.wallpaper.module.NoBackupImageWallpaper;
 import com.android.wallpaper.module.WallpaperPersister;
 import com.android.wallpaper.picker.BaseActivity;
 import com.android.wallpaper.picker.PreviewActivity.PreviewActivityIntentFactory;
-import com.android.wallpaper.util.ActivityUtils;
 import com.android.wallpaper.util.DiskBasedLogger;
 
 /**
@@ -61,13 +56,11 @@
     private static final String EXTRA_CATEGORY_COLLECTION_ID =
             "com.android.wallpaper.category_collection_id";
     private static final int PREVIEW_WALLPAPER_REQUEST_CODE = 0;
-    private static final int NO_BACKUP_IMAGE_WALLPAPER_REQUEST_CODE = 1;
     private static final int PREVIEW_LIVEWALLPAPER_REQUEST_CODE = 2;
     private static final String KEY_CATEGORY_COLLECTION_ID = "key_category_collection_id";
 
     private InlinePreviewIntentFactory mPreviewIntentFactory;
     private WallpaperPersister mWallpaperPersister;
-    private LiveWallpaperStatusChecker mLiveWallpaperStatusChecker;
     private Category mCategory;
     private String mCategoryCollectionId;
 
@@ -83,7 +76,6 @@
         mPreviewIntentFactory = new PreviewActivityIntentFactory();
         Injector injector = InjectorProvider.getInjector();
         mWallpaperPersister = injector.getWallpaperPersister(this);
-        mLiveWallpaperStatusChecker = injector.getLiveWallpaperStatusChecker(this);
 
         FragmentManager fm = getSupportFragmentManager();
         Fragment fragment = fm.findFragmentById(R.id.fragment_container);
@@ -160,8 +152,8 @@
     public boolean onOptionsItemSelected(MenuItem item) {
         int id = item.getItemId();
         if (id == android.R.id.home) {
-            // Handle Up as a Global back since the only entry point to IndividualPickerActivity is from
-            // TopLevelPickerActivity.
+            // Handle Up as a Global back since the only entry point to IndividualPickerActivity is
+            // from TopLevelPickerActivity.
             onBackPressed();
             return true;
         }
@@ -184,18 +176,6 @@
                     finishWithResultOk(shouldShowMessage);
                 }
                 break;
-
-            case NO_BACKUP_IMAGE_WALLPAPER_REQUEST_CODE:
-                // User clicked "Set wallpaper" in live wallpaper preview UI.
-                // NOTE: Don't check for the result code prior to KitKat MR2 because a bug on those versions
-                // caused the result code to be discarded from LivePicker so we can't rely on it.
-                if ((!BuildCompat.isAtLeastL() || resultCode == Activity.RESULT_OK)
-                        && mLiveWallpaperStatusChecker.isNoBackupImageWallpaperSet()
-                        && mCategory.getWallpaperRotationInitializer().startRotation(getApplicationContext())) {
-                    finishWithResultOk(true);
-                }
-                break;
-
             default:
                 Log.e(TAG, "Invalid request code: " + requestCode);
         }
@@ -211,18 +191,6 @@
                         : PREVIEW_WALLPAPER_REQUEST_CODE);
     }
 
-    /**
-     * Shows the system live wallpaper preview for the {@link NoBackupImageWallpaper} which is used to
-     * draw rotating wallpapers on pre-N Android builds.
-     */
-    public void showNoBackupImageWallpaperPreview() {
-        Intent intent = new Intent(WallpaperManager.ACTION_CHANGE_LIVE_WALLPAPER);
-        ComponentName componentName = new ComponentName(this, NoBackupImageWallpaper.class);
-        intent.putExtra(WallpaperManager.EXTRA_LIVE_WALLPAPER_COMPONENT, componentName);
-        ActivityUtils.startActivityForResultSafely(
-                this, intent, NO_BACKUP_IMAGE_WALLPAPER_REQUEST_CODE);
-    }
-
     private void finishWithResultOk(boolean shouldShowMessage) {
         if (shouldShowMessage) {
             try {
diff --git a/src/com/android/wallpaper/picker/individual/IndividualPickerFragment.java b/src/com/android/wallpaper/picker/individual/IndividualPickerFragment.java
index db21da3..c0c4ce5 100755
--- a/src/com/android/wallpaper/picker/individual/IndividualPickerFragment.java
+++ b/src/com/android/wallpaper/picker/individual/IndividualPickerFragment.java
@@ -58,13 +58,11 @@
 import com.android.wallpaper.model.WallpaperRotationInitializer.Listener;
 import com.android.wallpaper.model.WallpaperRotationInitializer.NetworkPreference;
 import com.android.wallpaper.model.WallpaperRotationInitializer.RotationInitializationState;
-import com.android.wallpaper.model.WallpaperRotationInitializer.RotationStateListener;
 import com.android.wallpaper.module.FormFactorChecker;
 import com.android.wallpaper.module.FormFactorChecker.FormFactor;
 import com.android.wallpaper.module.Injector;
 import com.android.wallpaper.module.InjectorProvider;
 import com.android.wallpaper.module.PackageStatusNotifier;
-import com.android.wallpaper.module.RotatingWallpaperComponentChecker;
 import com.android.wallpaper.module.WallpaperChangedNotifier;
 import com.android.wallpaper.module.WallpaperPersister;
 import com.android.wallpaper.module.WallpaperPersister.Destination;
@@ -116,7 +114,6 @@
 
     WallpaperPreferences mWallpaperPreferences;
     WallpaperChangedNotifier mWallpaperChangedNotifier;
-    RotatingWallpaperComponentChecker mRotatingWallpaperComponentChecker;
     RecyclerView mImageGrid;
     IndividualAdapter mAdapter;
     WallpaperCategory mCategory;
@@ -250,8 +247,6 @@
         mWallpaperChangedNotifier = WallpaperChangedNotifier.getInstance();
         mWallpaperChangedNotifier.registerListener(mWallpaperChangedListener);
 
-        mRotatingWallpaperComponentChecker = injector.getRotatingWallpaperComponentChecker();
-
         mFormFactor = injector.getFormFactorChecker(appContext).getFormFactor();
 
         mPackageStatusNotifier = injector.getPackageStatusNotifier(appContext);
@@ -445,7 +440,8 @@
     public void onResume() {
         super.onResume();
 
-        WallpaperPreferences preferences = InjectorProvider.getInjector().getPreferences(getActivity());
+        WallpaperPreferences preferences = InjectorProvider.getInjector()
+                .getPreferences(getActivity());
         preferences.setLastAppActiveTimestamp(new Date().getTime());
 
         // Reset Glide memory settings to a "normal" level of usage since it may have been lowered in
@@ -541,31 +537,23 @@
      * state of the user's device and binds the state of the current category's rotation to the "start
      * rotation" tile.
      */
-    private void refreshRotationHolder(final RotationHolder rotationHolder) {
+    private void refreshRotationHolder(RotationHolder rotationHolder) {
         mWallpaperRotationInitializer.fetchRotationInitializationState(getContext(),
-                new RotationStateListener() {
-                    @Override
-                    public void onRotationStateReceived(
-                            @RotationInitializationState final int rotationInitializationState) {
+                rotationState -> {
+                    // Update the UI state of the "start rotation" tile displayed on screen.
+                    // Do this in a Handler so it is scheduled at the end of the message queue.
+                    // This is necessary to ensure we do not remove or add data from the adapter
+                    // while the layout is still being computed. RecyclerView documentation
+                    // therefore recommends performing such changes in a Handler.
+                    new Handler().post(() -> {
+                        // A config change may have destroyed the activity since the refresh
+                        // started, so check for that to avoid an NPE.
+                        if (getActivity() == null) {
+                            return;
+                        }
 
-                        // Update the UI state of the "start rotation" tile displayed on screen. Do this in a
-                        // Handler so it is scheduled at the end of the message queue. This is necessary to
-                        // ensure we do not remove or add data from the adapter while the layout is still being
-                        // computed. RecyclerView documentation therefore recommends performing such changes in
-                        // a Handler.
-                        new android.os.Handler().post(new Runnable() {
-                            @Override
-                            public void run() {
-                                // A config change may have destroyed the activity since the refresh started, so
-                                // check for that to avoid an NPE.
-                                if (getActivity() == null) {
-                                    return;
-                                }
-
-                                rotationHolder.bindRotationInitializationState(rotationInitializationState);
-                            }
-                        });
-                    }
+                        rotationHolder.bindRotationInitializationState(rotationState);
+                    });
                 });
     }
 
@@ -614,34 +602,33 @@
                         // app before the first wallpaper image in rotation finishes downloading.
                         Activity activity = getActivity();
 
-                        if (activity != null
-                                && mWallpaperRotationInitializer
-                                .isNoBackupImageWallpaperPreviewNeeded(appContext)) {
-                            ((IndividualPickerActivity) activity).showNoBackupImageWallpaperPreview();
-                        } else {
-                            if (mWallpaperRotationInitializer.startRotation(appContext)) {
-                                if (activity != null && mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE) {
-                                    try {
-                                        Toast.makeText(getActivity(), R.string.wallpaper_set_successfully_message,
-                                                Toast.LENGTH_SHORT).show();
-                                    } catch (NotFoundException e) {
-                                        Log.e(TAG, "Could not show toast " + e);
-                                    }
 
-                                    activity.setResult(Activity.RESULT_OK);
-                                    activity.finish();
-                                } else if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
-                                    mAdapter.updateSelectedTile(SPECIAL_FIXED_TILE_ADAPTER_POSITION);
+                        if (mWallpaperRotationInitializer.startRotation(appContext)) {
+                            if (activity != null
+                                    && mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE) {
+                                try {
+                                    Toast.makeText(getActivity(),
+                                            R.string.wallpaper_set_successfully_message,
+                                            Toast.LENGTH_SHORT).show();
+                                } catch (NotFoundException e) {
+                                    Log.e(TAG, "Could not show toast " + e);
                                 }
-                            } else { // Failed to start rotation.
-                                showStartRotationErrorDialog(networkPreference);
 
-                                if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
-                                    DesktopRotationHolder rotationViewHolder =
-                                            (DesktopRotationHolder) mImageGrid.findViewHolderForAdapterPosition(
-                                                    SPECIAL_FIXED_TILE_ADAPTER_POSITION);
-                                    rotationViewHolder.setSelectionState(SelectableHolder.SELECTION_STATE_DESELECTED);
-                                }
+                                activity.setResult(Activity.RESULT_OK);
+                                activity.finish();
+                            } else if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
+                                mAdapter.updateSelectedTile(SPECIAL_FIXED_TILE_ADAPTER_POSITION);
+                            }
+                        } else { // Failed to start rotation.
+                            showStartRotationErrorDialog(networkPreference);
+
+                            if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
+                                DesktopRotationHolder rotationViewHolder =
+                                        (DesktopRotationHolder)
+                                                mImageGrid.findViewHolderForAdapterPosition(
+                                                SPECIAL_FIXED_TILE_ADAPTER_POSITION);
+                                rotationViewHolder.setSelectionState(
+                                        SelectableHolder.SELECTION_STATE_DESELECTED);
                             }
                         }
                     }
@@ -690,11 +677,7 @@
      * Returns whether rotation is enabled for this category.
      */
     boolean isRotationEnabled() {
-        boolean isRotationSupported =
-                mRotatingWallpaperComponentChecker.getRotatingWallpaperSupport(getContext())
-                        == RotatingWallpaperComponentChecker.ROTATING_WALLPAPER_SUPPORT_SUPPORTED;
-
-        return isRotationSupported && mWallpaperRotationInitializer != null;
+        return mWallpaperRotationInitializer != null;
     }
 
     @Override
@@ -732,10 +715,10 @@
             super(itemView);
             itemView.setOnClickListener(this);
 
-            mTileLayout = (FrameLayout) itemView.findViewById(R.id.daily_refresh);
-            mRotationMessage = (TextView) itemView.findViewById(R.id.rotation_tile_message);
-            mRotationTitle = (TextView) itemView.findViewById(R.id.rotation_tile_title);
-            mRefreshIcon = (ImageView) itemView.findViewById(R.id.rotation_tile_refresh_icon);
+            mTileLayout = itemView.findViewById(R.id.daily_refresh);
+            mRotationMessage = itemView.findViewById(R.id.rotation_tile_message);
+            mRotationTitle = itemView.findViewById(R.id.rotation_tile_title);
+            mRefreshIcon = itemView.findViewById(R.id.rotation_tile_refresh_icon);
             mTileLayout.getLayoutParams().height = mTileSizePx.y;
 
             // If the feature flag for "dynamic start rotation tile" is not enabled, fall back to the
@@ -749,7 +732,8 @@
                 mRotationMessage.setTextColor(
                         getResources().getColor(R.color.rotation_tile_enabled_subtitle_text_color));
                 mRefreshIcon.setColorFilter(
-                        getResources().getColor(R.color.rotation_tile_enabled_refresh_icon_color), Mode.SRC_IN);
+                        getResources().getColor(R.color.rotation_tile_enabled_refresh_icon_color),
+                        Mode.SRC_IN);
                 return;
             }
 
@@ -765,10 +749,7 @@
 
         @Override
         public void onClick(View v) {
-            boolean isLiveWallpaperNeeded = mWallpaperRotationInitializer
-                    .isNoBackupImageWallpaperPreviewNeeded(getActivity().getApplicationContext());
-            DialogFragment startRotationDialogFragment = StartRotationDialogFragment
-                    .newInstance(isLiveWallpaperNeeded);
+            DialogFragment startRotationDialogFragment = new StartRotationDialogFragment();
             startRotationDialogFragment.setTargetFragment(
                     IndividualPickerFragment.this, UNUSED_REQUEST_CODE);
             startRotationDialogFragment.show(getFragmentManager(), TAG_START_ROTATION_DIALOG);
diff --git a/src/com/android/wallpaper/widget/ConstraintViewPager.java b/src/com/android/wallpaper/widget/ConstraintViewPager.java
new file mode 100644
index 0000000..ad56750
--- /dev/null
+++ b/src/com/android/wallpaper/widget/ConstraintViewPager.java
@@ -0,0 +1,62 @@
+/*
+ * 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.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.viewpager.widget.ViewPager;
+
+/**
+ * When ConstraintViewPager is being measured, it will get all height of pages and makes itself
+ * height as the same as the maximum height.
+ */
+public class ConstraintViewPager extends ViewPager {
+
+    public ConstraintViewPager(@NonNull Context context) {
+        this(context, null /* attrs */);
+    }
+
+    public ConstraintViewPager(@NonNull Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    /**
+     * Visit all child views first and then determine the maximum height for ViewPager.
+     */
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        int maxChildHeight = 0;
+        for (int i = 0; i < getChildCount(); i++) {
+            View view = getChildAt(i);
+            view.measure(widthMeasureSpec,
+                    MeasureSpec.makeMeasureSpec(0 /* size */, MeasureSpec.UNSPECIFIED));
+            int childHeight = view.getMeasuredHeight();
+            if (childHeight > maxChildHeight) {
+                maxChildHeight = childHeight;
+            }
+        }
+
+        if (maxChildHeight != 0) {
+            heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxChildHeight, MeasureSpec.EXACTLY);
+        }
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+    }
+}
diff --git a/src_override/com/android/wallpaper/module/WallpapersInjector.java b/src_override/com/android/wallpaper/module/WallpapersInjector.java
index 8e3b3e9..5a39524 100755
--- a/src_override/com/android/wallpaper/module/WallpapersInjector.java
+++ b/src_override/com/android/wallpaper/module/WallpapersInjector.java
@@ -17,12 +17,12 @@
 
 import android.content.Context;
 
+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.PreviewFragment;
-
-import androidx.fragment.app.Fragment;
+import com.android.wallpaper.picker.ImagePreviewFragment;
 
 /**
  * A concrete, real implementation of the dependency provider.
@@ -69,7 +69,7 @@
         WallpaperInfo wallpaperInfo,
         int mode,
         boolean testingModeEnabled) {
-        return PreviewFragment.newInstance(wallpaperInfo, mode, testingModeEnabled);
+        return ImagePreviewFragment.newInstance(wallpaperInfo, mode, testingModeEnabled);
     }
 
     @Override
diff --git a/tests/Android.mk b/tests/Android.mk
new file mode 100644
index 0000000..be224e3
--- /dev/null
+++ b/tests/Android.mk
@@ -0,0 +1,59 @@
+# 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+#
+# Build rule for WallpaperPicker2 tests
+#
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    androidx.annotation_annotation \
+    androidx.test.core \
+    androidx.test.runner \
+    androidx.test.rules \
+    androidx.test.espresso.contrib \
+    androidx.test.espresso.intents \
+    mockito-target-minus-junit4 \
+    androidx.test.espresso.core \
+    hamcrest-library \
+    hamcrest
+
+ifneq (,$(wildcard frameworks/base))
+    LOCAL_PRIVATE_PLATFORM_APIS := true
+else
+    LOCAL_SDK_VERSION := 29
+    LOCAL_MIN_SDK_VERSION := 26
+endif
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_STATIC_ANDROID_LIBRARIES := WallpaperPicker2CommonDepsLib
+
+LOCAL_FULL_LIBS_MANIFEST_FILES := $(LOCAL_PATH)/AndroidManifest.xml
+LOCAL_MANIFEST_FILE := AndroidManifest.xml
+
+LOCAL_INSTRUMENTATION_FOR := WallpaperPicker2
+
+LOCAL_PACKAGE_NAME := WallpaperPicker2Tests
+
+include $(BUILD_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
new file mode 100644
index 0000000..1e35c92
--- /dev/null
+++ b/tests/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          xmlns:tools="http://schemas.android.com/tools"
+          package="com.android.wallpaper.tests">
+
+    <uses-sdk android:targetSdkVersion="29" android:minSdkVersion="29"/>
+
+    <application>
+        <uses-library android:name="android.test.runner"/>
+    </application>
+
+    <instrumentation
+        android:functionalTest="false"
+        android:handleProfiling="false"
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.wallpaper">
+    </instrumentation>
+</manifest>
diff --git a/tests/src/com/android/wallpaper/picker/PreviewActivityTest.java b/tests/src/com/android/wallpaper/picker/PreviewActivityTest.java
new file mode 100644
index 0000000..e1feba5
--- /dev/null
+++ b/tests/src/com/android/wallpaper/picker/PreviewActivityTest.java
@@ -0,0 +1,510 @@
+/*
+ * 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.picker;
+
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+
+import static junit.framework.TestCase.assertFalse;
+
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.graphics.Bitmap;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.widget.TextView;
+
+import androidx.test.espresso.intent.Intents;
+import androidx.test.filters.MediumTest;
+import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+
+import com.android.wallpaper.R;
+import com.android.wallpaper.module.Injector;
+import com.android.wallpaper.module.InjectorProvider;
+import com.android.wallpaper.module.UserEventLogger;
+import com.android.wallpaper.module.WallpaperPersister;
+import com.android.wallpaper.testing.TestAsset;
+import com.android.wallpaper.testing.TestExploreIntentChecker;
+import com.android.wallpaper.testing.TestInjector;
+import com.android.wallpaper.testing.TestUserEventLogger;
+import com.android.wallpaper.testing.TestWallpaperInfo;
+import com.android.wallpaper.testing.TestWallpaperPersister;
+import com.android.wallpaper.util.ScreenSizeCalculator;
+import com.android.wallpaper.util.WallpaperCropUtils;
+
+import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tests for {@link PreviewActivity}.
+ */
+@RunWith(AndroidJUnit4ClassRunner.class)
+@MediumTest
+public class PreviewActivityTest {
+
+    private static final float FLOAT_ERROR_MARGIN = 0.001f;
+    private static final String ACTION_URL = "http://google.com";
+
+    private TestWallpaperInfo mMockWallpaper;
+    private Injector mInjector;
+    private TestWallpaperPersister mWallpaperPersister;
+    private TestUserEventLogger mEventLogger;
+    private TestExploreIntentChecker mExploreIntentChecker;
+
+    @Rule
+    public ActivityTestRule<PreviewActivity> mActivityRule =
+            new ActivityTestRule<>(PreviewActivity.class, false, false);
+
+    @Before
+    public void setUp() {
+
+        mInjector = new TestInjector();
+        InjectorProvider.setInjector(mInjector);
+
+        Intents.init();
+
+        mMockWallpaper = new TestWallpaperInfo(TestWallpaperInfo.COLOR_BLACK);
+        List<String> attributions = new ArrayList<>();
+        attributions.add("Title");
+        attributions.add("Subtitle 1");
+        attributions.add("Subtitle 2");
+        mMockWallpaper.setAttributions(attributions);
+        mMockWallpaper.setCollectionId("collection");
+        mMockWallpaper.setWallpaperId("wallpaper");
+        mMockWallpaper.setActionUrl(ACTION_URL);
+
+        Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        mWallpaperPersister = (TestWallpaperPersister) mInjector.getWallpaperPersister(context);
+        mEventLogger = (TestUserEventLogger) mInjector.getUserEventLogger(context);
+        mExploreIntentChecker = (TestExploreIntentChecker)
+                mInjector.getExploreIntentChecker(context);
+    }
+
+    @After
+    public void tearDown() {
+        Intents.release();
+        mActivityRule.finishActivity();
+    }
+
+    private void launchActivityIntentWithMockWallpaper() {
+        Intent intent = PreviewActivity.newIntent(
+                InstrumentationRegistry.getInstrumentation().getTargetContext(), mMockWallpaper);
+        intent.putExtra(BasePreviewActivity.EXTRA_TESTING_MODE_ENABLED, true);
+
+        mActivityRule.launchActivity(intent);
+    }
+
+    private void finishSettingWallpaper() throws Throwable {
+        mActivityRule.runOnUiThread(() -> mWallpaperPersister.finishSettingWallpaper());
+    }
+
+    @Test
+    public void testRendersWallpaperDrawableFromIntent() {
+        launchActivityIntentWithMockWallpaper();
+        PreviewActivity activity = mActivityRule.getActivity();
+        SubsamplingScaleImageView mosaicView = activity.findViewById(R.id.full_res_image);
+
+        assertTrue(mosaicView.hasImage());
+    }
+
+    @Test
+    public void testClickSetWallpaper_Success_HomeScreen() throws Throwable {
+        launchActivityIntentWithMockWallpaper();
+        assertNull(mWallpaperPersister.getCurrentHomeWallpaper());
+
+        onView(withId(R.id.preview_attribution_pane_set_wallpaper_button)).perform(click());
+
+        // Destination dialog is shown; click "Home screen".
+        onView(withText(R.string.set_wallpaper_home_screen_destination)).perform(click());
+
+        assertNull(mWallpaperPersister.getCurrentHomeWallpaper());
+
+        finishSettingWallpaper();
+
+        // Mock system wallpaper bitmap should be equal to the mock WallpaperInfo's bitmap.
+        Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        Bitmap srcBitmap = ((TestAsset) mMockWallpaper.getAsset(context)).getBitmap();
+        assertTrue(srcBitmap.sameAs(mWallpaperPersister.getCurrentHomeWallpaper()));
+
+        // The wallpaper should have been set on the home screen.
+        assertEquals(WallpaperPersister.DEST_HOME_SCREEN, mWallpaperPersister.getLastDestination());
+        assertEquals(1, mEventLogger.getNumWallpaperSetEvents());
+
+        assertEquals(1, mEventLogger.getNumWallpaperSetResultEvents());
+        assertEquals(UserEventLogger.WALLPAPER_SET_RESULT_SUCCESS,
+                mEventLogger.getLastWallpaperSetResult());
+    }
+
+    @Test
+    public void testClickSetWallpaper_Success_LockScreen() throws Throwable {
+        launchActivityIntentWithMockWallpaper();
+        assertNull(mWallpaperPersister.getCurrentLockWallpaper());
+
+        onView(withId(R.id.preview_attribution_pane_set_wallpaper_button)).perform(click());
+
+        // Destination dialog is shown; click "Lock screen."
+        onView(withText(R.string.set_wallpaper_lock_screen_destination)).perform(click());
+
+        assertNull(mWallpaperPersister.getCurrentLockWallpaper());
+
+        finishSettingWallpaper();
+
+        // Mock system wallpaper bitmap should be equal to the mock WallpaperInfo's bitmap.
+        Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        Bitmap srcBitmap = ((TestAsset) mMockWallpaper.getAsset(context)).getBitmap();
+        assertTrue(srcBitmap.sameAs(mWallpaperPersister.getCurrentLockWallpaper()));
+
+        // The wallpaper should have been set on the lock screen.
+        assertEquals(WallpaperPersister.DEST_LOCK_SCREEN, mWallpaperPersister.getLastDestination());
+        assertEquals(1, mEventLogger.getNumWallpaperSetEvents());
+
+        assertEquals(1, mEventLogger.getNumWallpaperSetResultEvents());
+        assertEquals(UserEventLogger.WALLPAPER_SET_RESULT_SUCCESS,
+                mEventLogger.getLastWallpaperSetResult());
+    }
+
+    @Test
+    public void testClickSetWallpaper_Success_BothHomeAndLockScreen() throws Throwable {
+        launchActivityIntentWithMockWallpaper();
+        assertNull(mWallpaperPersister.getCurrentHomeWallpaper());
+        assertNull(mWallpaperPersister.getCurrentLockWallpaper());
+
+        onView(withId(R.id.preview_attribution_pane_set_wallpaper_button)).perform(click());
+
+        // Destination dialog is shown; click "Both."
+        onView(withText(R.string.set_wallpaper_both_destination)).perform(click());
+
+        assertNull(mWallpaperPersister.getCurrentHomeWallpaper());
+        assertNull(mWallpaperPersister.getCurrentLockWallpaper());
+
+        finishSettingWallpaper();
+
+        // Mock system wallpaper bitmap should be equal to the mock WallpaperInfo's bitmap.
+        Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        Bitmap srcBitmap = ((TestAsset) mMockWallpaper.getAsset(context)).getBitmap();
+        assertTrue(srcBitmap.sameAs(mWallpaperPersister.getCurrentHomeWallpaper()));
+        assertTrue(srcBitmap.sameAs(mWallpaperPersister.getCurrentLockWallpaper()));
+
+        // The wallpaper should have been set on both the home and the lock screen.
+        assertEquals(WallpaperPersister.DEST_BOTH, mWallpaperPersister.getLastDestination());
+        assertEquals(1, mEventLogger.getNumWallpaperSetEvents());
+
+        assertEquals(1, mEventLogger.getNumWallpaperSetResultEvents());
+        assertEquals(UserEventLogger.WALLPAPER_SET_RESULT_SUCCESS,
+                mEventLogger.getLastWallpaperSetResult());
+    }
+
+    @Test
+    public void testClickSetWallpaper_Fails_HomeScreen_ShowsErrorDialog()
+            throws Throwable {
+        launchActivityIntentWithMockWallpaper();
+        assertNull(mWallpaperPersister.getCurrentHomeWallpaper());
+
+        mWallpaperPersister.setFailNextCall(true);
+
+        onView(withId(R.id.preview_attribution_pane_set_wallpaper_button)).perform(click());
+
+        // Destination dialog is shown; click "Home screen."
+        onView(withText(R.string.set_wallpaper_home_screen_destination)).perform(click());
+
+        finishSettingWallpaper();
+
+        assertNull(mWallpaperPersister.getCurrentHomeWallpaper());
+        onView(withText(R.string.set_wallpaper_error_message)).check(matches(isDisplayed()));
+
+        assertEquals(1, mEventLogger.getNumWallpaperSetResultEvents());
+        assertEquals(UserEventLogger.WALLPAPER_SET_RESULT_FAILURE,
+                mEventLogger.getLastWallpaperSetResult());
+
+        // Set next call to succeed and current wallpaper bitmap should not be null and equals to
+        // the
+        // mock wallpaper bitmap after clicking "try again".
+        mWallpaperPersister.setFailNextCall(false);
+
+        onView(withText(R.string.try_again)).perform(click());
+        finishSettingWallpaper();
+
+        assertNotNull(mWallpaperPersister.getCurrentHomeWallpaper());
+        Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        Bitmap srcBitmap = ((TestAsset) mMockWallpaper.getAsset(context)).getBitmap();
+        assertTrue(srcBitmap.sameAs(mWallpaperPersister.getCurrentHomeWallpaper()));
+
+        // The wallpaper should have been set on the home screen.
+        assertEquals(WallpaperPersister.DEST_HOME_SCREEN, mWallpaperPersister.getLastDestination());
+    }
+
+    @Test
+    public void testClickSetWallpaper_Fails_LockScreen_ShowsErrorDialog()
+            throws Throwable {
+        launchActivityIntentWithMockWallpaper();
+        assertNull(mWallpaperPersister.getCurrentLockWallpaper());
+
+        mWallpaperPersister.setFailNextCall(true);
+
+        onView(withId(R.id.preview_attribution_pane_set_wallpaper_button)).perform(click());
+
+        // Destination dialog is shown; click "Lock screen."
+        onView(withText(R.string.set_wallpaper_lock_screen_destination)).perform(click());
+
+        finishSettingWallpaper();
+
+        assertNull(mWallpaperPersister.getCurrentLockWallpaper());
+        onView(withText(R.string.set_wallpaper_error_message)).check(matches(isDisplayed()));
+
+        assertEquals(1, mEventLogger.getNumWallpaperSetResultEvents());
+        assertEquals(UserEventLogger.WALLPAPER_SET_RESULT_FAILURE,
+                mEventLogger.getLastWallpaperSetResult());
+
+        // Set next call to succeed and current wallpaper bitmap should not be null and equals to
+        // the
+        // mock wallpaper bitmap after clicking "try again".
+        mWallpaperPersister.setFailNextCall(false);
+
+        onView(withText(R.string.try_again)).perform(click());
+        finishSettingWallpaper();
+
+        assertNotNull(mWallpaperPersister.getCurrentLockWallpaper());
+        Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        Bitmap srcBitmap = ((TestAsset) mMockWallpaper.getAsset(context)).getBitmap();
+        assertTrue(srcBitmap.sameAs(mWallpaperPersister.getCurrentLockWallpaper()));
+
+        // The wallpaper should have been set on the lock screen.
+        assertEquals(WallpaperPersister.DEST_LOCK_SCREEN, mWallpaperPersister.getLastDestination());
+    }
+
+    @Test
+    public void testClickSetWallpaper_Fails_BothHomeAndLock_ShowsErrorDialog()
+            throws Throwable {
+        launchActivityIntentWithMockWallpaper();
+        assertNull(mWallpaperPersister.getCurrentHomeWallpaper());
+        assertNull(mWallpaperPersister.getCurrentLockWallpaper());
+
+        mWallpaperPersister.setFailNextCall(true);
+
+        onView(withId(R.id.preview_attribution_pane_set_wallpaper_button)).perform(click());
+
+        // Destination dialog is shown; click "Both."
+        onView(withText(R.string.set_wallpaper_both_destination)).perform(click());
+
+        finishSettingWallpaper();
+
+        assertNull(mWallpaperPersister.getCurrentHomeWallpaper());
+        assertNull(mWallpaperPersister.getCurrentLockWallpaper());
+        onView(withText(R.string.set_wallpaper_error_message)).check(matches(isDisplayed()));
+
+        assertEquals(1, mEventLogger.getNumWallpaperSetResultEvents());
+        assertEquals(UserEventLogger.WALLPAPER_SET_RESULT_FAILURE,
+                mEventLogger.getLastWallpaperSetResult());
+
+        // Set next call to succeed and current wallpaper bitmap should not be null and equals to
+        // the mock wallpaper bitmap after clicking "try again".
+        mWallpaperPersister.setFailNextCall(false);
+
+        onView(withText(R.string.try_again)).perform(click());
+        finishSettingWallpaper();
+
+        assertNotNull(mWallpaperPersister.getCurrentHomeWallpaper());
+        assertNotNull(mWallpaperPersister.getCurrentLockWallpaper());
+        Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        Bitmap srcBitmap = ((TestAsset) mMockWallpaper.getAsset(context)).getBitmap();
+        assertTrue(srcBitmap.sameAs(mWallpaperPersister.getCurrentHomeWallpaper()));
+        assertTrue(srcBitmap.sameAs(mWallpaperPersister.getCurrentLockWallpaper()));
+
+        // The wallpaper should have been set on both the home screen and the lock screen.
+        assertEquals(WallpaperPersister.DEST_BOTH, mWallpaperPersister.getLastDestination());
+    }
+
+    @Test
+    public void testClickSetWallpaper_CropsAndScalesWallpaper() {
+        launchActivityIntentWithMockWallpaper();
+        // Scale should not have a meaningful value before clicking "set wallpaper".
+        assertTrue(mWallpaperPersister.getScale() < 0);
+
+        onView(withId(R.id.preview_attribution_pane_set_wallpaper_button)).perform(click());
+
+        // Destination dialog is shown; click "Home screen".
+        onView(withText(R.string.set_wallpaper_home_screen_destination)).perform(click());
+
+        // WallpaperPersister's scale should match the ScaleImageView's scale.
+        float zoom = ((SubsamplingScaleImageView)
+                mActivityRule.getActivity().findViewById(R.id.full_res_image)).getScale();
+        assertEquals(mWallpaperPersister.getScale(), zoom, FLOAT_ERROR_MARGIN);
+
+        Point screenSize = ScreenSizeCalculator.getInstance().getScreenSize(
+                mActivityRule.getActivity().getWindowManager().getDefaultDisplay());
+        int maxDim = Math.max(screenSize.x, screenSize.y);
+        Rect cropRect = mWallpaperPersister.getCropRect();
+
+        // Crop rect should be greater or equal than screen size in both directions.
+        assertTrue(cropRect.width() >= maxDim);
+        assertTrue(cropRect.height() >= maxDim);
+    }
+
+    @Test
+    public void testClickSetWallpaper_FailsCroppingAndScalingWallpaper_ShowsErrorDialog()
+            throws Throwable {
+        launchActivityIntentWithMockWallpaper();
+        mWallpaperPersister.setFailNextCall(true);
+        onView(withId(R.id.preview_attribution_pane_set_wallpaper_button)).perform(click());
+        // Destination dialog is shown; click "Home screen".
+        onView(withText(R.string.set_wallpaper_home_screen_destination)).perform(click());
+
+        finishSettingWallpaper();
+
+        onView(withText(R.string.set_wallpaper_error_message)).check(matches(isDisplayed()));
+    }
+
+    /**
+     * Tests that tapping Set Wallpaper shows the destination dialog (i.e., choosing
+     * between Home screen, Lock screen, or Both).
+     */
+    @Test
+    public void testClickSetWallpaper_ShowsDestinationDialog() {
+        launchActivityIntentWithMockWallpaper();
+        onView(withId(R.id.preview_attribution_pane_set_wallpaper_button)).perform(click());
+        onView(withText(R.string.set_wallpaper_dialog_message)).check(matches(isDisplayed()));
+    }
+
+    @Test
+    public void testSetsDefaultWallpaperZoomAndScroll() {
+        float expectedWallpaperZoom;
+        int expectedWallpaperScrollX;
+        int expectedWallpaperScrollY;
+
+        launchActivityIntentWithMockWallpaper();
+        PreviewActivity activity = mActivityRule.getActivity();
+        SubsamplingScaleImageView fullResImageView = activity.findViewById(R.id.full_res_image);
+
+        Point defaultCropSurfaceSize = WallpaperCropUtils.getDefaultCropSurfaceSize(
+                activity.getResources(), activity.getWindowManager().getDefaultDisplay());
+        Point screenSize = ScreenSizeCalculator.getInstance().getScreenSize(
+                activity.getWindowManager().getDefaultDisplay());
+        TestAsset asset = (TestAsset) mMockWallpaper.getAsset(activity);
+        Point wallpaperSize = new Point(asset.getBitmap().getWidth(),
+                asset.getBitmap().getHeight());
+
+        expectedWallpaperZoom = WallpaperCropUtils.calculateMinZoom(
+                wallpaperSize, defaultCropSurfaceSize);
+
+        // Current zoom should match the minimum zoom required to fit wallpaper
+        // completely on the crop surface.
+        assertEquals(expectedWallpaperZoom, fullResImageView.getScale(), FLOAT_ERROR_MARGIN);
+
+        Point scaledWallpaperSize = new Point(
+                (int) (wallpaperSize.x * expectedWallpaperZoom),
+                (int) (wallpaperSize.y * expectedWallpaperZoom));
+        Point cropSurfaceToScreen = WallpaperCropUtils.calculateCenterPosition(
+                defaultCropSurfaceSize, screenSize, true /* alignStart */, false /* isRtl */);
+        Point wallpaperToCropSurface = WallpaperCropUtils.calculateCenterPosition(
+                scaledWallpaperSize, defaultCropSurfaceSize, false /* alignStart */,
+                false /* isRtl */);
+
+        expectedWallpaperScrollX = wallpaperToCropSurface.x + cropSurfaceToScreen.x;
+        expectedWallpaperScrollY = wallpaperToCropSurface.y + cropSurfaceToScreen.y;
+
+        // ZoomView should be scrolled in X and Y directions such that the crop surface is centered
+        // relative to the wallpaper and the screen is centered (and aligned left) relative to the
+        // crop surface.
+        assertEquals(expectedWallpaperScrollX, fullResImageView.getScrollX());
+        assertEquals(expectedWallpaperScrollY, fullResImageView.getScrollY());
+    }
+
+    @Test
+    public void testSetWallpaper_TemporarilyLocksScreenOrientation() throws Throwable {
+        launchActivityIntentWithMockWallpaper();
+        PreviewActivity activity = mActivityRule.getActivity();
+
+        assertFalse(activity.getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_LOCKED);
+
+        onView(withId(R.id.preview_attribution_pane_set_wallpaper_button)).perform(click());
+
+        // Destination dialog is shown; click "Home screen".
+        onView(withText(R.string.set_wallpaper_home_screen_destination)).perform(click());
+
+        assertEquals(ActivityInfo.SCREEN_ORIENTATION_LOCKED, activity.getRequestedOrientation());
+
+        // Finish setting the wallpaper to check that the screen orientation is no longer locked.
+        finishSettingWallpaper();
+
+        assertNotEquals(activity.getRequestedOrientation(), ActivityInfo.SCREEN_ORIENTATION_LOCKED);
+    }
+    @Test
+    public void testShowsWallpaperAttribution() {
+        launchActivityIntentWithMockWallpaper();
+        PreviewActivity activity = mActivityRule.getActivity();
+
+        TextView titleView = activity.findViewById(R.id.preview_attribution_pane_title);
+        assertEquals("Title", titleView.getText());
+
+        TextView subtitle1View = activity.findViewById(R.id.preview_attribution_pane_subtitle1);
+        assertEquals("Subtitle 1", subtitle1View.getText());
+
+        TextView subtitle2View = activity.findViewById(R.id.preview_attribution_pane_subtitle2);
+        assertEquals("Subtitle 2", subtitle2View.getText());
+    }
+
+    /**
+     * Tests that if there was a failure decoding the wallpaper bitmap, then the activity shows an
+     * informative error dialog with an "OK" button, when clicked finishes the activity.
+     */
+    @Test
+    public void testLoadWallpaper_Failed_ShowsErrorDialog() {
+        // Simulate a corrupted asset that fails to perform decoding operations.
+        mMockWallpaper.corruptAssets();
+        launchActivityIntentWithMockWallpaper();
+
+        onView(withText(R.string.load_wallpaper_error_message)).check(matches(isDisplayed()));
+
+        onView(withText(android.R.string.ok)).perform(click());
+
+        assertTrue(mActivityRule.getActivity().isFinishing());
+    }
+
+    /**
+     * Tests that the explore button is not visible, even if there is an action URL present, if
+     * there is no activity on the device which can handle such an explore action.
+     */
+    @Test
+    public void testNoActionViewHandler_ExploreButtonNotVisible() {
+        mExploreIntentChecker.setViewHandlerExists(false);
+
+        launchActivityIntentWithMockWallpaper();
+        onView(withId(R.id.preview_attribution_pane_explore_button)).check(
+                matches(not(isDisplayed())));
+    }
+}
diff --git a/tests/src/com/android/wallpaper/picker/individual/IndividualPickerActivityTest.java b/tests/src/com/android/wallpaper/picker/individual/IndividualPickerActivityTest.java
new file mode 100644
index 0000000..d91c16a
--- /dev/null
+++ b/tests/src/com/android/wallpaper/picker/individual/IndividualPickerActivityTest.java
@@ -0,0 +1,496 @@
+/*
+ * 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.picker.individual;
+
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.intent.Intents.intended;
+import static androidx.test.espresso.intent.Intents.intending;
+import static androidx.test.espresso.intent.matcher.ComponentNameMatchers.hasClassName;
+import static androidx.test.espresso.intent.matcher.IntentMatchers.hasAction;
+import static androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent;
+import static androidx.test.espresso.intent.matcher.IntentMatchers.hasExtra;
+import static androidx.test.espresso.matcher.ViewMatchers.isChecked;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+
+import static junit.framework.TestCase.assertFalse;
+
+import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.equalTo;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Activity;
+import android.app.Instrumentation.ActivityResult;
+import android.app.WallpaperManager;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.ColorDrawable;
+import android.widget.FrameLayout;
+
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.test.espresso.contrib.RecyclerViewActions;
+import androidx.test.espresso.intent.Intents;
+import androidx.test.filters.MediumTest;
+import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+
+import com.android.wallpaper.R;
+import com.android.wallpaper.config.Flags;
+import com.android.wallpaper.model.Category;
+import com.android.wallpaper.model.PickerIntentFactory;
+import com.android.wallpaper.model.WallpaperInfo;
+import com.android.wallpaper.model.WallpaperRotationInitializer;
+import com.android.wallpaper.model.WallpaperRotationInitializer.RotationInitializationState;
+import com.android.wallpaper.module.FormFactorChecker;
+import com.android.wallpaper.module.Injector;
+import com.android.wallpaper.module.InjectorProvider;
+import com.android.wallpaper.testing.TestCategoryProvider;
+import com.android.wallpaper.testing.TestFormFactorChecker;
+import com.android.wallpaper.testing.TestInjector;
+import com.android.wallpaper.testing.TestUserEventLogger;
+import com.android.wallpaper.testing.TestWallpaperCategory;
+import com.android.wallpaper.testing.TestWallpaperInfo;
+import com.android.wallpaper.testing.TestWallpaperRotationInitializer;
+
+import org.hamcrest.Matcher;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Tests for {@link IndividualPickerActivity}.
+ */
+@RunWith(AndroidJUnit4ClassRunner.class)
+@MediumTest
+public class IndividualPickerActivityTest {
+
+    private static final String EXTRA_WALLPAPER_INFO =
+            "com.android.wallpaper.picker.wallpaper_info";
+    private static final TestWallpaperInfo sWallpaperInfo1 = new TestWallpaperInfo(
+            TestWallpaperInfo.COLOR_BLACK, "test-wallpaper-1");
+    private static final TestWallpaperInfo sWallpaperInfo2 = new TestWallpaperInfo(
+            TestWallpaperInfo.COLOR_BLACK, "test-wallpaper-2");
+    private static final TestWallpaperInfo sWallpaperInfo3 = new TestWallpaperInfo(
+            TestWallpaperInfo.COLOR_BLACK, "test-wallpaper-3");
+
+    private TestCategoryProvider mTestCategoryProvider;
+
+    private TestFormFactorChecker mTestFormFactorChecker;
+    private Injector mInjector;
+
+    private TestWallpaperCategory mTestCategory;
+
+    @Rule
+    public ActivityTestRule<IndividualPickerActivity> mActivityRule =
+            new ActivityTestRule<>(IndividualPickerActivity.class, false, false);
+
+    @Before
+    public void setUp() {
+        Intents.init();
+
+        mInjector = new TestInjector();
+        InjectorProvider.setInjector(mInjector);
+
+        Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        mTestFormFactorChecker = (TestFormFactorChecker) mInjector.getFormFactorChecker(context);
+        mTestCategoryProvider = (TestCategoryProvider) mInjector.getCategoryProvider(context);
+
+        sWallpaperInfo1.setAttributions(Arrays.asList(
+                "Attribution 0", "Attribution 1", "Attribution 2"));
+    }
+
+    @After
+    public void tearDown() {
+        Intents.release();
+        mActivityRule.finishActivity();
+    }
+
+    private IndividualPickerActivity getActivity() {
+        return mActivityRule.getActivity();
+    }
+
+    private void setUpFragmentForTesting() {
+        IndividualPickerFragment fragment = (IndividualPickerFragment)
+                getActivity().getSupportFragmentManager().findFragmentById(R.id.fragment_container);
+        fragment.setTestingMode(true);
+    }
+
+    private void setActivityWithMockWallpapers(boolean isRotationEnabled,
+            @RotationInitializationState int rotationState) {
+        sWallpaperInfo1.setCollectionId("collection");
+
+        ArrayList<WallpaperInfo> wallpapers = new ArrayList<>();
+        wallpapers.add(sWallpaperInfo1);
+        wallpapers.add(sWallpaperInfo2);
+        wallpapers.add(sWallpaperInfo3);
+
+        mTestCategory = new TestWallpaperCategory(
+                "Test category", "collection", wallpapers, 0 /* priority */);
+        mTestCategory.setIsRotationEnabled(isRotationEnabled);
+        mTestCategory.setRotationInitializationState(rotationState);
+
+        List<Category> testCategories = mTestCategoryProvider.getTestCategories();
+        testCategories.set(0, mTestCategory);
+
+        PickerIntentFactory intentFactory =
+                new IndividualPickerActivity.IndividualPickerActivityIntentFactory();
+        Intent intent = intentFactory.newIntent(
+                InstrumentationRegistry.getInstrumentation().getTargetContext(),
+                mTestCategory.getCollectionId());
+        mActivityRule.launchActivity(intent);
+    }
+
+    @Test
+    public void testDrawsTilesForProvidedWallpapers() {
+        setActivityWithMockWallpapers(false /* isRotationEnabled */,
+                WallpaperRotationInitializer.ROTATION_NOT_INITIALIZED);
+        IndividualPickerActivity activity = getActivity();
+
+        RecyclerView recyclerView = activity.findViewById(R.id.wallpaper_grid);
+
+        // There are only three wallpapers in the category, so the grid should only have three
+        // items.
+        assertNotNull(recyclerView.findViewHolderForAdapterPosition(0));
+        assertNotNull(recyclerView.findViewHolderForAdapterPosition(1));
+        assertNotNull(recyclerView.findViewHolderForAdapterPosition(2));
+        assertNull(recyclerView.findViewHolderForAdapterPosition(3));
+    }
+
+    @Test
+    public void testClickTile_Mobile_LaunchesPreviewActivityWithWallpaper() {
+        mTestFormFactorChecker.setFormFactor(FormFactorChecker.FORM_FACTOR_MOBILE);
+
+        setActivityWithMockWallpapers(false /* isRotationEnabled */,
+                WallpaperRotationInitializer.ROTATION_NOT_INITIALIZED);
+        getActivity();
+
+        onView(withId(R.id.wallpaper_grid)).perform(
+                RecyclerViewActions.actionOnItemAtPosition(0, click()));
+        intended(allOf(
+                hasComponent(hasClassName("com.android.wallpaper.picker.PreviewActivity")),
+                hasExtra(equalTo(EXTRA_WALLPAPER_INFO), equalTo(sWallpaperInfo1))));
+
+        Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        TestUserEventLogger eventLogger = (TestUserEventLogger) mInjector.getUserEventLogger(
+                context);
+        assertEquals(1, eventLogger.getNumIndividualWallpaperSelectedEvents());
+        assertEquals(sWallpaperInfo1.getCollectionId(context), eventLogger.getLastCollectionId());
+    }
+
+    /**
+     * Tests that the static daily rotation tile (with flag dynamicStartRotationTileEnabled=false)
+     * has a background of the light blue accent color and says "Tap to turn on".
+     */
+    @Test
+    public void testRotationEnabled_StaticDailyRotationTile() {
+        Flags.dynamicStartRotationTileEnabled = false;
+        setActivityWithMockWallpapers(true /* isRotationEnabled */,
+                WallpaperRotationInitializer.ROTATION_NOT_INITIALIZED);
+        getActivity();
+
+        onView(withText(R.string.daily_refresh_tile_title)).check(matches(isDisplayed()));
+        onView(withText(R.string.daily_refresh_tile_subtitle)).check(matches(isDisplayed()));
+
+        // Check that the background color of the "daily refresh" tile is the blue accent color.
+        FrameLayout rotationTile = getActivity().findViewById(R.id.daily_refresh);
+        int backgroundColor = ((ColorDrawable) rotationTile.getBackground()).getColor();
+        assertEquals(getActivity().getResources().getColor(R.color.accent_color),
+                backgroundColor);
+    }
+
+    /**
+     * Tests that when rotation is enabled and the rotation for this category is already in effect
+     * on both home & lock screens, then the "start rotation" tile reads "Home & Lock".
+     */
+    @Test
+    public void testRotationEnabled_RotationInitializedHomeAndLock() {
+        Flags.dynamicStartRotationTileEnabled = true;
+
+        setActivityWithMockWallpapers(true /* isRotationEnabled */,
+                WallpaperRotationInitializer.ROTATION_HOME_AND_LOCK);
+        getActivity();
+
+        onView(withText(R.string.daily_refresh_tile_title)).check(matches(isDisplayed()));
+        onView(withText(R.string.home_and_lock_short_label)).check(matches(isDisplayed()));
+    }
+
+    /**
+     * Tests that when rotation is enabled and the rotation is aleady for this category is already
+     * in
+     * effect on the home-screen only, then the "start rotation" tile reads "Home screen".
+     */
+    @Test
+    public void testRotationEnabled_RotationInitializedHomeScreenOnly() {
+        Flags.dynamicStartRotationTileEnabled = true;
+
+        setActivityWithMockWallpapers(true /* isRotationEnabled */,
+                WallpaperRotationInitializer.ROTATION_HOME_ONLY);
+        getActivity();
+
+        onView(withText(R.string.daily_refresh_tile_title)).check(matches(isDisplayed()));
+        onView(withText(R.string.home_screen_message)).check(matches(isDisplayed()));
+    }
+
+    /**
+     * Tests that after the IndividualPickerActivity loads, if the state of the current category's
+     * rotation changes while the activity is restarted, then the UI for the "start rotation" tile
+     * changes to reflect that new state.
+     */
+    @Test
+    public void testActivityRestarted_RotationStateChanges_StartRotationTileUpdates()
+            throws Throwable {
+        Flags.dynamicStartRotationTileEnabled = true;
+
+        setActivityWithMockWallpapers(true /* isRotationEnabled */,
+                WallpaperRotationInitializer.ROTATION_HOME_ONLY);
+
+        onView(withText(R.string.daily_refresh_tile_title)).check(matches(isDisplayed()));
+        onView(withText(R.string.home_screen_message)).check(matches(isDisplayed()));
+
+        // Now change the rotation initialization state such that the tile should say
+        // "Tap to turn on" after the activity resumes.
+        setUpFragmentForTesting();
+        TestWallpaperRotationInitializer
+                testWPRotationInitializer = (TestWallpaperRotationInitializer)
+                mTestCategory.getWallpaperRotationInitializer();
+        testWPRotationInitializer.setRotationInitializationState(
+                WallpaperRotationInitializer.ROTATION_NOT_INITIALIZED);
+
+        // Restart the activity.
+        IndividualPickerActivity activity = getActivity();
+        mActivityRule.runOnUiThread(activity::recreate);
+
+        onView(withText(R.string.daily_refresh_tile_title)).check(matches(isDisplayed()));
+        onView(withText(R.string.daily_refresh_tile_subtitle)).check(matches(isDisplayed()));
+    }
+
+    @Test
+    public void testRotationDisabled_DoesNotRenderDailyRefreshTile() {
+        setActivityWithMockWallpapers(false /* isRotationEnabled */,
+                WallpaperRotationInitializer.ROTATION_NOT_INITIALIZED);
+        getActivity();
+
+        onView(withText(R.string.daily_refresh_tile_title)).check(doesNotExist());
+        onView(withText(R.string.daily_refresh_tile_subtitle)).check(doesNotExist());
+    }
+
+    @Test
+    public void testClickDailyRefreshTile_ShowsStartRotationDialog() {
+        setActivityWithMockWallpapers(true /* isRotationEnabled */,
+                WallpaperRotationInitializer.ROTATION_NOT_INITIALIZED);
+        getActivity();
+
+        onView(withId(R.id.daily_refresh)).perform(click());
+
+        onView(withId(R.id.start_rotation_wifi_only_checkbox))
+                .check(matches(isDisplayed()));
+        // WiFi-only option should be checked by default.
+        onView(withId(R.id.start_rotation_wifi_only_checkbox))
+                .check(matches(isChecked()));
+    }
+
+    @Test
+    public void testShowStartRotationDialog_WifiOnly_ClickOK_StartsRotation() throws Throwable {
+        setActivityWithMockWallpapers(true /* isRotationEnabled */,
+                WallpaperRotationInitializer.ROTATION_NOT_INITIALIZED);
+        getActivity();
+
+        setUpFragmentForTesting();
+        TestWallpaperRotationInitializer
+                testWPRotationInitializer = (TestWallpaperRotationInitializer)
+                mTestCategory.getWallpaperRotationInitializer();
+        assertFalse(testWPRotationInitializer.isRotationInitialized());
+
+        // Mock out the intent and response for the live wallpaper preview.
+        Matcher<Intent> expectedIntent = hasAction(WallpaperManager.ACTION_CHANGE_LIVE_WALLPAPER);
+        intending(expectedIntent).respondWith(new ActivityResult(Activity.RESULT_OK, null));
+
+        onView(withId(R.id.daily_refresh)).perform(click());
+
+        onView(withText(android.R.string.ok)).perform(click());
+        mActivityRule.runOnUiThread(() -> {
+            testWPRotationInitializer.finishDownloadingFirstWallpaper(true /* isSuccessful */);
+            assertTrue(testWPRotationInitializer.isRotationInitialized());
+            assertTrue(testWPRotationInitializer.isWifiOnly());
+
+            // The activity should finish if starting a rotation was successful.
+            assertTrue(getActivity().isFinishing());
+        });
+    }
+
+    @Test
+    public void testShowStartRotationDialog_WifiOnly_ClickOK_Fails_ShowsErrorDialog() {
+        setActivityWithMockWallpapers(true /* isRotationEnabled */,
+                WallpaperRotationInitializer.ROTATION_NOT_INITIALIZED);
+        getActivity();
+
+        setUpFragmentForTesting();
+        TestWallpaperRotationInitializer
+                testWPRotationInitializer = (TestWallpaperRotationInitializer)
+                mTestCategory.getWallpaperRotationInitializer();
+        assertFalse(testWPRotationInitializer.isRotationInitialized());
+
+        onView(withId(R.id.daily_refresh)).perform(click());
+        onView(withText(android.R.string.ok)).perform(click());
+
+        testWPRotationInitializer.finishDownloadingFirstWallpaper(false /* isSuccessful */);
+        assertFalse(testWPRotationInitializer.isRotationInitialized());
+
+        // Error dialog should be shown with retry option.
+        onView(withText(R.string.start_rotation_error_message)).check(matches(isDisplayed()));
+        onView(withText(R.string.try_again)).check(matches(isDisplayed()));
+    }
+
+    @Test
+    public void testStartRotation_WifiOnly_FailOnce_Retry() throws Throwable {
+        setActivityWithMockWallpapers(true /* isRotationEnabled */,
+                WallpaperRotationInitializer.ROTATION_NOT_INITIALIZED);
+        getActivity();
+
+        setUpFragmentForTesting();
+        TestWallpaperRotationInitializer
+                testWPRotationInitializer = (TestWallpaperRotationInitializer)
+                mTestCategory.getWallpaperRotationInitializer();
+        assertFalse(testWPRotationInitializer.isRotationInitialized());
+
+        onView(withId(R.id.daily_refresh)).perform(click());
+        onView(withText(android.R.string.ok)).perform(click());
+
+        testWPRotationInitializer.finishDownloadingFirstWallpaper(false /* isSuccessful */);
+        assertFalse(testWPRotationInitializer.isRotationInitialized());
+
+        // Click try again to retry.
+        onView(withText(R.string.try_again)).perform(click());
+
+        mActivityRule.runOnUiThread(() -> {
+            testWPRotationInitializer.finishDownloadingFirstWallpaper(true /* isSuccessful */);
+            assertTrue(testWPRotationInitializer.isRotationInitialized());
+            assertTrue(testWPRotationInitializer.isWifiOnly());
+        });
+    }
+
+    @Test
+    public void testShowStartRotationDialog_TurnOffWifiOnly_ClickOK_StartsRotation()
+            throws Throwable {
+        setActivityWithMockWallpapers(true /* isRotationEnabled */,
+                WallpaperRotationInitializer.ROTATION_NOT_INITIALIZED);
+        getActivity();
+
+        setUpFragmentForTesting();
+        TestWallpaperRotationInitializer
+                testWPRotationInitializer = (TestWallpaperRotationInitializer)
+                mTestCategory.getWallpaperRotationInitializer();
+        assertFalse(testWPRotationInitializer.isRotationInitialized());
+
+        onView(withId(R.id.daily_refresh)).perform(click());
+        // Click on WiFi-only option to toggle it off.
+        onView(withId(R.id.start_rotation_wifi_only_checkbox)).perform(click());
+        onView(withText(android.R.string.ok)).perform(click());
+
+        mActivityRule.runOnUiThread(() -> {
+            testWPRotationInitializer.finishDownloadingFirstWallpaper(true /* isSuccessful */);
+            assertTrue(testWPRotationInitializer.isRotationInitialized());
+            assertFalse(testWPRotationInitializer.isWifiOnly());
+        });
+    }
+
+    @Test
+    public void testStartRotation_WifiOnly_FailOnce_Retry_ShouldStillHaveWifiTurnedOff()
+            throws Throwable {
+        setActivityWithMockWallpapers(true /* isRotationEnabled */,
+                WallpaperRotationInitializer.ROTATION_NOT_INITIALIZED);
+        getActivity();
+
+        setUpFragmentForTesting();
+        TestWallpaperRotationInitializer
+                testWPRotationInitializer = (TestWallpaperRotationInitializer)
+                mTestCategory.getWallpaperRotationInitializer();
+        assertFalse(testWPRotationInitializer.isRotationInitialized());
+
+        onView(withId(R.id.daily_refresh)).perform(click());
+        // Click on WiFi-only option to toggle it off.
+        onView(withId(R.id.start_rotation_wifi_only_checkbox)).perform(click());
+        onView(withText(android.R.string.ok)).perform(click());
+
+        testWPRotationInitializer.finishDownloadingFirstWallpaper(false /* isSuccessful */);
+        assertFalse(testWPRotationInitializer.isRotationInitialized());
+
+        // Click try again to retry.
+        onView(withText(R.string.try_again)).perform(click());
+
+        mActivityRule.runOnUiThread(() -> {
+            testWPRotationInitializer.finishDownloadingFirstWallpaper(true /* isSuccessful */);
+            assertTrue(testWPRotationInitializer.isRotationInitialized());
+            assertFalse(testWPRotationInitializer.isWifiOnly());
+        });
+    }
+
+    @Test
+    public void testShowStartRotationDialog_ClickCancel_DismissesDialog() {
+        setActivityWithMockWallpapers(true /* isRotationEnabled */,
+                WallpaperRotationInitializer.ROTATION_NOT_INITIALIZED);
+        getActivity();
+
+        setUpFragmentForTesting();
+        TestWallpaperRotationInitializer
+                testWPRotationInitializer = (TestWallpaperRotationInitializer)
+                mTestCategory.getWallpaperRotationInitializer();
+        assertFalse(testWPRotationInitializer.isRotationInitialized());
+
+        onView(withId(R.id.daily_refresh)).perform(click());
+        onView(withId(R.id.start_rotation_wifi_only_checkbox)).check(matches(isDisplayed()));
+        // WiFi-only option should be checked by default.
+        onView(withId(R.id.start_rotation_wifi_only_checkbox)).check(matches(isChecked()));
+
+        // Click "Cancel" to dismiss the dialog.
+        onView(withText(android.R.string.cancel)).perform(click());
+
+        // Rotation was not initialized and dialog is no longer visible.
+        assertFalse(testWPRotationInitializer.isRotationInitialized());
+        onView(withId(R.id.start_rotation_wifi_only_checkbox)).check(doesNotExist());
+    }
+
+    /**
+     * Tests that when no title is present, a wallpaper tile's content description attribute is
+     * set to the first attribution.
+     */
+    @Test
+    public void testWallpaperHasContentDescriptionFromAttribution() {
+        setActivityWithMockWallpapers(false /* isRotationEnabled */,
+                WallpaperRotationInitializer.ROTATION_NOT_INITIALIZED);
+        IndividualPickerActivity activity = getActivity();
+
+        RecyclerView recyclerView = activity.findViewById(R.id.wallpaper_grid);
+
+        RecyclerView.ViewHolder holder = recyclerView.findViewHolderForAdapterPosition(0);
+        assertEquals("Attribution 0", holder.itemView.findViewById(R.id.tile)
+                .getContentDescription());
+    }
+}
diff --git a/tests/src/com/android/wallpaper/testing/TestAlarmManagerWrapper.java b/tests/src/com/android/wallpaper/testing/TestAlarmManagerWrapper.java
new file mode 100644
index 0000000..c9bbdd2
--- /dev/null
+++ b/tests/src/com/android/wallpaper/testing/TestAlarmManagerWrapper.java
@@ -0,0 +1,71 @@
+/*
+ * 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.PendingIntent;
+
+import com.android.wallpaper.module.AlarmManagerWrapper;
+
+/**
+ * Mock of {@link AlarmManagerWrapper}.
+ */
+public class TestAlarmManagerWrapper implements AlarmManagerWrapper {
+
+    private int mExactAlarmSetCount;
+    private int mInexactAlarmSetCount;
+    private int mAlarmCanceledCount;
+
+    private long mLastInexactTriggerAtMillis;
+
+    @Override
+    public void set(int type, long triggerAtMillis, PendingIntent operation) {
+        mExactAlarmSetCount++;
+    }
+
+    @Override
+    public void setWindow(int type, long windowStartMillis, long windowLengthMillis,
+            PendingIntent operation) {
+        mExactAlarmSetCount++;
+    }
+
+    @Override
+    public void setInexactRepeating(int type, long triggerAtMillis, long intervalMillis,
+            PendingIntent operation) {
+        mInexactAlarmSetCount++;
+        mLastInexactTriggerAtMillis = triggerAtMillis;
+    }
+
+    @Override
+    public void cancel(PendingIntent operation) {
+        mAlarmCanceledCount++;
+    }
+
+    public int getExactAlarmSetCount() {
+        return mExactAlarmSetCount;
+    }
+
+    public int getInexactAlarmSetCount() {
+        return mInexactAlarmSetCount;
+    }
+
+    public int getAlarmCanceledCount() {
+        return mAlarmCanceledCount;
+    }
+
+    public long getLastInexactTriggerAtMillis() {
+        return mLastInexactTriggerAtMillis;
+    }
+}
diff --git a/tests/src/com/android/wallpaper/testing/TestAsset.java b/tests/src/com/android/wallpaper/testing/TestAsset.java
new file mode 100644
index 0000000..368919a
--- /dev/null
+++ b/tests/src/com/android/wallpaper/testing/TestAsset.java
@@ -0,0 +1,99 @@
+/*
+ * 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.graphics.Bitmap;
+import android.graphics.Bitmap.Config;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.widget.ImageView;
+
+import androidx.annotation.Nullable;
+
+import com.android.wallpaper.asset.Asset;
+
+
+/**
+ * Test implementation of Asset which blocks on Bitmap decoding operations.
+ */
+public final class TestAsset extends Asset {
+
+    private Bitmap mBitmap;
+    private boolean mIsCorrupt;
+
+    /**
+     * Constructs an asset underpinned by a 1x1 bitmap uniquely identifiable by the given pixel
+     * color.
+     *
+     * @param pixelColor Color of the asset's single pixel.
+     * @param isCorrupt  Whether or not the asset is corrupt and fails to validly decode bitmaps and
+     *                   dimensions.
+     */
+    public TestAsset(int pixelColor, boolean isCorrupt) {
+        mIsCorrupt = isCorrupt;
+
+        if (!mIsCorrupt) {
+            mBitmap = Bitmap.createBitmap(1, 1, Config.ARGB_8888);
+            mBitmap.setPixel(0, 0, pixelColor);
+        } else {
+            mBitmap = null;
+        }
+    }
+
+    @Override
+    public void decodeBitmap(int targetWidth, int targetHeight, BitmapReceiver receiver) {
+        receiver.onBitmapDecoded(mBitmap);
+    }
+
+    @Override
+    public void decodeBitmapRegion(Rect unused, int targetWidth, int targetHeight,
+            BitmapReceiver receiver) {
+        receiver.onBitmapDecoded(mBitmap);
+    }
+
+    @Override
+    public void decodeRawDimensions(Activity unused, DimensionsReceiver receiver) {
+        receiver.onDimensionsDecoded(mIsCorrupt ? null : new Point(1, 1));
+    }
+
+    @Override
+    public boolean supportsTiling() {
+        return false;
+    }
+
+    @Override
+    public void loadDrawableWithTransition(
+            Context context,
+            ImageView imageView,
+            int transitionDurationMillis,
+            @Nullable DrawableLoadedListener drawableLoadedListener,
+            int placeholderColor) {
+        if (drawableLoadedListener != null) {
+            drawableLoadedListener.onDrawableLoaded();
+        }
+    }
+
+    /** Returns the bitmap synchronously. Convenience method for tests. */
+    public Bitmap getBitmap() {
+        return mBitmap;
+    }
+
+    public void setBitmap(Bitmap bitmap) {
+        mBitmap = bitmap;
+    }
+}
diff --git a/tests/src/com/android/wallpaper/testing/TestBitmapCropper.java b/tests/src/com/android/wallpaper/testing/TestBitmapCropper.java
new file mode 100644
index 0000000..7daeeab
--- /dev/null
+++ b/tests/src/com/android/wallpaper/testing/TestBitmapCropper.java
@@ -0,0 +1,58 @@
+/*
+ * 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.graphics.Bitmap;
+import android.graphics.Rect;
+
+import com.android.wallpaper.asset.Asset;
+import com.android.wallpaper.asset.Asset.BitmapReceiver;
+import com.android.wallpaper.module.BitmapCropper;
+
+/**
+ * Test double for BitmapCropper.
+ */
+public class TestBitmapCropper implements BitmapCropper {
+
+    private boolean mFailNextCall;
+
+    public TestBitmapCropper() {
+        mFailNextCall = false;
+    }
+
+    @Override
+    public void cropAndScaleBitmap(Asset asset, float scale, Rect cropRect,
+            Callback callback) {
+        if (mFailNextCall) {
+            callback.onError(null /* throwable */);
+            return;
+        }
+        // Crop rect in pixels of source image.
+        Rect scaledCropRect = new Rect(
+                Math.round((float) cropRect.left / scale),
+                Math.round((float) cropRect.top / scale),
+                Math.round((float) cropRect.right / scale),
+                Math.round((float) cropRect.bottom / scale));
+
+        asset.decodeBitmapRegion(scaledCropRect, cropRect.width(), cropRect.height(),
+                new BitmapReceiver() {
+                    @Override
+                    public void onBitmapDecoded(Bitmap bitmap) {
+                        callback.onBitmapCropped(bitmap);
+                    }
+                });
+    }
+}
diff --git a/tests/src/com/android/wallpaper/testing/TestCategoryProvider.java b/tests/src/com/android/wallpaper/testing/TestCategoryProvider.java
new file mode 100644
index 0000000..310840c
--- /dev/null
+++ b/tests/src/com/android/wallpaper/testing/TestCategoryProvider.java
@@ -0,0 +1,94 @@
+/*
+ * 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.os.Handler;
+
+import com.android.wallpaper.model.Category;
+import com.android.wallpaper.model.CategoryProvider;
+import com.android.wallpaper.model.CategoryReceiver;
+import com.android.wallpaper.model.ImageCategory;
+import com.android.wallpaper.model.WallpaperInfo;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Test implementation of {@link CategoryProvider}.
+ */
+public class TestCategoryProvider implements CategoryProvider {
+    private final List<Category> mCategories;
+
+    public TestCategoryProvider() {
+        Category category1 = new ImageCategory(
+                "My photos" /* title */,
+                "image_wallpapers" /* collection */,
+                0 /* priority */);
+
+        ArrayList<WallpaperInfo> wallpapers = new ArrayList<>();
+        WallpaperInfo wallpaperInfo = new com.android.wallpaper.testing.TestWallpaperInfo(0);
+        wallpapers.add(wallpaperInfo);
+        Category category2 = new com.android.wallpaper.testing.TestWallpaperCategory(
+                "Test category", "init_collection", wallpapers,
+                1 /* priority */);
+
+        mCategories = new ArrayList<>();
+        mCategories.add(category1);
+        mCategories.add(category2);
+    }
+
+    @Override
+    public void fetchCategories(CategoryReceiver receiver, boolean forceRefresh) {
+        // Mimic real behavior by fetching asynchronously.
+        new Handler().post(new Runnable() {
+            @Override
+            public void run() {
+                List<Category> categories = getTestCategories();
+                for (Category category : categories) {
+                    receiver.onCategoryReceived(category);
+                }
+                receiver.doneFetchingCategories();
+            }
+        });
+    }
+
+    @Override
+    public int getSize() {
+        return mCategories == null ? 0 : mCategories.size();
+    }
+
+    @Override
+    public Category getCategory(int index) {
+        return mCategories == null ? null : mCategories.get(index);
+    }
+
+    @Override
+    public Category getCategory(String collectionId) {
+        Category category;
+        for (int i = 0; i < mCategories.size(); i++) {
+            category = mCategories.get(i);
+            if (category.getCollectionId().equals(collectionId)) {
+                return category;
+            }
+        }
+        return null;
+    }
+
+    /** Returns a list of test Category objects used by this TestCategoryProvider. */
+    public List<Category> getTestCategories() {
+        return mCategories;
+    }
+}
diff --git a/tests/src/com/android/wallpaper/testing/TestCurrentWallpaperInfoFactory.java b/tests/src/com/android/wallpaper/testing/TestCurrentWallpaperInfoFactory.java
new file mode 100644
index 0000000..3e8c665
--- /dev/null
+++ b/tests/src/com/android/wallpaper/testing/TestCurrentWallpaperInfoFactory.java
@@ -0,0 +1,70 @@
+/*
+ * 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 com.android.wallpaper.compat.BuildCompat;
+import com.android.wallpaper.model.WallpaperInfo;
+import com.android.wallpaper.module.CurrentWallpaperInfoFactory;
+import com.android.wallpaper.module.InjectorProvider;
+import com.android.wallpaper.module.WallpaperRefresher;
+
+import java.util.List;
+
+/**
+ * Test double of {@link CurrentWallpaperInfoFactory}.
+ */
+public class TestCurrentWallpaperInfoFactory implements CurrentWallpaperInfoFactory {
+
+    private WallpaperRefresher mRefresher;
+
+    public TestCurrentWallpaperInfoFactory(Context context) {
+        mRefresher = InjectorProvider.getInjector().getWallpaperRefresher(
+                context.getApplicationContext());
+    }
+
+    @Override
+    public void createCurrentWallpaperInfos(final WallpaperInfoCallback callback,
+            boolean forceRefresh) {
+        mRefresher.refresh((homeWallpaperMetadata, lockWallpaperMetadata, presentationMode) -> {
+
+            WallpaperInfo homeWallpaper = createTestWallpaperInfo(
+                    homeWallpaperMetadata.getAttributions(),
+                    homeWallpaperMetadata.getActionUrl(),
+                    homeWallpaperMetadata.getCollectionId());
+
+            WallpaperInfo lockWallpaper = null;
+            if (lockWallpaperMetadata != null && BuildCompat.isAtLeastN()) {
+                lockWallpaper = createTestWallpaperInfo(
+                        lockWallpaperMetadata.getAttributions(),
+                        lockWallpaperMetadata.getActionUrl(),
+                        lockWallpaperMetadata.getCollectionId());
+            }
+
+            callback.onWallpaperInfoCreated(homeWallpaper, lockWallpaper, presentationMode);
+        });
+    }
+
+    private static WallpaperInfo createTestWallpaperInfo(List<String> attributions,
+            String actionUrl, String collectionId) {
+        TestWallpaperInfo wallpaper = new TestWallpaperInfo(TestWallpaperInfo.COLOR_BLACK);
+        wallpaper.setAttributions(attributions);
+        wallpaper.setActionUrl(actionUrl);
+        wallpaper.setCollectionId(collectionId);
+        return wallpaper;
+    }
+}
diff --git a/tests/src/com/android/wallpaper/testing/TestExploreIntentChecker.java b/tests/src/com/android/wallpaper/testing/TestExploreIntentChecker.java
new file mode 100644
index 0000000..dc1f1a8
--- /dev/null
+++ b/tests/src/com/android/wallpaper/testing/TestExploreIntentChecker.java
@@ -0,0 +1,44 @@
+/*
+ * 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.Intent;
+import android.net.Uri;
+
+import com.android.wallpaper.module.ExploreIntentChecker;
+
+/**
+ * Test implementation of ExploreIntentChecker.
+ */
+public class TestExploreIntentChecker implements ExploreIntentChecker {
+
+    private boolean mViewHandlerExists;
+
+    public TestExploreIntentChecker() {
+        // True by default.
+        mViewHandlerExists = true;
+    }
+
+    @Override
+    public void fetchValidActionViewIntent(Uri uri, IntentReceiver receiver) {
+        Intent intent = mViewHandlerExists ? new Intent(Intent.ACTION_VIEW, uri) : null;
+        receiver.onIntentReceived(intent);
+    }
+
+    public void setViewHandlerExists(boolean exists) {
+        mViewHandlerExists = exists;
+    }
+}
diff --git a/tests/src/com/android/wallpaper/testing/TestFormFactorChecker.java b/tests/src/com/android/wallpaper/testing/TestFormFactorChecker.java
new file mode 100644
index 0000000..ba77172
--- /dev/null
+++ b/tests/src/com/android/wallpaper/testing/TestFormFactorChecker.java
@@ -0,0 +1,41 @@
+/*
+ * 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.FormFactorChecker;
+
+/**
+ * Test implementation of {@code FormFactorChecker}.
+ */
+public class TestFormFactorChecker implements FormFactorChecker {
+
+    @FormFactor
+    private int mFormFactor;
+
+    public TestFormFactorChecker() {
+        mFormFactor = FORM_FACTOR_MOBILE;
+    }
+
+    @FormFactor
+    @Override
+    public int getFormFactor() {
+        return mFormFactor;
+    }
+
+    public void setFormFactor(@FormFactor int formFactor) {
+        mFormFactor = formFactor;
+    }
+}
diff --git a/tests/src/com/android/wallpaper/testing/TestInjector.java b/tests/src/com/android/wallpaper/testing/TestInjector.java
new file mode 100644
index 0000000..0da755e
--- /dev/null
+++ b/tests/src/com/android/wallpaper/testing/TestInjector.java
@@ -0,0 +1,244 @@
+/*
+ * 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 androidx.fragment.app.Fragment;
+
+import com.android.wallpaper.compat.WallpaperManagerCompat;
+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.DefaultLiveWallpaperInfoFactory;
+import com.android.wallpaper.module.DrawableLayerResolver;
+import com.android.wallpaper.module.ExploreIntentChecker;
+import com.android.wallpaper.module.FormFactorChecker;
+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.monitor.PerformanceMonitor;
+import com.android.wallpaper.network.Requester;
+import com.android.wallpaper.picker.ImagePreviewFragment;
+import com.android.wallpaper.picker.individual.IndividualPickerFragment;
+
+/**
+ * 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 FormFactorChecker mFormFactorChecker;
+    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 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 FormFactorChecker getFormFactorChecker(Context unused) {
+        if (mFormFactorChecker == null) {
+            mFormFactorChecker = new TestFormFactorChecker();
+        }
+        return mFormFactorChecker;
+    }
+
+    @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 testingModeEnabled) {
+        return ImagePreviewFragment.newInstance(wallpaperInfo, mode, 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 PerformanceMonitor getPerformanceMonitor() {
+        if (mPerformanceMonitor == null) {
+            mPerformanceMonitor = new TestPerformanceMonitor();
+        }
+        return mPerformanceMonitor;
+    }
+}
diff --git a/tests/src/com/android/wallpaper/testing/TestLoggingOptInStatusProvider.java b/tests/src/com/android/wallpaper/testing/TestLoggingOptInStatusProvider.java
new file mode 100644
index 0000000..f4434b1
--- /dev/null
+++ b/tests/src/com/android/wallpaper/testing/TestLoggingOptInStatusProvider.java
@@ -0,0 +1,27 @@
+/*
+ * 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/TestNetworkStatusNotifier.java b/tests/src/com/android/wallpaper/testing/TestNetworkStatusNotifier.java
new file mode 100644
index 0000000..b09ee43
--- /dev/null
+++ b/tests/src/com/android/wallpaper/testing/TestNetworkStatusNotifier.java
@@ -0,0 +1,61 @@
+/*
+ * 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.NetworkStatusNotifier;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Test implementation of {@link NetworkStatusNotifier} which enables clients to manually notify
+ * listeners of a network status change.
+ */
+public class TestNetworkStatusNotifier implements NetworkStatusNotifier {
+
+    private List<Listener> mListeners;
+    @NetworkStatus
+    private int mNetworkStatus;
+
+    public TestNetworkStatusNotifier() {
+        mListeners = new ArrayList<>();
+        mNetworkStatus = NETWORK_CONNECTED;
+    }
+
+    @Override
+    public int getNetworkStatus() {
+        return mNetworkStatus;
+    }
+
+    @Override
+    public void registerListener(Listener listener) {
+        mListeners.add(listener);
+        listener.onNetworkChanged(mNetworkStatus);
+    }
+
+    @Override
+    public void unregisterListener(Listener listener) {
+        mListeners.remove(listener);
+    }
+
+    public void setAndNotifyNetworkStatus(@NetworkStatus int networkStatus) {
+        mNetworkStatus = networkStatus;
+        for (Listener listener : mListeners) {
+            listener.onNetworkChanged(mNetworkStatus);
+        }
+    }
+}
diff --git a/tests/src/com/android/wallpaper/testing/TestPartnerProvider.java b/tests/src/com/android/wallpaper/testing/TestPartnerProvider.java
new file mode 100644
index 0000000..fbdd0af
--- /dev/null
+++ b/tests/src/com/android/wallpaper/testing/TestPartnerProvider.java
@@ -0,0 +1,58 @@
+/*
+ * 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.res.Resources;
+
+import com.android.wallpaper.module.PartnerProvider;
+
+import java.io.File;
+
+/**
+ * Test implementation for PartnerProvider.
+ */
+public class TestPartnerProvider implements PartnerProvider {
+    private File mLegacyWallpaperDirectory;
+
+    @Override
+    public Resources getResources() {
+        return null;
+    }
+
+    @Override
+    public File getLegacyWallpaperDirectory() {
+        return mLegacyWallpaperDirectory;
+    }
+
+    /**
+     * Sets the File to be returned by subsequent calls to getLegacyWallpaperDirectory().
+     *
+     * @param dir The legacy wallpaper directory.
+     */
+    public void setLegacyWallpaperDirectory(File dir) {
+        mLegacyWallpaperDirectory = dir;
+    }
+
+    @Override
+    public String getPackageName() {
+        return null;
+    }
+
+    @Override
+    public boolean shouldHideDefaultWallpaper() {
+        return false;
+    }
+}
diff --git a/src/com/android/wallpaper/module/LiveWallpaperStatusChecker.java b/tests/src/com/android/wallpaper/testing/TestPerformanceMonitor.java
old mode 100755
new mode 100644
similarity index 60%
rename from src/com/android/wallpaper/module/LiveWallpaperStatusChecker.java
rename to tests/src/com/android/wallpaper/testing/TestPerformanceMonitor.java
index 67d87f8..7fe3ade
--- a/src/com/android/wallpaper/module/LiveWallpaperStatusChecker.java
+++ b/tests/src/com/android/wallpaper/testing/TestPerformanceMonitor.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -13,15 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.wallpaper.module;
+package com.android.wallpaper.testing;
+
+import com.android.wallpaper.monitor.PerformanceMonitor;
 
 /**
- * Reads whether the application's live wallpaper service is set to the device.
+ * No-op performance monitor for test.
  */
-public interface LiveWallpaperStatusChecker {
+public class TestPerformanceMonitor implements PerformanceMonitor {
 
-    /**
-     * Returns whether the live wallpaper for daily wallpapers is set to the device.
-     */
-    boolean isNoBackupImageWallpaperSet();
+    @Override
+    public void recordFullResPreviewLoadedMemorySnapshot() {
+    }
 }
diff --git a/tests/src/com/android/wallpaper/testing/TestSystemFeatureChecker.java b/tests/src/com/android/wallpaper/testing/TestSystemFeatureChecker.java
new file mode 100644
index 0000000..775e693
--- /dev/null
+++ b/tests/src/com/android/wallpaper/testing/TestSystemFeatureChecker.java
@@ -0,0 +1,41 @@
+/*
+ * 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 com.android.wallpaper.module.SystemFeatureChecker;
+
+/**
+ * Test implementation of {@link SystemFeatureChecker}.
+ */
+public class TestSystemFeatureChecker implements SystemFeatureChecker {
+
+    private boolean mHasTelephony;
+
+    public TestSystemFeatureChecker() {
+        mHasTelephony = true;
+    }
+
+    @Override
+    public boolean hasTelephony(Context context) {
+        return mHasTelephony;
+    }
+
+    public void setHasTelephony(boolean hasTelephony) {
+        mHasTelephony = hasTelephony;
+    }
+}
diff --git a/tests/src/com/android/wallpaper/testing/TestUserEventLogger.java b/tests/src/com/android/wallpaper/testing/TestUserEventLogger.java
new file mode 100644
index 0000000..9f0489e
--- /dev/null
+++ b/tests/src/com/android/wallpaper/testing/TestUserEventLogger.java
@@ -0,0 +1,289 @@
+/*
+ * 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.UserEventLogger;
+import com.android.wallpaper.module.WallpaperPersister.WallpaperPosition;
+
+/**
+ * Test implementation of {@link UserEventLogger}.
+ */
+public class TestUserEventLogger implements UserEventLogger {
+
+    private int mNumDailyRefreshTurnedOnEvents;
+    private int mNumCurrentWallpaperPreviewedEvents;
+    private int mNumActionClickedEvents;
+    private int mNumIndividualWallpaperSelectedEvents;
+    private int mNumCategorySelectedEvents;
+    private int mNumWallpaperSetEvents;
+    private int mNumWallpaperSetResultEvents;
+    private String mLastCollectionId;
+    private String mLastWallpaperId;
+    @WallpaperSetResult
+    private int mLastWallpaperSetResult;
+    private int mLastDailyRotationHour;
+    private int mNum1DayActiveLogs;
+    private int mNum7DayActiveLogs;
+    private int mNum14DayActiveLogs;
+    private int mNum28DayActiveLogs;
+    private int mLastDailyWallpaperRotationStatus;
+    private int mNumDaysDailyRotationFailed;
+    private int mNumDaysDailyRotationNotAttempted;
+    private int mLastDailyWallpaperUpdateResult;
+    private int mStandalonePreviewLaunches;
+    private int mNumRestores;
+    @WallpaperPosition
+    private int mWallpaperPosition;
+
+    public TestUserEventLogger() {
+        mLastDailyRotationHour = -1;
+        mLastDailyWallpaperRotationStatus = -1;
+        mNumDaysDailyRotationFailed = -1;
+        mNumDaysDailyRotationNotAttempted = -1;
+    }
+
+    @Override
+    public void logResumed(boolean provisioned, boolean wallpaper) {
+
+    }
+
+    @Override
+    public void logStopped() {
+
+    }
+
+    @Override
+    public void logAppLaunched() {
+        // Do nothing.
+    }
+
+    @Override
+    public void logDailyRefreshTurnedOn() {
+        mNumDailyRefreshTurnedOnEvents++;
+    }
+
+    public int getNumDailyRefreshTurnedOnEvents() {
+        return mNumDailyRefreshTurnedOnEvents;
+    }
+
+    @Override
+    public void logCurrentWallpaperPreviewed() {
+        mNumCurrentWallpaperPreviewedEvents++;
+    }
+
+    @Override
+    public void logActionClicked(String collectionId, int actionLabelResId) {
+        mNumActionClickedEvents++;
+        mLastCollectionId = collectionId;
+    }
+
+    public int getNumCurrentWallpaperPreviewedEvents() {
+        return mNumCurrentWallpaperPreviewedEvents;
+    }
+
+    public int getNumActionClickedEvents() {
+        return mNumActionClickedEvents;
+    }
+
+    @Override
+    public void logIndividualWallpaperSelected(String collectionId) {
+        mNumIndividualWallpaperSelectedEvents++;
+        mLastCollectionId = collectionId;
+    }
+
+    public int getNumIndividualWallpaperSelectedEvents() {
+        return mNumIndividualWallpaperSelectedEvents;
+    }
+
+    @Override
+    public void logCategorySelected(String collectionId) {
+        mNumCategorySelectedEvents++;
+        mLastCollectionId = collectionId;
+    }
+
+    public int getNumCategorySelectedEvents() {
+        return mNumCategorySelectedEvents;
+    }
+
+    @Override
+    public void logWallpaperSet(String collectionId, String wallpaperId) {
+        mNumWallpaperSetEvents++;
+        mLastCollectionId = collectionId;
+        mLastWallpaperId = wallpaperId;
+    }
+
+    @Override
+    public void logWallpaperSetResult(@WallpaperSetResult int result) {
+        mNumWallpaperSetResultEvents++;
+        mLastWallpaperSetResult = result;
+    }
+
+    @Override
+    public void logWallpaperSetFailureReason(@WallpaperSetFailureReason int reason) {
+        // No-op
+    }
+
+
+    @Override
+    public void logNumDailyWallpaperRotationsInLastWeek() {
+        // No-op
+    }
+
+    @Override
+    public void logNumDailyWallpaperRotationsPreviousDay() {
+        // No-op
+    }
+
+    @Override
+    public void logDailyWallpaperRotationHour(int hour) {
+        mLastDailyRotationHour = hour;
+    }
+
+    @Override
+    public void logDailyWallpaperDecodes(boolean decodes) {
+        // No-op
+    }
+
+    @Override
+    public void logRefreshDailyWallpaperButtonClicked() {
+        // No-op
+    }
+
+    @Override
+    public void logDailyWallpaperRotationStatus(int status) {
+        mLastDailyWallpaperRotationStatus = status;
+    }
+
+    @Override
+    public void logDailyWallpaperSetNextWallpaperResult(@DailyWallpaperUpdateResult int result) {
+        mLastDailyWallpaperUpdateResult = result;
+    }
+
+    @Override
+    public void logDailyWallpaperSetNextWallpaperCrash(@DailyWallpaperUpdateCrash int crash) {
+        // No-op
+    }
+
+    @Override
+    public void logNumDaysDailyRotationFailed(int days) {
+        mNumDaysDailyRotationFailed = days;
+    }
+
+    @Override
+    public void logDailyWallpaperMetadataRequestFailure(
+            @DailyWallpaperMetadataFailureReason int reason) {
+        // No-op
+    }
+
+    @Override
+    public void logNumDaysDailyRotationNotAttempted(int days) {
+        mNumDaysDailyRotationNotAttempted = days;
+    }
+
+    @Override
+    public void logStandalonePreviewLaunched() {
+        mStandalonePreviewLaunches++;
+    }
+
+    @Override
+    public void logStandalonePreviewImageUriHasReadPermission(boolean isReadPermissionGranted) {
+        // No-op
+    }
+
+    @Override
+    public void logStandalonePreviewStorageDialogApproved(boolean isApproved) {
+        // No-op
+    }
+
+    @Override
+    public void logWallpaperPresentationMode() {
+        // No-op
+    }
+
+    @Override
+    public void logRestored() {
+        mNumRestores++;
+    }
+
+    public int getNumWallpaperSetEvents() {
+        return mNumWallpaperSetEvents;
+    }
+
+    public String getLastCollectionId() {
+        return mLastCollectionId;
+    }
+
+    public String getLastWallpaperId() {
+        return mLastWallpaperId;
+    }
+
+    public int getNumWallpaperSetResultEvents() {
+        return mNumWallpaperSetResultEvents;
+    }
+
+    @WallpaperSetResult
+    public int getLastWallpaperSetResult() {
+        return mLastWallpaperSetResult;
+    }
+
+    public int getLastDailyRotationHour() {
+        return mLastDailyRotationHour;
+    }
+
+    public int getNum1DayActiveLogs() {
+        return mNum1DayActiveLogs;
+    }
+
+    public int getNum7DayActiveLogs() {
+        return mNum7DayActiveLogs;
+    }
+
+    public int getNum14DayActiveLogs() {
+        return mNum14DayActiveLogs;
+    }
+
+    public int getNum28DayActiveLogs() {
+        return mNum28DayActiveLogs;
+    }
+
+    public int getLastDailyWallpaperRotationStatus() {
+        return mLastDailyWallpaperRotationStatus;
+    }
+
+    public int getNumDaysDailyRotationFailed() {
+        return mNumDaysDailyRotationFailed;
+    }
+
+    public int getNumDaysDailyRotationNotAttempted() {
+        return mNumDaysDailyRotationNotAttempted;
+    }
+
+    public int getLastDailyWallpaperUpdateResult() {
+        return mLastDailyWallpaperUpdateResult;
+    }
+
+    public int getStandalonePreviewLaunches() {
+        return mStandalonePreviewLaunches;
+    }
+
+    public int getNumRestores() {
+        return mNumRestores;
+    }
+
+    public int getWallpaperPosition() {
+        return mWallpaperPosition;
+    }
+}
diff --git a/tests/src/com/android/wallpaper/testing/TestWallpaperCategory.java b/tests/src/com/android/wallpaper/testing/TestWallpaperCategory.java
new file mode 100644
index 0000000..c69ecf8
--- /dev/null
+++ b/tests/src/com/android/wallpaper/testing/TestWallpaperCategory.java
@@ -0,0 +1,59 @@
+/*
+ * 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.model.WallpaperCategory;
+import com.android.wallpaper.model.WallpaperInfo;
+import com.android.wallpaper.model.WallpaperRotationInitializer;
+import com.android.wallpaper.model.WallpaperRotationInitializer.RotationInitializationState;
+
+import java.util.List;
+
+/**
+ * Test-only subclass of {@link WallpaperCategory} which can be configured to provide a test double
+ * {@link WallpaperRotationInitializer}.
+ */
+public class TestWallpaperCategory extends WallpaperCategory {
+    private boolean mIsRotationEnabled;
+    private TestWallpaperRotationInitializer mWallpaperRotationInitializer;
+
+    public TestWallpaperCategory(String title, String collectionId, List<WallpaperInfo> wallpapers,
+            int priority) {
+        super(title, collectionId, wallpapers, priority);
+        mIsRotationEnabled = false;
+        mWallpaperRotationInitializer = new TestWallpaperRotationInitializer();
+    }
+
+    @Override
+    public WallpaperRotationInitializer getWallpaperRotationInitializer() {
+        return (mIsRotationEnabled) ? mWallpaperRotationInitializer : null;
+    }
+
+    @Override
+    public List<WallpaperInfo> getMutableWallpapers() {
+        return super.getMutableWallpapers();
+    }
+
+    /** Sets whether rotation is enabled on this category. */
+    public void setIsRotationEnabled(boolean isRotationEnabled) {
+        mIsRotationEnabled = isRotationEnabled;
+    }
+
+    public void setRotationInitializationState(@RotationInitializationState int rotationState) {
+        mWallpaperRotationInitializer.setRotationInitializationState(rotationState);
+    }
+}
diff --git a/tests/src/com/android/wallpaper/testing/TestWallpaperInfo.java b/tests/src/com/android/wallpaper/testing/TestWallpaperInfo.java
new file mode 100644
index 0000000..c0ccee6
--- /dev/null
+++ b/tests/src/com/android/wallpaper/testing/TestWallpaperInfo.java
@@ -0,0 +1,228 @@
+/*
+ * 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.graphics.drawable.Drawable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.wallpaper.asset.Asset;
+import com.android.wallpaper.model.InlinePreviewIntentFactory;
+import com.android.wallpaper.model.WallpaperInfo;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Test model object for a wallpaper coming from local drawable resources.
+ */
+public class TestWallpaperInfo extends WallpaperInfo {
+    public static final int COLOR_BLACK = 0;
+    public static final Parcelable.Creator<TestWallpaperInfo> CREATOR =
+            new Parcelable.Creator<TestWallpaperInfo>() {
+                @Override
+                public TestWallpaperInfo createFromParcel(Parcel in) {
+                    return new TestWallpaperInfo(in);
+                }
+
+                @Override
+                public TestWallpaperInfo[] newArray(int size) {
+                    return new TestWallpaperInfo[size];
+                }
+            };
+    private int mPixelColor;
+    private TestAsset mAsset;
+    private TestAsset mThumbAsset;
+    private List<String> mAttributions;
+    private android.app.WallpaperInfo mWallpaperComponent;
+    private String mActionUrl;
+    private String mBaseImageUrl;
+    private String mCollectionId;
+    private String mWallpaperId;
+    private boolean mIsAssetCorrupt;
+    private int mBackupPermission;
+
+    /** Constructs a test WallpaperInfo object representing a 1x1 wallpaper of the given color. */
+    public TestWallpaperInfo(int pixelColor) {
+        this(pixelColor, "test-wallpaper");
+    }
+
+    /** Constructs a test WallpaperInfo object representing a 1x1 wallpaper of the given color. */
+    public TestWallpaperInfo(int pixelColor, String id) {
+        mPixelColor = pixelColor;
+        mAttributions = Arrays.asList("Test wallpaper");
+        mWallpaperComponent = null;
+        mIsAssetCorrupt = false;
+        mBackupPermission = BACKUP_ALLOWED;
+        mWallpaperId = id;
+    }
+
+    private TestWallpaperInfo(Parcel in) {
+        mPixelColor = in.readInt();
+        mAttributions = in.createStringArrayList();
+        mActionUrl = in.readString();
+        mBaseImageUrl = in.readString();
+        mCollectionId = in.readString();
+        mWallpaperId = in.readString();
+        mIsAssetCorrupt = in.readInt() == 1;
+        mBackupPermission = in.readInt();
+    }
+
+    @Override
+    public Drawable getOverlayIcon(Context context) {
+        return null;
+    }
+
+    @Override
+    public List<String> getAttributions(Context context) {
+        return mAttributions;
+    }
+
+    /**
+     * Override default "Test wallpaper" attributions for testing.
+     */
+    public void setAttributions(List<String> attributions) {
+        mAttributions = attributions;
+    }
+
+    @Override
+    public String getActionUrl(Context unused) {
+        return mActionUrl;
+    }
+
+    /** Sets the action URL for this wallpaper. */
+    public void setActionUrl(String actionUrl) {
+        mActionUrl = actionUrl;
+    }
+
+    @Override
+    public String getBaseImageUrl() {
+        return mBaseImageUrl;
+    }
+
+    /** Sets the base image URL for this wallpaper. */
+    public void setBaseImageUrl(String baseImageUrl) {
+        mBaseImageUrl = baseImageUrl;
+    }
+
+    @Override
+    public String getCollectionId(Context unused) {
+        return mCollectionId;
+    }
+
+    /** Sets the collection ID for this wallpaper. */
+    public void setCollectionId(String collectionId) {
+        mCollectionId = collectionId;
+    }
+
+    @Override
+    public String getWallpaperId() {
+        return mWallpaperId;
+    }
+
+    /** Sets the ID for this wallpaper. */
+    public void setWallpaperId(String wallpaperId) {
+        mWallpaperId = wallpaperId;
+    }
+
+    @Override
+    public Asset getAsset(Context context) {
+        if (mAsset == null) {
+            mAsset = new TestAsset(mPixelColor, mIsAssetCorrupt);
+        }
+        return mAsset;
+    }
+
+    @Override
+    public Asset getThumbAsset(Context context) {
+        if (mThumbAsset == null) {
+            mThumbAsset = new TestAsset(mPixelColor, mIsAssetCorrupt);
+        }
+        return mThumbAsset;
+    }
+
+    @Override
+    public void showPreview(Activity srcActivity,
+            InlinePreviewIntentFactory inlinePreviewIntentFactory, int requestCode) {
+        srcActivity.startActivityForResult(
+                inlinePreviewIntentFactory.newIntent(srcActivity, this), requestCode);
+    }
+
+    @Override
+    @BackupPermission
+    public int getBackupPermission() {
+        return mBackupPermission;
+    }
+
+    public void setBackupPermission(@BackupPermission int backupPermission) {
+        mBackupPermission = backupPermission;
+    }
+
+    @Override
+    public android.app.WallpaperInfo getWallpaperComponent() {
+        return mWallpaperComponent;
+    }
+
+    public void setWallpaperComponent(android.app.WallpaperInfo wallpaperComponent) {
+        mWallpaperComponent = wallpaperComponent;
+    }
+
+    /**
+     * Simulates that the {@link Asset} instances returned by calls to #getAsset and #getThumbAsset
+     * on
+     * this object are "corrupt" and will fail to perform decode operations such as #decodeBitmap,
+     * #decodeBitmapRegion, #decodeRawDimensions, etc (these methods will call their callbacks with
+     * null instead of meaningful objects).
+     */
+    public void corruptAssets() {
+        mIsAssetCorrupt = true;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int i) {
+        parcel.writeInt(mPixelColor);
+        parcel.writeStringList(mAttributions);
+        parcel.writeString(mActionUrl);
+        parcel.writeString(mBaseImageUrl);
+        parcel.writeString(mCollectionId);
+        parcel.writeString(mWallpaperId);
+        parcel.writeInt(mIsAssetCorrupt ? 1 : 0);
+        parcel.writeInt(mBackupPermission);
+    }
+
+    @Override
+    public boolean equals(Object object) {
+        if (object == this) {
+            return true;
+        }
+        if (object instanceof TestWallpaperInfo) {
+            return mPixelColor == ((TestWallpaperInfo) object).mPixelColor;
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return mPixelColor;
+    }
+}
diff --git a/tests/src/com/android/wallpaper/testing/TestWallpaperManagerCompat.java b/tests/src/com/android/wallpaper/testing/TestWallpaperManagerCompat.java
new file mode 100644
index 0000000..69f6839
--- /dev/null
+++ b/tests/src/com/android/wallpaper/testing/TestWallpaperManagerCompat.java
@@ -0,0 +1,135 @@
+/*
+ * 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.WallpaperManager;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import com.android.wallpaper.compat.WallpaperManagerCompat;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/** Test double for {@link WallpaperManagerCompat}. */
+public class TestWallpaperManagerCompat extends WallpaperManagerCompat {
+    private static final String TAG = "TestWPManagerCompat";
+
+    ParcelFileDescriptor mSystemParcelFd;
+    ParcelFileDescriptor mLockParcelFd;
+    int mHomeWallpaperId = 0;
+    int mLockWallpaperId = 0;
+
+    private boolean mAllowBackup;
+    private Context mAppContext;
+    private Drawable mTestDrawable;
+
+    public TestWallpaperManagerCompat(Context appContext) {
+        mAppContext = appContext;
+        mAllowBackup = true;
+    }
+
+    @Override
+    public int setStream(InputStream stream, Rect visibleCropHint, boolean allowBackup,
+            int whichWallpaper) throws IOException {
+        mAllowBackup = allowBackup;
+        return ++mHomeWallpaperId;
+    }
+
+    @Override
+    public int setBitmap(Bitmap fullImage, Rect visibleCropHint, boolean allowBackup,
+            int whichWallpaper) throws IOException {
+        mAllowBackup = allowBackup;
+        return ++mLockWallpaperId;
+    }
+
+    @Override
+    public ParcelFileDescriptor getWallpaperFile(int whichWallpaper) {
+        if (whichWallpaper == WallpaperManagerCompat.FLAG_SYSTEM) {
+            return mSystemParcelFd;
+        } else if (whichWallpaper == WallpaperManagerCompat.FLAG_LOCK) {
+            return mLockParcelFd;
+        } else {
+            // :(
+            return null;
+        }
+    }
+
+    @Override
+    public Drawable getDrawable() {
+        if (mTestDrawable != null) {
+            return mTestDrawable;
+        }
+
+        // Retrieve WallpaperManager using Context#getSystemService instead of
+        // WallpaperManager#getInstance so it can be mocked out in test.
+        WallpaperManager wallpaperManager =
+                (WallpaperManager) mAppContext.getSystemService(Context.WALLPAPER_SERVICE);
+        return wallpaperManager.getDrawable();
+    }
+
+    @Override
+    public int getWallpaperId(@WallpaperLocation int whichWallpaper) {
+        switch (whichWallpaper) {
+            case WallpaperManagerCompat.FLAG_SYSTEM:
+                return mHomeWallpaperId;
+            case WallpaperManagerCompat.FLAG_LOCK:
+                return mLockWallpaperId;
+            default:
+                throw new IllegalArgumentException(
+                        "Wallpaper location must be one of FLAG_SYSTEM or "
+                                + "FLAG_LOCK but the value " + whichWallpaper + " was provided.");
+        }
+    }
+
+    public void setWallpaperFile(int whichWallpaper, ParcelFileDescriptor file) {
+        if (whichWallpaper == WallpaperManagerCompat.FLAG_SYSTEM) {
+            mSystemParcelFd = file;
+        } else if (whichWallpaper == WallpaperManagerCompat.FLAG_LOCK) {
+            mLockParcelFd = file;
+        } else {
+            Log.e(TAG, "Called setWallpaperFile without a valid distinct 'which' argument.");
+        }
+    }
+
+    public void setWallpaperId(@WallpaperLocation int whichWallpaper, int wallpaperId) {
+        switch (whichWallpaper) {
+            case WallpaperManagerCompat.FLAG_SYSTEM:
+                mHomeWallpaperId = wallpaperId;
+                break;
+            case WallpaperManagerCompat.FLAG_LOCK:
+                mLockWallpaperId = wallpaperId;
+                break;
+            default:
+                throw new IllegalArgumentException(
+                        "Wallpaper location must be one of FLAG_SYSTEM or "
+                                + "FLAG_LOCK but the value " + whichWallpaper + " was provided.");
+        }
+    }
+
+    public void setDrawable(Drawable drawable) {
+        mTestDrawable = drawable;
+    }
+
+    /** Returns whether backup is allowed for the last set wallpaper. */
+    public boolean isBackupAllowed() {
+        return mAllowBackup;
+    }
+}
diff --git a/tests/src/com/android/wallpaper/testing/TestWallpaperPersister.java b/tests/src/com/android/wallpaper/testing/TestWallpaperPersister.java
new file mode 100644
index 0000000..5fc9421
--- /dev/null
+++ b/tests/src/com/android/wallpaper/testing/TestWallpaperPersister.java
@@ -0,0 +1,223 @@
+/*
+ * 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.graphics.Bitmap;
+import android.graphics.Rect;
+
+import androidx.annotation.Nullable;
+
+import com.android.wallpaper.asset.Asset;
+import com.android.wallpaper.asset.Asset.BitmapReceiver;
+import com.android.wallpaper.model.WallpaperInfo;
+import com.android.wallpaper.module.InjectorProvider;
+import com.android.wallpaper.module.WallpaperChangedNotifier;
+import com.android.wallpaper.module.WallpaperPersister;
+import com.android.wallpaper.module.WallpaperPreferences;
+
+import java.util.List;
+
+/**
+ * Test double for {@link WallpaperPersister}.
+ */
+public class TestWallpaperPersister implements WallpaperPersister {
+
+    private Context mAppContext;
+    private WallpaperPreferences mPrefs;
+    private WallpaperChangedNotifier mWallpaperChangedNotifier;
+    private Bitmap mCurrentHomeWallpaper;
+    private Bitmap mCurrentLockWallpaper;
+    private Bitmap mPendingHomeWallpaper;
+    private Bitmap mPendingLockWallpaper;
+    private List<String> mHomeAttributions;
+    private String mHomeActionUrl;
+    @Destination
+    private int mDestination;
+    private WallpaperPersister.SetWallpaperCallback mCallback;
+    private boolean mFailNextCall;
+    private Rect mCropRect;
+    private float mScale;
+    @WallpaperPosition
+    private int mWallpaperPosition;
+
+    public TestWallpaperPersister(Context appContext) {
+        mAppContext = appContext;
+        mPrefs = InjectorProvider.getInjector().getPreferences(appContext);
+        mWallpaperChangedNotifier = WallpaperChangedNotifier.getInstance();
+
+        mCurrentHomeWallpaper = null;
+        mCurrentLockWallpaper = null;
+        mPendingHomeWallpaper = null;
+        mPendingLockWallpaper = null;
+        mFailNextCall = false;
+        mScale = -1.0f;
+    }
+
+    @Override
+    public void setIndividualWallpaper(final WallpaperInfo wallpaperInfo, Asset asset,
+            @Nullable final Rect cropRect, final float scale, final @Destination int destination,
+            final WallpaperPersister.SetWallpaperCallback callback) {
+        asset.decodeBitmap(50, 50, bitmap -> {
+            if (destination == DEST_HOME_SCREEN || destination == DEST_BOTH) {
+                mPendingHomeWallpaper = bitmap;
+                mPrefs.setHomeWallpaperAttributions(wallpaperInfo.getAttributions(mAppContext));
+                mPrefs.setWallpaperPresentationMode(
+                        WallpaperPreferences.PRESENTATION_MODE_STATIC);
+                mPrefs.setHomeWallpaperRemoteId(wallpaperInfo.getWallpaperId());
+            }
+            if (destination == DEST_LOCK_SCREEN || destination == DEST_BOTH) {
+                mPendingLockWallpaper = bitmap;
+                mPrefs.setLockWallpaperAttributions(wallpaperInfo.getAttributions(mAppContext));
+            }
+            mDestination = destination;
+            mCallback = callback;
+            mCropRect = cropRect;
+            mScale = scale;
+        });
+    }
+
+    @Override
+    public void setIndividualWallpaperWithPosition(Activity activity, WallpaperInfo wallpaper,
+            @WallpaperPosition int wallpaperPosition, SetWallpaperCallback callback) {
+        wallpaper.getAsset(activity).decodeBitmap(50, 50, new BitmapReceiver() {
+            @Override
+            public void onBitmapDecoded(@Nullable Bitmap bitmap) {
+                mPendingHomeWallpaper = bitmap;
+                mPrefs.setHomeWallpaperAttributions(wallpaper.getAttributions(mAppContext));
+                mPrefs.setHomeWallpaperBaseImageUrl(wallpaper.getBaseImageUrl());
+                mPrefs.setHomeWallpaperActionUrl(wallpaper.getActionUrl(mAppContext));
+                mPrefs.setHomeWallpaperCollectionId(wallpaper.getCollectionId(mAppContext));
+                mPrefs.setHomeWallpaperRemoteId(wallpaper.getWallpaperId());
+                mPrefs.setWallpaperPresentationMode(WallpaperPreferences.PRESENTATION_MODE_STATIC);
+                mPendingLockWallpaper = bitmap;
+                mPrefs.setLockWallpaperAttributions(wallpaper.getAttributions(mAppContext));
+
+                mDestination = WallpaperPersister.DEST_BOTH;
+                mCallback = callback;
+                mWallpaperPosition = wallpaperPosition;
+            }
+        });
+    }
+
+    @Override
+    public boolean setWallpaperInRotation(Bitmap wallpaperBitmap, List<String> attributions,
+            int actionLabelRes, int actionIconRes, String actionUrl, String collectionId) {
+        if (mFailNextCall) {
+            return false;
+        }
+
+        mCurrentHomeWallpaper = wallpaperBitmap;
+        mCurrentLockWallpaper = wallpaperBitmap;
+        mHomeAttributions = attributions;
+        mHomeActionUrl = actionUrl;
+        return true;
+    }
+
+    @Override
+    public int setWallpaperBitmapInNextRotation(Bitmap wallpaperBitmap) {
+        mCurrentHomeWallpaper = wallpaperBitmap;
+        mCurrentLockWallpaper = wallpaperBitmap;
+        return 1;
+    }
+
+    @Override
+    public boolean finalizeWallpaperForNextRotation(List<String> attributions, String actionUrl,
+            int actionLabelRes, int actionIconRes, String collectionId, int wallpaperId) {
+        mHomeAttributions = attributions;
+        mHomeActionUrl = actionUrl;
+        return true;
+    }
+
+    /** Returns mock system wallpaper bitmap. */
+    public Bitmap getCurrentHomeWallpaper() {
+        return mCurrentHomeWallpaper;
+    }
+
+    /** Returns mock lock screen wallpaper bitmap. */
+    public Bitmap getCurrentLockWallpaper() {
+        return mCurrentLockWallpaper;
+    }
+
+    /** Returns mock home attributions. */
+    public List<String> getHomeAttributions() {
+        return mHomeAttributions;
+    }
+
+    /** Returns the home wallpaper action URL. */
+    public String getHomeActionUrl() {
+        return mHomeActionUrl;
+    }
+
+    /** Returns the Destination a wallpaper was most recently set on. */
+    @Destination
+    public int getLastDestination() {
+        return mDestination;
+    }
+
+    /**
+     * Sets whether the next "set wallpaper" operation should fail or succeed.
+     */
+    public void setFailNextCall(Boolean failNextCall) {
+        mFailNextCall = failNextCall;
+    }
+
+    /**
+     * Implemented so synchronous test methods can control the completion of what would otherwise be
+     * an asynchronous operation.
+     */
+    public void finishSettingWallpaper() {
+        if (mFailNextCall) {
+            mCallback.onError(null /* throwable */);
+        } else {
+            if (mDestination == DEST_HOME_SCREEN || mDestination == DEST_BOTH) {
+                mCurrentHomeWallpaper = mPendingHomeWallpaper;
+                mPendingHomeWallpaper = null;
+            }
+            if (mDestination == DEST_LOCK_SCREEN || mDestination == DEST_BOTH) {
+                mCurrentLockWallpaper = mPendingLockWallpaper;
+                mPendingLockWallpaper = null;
+            }
+            mCallback.onSuccess();
+            mWallpaperChangedNotifier.notifyWallpaperChanged();
+        }
+    }
+
+    @Override
+    public void setWallpaperInfoInPreview(WallpaperInfo wallpaperInfo) {
+    }
+
+    @Override
+    public void onLiveWallpaperSet() {
+    }
+
+    /** Returns the last requested wallpaper bitmap scale. */
+    public float getScale() {
+        return mScale;
+    }
+
+    /** Returns the last requested wallpaper crop. */
+    public Rect getCropRect() {
+        return mCropRect;
+    }
+
+    /** Returns the last selected wallpaper position option. */
+    @WallpaperPosition
+    public int getWallpaperPosition() {
+        return mWallpaperPosition;
+    }
+}
diff --git a/tests/src/com/android/wallpaper/testing/TestWallpaperPreferences.java b/tests/src/com/android/wallpaper/testing/TestWallpaperPreferences.java
new file mode 100644
index 0000000..715e81b
--- /dev/null
+++ b/tests/src/com/android/wallpaper/testing/TestWallpaperPreferences.java
@@ -0,0 +1,457 @@
+/*
+ * 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 androidx.annotation.Nullable;
+
+import com.android.wallpaper.module.WallpaperPreferences;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Test implementation of the WallpaperPreferences interface. Just keeps prefs in memory.
+ */
+public class TestWallpaperPreferences implements WallpaperPreferences {
+
+    @PresentationMode
+    private int mWallpaperPresentationMode;
+
+    private List<String> mHomeScreenAttributions;
+    private long mHomeScreenBitmapHashCode;
+    private int mHomeWallpaperManagerId;
+    private String mHomeScreenPackageName;
+    private String mHomeActionUrl;
+    private String mHomeBaseImageUrl;
+    private String mHomeCollectionId;
+    private String mHomeWallpaperRemoteId;
+
+    private List<String> mLockScreenAttributions;
+    private long mLockScreenBitmapHashCode;
+    private int mLockWallpaperManagerId;
+    private String mLockActionUrl;
+    private String mLockCollectionId;
+
+    private List<Long> mDailyRotations;
+    private long mDailyWallpaperEnabledTimestamp;
+    private long mLastDailyLogTimestamp;
+    private long mLastAppActiveTimestamp;
+    private int mLastDailyWallpaperRotationStatus;
+    private long mLastDailyWallpaperRotationStatusTimestamp;
+    private long mLastSyncTimestamp;
+    @PendingWallpaperSetStatus
+    private int mPendingWallpaperSetStatus;
+    @PendingDailyWallpaperUpdateStatus
+    private int mPendingDailyWallpaperUpdateStatus;
+    private int mNumDaysDailyRotationFailed;
+    private int mNumDaysDailyRotationNotAttempted;
+    private int mHomeWallpaperActionLabelRes;
+    private int mHomeWallpaperActionIconRes;
+    private int mLockWallpaperActionLabelRes;
+    private int mLockWallpaperActionIconRes;
+
+    public TestWallpaperPreferences() {
+        mWallpaperPresentationMode = WallpaperPreferences.PRESENTATION_MODE_STATIC;
+        mHomeScreenAttributions = Arrays.asList("Android wallpaper");
+        mHomeScreenBitmapHashCode = 0;
+        mHomeWallpaperManagerId = 0;
+
+        mLockScreenAttributions = Arrays.asList("Android wallpaper");
+        mLockScreenBitmapHashCode = 0;
+        mLockWallpaperManagerId = 0;
+
+        mDailyRotations = new ArrayList<>();
+        mDailyWallpaperEnabledTimestamp = -1;
+        mLastDailyLogTimestamp = -1;
+        mLastDailyWallpaperRotationStatus = -1;
+        mLastDailyWallpaperRotationStatusTimestamp = 0;
+        mLastSyncTimestamp = 0;
+        mPendingWallpaperSetStatus = WALLPAPER_SET_NOT_PENDING;
+    }
+
+    @Override
+    public int getWallpaperPresentationMode() {
+        return mWallpaperPresentationMode;
+    }
+
+    @Override
+    public void setWallpaperPresentationMode(@PresentationMode int presentationMode) {
+        mWallpaperPresentationMode = presentationMode;
+    }
+
+    @Override
+    public List<String> getHomeWallpaperAttributions() {
+        return mHomeScreenAttributions;
+    }
+
+    @Override
+    public void setHomeWallpaperAttributions(List<String> attributions) {
+        mHomeScreenAttributions = attributions;
+    }
+
+    @Override
+    public String getHomeWallpaperActionUrl() {
+        return mHomeActionUrl;
+    }
+
+    @Override
+    public void setHomeWallpaperActionUrl(String actionUrl) {
+        mHomeActionUrl = actionUrl;
+    }
+
+    @Override
+    public int getHomeWallpaperActionLabelRes() {
+        return mHomeWallpaperActionLabelRes;
+    }
+
+    @Override
+    public void setHomeWallpaperActionLabelRes(int resId) {
+        mHomeWallpaperActionLabelRes = resId;
+    }
+
+    @Override
+    public int getHomeWallpaperActionIconRes() {
+        return mHomeWallpaperActionIconRes;
+    }
+
+    @Override
+    public void setHomeWallpaperActionIconRes(int resId) {
+        mHomeWallpaperActionIconRes = resId;
+    }
+
+    @Override
+    public String getHomeWallpaperBaseImageUrl() {
+        return mHomeBaseImageUrl;
+    }
+
+    @Override
+    public void setHomeWallpaperBaseImageUrl(String baseImageUrl) {
+        mHomeBaseImageUrl = baseImageUrl;
+    }
+
+    @Override
+    public String getHomeWallpaperCollectionId() {
+        return mHomeCollectionId;
+    }
+
+    @Override
+    public void setHomeWallpaperCollectionId(String collectionId) {
+        mHomeCollectionId = collectionId;
+    }
+
+    @Override
+    public String getHomeWallpaperBackingFileName() {
+        return null;
+    }
+
+    @Override
+    public void setHomeWallpaperBackingFileName(String fileName) {
+
+    }
+
+    @Override
+    public void clearHomeWallpaperMetadata() {
+        mHomeScreenAttributions = null;
+        mWallpaperPresentationMode = WallpaperPreferences.PRESENTATION_MODE_STATIC;
+        mHomeScreenBitmapHashCode = 0;
+        mHomeScreenPackageName = null;
+        mHomeWallpaperManagerId = 0;
+    }
+
+    @Override
+    public long getHomeWallpaperHashCode() {
+        return mHomeScreenBitmapHashCode;
+    }
+
+    @Override
+    public void setHomeWallpaperHashCode(long hashCode) {
+        mHomeScreenBitmapHashCode = hashCode;
+    }
+
+    @Override
+    public String getHomeWallpaperPackageName() {
+        return mHomeScreenPackageName;
+    }
+
+    @Override
+    public void setHomeWallpaperPackageName(String packageName) {
+        mHomeScreenPackageName = packageName;
+    }
+
+    @Override
+    public int getHomeWallpaperManagerId() {
+        return mHomeWallpaperManagerId;
+    }
+
+    @Override
+    public void setHomeWallpaperManagerId(int homeWallpaperId) {
+        mHomeWallpaperManagerId = homeWallpaperId;
+    }
+
+    @Override
+    public String getHomeWallpaperRemoteId() {
+        return mHomeWallpaperRemoteId;
+    }
+
+    @Override
+    public void setHomeWallpaperRemoteId(String wallpaperRemoteId) {
+        mHomeWallpaperRemoteId = wallpaperRemoteId;
+    }
+
+    @Override
+    public List<String> getLockWallpaperAttributions() {
+        return mLockScreenAttributions;
+    }
+
+    @Override
+    public void setLockWallpaperAttributions(List<String> attributions) {
+        mLockScreenAttributions = attributions;
+    }
+
+    @Override
+    public String getLockWallpaperActionUrl() {
+        return mLockActionUrl;
+    }
+
+    @Override
+    public void setLockWallpaperActionUrl(String actionUrl) {
+        mLockActionUrl = actionUrl;
+    }
+
+    @Override
+    public int getLockWallpaperActionLabelRes() {
+        return mLockWallpaperActionLabelRes;
+    }
+
+    @Override
+    public void setLockWallpaperActionLabelRes(int resId) {
+        mLockWallpaperActionLabelRes = resId;
+    }
+
+    @Override
+    public int getLockWallpaperActionIconRes() {
+        return mLockWallpaperActionIconRes;
+    }
+
+    @Override
+    public void setLockWallpaperActionIconRes(int resId) {
+        mLockWallpaperActionIconRes = resId;
+    }
+
+    @Override
+    public String getLockWallpaperCollectionId() {
+        return mLockCollectionId;
+    }
+
+    @Override
+    public void setLockWallpaperCollectionId(String collectionId) {
+        mLockCollectionId = collectionId;
+    }
+
+    @Override
+    public String getLockWallpaperBackingFileName() {
+        return null;
+    }
+
+    @Override
+    public void setLockWallpaperBackingFileName(String fileName) {
+
+    }
+
+    @Override
+    public void clearLockWallpaperMetadata() {
+        mLockScreenAttributions = null;
+        mLockScreenBitmapHashCode = 0;
+        mLockWallpaperManagerId = 0;
+    }
+
+    @Override
+    public long getLockWallpaperHashCode() {
+        return mLockScreenBitmapHashCode;
+    }
+
+    @Override
+    public void setLockWallpaperHashCode(long hashCode) {
+        mLockScreenBitmapHashCode = hashCode;
+    }
+
+    @Override
+    public int getLockWallpaperId() {
+        return mLockWallpaperManagerId;
+    }
+
+    @Override
+    public void setLockWallpaperId(int lockWallpaperId) {
+        mLockWallpaperManagerId = lockWallpaperId;
+    }
+
+    @Override
+    public void addDailyRotation(long timestamp) {
+        mDailyRotations.add(timestamp);
+    }
+
+    @Override
+    public long getLastDailyRotationTimestamp() {
+        if (mDailyRotations.size() == 0) {
+            return -1;
+        }
+
+        return mDailyRotations.get(mDailyRotations.size() - 1);
+    }
+
+    @Override
+    public List<Long> getDailyRotationsInLastWeek() {
+        return mDailyRotations;
+    }
+
+    @Nullable
+    @Override
+    public List<Long> getDailyRotationsPreviousDay() {
+        return null;
+    }
+
+    @Override
+    public long getDailyWallpaperEnabledTimestamp() {
+        return mDailyWallpaperEnabledTimestamp;
+    }
+
+    @Override
+    public void setDailyWallpaperEnabledTimestamp(long timestamp) {
+        mDailyWallpaperEnabledTimestamp = timestamp;
+    }
+
+    @Override
+    public void clearDailyRotations() {
+        mDailyRotations.clear();
+    }
+
+    @Override
+    public long getLastDailyLogTimestamp() {
+        return mLastDailyLo