[WPP2] Image Preview Fragment

Created a new preview fragment for previewing static image wallpaper.
This is the first implementation. Further refining work is needed.

Test: Manual
Bug: 242908637
Change-Id: I50828cde9f5ed458db0b51905442d3196e0ab70a
diff --git a/Android.bp b/Android.bp
index dbffb77..4827264 100644
--- a/Android.bp
+++ b/Android.bp
@@ -109,6 +109,7 @@
     static_libs: [
         "renderscript_toolkit",
         "wallpaper-common-deps",
+        "monet",
     ],
 
     jni_libs: [
diff --git a/res/color/wallpaper_control_button_ic_color_tint.xml b/res/color/wallpaper_control_button_ic_color_tint.xml
new file mode 100644
index 0000000..534a5e5
--- /dev/null
+++ b/res/color/wallpaper_control_button_ic_color_tint.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+    <item android:state_checked="true" android:color="?androidprv:attr/textColorOnAccent" />
+    <item android:state_checked="false" android:color="?android:textColorTertiary" />
+</selector>
\ No newline at end of file
diff --git a/res/drawable/duo_tabs_button_background.xml b/res/drawable/duo_tabs_button_background.xml
new file mode 100644
index 0000000..b06f36f
--- /dev/null
+++ b/res/drawable/duo_tabs_button_background.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="?android:colorControlHighlight">
+
+    <item android:id="@android:id/mask">
+        <shape android:shape="rectangle">
+            <corners android:radius="@dimen/duo_tabs_button_corner_radius" />
+            <solid android:color="?android:colorControlHighlight" />
+        </shape>
+    </item>
+
+    <item android:drawable="@drawable/duo_tabs_button_background_base" />
+</ripple>
\ No newline at end of file
diff --git a/res/drawable/duo_tabs_button_background_base.xml b/res/drawable/duo_tabs_button_background_base.xml
new file mode 100644
index 0000000..cc716c2
--- /dev/null
+++ b/res/drawable/duo_tabs_button_background_base.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+    android:shape="rectangle">
+    <corners android:radius="@dimen/duo_tabs_button_corner_radius" />
+    <solid android:color="?androidprv:attr/colorSurface" />
+</shape>
\ No newline at end of file
diff --git a/res/drawable/duo_tabs_button_indicator_background.xml b/res/drawable/duo_tabs_button_indicator_background.xml
new file mode 100644
index 0000000..86841c5
--- /dev/null
+++ b/res/drawable/duo_tabs_button_indicator_background.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="?android:colorControlHighlight">
+
+    <item android:id="@android:id/mask">
+        <shape android:shape="rectangle">
+            <corners android:radius="@dimen/duo_tabs_button_corner_radius" />
+            <solid android:color="?android:colorControlHighlight" />
+        </shape>
+    </item>
+
+    <item android:drawable="@drawable/duo_tabs_button_indicator_background_base" />
+</ripple>
\ No newline at end of file
diff --git a/res/drawable/duo_tabs_button_indicator_background_base.xml b/res/drawable/duo_tabs_button_indicator_background_base.xml
new file mode 100644
index 0000000..61c9080
--- /dev/null
+++ b/res/drawable/duo_tabs_button_indicator_background_base.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+    android:shape="rectangle">
+    <corners android:radius="@dimen/duo_tabs_button_corner_radius" />
+    <solid android:color="?androidprv:attr/colorAccentPrimary"  />
+</shape>
\ No newline at end of file
diff --git a/res/drawable/duo_tabs_divider.xml b/res/drawable/duo_tabs_divider.xml
new file mode 100644
index 0000000..8325048
--- /dev/null
+++ b/res/drawable/duo_tabs_divider.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <size
+        android:height="0dp"
+        android:width="@dimen/duo_tabs_divider_space" />
+</shape>
\ No newline at end of file
diff --git a/res/drawable/ic_effect.xml b/res/drawable/ic_effect.xml
new file mode 100644
index 0000000..fe4acba
--- /dev/null
+++ b/res/drawable/ic_effect.xml
@@ -0,0 +1,26 @@
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="@dimen/wallpaper_control_icon_size"
+    android:height="@dimen/wallpaper_control_icon_size"
+    android:viewportHeight="@dimen/wallpaper_control_icon_viewport_size"
+    android:viewportWidth="@dimen/wallpaper_control_icon_viewport_size"
+    android:tintMode="multiply"
+    android:tint="@color/wallpaper_control_button_ic_color_tint">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M19.6352 8.72727L20.9984 5.72648L24 4.36364L20.9984 3.0008L19.6352 0L18.272 3.0008L15.2704 4.36364L18.272 5.72648L19.6352 8.72727ZM11.456 9.27113L8.7296 3.26953L6.0032 9.27113L0 11.9968L6.0032 14.7225L8.7296 20.7241L11.456 14.7225L17.4592 11.9968L11.456 9.27113ZM19.6352 15.2727L18.272 18.2735L15.2704 19.6364L18.272 20.9992L19.6352 24L20.9984 20.9992L24 19.6364L20.9984 18.2735L19.6352 15.2727Z" />
+</vector>
diff --git a/res/drawable/ic_info.xml b/res/drawable/ic_info.xml
new file mode 100644
index 0000000..1cbabe7
--- /dev/null
+++ b/res/drawable/ic_info.xml
@@ -0,0 +1,26 @@
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="@dimen/wallpaper_control_icon_size"
+    android:height="@dimen/wallpaper_control_icon_size"
+    android:viewportWidth="@dimen/wallpaper_control_icon_viewport_size"
+    android:viewportHeight="@dimen/wallpaper_control_icon_viewport_size"
+    android:tintMode="multiply"
+    android:tint="@color/wallpaper_control_button_ic_color_tint">
+  <path
+      android:fillColor="@android:color/white"
+      android:pathData="M11,7h2v2h-2zM11,11h2v6h-2zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z"/>
+</vector>
diff --git a/res/drawable/set_wallpaper_button_background.xml b/res/drawable/set_wallpaper_button_background.xml
new file mode 100644
index 0000000..a19a3fc
--- /dev/null
+++ b/res/drawable/set_wallpaper_button_background.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="?android:colorControlHighlight">
+
+    <item android:id="@android:id/mask">
+        <shape android:shape="rectangle">
+            <corners android:radius="@dimen/set_wallpaper_button_corner_radius" />
+            <padding
+                android:left="@dimen/set_wallpaper_button_horizontal_padding"
+                android:top="@dimen/set_wallpaper_button_vertical_padding"
+                android:right="@dimen/set_wallpaper_button_horizontal_padding"
+                android:bottom="@dimen/set_wallpaper_button_vertical_padding" />
+            <solid android:color="?android:colorControlHighlight" />
+        </shape>
+    </item>
+
+    <item android:drawable="@drawable/set_wallpaper_button_background_base" />
+</ripple>
\ No newline at end of file
diff --git a/res/drawable/set_wallpaper_button_background_base.xml b/res/drawable/set_wallpaper_button_background_base.xml
new file mode 100644
index 0000000..027b06c
--- /dev/null
+++ b/res/drawable/set_wallpaper_button_background_base.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+    android:shape="rectangle">
+    <corners android:radius="@dimen/set_wallpaper_button_corner_radius" />
+    <padding
+        android:left="@dimen/set_wallpaper_button_horizontal_padding"
+        android:top="@dimen/set_wallpaper_button_vertical_padding"
+        android:right="@dimen/set_wallpaper_button_horizontal_padding"
+        android:bottom="@dimen/set_wallpaper_button_vertical_padding" />
+    <solid android:color="?androidprv:attr/colorAccentPrimary"/>
+</shape>
\ No newline at end of file
diff --git a/res/drawable/wallpaper_control_button_background.xml b/res/drawable/wallpaper_control_button_background.xml
new file mode 100644
index 0000000..e3954dc
--- /dev/null
+++ b/res/drawable/wallpaper_control_button_background.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_checked="true"
+        android:drawable="@drawable/wallpaper_control_button_on_background" />
+    <item android:drawable="@drawable/wallpaper_control_button_off_background" />
+</selector>
\ No newline at end of file
diff --git a/res/drawable/wallpaper_control_button_effect.xml b/res/drawable/wallpaper_control_button_effect.xml
new file mode 100644
index 0000000..a3c975b
--- /dev/null
+++ b/res/drawable/wallpaper_control_button_effect.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/wallpaper_control_button_background" />
+    <item android:drawable="@drawable/ic_effect" />
+</layer-list>
\ No newline at end of file
diff --git a/res/drawable/wallpaper_control_button_group_divider.xml b/res/drawable/wallpaper_control_button_group_divider.xml
new file mode 100644
index 0000000..b8f115c
--- /dev/null
+++ b/res/drawable/wallpaper_control_button_group_divider.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <size
+        android:height="@dimen/wallpaper_control_button_group_divider_space"
+        android:width="0dp" />
+</shape>
\ No newline at end of file
diff --git a/res/drawable/wallpaper_control_button_info.xml b/res/drawable/wallpaper_control_button_info.xml
new file mode 100644
index 0000000..c3954c1
--- /dev/null
+++ b/res/drawable/wallpaper_control_button_info.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/wallpaper_control_button_background" />
+    <item android:drawable="@drawable/ic_info" />
+</layer-list>
\ No newline at end of file
diff --git a/res/drawable/wallpaper_control_button_off_background.xml b/res/drawable/wallpaper_control_button_off_background.xml
new file mode 100644
index 0000000..7c0b1bc
--- /dev/null
+++ b/res/drawable/wallpaper_control_button_off_background.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="?android:colorControlHighlight">
+
+    <item android:id="@android:id/mask">
+        <shape android:shape="oval">
+            <padding
+                android:bottom="@dimen/wallpaper_control_button_padding"
+                android:left="@dimen/wallpaper_control_button_padding"
+                android:right="@dimen/wallpaper_control_button_padding"
+                android:top="@dimen/wallpaper_control_button_padding" />
+            <solid android:color="?android:colorControlHighlight" />
+        </shape>
+    </item>
+
+    <item android:drawable="@drawable/wallpaper_control_button_off_background_base" />
+</ripple>
\ No newline at end of file
diff --git a/res/drawable/wallpaper_control_button_off_background_base.xml b/res/drawable/wallpaper_control_button_off_background_base.xml
new file mode 100644
index 0000000..97294e5
--- /dev/null
+++ b/res/drawable/wallpaper_control_button_off_background_base.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+    android:shape="oval">
+    <padding
+        android:left="@dimen/wallpaper_control_button_padding"
+        android:right="@dimen/wallpaper_control_button_padding"
+        android:top="@dimen/wallpaper_control_button_padding"
+        android:bottom="@dimen/wallpaper_control_button_padding"/>
+    <solid android:color="?androidprv:attr/colorSurface"/>
+</shape>
\ No newline at end of file
diff --git a/res/drawable/wallpaper_control_button_on_background.xml b/res/drawable/wallpaper_control_button_on_background.xml
new file mode 100644
index 0000000..3e35d09
--- /dev/null
+++ b/res/drawable/wallpaper_control_button_on_background.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="?android:colorControlHighlight">
+
+    <item android:id="@android:id/mask">
+        <shape android:shape="oval">
+            <padding
+                android:bottom="@dimen/wallpaper_control_button_padding"
+                android:left="@dimen/wallpaper_control_button_padding"
+                android:right="@dimen/wallpaper_control_button_padding"
+                android:top="@dimen/wallpaper_control_button_padding" />
+            <solid android:color="?android:colorControlHighlight" />
+        </shape>
+    </item>
+
+    <item android:drawable="@drawable/wallpaper_control_button_on_background_base" />
+</ripple>
\ No newline at end of file
diff --git a/res/drawable/wallpaper_control_button_on_background_base.xml b/res/drawable/wallpaper_control_button_on_background_base.xml
new file mode 100644
index 0000000..e0f4d78
--- /dev/null
+++ b/res/drawable/wallpaper_control_button_on_background_base.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+    android:shape="oval">
+    <padding
+        android:left="@dimen/wallpaper_control_button_padding"
+        android:right="@dimen/wallpaper_control_button_padding"
+        android:top="@dimen/wallpaper_control_button_padding"
+        android:bottom="@dimen/wallpaper_control_button_padding"/>
+    <solid android:color="?androidprv:attr/colorAccentPrimary"/>
+</shape>
\ No newline at end of file
diff --git a/res/layout/duo_tabs.xml b/res/layout/duo_tabs.xml
new file mode 100644
index 0000000..5a25376
--- /dev/null
+++ b/res/layout/duo_tabs.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_gravity="bottom"
+    android:divider="@drawable/duo_tabs_divider"
+    android:orientation="horizontal"
+    android:showDividers="middle">
+
+    <Button
+        android:id="@+id/tab_primary"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/separated_tabs_height"
+        android:layout_weight="1"
+        android:background="@drawable/duo_tabs_button_background"
+        android:gravity="center" />
+
+    <Button
+        android:id="@+id/tab_secondary"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/separated_tabs_height"
+        android:layout_weight="1"
+        android:background="@drawable/duo_tabs_button_background"
+        android:gravity="center" />
+</LinearLayout>
diff --git a/res/layout/fragment_image_preview_2.xml b/res/layout/fragment_image_preview_2.xml
new file mode 100644
index 0000000..7ffe43f
--- /dev/null
+++ b/res/layout/fragment_image_preview_2.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <com.android.wallpaper.picker.TouchForwardingLayout2
+        android:id="@+id/touch_forwarding_layout"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@android:color/transparent"/>
+
+    <include layout="@layout/wallpaper_preview" />
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@android:color/transparent"
+        android:fitsSystemWindows="true">
+
+        <Toolbar
+            android:id="@+id/toolbar"
+            android:layout_width="match_parent"
+            android:layout_height="?android:attr/actionBarSize"
+            app:layout_constraintEnd_toStartOf="@+id/button_set_wallpaper_container"
+            app:layout_constraintTop_toTopOf="parent" />
+
+        <FrameLayout
+            android:id="@+id/button_set_wallpaper_container"
+            android:layout_width="wrap_content"
+            android:layout_height="?android:attr/actionBarSize"
+            android:layout_marginEnd="@dimen/set_wallpaper_button_margin_end"
+            android:layout_marginBottom="@dimen/set_wallpaper_button_margin_bottom"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintTop_toTopOf="parent">
+
+            <Button
+                android:id="@+id/button_set_wallpaper"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_vertical"
+                android:background="@drawable/set_wallpaper_button_background"
+                android:gravity="center"
+                android:text="@string/set_wallpaper_button_text"
+                android:textColor="?androidprv:attr/textColorOnAccent" />
+        </FrameLayout>
+
+        <com.android.wallpaper.widget.WallpaperControlButtonGroup
+            android:id="@+id/wallpaper_control_button_group"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginEnd="@dimen/wallpaper_control_button_group_margin_end"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintTop_toBottomOf="@+id/button_set_wallpaper_container" />
+
+        <com.android.wallpaper.widget.DuoTabs
+            android:id="@+id/overlay_tabs"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingHorizontal="@dimen/full_preview_page_tabs_horizontal_padding"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toStartOf="parent" />
+    </androidx.constraintlayout.widget.ConstraintLayout>
+</FrameLayout>
diff --git a/res/layout/wallpaper_control_button_group.xml b/res/layout/wallpaper_control_button_group.xml
new file mode 100644
index 0000000..1b749f0
--- /dev/null
+++ b/res/layout/wallpaper_control_button_group.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/wallpaper_control_container"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:divider="@drawable/wallpaper_control_button_group_divider"
+    android:showDividers="middle">
+
+    <ToggleButton
+        android:id="@+id/effects_button"
+        android:layout_width="@dimen/wallpaper_control_button_size"
+        android:layout_height="@dimen/wallpaper_control_button_size"
+        android:background="@android:color/transparent"
+        android:contentDescription="@string/tab_effects"
+        android:foreground="@drawable/wallpaper_control_button_effect"
+        android:textOff=""
+        android:textOn=""
+        android:visibility="gone" />
+
+    <ToggleButton
+        android:id="@+id/information_button"
+        android:layout_width="@dimen/wallpaper_control_button_size"
+        android:layout_height="@dimen/wallpaper_control_button_size"
+        android:background="@android:color/transparent"
+        android:contentDescription="@string/tab_info"
+        android:foreground="@drawable/wallpaper_control_button_info"
+        android:textOff=""
+        android:textOn=""
+        android:visibility="gone" />
+</LinearLayout>
+
diff --git a/res/layout/wallpaper_preview.xml b/res/layout/wallpaper_preview.xml
new file mode 100644
index 0000000..f7dbf64
--- /dev/null
+++ b/res/layout/wallpaper_preview.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+     Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:contentDescription="@string/wallpaper_preview_card_content_description">
+
+    <SurfaceView
+        android:id="@+id/wallpaper_surface"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+    <SurfaceView
+        android:id="@+id/wallpaper_surface_cinematic"
+        android:visibility="gone"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+    <SurfaceView
+        android:id="@+id/workspace_surface"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:importantForAccessibility="noHideDescendants" />
+
+    <FrameLayout
+        android:id="@+id/lock_screen_preview_container"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:importantForAccessibility="noHideDescendants" />
+
+    <androidx.core.widget.ContentLoadingProgressBar
+        android:id="@+id/wallpaper_preview_spinner"
+        style="?android:progressBarStyleLarge"
+        android:background="@android:color/transparent"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:visibility="gone" />
+</FrameLayout>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 023aba4..9e0001f 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -238,4 +238,26 @@
     <dimen name="wallpaper_effect_failed_container_margin_top">24dp</dimen>
     <dimen name="wallpaper_effect_failed_button_size">16sp</dimen>
 
+    <!-- Clipping of the home screen overlay -->
+    <dimen name="home_screen_overlay_top_clipping">100dp</dimen>
+    <dimen name="home_screen_overlay_bottom_clipping">40dp</dimen>
+
+    <!-- Set wallpaper button -->
+    <dimen name="set_wallpaper_button_horizontal_padding">18dp</dimen>
+    <dimen name="set_wallpaper_button_vertical_padding">10dp</dimen>
+    <dimen name="set_wallpaper_button_corner_radius">20dp</dimen>
+    <dimen name="set_wallpaper_button_margin_end">20dp</dimen>
+    <dimen name="set_wallpaper_button_margin_bottom">24dp</dimen>
+
+    <!-- Wallpaper control button group -->
+    <dimen name="wallpaper_control_button_group_divider_space">12dp</dimen>
+    <dimen name="wallpaper_control_button_group_margin_end">18dp</dimen>
+    <dimen name="wallpaper_control_button_padding">12dp</dimen>
+    <dimen name="wallpaper_control_button_size">48dp</dimen>
+    <dimen name="wallpaper_control_icon_size">24dp</dimen>
+    <item name="wallpaper_control_icon_viewport_size" format="float" type="dimen">24</item>
+
+    <!-- Duo tabs -->
+    <dimen name="duo_tabs_button_corner_radius">12dp</dimen>
+    <dimen name="duo_tabs_divider_space">8dp</dimen>
 </resources>
diff --git a/src/com/android/wallpaper/module/WallpaperSetter2.java b/src/com/android/wallpaper/module/WallpaperSetter2.java
new file mode 100644
index 0000000..e07ba19
--- /dev/null
+++ b/src/com/android/wallpaper/module/WallpaperSetter2.java
@@ -0,0 +1,411 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wallpaper.module;
+
+import static android.app.WallpaperManager.FLAG_LOCK;
+
+import android.app.Activity;
+import android.app.ProgressDialog;
+import android.app.WallpaperColors;
+import android.app.WallpaperManager;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.util.Log;
+import android.view.Display;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
+import androidx.fragment.app.FragmentManager;
+import androidx.lifecycle.Lifecycle.Event;
+import androidx.lifecycle.LifecycleEventObserver;
+import androidx.lifecycle.LifecycleOwner;
+
+import com.android.wallpaper.R;
+import com.android.wallpaper.asset.Asset;
+import com.android.wallpaper.model.LiveWallpaperInfo;
+import com.android.wallpaper.model.WallpaperInfo;
+import com.android.wallpaper.module.UserEventLogger.WallpaperSetFailureReason;
+import com.android.wallpaper.module.WallpaperPersister.Destination;
+import com.android.wallpaper.module.WallpaperPersister.SetWallpaperCallback;
+import com.android.wallpaper.picker.SetWallpaperDialogFragment;
+import com.android.wallpaper.picker.SetWallpaperDialogFragment.Listener;
+import com.android.wallpaper.util.ScreenSizeCalculator;
+import com.android.wallpaper.util.ThrowableAnalyzer;
+import com.android.wallpaper.util.WallpaperCropUtils;
+
+import com.bumptech.glide.Glide;
+
+import java.io.IOException;
+import java.util.Optional;
+
+/**
+ * Helper class used to set the current wallpaper. It handles showing the destination request dialog
+ * and actually setting the wallpaper on a given destination.
+ * It is expected to be instantiated within a Fragment or Activity, and {@link #cleanUp()} should
+ * be called from its owner's onDestroy method (or equivalent).
+ * TODO b/256819974 This is a duplicate of WallpaperSetter and should be removed after
+ * WallpaperSetter in the branch of tm-qpr-dev is synced with master.
+ */
+public class WallpaperSetter2 {
+
+    private static final String TAG = "WallpaperSetter2";
+    private static final String PROGRESS_DIALOG_NO_TITLE = null;
+    private static final boolean PROGRESS_DIALOG_INDETERMINATE = true;
+
+    private static final int UNUSED_REQUEST_CODE = 1;
+    private static final String TAG_SET_WALLPAPER_DIALOG_FRAGMENT = "set_wallpaper_dialog";
+
+    private final WallpaperPersister mWallpaperPersister;
+    private final WallpaperPreferences mPreferences;
+    private final boolean mTestingModeEnabled;
+    private final UserEventLogger mUserEventLogger;
+    private ProgressDialog mProgressDialog;
+    private Optional<Integer> mCurrentScreenOrientation = Optional.empty();
+
+    public WallpaperSetter2(WallpaperPersister wallpaperPersister,
+            WallpaperPreferences preferences, UserEventLogger userEventLogger,
+            boolean isTestingModeEnabled) {
+        mTestingModeEnabled = isTestingModeEnabled;
+        mWallpaperPersister = wallpaperPersister;
+        mPreferences = preferences;
+        mUserEventLogger = userEventLogger;
+    }
+
+    /**
+     * Sets current wallpaper to the device with the minimum scale to fit the screen size.
+     *
+     * @param containerActivity main Activity that owns the current fragment
+     * @param wallpaper         info for the actual wallpaper to set
+     * @param destination       the wallpaper destination i.e. home vs. lockscreen vs. both.
+     * @param callback          optional callback to be notified when the wallpaper is set.
+     */
+    public void setCurrentWallpaper(Activity containerActivity, WallpaperInfo wallpaper,
+            @Destination final int destination,
+            @Nullable SetWallpaperCallback callback) {
+        Asset wallpaperAsset = wallpaper.getAsset(containerActivity.getApplicationContext());
+        wallpaperAsset.decodeRawDimensions(containerActivity, dimensions -> {
+            if (dimensions == null) {
+                Log.e(TAG, "Raw wallpaper's dimensions are null");
+                return;
+            }
+
+            Display defaultDisplay = containerActivity.getWindowManager().getDefaultDisplay();
+            Point screenSize = ScreenSizeCalculator.getInstance().getScreenSize(defaultDisplay);
+            Rect visibleRawWallpaperRect =
+                    WallpaperCropUtils.calculateVisibleRect(dimensions, screenSize);
+            float wallpaperScale = WallpaperCropUtils.calculateMinZoom(dimensions, screenSize);
+            Rect cropRect = WallpaperCropUtils.calculateCropRect(
+                    containerActivity.getApplicationContext(), defaultDisplay,
+                    dimensions, visibleRawWallpaperRect, wallpaperScale);
+
+            setCurrentWallpaper(containerActivity, wallpaper, wallpaperAsset, destination,
+                    wallpaperScale, cropRect, null, callback);
+        });
+    }
+
+    /**
+     * Sets current wallpaper to the device based on current zoom and scroll state.
+     *
+     * @param containerActivity main Activity that owns the current fragment
+     * @param wallpaper         info for the actual wallpaper to set
+     * @param wallpaperAsset    Wallpaper asset from which to retrieve image data.
+     * @param destination       The wallpaper destination i.e. home vs. lockscreen vs. both.
+     * @param wallpaperScale    Scaling factor applied to the source image before setting the
+     *                          wallpaper to the device.
+     * @param cropRect          Desired crop area of the wallpaper in post-scale units. If null,
+     *                          then the
+     *                          wallpaper image will be set without any scaling or cropping.
+     * @param callback          optional callback to be notified when the wallpaper is set.
+     */
+    public void setCurrentWallpaper(Activity containerActivity, WallpaperInfo wallpaper,
+            @Nullable Asset wallpaperAsset, @Destination final int destination,
+            float wallpaperScale, @Nullable Rect cropRect, WallpaperColors wallpaperColors,
+            @Nullable SetWallpaperCallback callback) {
+        if (wallpaper instanceof LiveWallpaperInfo) {
+            setCurrentLiveWallpaper(containerActivity, (LiveWallpaperInfo) wallpaper, destination,
+                    wallpaperColors, callback);
+            return;
+        }
+        mPreferences.setPendingWallpaperSetStatus(
+                WallpaperPreferences.WALLPAPER_SET_PENDING);
+
+        // Save current screen rotation so we can temporarily disable rotation while setting the
+        // wallpaper and restore after setting the wallpaper finishes.
+        saveAndLockScreenOrientationIfNeeded(containerActivity);
+
+        // Clear MosaicView tiles and Glide's cache and pools to reclaim memory for final cropped
+        // bitmap.
+        Glide.get(containerActivity).clearMemory();
+
+        // ProgressDialog endlessly updates the UI thread, keeping it from going idle which
+        // therefore
+        // causes Espresso to hang once the dialog is shown.
+        if (!mTestingModeEnabled && !containerActivity.isFinishing()) {
+            int themeResId = (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP)
+                    ? R.style.ProgressDialogThemePreL : R.style.LightDialogTheme;
+            mProgressDialog = new ProgressDialog(containerActivity, themeResId);
+
+            mProgressDialog.setTitle(PROGRESS_DIALOG_NO_TITLE);
+            mProgressDialog.setMessage(containerActivity.getString(
+                    R.string.set_wallpaper_progress_message));
+            mProgressDialog.setIndeterminate(PROGRESS_DIALOG_INDETERMINATE);
+            if (containerActivity instanceof LifecycleOwner) {
+                ((LifecycleOwner) containerActivity).getLifecycle().addObserver(
+                        new LifecycleEventObserver() {
+                            @Override
+                            public void onStateChanged(@NonNull LifecycleOwner source,
+                                    @NonNull Event event) {
+                                if (event == Event.ON_DESTROY) {
+                                    if (mProgressDialog != null) {
+                                        mProgressDialog.dismiss();
+                                        mProgressDialog = null;
+                                    }
+                                }
+                            }
+                        });
+            }
+            mProgressDialog.show();
+        }
+
+        mWallpaperPersister.setIndividualWallpaper(
+                wallpaper, wallpaperAsset, cropRect,
+                wallpaperScale, destination, new SetWallpaperCallback() {
+                    @Override
+                    public void onSuccess(WallpaperInfo wallpaperInfo) {
+                        onWallpaperApplied(wallpaper, containerActivity);
+                        if (callback != null) {
+                            callback.onSuccess(wallpaper);
+                        }
+                    }
+
+                    @Override
+                    public void onError(Throwable throwable) {
+                        onWallpaperApplyError(throwable, containerActivity);
+                        if (callback != null) {
+                            callback.onError(throwable);
+                        }
+                    }
+                });
+    }
+
+    private void setCurrentLiveWallpaper(Activity activity, LiveWallpaperInfo wallpaper,
+            @Destination final int destination, WallpaperColors colors,
+            @Nullable SetWallpaperCallback callback) {
+        try {
+            // Save current screen rotation so we can temporarily disable rotation while setting the
+            // wallpaper and restore after setting the wallpaper finishes.
+            saveAndLockScreenOrientationIfNeeded(activity);
+
+            if (destination == WallpaperPersister.DEST_LOCK_SCREEN) {
+                throw new IllegalArgumentException(
+                        "Live wallpaper cannot be applied on lock screen only");
+            }
+            WallpaperManager wallpaperManager = WallpaperManager.getInstance(activity);
+            wallpaperManager.setWallpaperComponentWithFlags(
+                    wallpaper.getWallpaperComponent().getComponent(),
+                    WallpaperPersister.destinationToFlags(destination));
+            wallpaperManager.setWallpaperOffsetSteps(0.5f /* xStep */, 0.0f /* yStep */);
+            wallpaperManager.setWallpaperOffsets(
+                    activity.getWindow().getDecorView().getRootView().getWindowToken(),
+                    0.5f /* xOffset */, 0.0f /* yOffset */);
+            if (destination == WallpaperPersister.DEST_BOTH) {
+                wallpaperManager.clear(FLAG_LOCK);
+            }
+            mPreferences.storeLatestHomeWallpaper(wallpaper.getWallpaperId(), wallpaper, colors);
+            onWallpaperApplied(wallpaper, activity);
+            if (callback != null) {
+                callback.onSuccess(wallpaper);
+            }
+        } catch (RuntimeException | IOException e) {
+            onWallpaperApplyError(e, activity);
+            if (callback != null) {
+                callback.onError(e);
+            }
+        }
+    }
+
+    /**
+     * Sets current live wallpaper to the device (restore case)
+     *
+     * @param context     The context for initiating wallpaper manager
+     * @param wallpaper   Information for the actual wallpaper to set
+     * @param destination The wallpaper destination i.e. home vs. lockscreen vs. both
+     * @param colors      The {@link WallpaperColors} for placeholder of quickswitching
+     * @param callback    Optional callback to be notified when the wallpaper is set.
+     */
+    public void setCurrentLiveWallpaper(Context context, LiveWallpaperInfo wallpaper,
+            @Destination final int destination, @Nullable WallpaperColors colors,
+            @Nullable SetWallpaperCallback callback) {
+        try {
+            if (destination == WallpaperPersister.DEST_LOCK_SCREEN) {
+                throw new IllegalArgumentException(
+                        "Live wallpaper cannot be applied on lock screen only");
+            }
+            WallpaperManager wallpaperManager = WallpaperManager.getInstance(context);
+            wallpaperManager.setWallpaperComponentWithFlags(
+                    wallpaper.getWallpaperComponent().getComponent(),
+                    WallpaperPersister.destinationToFlags(destination));
+            if (destination == WallpaperPersister.DEST_BOTH) {
+                wallpaperManager.clear(FLAG_LOCK);
+            }
+            mPreferences.storeLatestHomeWallpaper(wallpaper.getWallpaperId(), wallpaper,
+                    colors != null ? colors :
+                            WallpaperColors.fromBitmap(wallpaper.getThumbAsset(context)
+                                    .getLowResBitmap(context)));
+            // Not call onWallpaperApplied() as no UI is presented.
+            if (callback != null) {
+                callback.onSuccess(wallpaper);
+            }
+        } catch (RuntimeException | IOException e) {
+            // Not call onWallpaperApplyError() as no UI is presented.
+            if (callback != null) {
+                callback.onError(e);
+            }
+        }
+    }
+
+    private void onWallpaperApplied(WallpaperInfo wallpaper, Activity containerActivity) {
+        mUserEventLogger.logWallpaperSet(
+                wallpaper.getCollectionId(containerActivity),
+                wallpaper.getWallpaperId(), wallpaper.getEffectNames());
+        mPreferences.setPendingWallpaperSetStatus(
+                WallpaperPreferences.WALLPAPER_SET_NOT_PENDING);
+        mUserEventLogger.logWallpaperSetResult(
+                UserEventLogger.WALLPAPER_SET_RESULT_SUCCESS);
+        cleanUp();
+        restoreScreenOrientationIfNeeded(containerActivity);
+    }
+
+    private void onWallpaperApplyError(Throwable throwable, Activity containerActivity) {
+        mPreferences.setPendingWallpaperSetStatus(
+                WallpaperPreferences.WALLPAPER_SET_NOT_PENDING);
+        mUserEventLogger.logWallpaperSetResult(
+                UserEventLogger.WALLPAPER_SET_RESULT_FAILURE);
+        @WallpaperSetFailureReason int failureReason = ThrowableAnalyzer.isOOM(
+                throwable)
+                ? UserEventLogger.WALLPAPER_SET_FAILURE_REASON_OOM
+                : UserEventLogger.WALLPAPER_SET_FAILURE_REASON_OTHER;
+        mUserEventLogger.logWallpaperSetFailureReason(failureReason);
+
+        cleanUp();
+        restoreScreenOrientationIfNeeded(containerActivity);
+    }
+
+    /**
+     * Call this method to clean up this instance's state.
+     */
+    public void cleanUp() {
+        if (mProgressDialog != null) {
+            mProgressDialog.dismiss();
+            mProgressDialog = null;
+        }
+    }
+
+    /**
+     * Show a dialog asking the user for the Wallpaper's destination
+     * (eg, "Home screen", "Lock Screen")
+     *
+     * @param isLiveWallpaper whether the wallpaper that we want to set is a live wallpaper.
+     * @param listener        {@link Listener} that will receive the response.
+     * @see Destination
+     */
+    public void requestDestination(Activity activity, FragmentManager fragmentManager,
+            Listener listener, boolean isLiveWallpaper) {
+        requestDestination(activity, fragmentManager, R.string.set_wallpaper_dialog_message,
+                listener, isLiveWallpaper);
+    }
+
+    /**
+     * Show a dialog asking the user for the Wallpaper's destination
+     * (eg, "Home screen", "Lock Screen")
+     *
+     * @param isLiveWallpaper whether the wallpaper that we want to set is a live wallpaper.
+     * @param listener        {@link Listener} that will receive the response.
+     * @param titleResId      title for the dialog
+     * @see Destination
+     */
+    public void requestDestination(Activity activity, FragmentManager fragmentManager,
+            @StringRes int titleResId, Listener listener, boolean isLiveWallpaper) {
+        saveAndLockScreenOrientationIfNeeded(activity);
+        Listener listenerWrapper = new Listener() {
+            @Override
+            public void onSet(int destination) {
+                if (listener != null) {
+                    listener.onSet(destination);
+                }
+            }
+
+            @Override
+            public void onDialogDismissed(boolean withItemSelected) {
+                if (!withItemSelected) {
+                    restoreScreenOrientationIfNeeded(activity);
+                }
+                if (listener != null) {
+                    listener.onDialogDismissed(withItemSelected);
+                }
+            }
+        };
+
+        WallpaperStatusChecker wallpaperStatusChecker =
+                InjectorProvider.getInjector().getWallpaperStatusChecker();
+        boolean isLiveWallpaperSet =
+                WallpaperManager.getInstance(activity).getWallpaperInfo() != null;
+        // Alternative of ag/15567276
+        boolean isBuiltIn = !isLiveWallpaperSet
+                && !wallpaperStatusChecker.isHomeStaticWallpaperSet(activity);
+
+        SetWallpaperDialogFragment setWallpaperDialog = new SetWallpaperDialogFragment();
+        setWallpaperDialog.setTitleResId(titleResId);
+        setWallpaperDialog.setListener(listenerWrapper);
+        if ((isLiveWallpaperSet || isBuiltIn)
+                && !wallpaperStatusChecker.isLockWallpaperSet(activity)) {
+            if (isLiveWallpaper) {
+                // If lock wallpaper is live and we're setting a live wallpaper, we can only
+                // set it to both, so bypass the dialog.
+                listener.onSet(WallpaperPersister.DEST_BOTH);
+                restoreScreenOrientationIfNeeded(activity);
+                return;
+            }
+            // if the lock wallpaper is a live wallpaper, we cannot set a home-only static one
+            setWallpaperDialog.setHomeOptionAvailable(false);
+        }
+        if (isLiveWallpaper) {
+            setWallpaperDialog.setLockOptionAvailable(false);
+        }
+        setWallpaperDialog.show(fragmentManager, TAG_SET_WALLPAPER_DIALOG_FRAGMENT);
+    }
+
+    private void saveAndLockScreenOrientationIfNeeded(Activity activity) {
+        if (!mCurrentScreenOrientation.isPresent()) {
+            mCurrentScreenOrientation = Optional.of(activity.getRequestedOrientation());
+            activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
+        }
+    }
+
+    private void restoreScreenOrientationIfNeeded(Activity activity) {
+        mCurrentScreenOrientation.ifPresent(orientation -> {
+            if (activity.getRequestedOrientation() != orientation) {
+                activity.setRequestedOrientation(orientation);
+            }
+            mCurrentScreenOrientation = Optional.empty();
+        });
+    }
+}
diff --git a/src/com/android/wallpaper/picker/TouchForwardingLayout2.java b/src/com/android/wallpaper/picker/TouchForwardingLayout2.java
new file mode 100644
index 0000000..d83700e
--- /dev/null
+++ b/src/com/android/wallpaper/picker/TouchForwardingLayout2.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wallpaper.picker;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.FrameLayout;
+
+/**
+ * A frame layout that listens to touch events and routes them to another view.
+ * TODO b/256819974 This is a duplicate of TouchForwardingLayout and should be removed after
+ * TouchForwardingLayout in the branch of tm-qpr-dev is synced with master.
+ */
+public class TouchForwardingLayout2 extends FrameLayout {
+
+    private View mView;
+    private boolean mForwardingEnabled;
+
+    public TouchForwardingLayout2(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    public boolean dispatchTouchEvent(MotionEvent ev) {
+        if (mView != null && mForwardingEnabled) {
+            mView.dispatchTouchEvent(ev);
+        }
+        return true;
+    }
+
+    /** Set the view that the touch events are routed to */
+    public void setTargetView(View view) {
+        mView = view;
+    }
+
+    public void setForwardingEnabled(boolean forwardingEnabled) {
+        mForwardingEnabled = forwardingEnabled;
+    }
+}
diff --git a/src/com/android/wallpaper/util/SystemColors.kt b/src/com/android/wallpaper/util/SystemColors.kt
new file mode 100644
index 0000000..ec1738c
--- /dev/null
+++ b/src/com/android/wallpaper/util/SystemColors.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wallpaper.util
+
+import android.annotation.AttrRes
+import android.annotation.ColorInt
+import android.content.Context
+import android.util.TypedValue
+import androidx.core.content.ContextCompat
+
+object SystemColors {
+
+    /**
+     * Returns the color by fetching the resId from the theme. Throws an exception when resource Id
+     * is not available in the theme.
+     */
+    @JvmStatic
+    @ColorInt
+    fun getColor(context: Context, @AttrRes resId: Int): Int {
+        val colorValue = TypedValue()
+        val theme = context.theme
+        if (theme.resolveAttribute(resId, colorValue, /* resolveRefs= */ true)) {
+            if (
+                TypedValue.TYPE_FIRST_COLOR_INT <= colorValue.type &&
+                    colorValue.type <= TypedValue.TYPE_LAST_COLOR_INT
+            ) {
+                return colorValue.data
+            }
+            if (colorValue.type == TypedValue.TYPE_STRING) {
+                return ContextCompat.getColor(context, colorValue.resourceId)
+            }
+        }
+        throw IllegalArgumentException(
+            "Theme is missing expected color ${context.resources.getResourceName(resId)} " +
+                "($resId) references a missing resource."
+        )
+    }
+}
diff --git a/src/com/android/wallpaper/widget/DuoTabs.java b/src/com/android/wallpaper/widget/DuoTabs.java
new file mode 100644
index 0000000..8b78508
--- /dev/null
+++ b/src/com/android/wallpaper/widget/DuoTabs.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wallpaper.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.widget.Button;
+import android.widget.FrameLayout;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.wallpaper.R;
+import com.android.wallpaper.util.SystemColors;
+
+/**
+ * Custom layout for duo tabs.
+ */
+public final class DuoTabs extends FrameLayout {
+
+    public static final int TAB_PRIMARY = 0;
+    public static final int TAB_SECONDARY = 1;
+
+    /**
+     * Overlay tab
+     */
+    @IntDef({TAB_PRIMARY, TAB_SECONDARY})
+    public @interface Tab {
+    }
+
+    /**
+     * Overlay tab selected listener
+     */
+    public interface OnTabSelectedListener {
+
+        /**
+         * On tab selected
+         */
+        void onTabSelected(@Tab int tab);
+    }
+
+    OnTabSelectedListener mOnTabSelectedListener;
+    Button mPrimaryTab;
+    Button mSecondaryTab;
+    @Tab int mCurrentOverlayTab;
+
+    /**
+     * Constructor
+     */
+    public DuoTabs(@NonNull Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+        LayoutInflater.from(context).inflate(R.layout.duo_tabs, this, true);
+        mPrimaryTab = findViewById(R.id.tab_primary);
+        mSecondaryTab = findViewById(R.id.tab_secondary);
+        mPrimaryTab.setOnClickListener(v -> selectTab(TAB_PRIMARY));
+        mSecondaryTab.setOnClickListener(v -> selectTab(TAB_SECONDARY));
+    }
+
+    /**
+     * Set tab text
+     */
+    public void setTabText(String primaryTabText, String secondaryTabText) {
+        mPrimaryTab.setText(primaryTabText);
+        mSecondaryTab.setText(secondaryTabText);
+    }
+
+    /**
+     * Select a tab
+     */
+    public void selectTab(@Tab int tab) {
+        updateTabIndicator(tab);
+        if (mOnTabSelectedListener != null) {
+            mOnTabSelectedListener.onTabSelected(tab);
+        }
+        mCurrentOverlayTab = tab;
+    }
+
+    /**
+     * Set listener
+     */
+    public void setOnTabSelectedListener(
+            OnTabSelectedListener onTabSelectedListener) {
+        mOnTabSelectedListener = onTabSelectedListener;
+    }
+
+    /**
+     * Update the background color in case the context theme has changed.
+     */
+    public void updateBackgroundColor() {
+        mPrimaryTab.setBackground(null);
+        mSecondaryTab.setBackground(null);
+        updateTabIndicator(mCurrentOverlayTab);
+    }
+
+    private void updateTabIndicator(@Tab int tab) {
+        Context c = getContext();
+        mPrimaryTab.setBackgroundResource(
+                tab == TAB_PRIMARY
+                        ? R.drawable.duo_tabs_button_indicator_background
+                        : R.drawable.duo_tabs_button_background);
+        mPrimaryTab.setTextColor(
+                tab == TAB_PRIMARY
+                        ? SystemColors.getColor(c, com.android.internal.R.attr.textColorOnAccent)
+                        : SystemColors.getColor(c, android.R.attr.textColorPrimary));
+        mSecondaryTab.setBackgroundResource(
+                tab == TAB_SECONDARY
+                        ? R.drawable.duo_tabs_button_indicator_background
+                        : R.drawable.duo_tabs_button_background);
+        mSecondaryTab.setTextColor(
+                tab == TAB_SECONDARY
+                        ? SystemColors.getColor(c, com.android.internal.R.attr.textColorOnAccent)
+                        : SystemColors.getColor(c, android.R.attr.textColorPrimary));
+    }
+}
diff --git a/src/com/android/wallpaper/widget/WallpaperControlButtonGroup.java b/src/com/android/wallpaper/widget/WallpaperControlButtonGroup.java
new file mode 100644
index 0000000..462dbb4
--- /dev/null
+++ b/src/com/android/wallpaper/widget/WallpaperControlButtonGroup.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wallpaper.widget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.widget.CompoundButton;
+import android.widget.FrameLayout;
+import android.widget.ToggleButton;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.content.res.AppCompatResources;
+
+import com.android.wallpaper.R;
+
+/**
+ * Custom layout for tabs to select in between home screen overlay or lock screen overlay.
+ */
+public final class WallpaperControlButtonGroup extends FrameLayout {
+
+    public static final int INFORMATION = 0;
+    public static final int EFFECTS = 1;
+
+    /**
+     * Overlay tab
+     */
+    @IntDef({INFORMATION, EFFECTS})
+    public @interface WallpaperControlType {
+    }
+
+    ToggleButton mInformationButton;
+    ToggleButton mEffectsButton;
+
+    /**
+     * Constructor
+     */
+    public WallpaperControlButtonGroup(@NonNull Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+        LayoutInflater.from(context).inflate(R.layout.wallpaper_control_button_group, this, true);
+        mInformationButton = findViewById(R.id.information_button);
+        mEffectsButton = findViewById(R.id.effects_button);
+    }
+
+    /**
+     * Show a button by giving a correspondent listener
+     */
+    public void showButton(@WallpaperControlType int type,
+            CompoundButton.OnCheckedChangeListener listener) {
+        switch (type) {
+            case INFORMATION:
+                mInformationButton.setVisibility(VISIBLE);
+                mInformationButton.setOnCheckedChangeListener(listener);
+                break;
+            case EFFECTS:
+                mEffectsButton.setVisibility(VISIBLE);
+                mEffectsButton.setOnCheckedChangeListener(listener);
+                break;
+            default:
+                break;
+        }
+    }
+
+    /**
+     * Set checked for a button
+     */
+    public void setChecked(@WallpaperControlType int type, boolean checked) {
+        switch (type) {
+            case INFORMATION:
+                mInformationButton.setChecked(checked);
+                break;
+            case EFFECTS:
+                mEffectsButton.setChecked(checked);
+                break;
+            default:
+                break;
+        }
+    }
+
+    /**
+     * Update the background color in case the context theme has changed.
+     */
+    public void updateBackgroundColor() {
+        Context context = getContext();
+        if (context == null) {
+            return;
+        }
+        mInformationButton.setForeground(null);
+        mEffectsButton.setForeground(null);
+        mInformationButton.setForeground(
+                AppCompatResources.getDrawable(context, R.drawable.wallpaper_control_button_info));
+        mEffectsButton.setForeground(AppCompatResources.getDrawable(context,
+                R.drawable.wallpaper_control_button_effect));
+    }
+}