Merge "[WorkProfile] Update work/personal tab to pills" into sc-dev
diff --git a/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java b/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java
index 79e50ef..67e9d89 100644
--- a/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java
+++ b/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java
@@ -49,6 +49,7 @@
     public static final String ACTION_SEARCH = "com.android.quickstep.ACTION_SEARCH";
     public static final String ELAPSED_NANOS = "niu_actions_elapsed_realtime_nanos";
     public static final String ACTIONS_URL = "niu_actions_app_url";
+    public static final String ACTIONS_APP_PACKAGE = "niu_actions_app_package";
     public static final String ACTIONS_ERROR_CODE = "niu_actions_app_error_code";
     public static final int ERROR_PERMISSIONS = 1;
     private static final String TAG = "TaskOverlayFactoryGo";
@@ -72,6 +73,7 @@
      */
     public static final class TaskOverlayGo<T extends OverviewActionsView> extends TaskOverlay {
         private String mNIUPackageName;
+        private String mTaskPackageName;
         private String mWebUrl;
         private boolean mAssistPermissionsEnabled;
 
@@ -87,7 +89,7 @@
                 boolean rotated) {
             getActionsView().updateDisabledFlags(DISABLED_NO_THUMBNAIL, thumbnail == null);
             mNIUPackageName =
-                    mApplicationContext.getResources().getString(R.string.niu_actions_package);
+                    mApplicationContext.getString(R.string.niu_actions_package);
 
             if (thumbnail == null || TextUtils.isEmpty(mNIUPackageName)) {
                 return;
@@ -96,6 +98,7 @@
             getActionsView().updateDisabledFlags(DISABLED_ROTATED, rotated);
             boolean isAllowedByPolicy = mThumbnailView.isRealSnapshot();
             getActionsView().setCallbacks(new OverlayUICallbacksGoImpl(isAllowedByPolicy, task));
+            mTaskPackageName = task.key.getPackageName();
 
             checkPermissions();
             if (!mAssistPermissionsEnabled) {
@@ -150,6 +153,7 @@
                     .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK)
                     .addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION)
                     .setPackage(mNIUPackageName)
+                    .putExtra(ACTIONS_APP_PACKAGE, mTaskPackageName)
                     .putExtra(ELAPSED_NANOS, SystemClock.elapsedRealtimeNanos());
 
             if (mWebUrl != null) {
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index 842abc3..7ab09c5 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -103,16 +103,40 @@
              android:clearTaskOnLaunch="true"
              android:exported="false"/>
 
+        <!--
+        Activity for gesture nav onboarding.
+        It's protected by android.permission.REBOOT to ensure that only system apps can start it
+        (setup wizard already has this permission)
+        -->
         <activity android:name="com.android.quickstep.interaction.GestureSandboxActivity"
-             android:autoRemoveFromRecents="true"
-             android:excludeFromRecents="true"
-             android:screenOrientation="portrait"
-             android:exported="true">
+            android:autoRemoveFromRecents="true"
+            android:excludeFromRecents="true"
+            android:screenOrientation="portrait"
+            android:permission="android.permission.REBOOT"
+            android:exported="true">
             <intent-filter>
                 <action android:name="com.android.quickstep.action.GESTURE_SANDBOX"/>
                 <category android:name="android.intent.category.DEFAULT"/>
             </intent-filter>
         </activity>
+
+        <!--
+        Activity following gesture nav onboarding.
+        It's protected by android.permission.REBOOT to ensure that only system apps can start it
+        (setup wizard already has this permission)
+        -->
+        <activity android:name="com.android.quickstep.interaction.AllSetActivity"
+            android:autoRemoveFromRecents="true"
+            android:excludeFromRecents="true"
+            android:screenOrientation="portrait"
+            android:permission="android.permission.REBOOT"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="com.android.quickstep.action.GESTURE_ONBOARDING_ALL_SET"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
+        </activity>
+
         <activity
             android:name=".hybridhotseat.HotseatEduActivity"
             android:theme="@android:style/Theme.NoDisplay"
diff --git a/quickstep/res/drawable/ic_all_set.xml b/quickstep/res/drawable/ic_all_set.xml
new file mode 100644
index 0000000..a6852aa
--- /dev/null
+++ b/quickstep/res/drawable/ic_all_set.xml
@@ -0,0 +1,24 @@
+<!--
+     Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="42dp"
+    android:height="40dp"
+    android:viewportWidth="42"
+    android:viewportHeight="40">
+    <path
+        android:pathData="M38,14H25.38L27.28,4.86L27.34,4.22C27.34,3.4 27,2.64 26.46,2.1L24.34,0C24.34,0 10.16,13.7 10,14H0V40H32C33.66,40 35.08,39 35.68,37.56L41.72,23.46C41.9,23 42,22.52 42,22V18C42,15.8 40.2,14 38,14ZM10,36H4V18H10V36ZM38,22L32,36H14V16L22.68,7.32L20,18H38V22Z"
+        android:fillColor="#FFFFFF"/>
+</vector>
diff --git a/quickstep/res/drawable/ic_ime_switcher.xml b/quickstep/res/drawable/ic_ime_switcher.xml
new file mode 100644
index 0000000..a86d390
--- /dev/null
+++ b/quickstep/res/drawable/ic_ime_switcher.xml
@@ -0,0 +1,25 @@
+<!--
+  ~ Copyright (C) 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="20dp"
+    android:height="20dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+    <path
+        android:pathData="M19,7h2v2h-2V7zM15,7h2v2h-2V7zM3,7h2v2H3V7zM7,7h2v2H7V7zM11,7h2v2h-2V7zM19,11h2v2h-2V11zM15,11h2v2h-2V11zM3,11h2v2H3V11zM7,11h2v2H7V11zM11,11h2v2h-2V11zM7,15h10v2H7V15z"
+        android:fillColor="@android:color/white" />
+</vector>
diff --git a/quickstep/res/drawable/ic_sysbar_back.xml b/quickstep/res/drawable/ic_sysbar_back.xml
new file mode 100644
index 0000000..1eea677
--- /dev/null
+++ b/quickstep/res/drawable/ic_sysbar_back.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="28dp"
+    android:height="28dp"
+    android:autoMirrored="true"
+    android:viewportWidth="28"
+    android:viewportHeight="28">
+
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M6.49,14.86c-0.66-0.39-0.66-1.34,0-1.73l6.02-3.53l5.89-3.46C19.11,5.73,20,6.26,20,7.1V14v6.9 c0,0.84-0.89,1.37-1.6,0.95l-5.89-3.46L6.49,14.86z" />
+</vector>
\ No newline at end of file
diff --git a/quickstep/res/drawable/ic_sysbar_home.xml b/quickstep/res/drawable/ic_sysbar_home.xml
new file mode 100644
index 0000000..b4b397b
--- /dev/null
+++ b/quickstep/res/drawable/ic_sysbar_home.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="28dp"
+    android:height="28dp"
+    android:viewportWidth="28"
+    android:viewportHeight="28">
+
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M 14 7 C 17.8659932488 7 21 10.1340067512 21 14 C 21 17.8659932488 17.8659932488 21 14 21 C 10.1340067512 21 7 17.8659932488 7 14 C 7 10.1340067512 10.1340067512 7 14 7 Z" />
+</vector>
\ No newline at end of file
diff --git a/quickstep/res/drawable/ic_sysbar_recent.xml b/quickstep/res/drawable/ic_sysbar_recent.xml
new file mode 100644
index 0000000..f8c4778
--- /dev/null
+++ b/quickstep/res/drawable/ic_sysbar_recent.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="28dp"
+    android:height="28dp"
+    android:viewportWidth="28"
+    android:viewportHeight="28">
+
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M19.9,21.5H8.1c-0.88,0-1.6-0.72-1.6-1.6V8.1c0-0.88,0.72-1.6,1.6-1.6h11.8c0.88,0,1.6,0.72,1.6,1.6v11.8 C21.5,20.78,20.78,21.5,19.9,21.5z" />
+</vector>
\ No newline at end of file
diff --git a/quickstep/res/drawable/taskbar_icon_click_feedback_roundrect.xml b/quickstep/res/drawable/taskbar_icon_click_feedback_roundrect.xml
new file mode 100644
index 0000000..d6160de
--- /dev/null
+++ b/quickstep/res/drawable/taskbar_icon_click_feedback_roundrect.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright 2021 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="@color/taskbar_icon_selection_ripple">
+    <item android:id="@android:id/mask">
+        <shape android:shape="rectangle">
+            <solid android:color="@android:color/white" />
+            <corners android:radius="8dp" />
+        </shape>
+    </item>
+</ripple>
\ No newline at end of file
diff --git a/quickstep/res/layout/activity_allset.xml b/quickstep/res/layout/activity_allset.xml
new file mode 100644
index 0000000..a6a17e5
--- /dev/null
+++ b/quickstep/res/layout/activity_allset.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:paddingStart="@dimen/allset_page_margin_horizontal"
+    android:paddingEnd="@dimen/allset_page_margin_horizontal"
+    android:layoutDirection="locale"
+    android:textDirection="locale"
+    android:background="?android:attr/colorBackground">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_alignParentTop="true"
+        android:layout_gravity="start"
+        android:gravity="start"
+        android:orientation="vertical">
+
+        <ImageView
+            android:id="@+id/icon"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/allset_title_icon_margin_top"
+            android:src="@drawable/ic_all_set"/>
+
+        <TextView
+            android:id="@+id/title"
+            style="@style/TextAppearance.GestureTutorial.Feedback.Title"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/allset_title_margin_top"
+            android:gravity="start"
+            android:text="@string/allset_title"/>
+
+        <TextView
+            android:id="@+id/subtitle"
+            style="@style/TextAppearance.GestureTutorial.Feedback.Subtitle"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/allset_subtitle_margin_top"
+            android:gravity="start"
+            android:text="@string/allset_description"/>
+    </LinearLayout>
+
+    <TextView
+        android:id="@+id/navigation_settings"
+        style="@style/TextAppearance.GestureTutorial.Feedback.Subtitle"
+        android:textSize="14sp"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_above="@+id/hint"
+        android:gravity="center_horizontal"
+        android:layout_marginBottom="72dp"/>
+
+    <TextView
+        android:id="@id/hint"
+        style="@style/TextAppearance.GestureTutorial.Feedback.Subtitle"
+        android:textSize="14sp"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="@dimen/allset_hint_margin_bottom"
+        android:layout_alignParentBottom="true"
+        android:layout_centerHorizontal="true"
+        android:text="@string/allset_hint"/>
+</RelativeLayout>
diff --git a/quickstep/res/layout/overview_panel.xml b/quickstep/res/layout/overview_panel.xml
index d7bcd9e..f303f31 100644
--- a/quickstep/res/layout/overview_panel.xml
+++ b/quickstep/res/layout/overview_panel.xml
@@ -15,12 +15,6 @@
 -->
 <merge xmlns:android="http://schemas.android.com/apk/res/android">
 
-    <com.android.quickstep.views.SplitPlaceholderView
-        android:id="@+id/split_placeholder"
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/split_placeholder_size"
-        android:background="@android:color/darker_gray"
-        android:visibility="gone" />
 
     <com.android.quickstep.views.LauncherRecentsView
         android:id="@+id/overview_panel"
@@ -31,6 +25,13 @@
         android:clipToPadding="false"
         android:visibility="invisible" />
 
+    <com.android.quickstep.views.SplitPlaceholderView
+        android:id="@+id/split_placeholder"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/split_placeholder_size"
+        android:background="@android:color/darker_gray"
+        android:visibility="gone" />
+
     <include
         android:id="@+id/overview_actions_view"
         layout="@layout/overview_actions_container" />
diff --git a/quickstep/res/layout/taskbar.xml b/quickstep/res/layout/taskbar.xml
index 732222a..240fe55 100644
--- a/quickstep/res/layout/taskbar.xml
+++ b/quickstep/res/layout/taskbar.xml
@@ -26,4 +26,10 @@
         android:layout_height="wrap_content"
         android:gravity="center"/>
 
+    <com.android.launcher3.taskbar.ImeBarView
+        android:id="@+id/ime_bar_view"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:visibility="gone"/>
+
 </com.android.launcher3.taskbar.TaskbarContainerView>
\ No newline at end of file
diff --git a/quickstep/res/values/attrs.xml b/quickstep/res/values/attrs.xml
new file mode 100644
index 0000000..336fb57
--- /dev/null
+++ b/quickstep/res/values/attrs.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2021 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources>
+    <declare-styleable name="AllSetLinkSpan">
+        <attr name="android:textSize"/>
+        <attr name="android:fontFamily"/>
+    </declare-styleable>
+</resources>
diff --git a/quickstep/res/values/colors.xml b/quickstep/res/values/colors.xml
index 3bc8ddc..167c7c3 100644
--- a/quickstep/res/values/colors.xml
+++ b/quickstep/res/values/colors.xml
@@ -27,4 +27,5 @@
 
     <!-- Taskbar -->
     <color name="taskbar_background">#101010</color>
+    <color name="taskbar_icon_selection_ripple">#E0E0E0</color>
 </resources>
\ No newline at end of file
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 9c0a083..4f9b3eb 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -99,6 +99,13 @@
     <dimen name="gesture_tutorial_feedback_margin_start_end">24dp</dimen>
     <dimen name="gesture_tutorial_button_margin_start_end">18dp</dimen>
 
+    <!-- All Set page -->
+    <dimen name="allset_page_margin_horizontal">40dp</dimen>
+    <dimen name="allset_title_margin_top">28dp</dimen>
+    <dimen name="allset_title_icon_margin_top">80dp</dimen>
+    <dimen name="allset_hint_margin_bottom">52dp</dimen>
+    <dimen name="allset_subtitle_margin_top">24dp</dimen>
+
     <!-- All Apps Education tutorial -->
     <dimen name="swipe_edu_padding">8dp</dimen>
     <dimen name="swipe_edu_circle_size">64dp</dimen>
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index a0f1638..7ada496 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -177,6 +177,15 @@
     <!-- Feedback subtext displaying the current step and the total number of steps for the tutorial. [CHAR LIMIT=30] -->
     <string name="gesture_tutorial_step">Tutorial <xliff:g id="current">%1$d</xliff:g>/<xliff:g id="total">%2$d</xliff:g></string>
 
+    <!-- Title of "All Set" page [CHAR LIMIT=NONE] -->
+    <string name="allset_title">All set!</string>
+    <!-- Hint string at the bottom of "All Set" page [CHAR LIMIT=NONE] -->
+    <string name="allset_hint">Swipe up to go home</string>
+    <!-- Description of "All Set" page [CHAR LIMIT=NONE] -->
+    <string name="allset_description">You\u2019re ready to start using your phone</string>
+    <!-- String linking to navigation settings on "All Set" page [CHAR LIMIT=NONE] -->
+    <string name="allset_navigation_settings"><annotation id="link">Navigation settings for accessibility</annotation></string>
+
     <!-- ******* Overview ******* -->
     <!-- Label for a button that causes the current overview app to be shared. [CHAR_LIMIT=40] -->
     <string name="action_share">Share</string>
diff --git a/quickstep/res/values/styles.xml b/quickstep/res/values/styles.xml
index 7c7d20a..0a8ecb8 100644
--- a/quickstep/res/values/styles.xml
+++ b/quickstep/res/values/styles.xml
@@ -59,7 +59,7 @@
         parent="TextAppearance.GestureTutorial">
         <item name="android:gravity">start</item>
         <item name="android:textColor">?android:attr/textColorPrimary</item>
-        <item name="android:fontFamily">google-sans</item>
+        <item name="android:fontFamily">google-sans-text</item>
         <item name="android:letterSpacing">0.03</item>
         <item name="android:textSize">18sp</item>
         <item name="android:lineHeight">24sp</item>
@@ -99,6 +99,11 @@
         <item name="android:textColor">@color/gesture_tutorial_primary_color</item>
     </style>
 
+    <style name="TextAppearance.GestureTutorial.LinkText"
+        parent="TextAppearance.GestureTutorial.Feedback.Subtitle">
+        <item name="android:textSize">14sp</item>
+    </style>
+
     <!--
       Can be applied to views to color things like ripples and list highlights the workspace text
       color.
diff --git a/quickstep/robolectric_tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java b/quickstep/robolectric_tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
index 70a143e..7c97b93 100644
--- a/quickstep/robolectric_tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
+++ b/quickstep/robolectric_tests/src/com/android/launcher3/model/WidgetsPredicationUpdateTaskTest.java
@@ -36,6 +36,7 @@
 
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.icons.ComponentWithLabel;
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.model.BgDataModel.FixedContainerItems;
@@ -44,6 +45,7 @@
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.shadows.ShadowDeviceFlag;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.ItemInfoMatcher;
@@ -60,6 +62,7 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
+import org.robolectric.shadow.api.Shadow;
 import org.robolectric.shadows.ShadowAppWidgetManager;
 import org.robolectric.shadows.ShadowPackageManager;
 import org.robolectric.util.ReflectionHelpers;
@@ -174,6 +177,41 @@
         assertWidgetInfo(recommendedWidgets.get(2).info, mApp1Provider1);
     }
 
+    @Test
+    public void widgetsRecommendationRan_localFilterDisabled_shouldReturnWidgetsInPredicationOrder()
+            throws Exception {
+        ShadowDeviceFlag shadowDeviceFlag = Shadow.extract(
+                FeatureFlags.ENABLE_LOCAL_RECOMMENDED_WIDGETS_FILTER);
+        shadowDeviceFlag.setValue(false);
+
+        // WHEN newPredicationTask is executed with 5 predicated widgets.
+        AppTarget widget1 = new AppTarget(new AppTargetId("app1"), "app1", "provider1",
+                mUserHandle);
+        AppTarget widget2 = new AppTarget(new AppTargetId("app1"), "app1", "provider2",
+                mUserHandle);
+        // Not installed app
+        AppTarget widget3 = new AppTarget(new AppTargetId("app2"), "app3", "provider1",
+                mUserHandle);
+        // Not installed widget
+        AppTarget widget4 = new AppTarget(new AppTargetId("app4"), "app4", "provider3",
+                mUserHandle);
+        AppTarget widget5 = new AppTarget(new AppTargetId("app5"), "app5", "provider1",
+                mUserHandle);
+        mModelHelper.executeTaskForTest(
+                newWidgetsPredicationTask(List.of(widget5, widget3, widget2, widget4, widget1)))
+                .forEach(Runnable::run);
+
+        // THEN only 3 widgets are returned because the launcher only filters out non-exist widgets.
+        List<PendingAddWidgetInfo> recommendedWidgets = mCallback.mRecommendedWidgets.items
+                .stream()
+                .map(itemInfo -> (PendingAddWidgetInfo) itemInfo)
+                .collect(Collectors.toList());
+        assertThat(recommendedWidgets).hasSize(3);
+        assertWidgetInfo(recommendedWidgets.get(0).info, mApp5Provider1);
+        assertWidgetInfo(recommendedWidgets.get(1).info, mApp1Provider2);
+        assertWidgetInfo(recommendedWidgets.get(2).info, mApp1Provider1);
+    }
+
     private void assertWidgetInfo(
             LauncherAppWidgetProviderInfo actual, AppWidgetProviderInfo expected) {
         assertThat(actual.provider).isEqualTo(expected.provider);
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index 910e473..2aac877 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -19,7 +19,8 @@
 import static com.android.launcher3.AbstractFloatingView.TYPE_HIDE_BACK_BUTTON;
 import static com.android.launcher3.LauncherState.FLAG_HIDE_BACK_BUTTON;
 import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.util.DisplayController.CHANGE_SIZE;
+import static com.android.launcher3.LauncherState.NO_OFFSET;
+import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
@@ -30,8 +31,11 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentSender;
+import android.content.ComponentName;
+import android.content.ServiceConnection;
 import android.os.Bundle;
 import android.os.CancellationSignal;
+import android.os.IBinder;
 import android.view.View;
 
 import androidx.annotation.Nullable;
@@ -61,6 +65,7 @@
 import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
 import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.TaskUtils;
+import com.android.quickstep.TouchInteractionService;
 import com.android.quickstep.util.RemoteAnimationProvider;
 import com.android.quickstep.util.RemoteFadeOutAnimationListener;
 import com.android.quickstep.util.SplitSelectStateController;
@@ -82,6 +87,8 @@
 
     private DepthController mDepthController = new DepthController(this);
     private QuickstepTransitionManager mAppTransitionManager;
+    private ServiceConnection mTisBinderConnection;
+    protected TouchInteractionService.TISBinder mTisBinder;
 
     /**
      * Reusable command for applying the back button alpha on the background thread.
@@ -103,6 +110,24 @@
         super.onCreate(savedInstanceState);
         SysUINavigationMode.INSTANCE.get(this).addModeChangeListener(this);
         addMultiWindowModeChangedListener(mDepthController);
+        setupTouchInteractionServiceBinder();
+    }
+
+    private void setupTouchInteractionServiceBinder() {
+        Intent intent = new Intent(this, TouchInteractionService.class);
+        mTisBinderConnection = new ServiceConnection() {
+            @Override
+            public void onServiceConnected(ComponentName componentName, IBinder binder) {
+                mTisBinder = ((TouchInteractionService.TISBinder) binder);
+                mTisBinder.setTaskbarOverviewProxyDelegate(mTaskbarController);
+            }
+
+            @Override
+            public void onServiceDisconnected(ComponentName componentName) {
+                mTisBinder = null;
+            }
+        };
+        bindService(intent, mTisBinderConnection, 0);
     }
 
     @Override
@@ -113,6 +138,10 @@
         if (mTaskbarController != null) {
             mTaskbarController.cleanup();
             mTaskbarController = null;
+            if (mTisBinder != null) {
+                mTisBinder.setTaskbarOverviewProxyDelegate(null);
+                unbindService(mTisBinderConnection);
+            }
         }
 
         super.onDestroy();
@@ -240,7 +269,7 @@
     public void onDisplayInfoChanged(Context context, DisplayController.Info info,
             int flags) {
         super.onDisplayInfoChanged(context, info, flags);
-        if ((flags & CHANGE_SIZE) != 0) {
+        if ((flags & CHANGE_ACTIVE_SCREEN) != 0) {
             addTaskbarIfNecessary();
         }
     }
@@ -248,6 +277,9 @@
     private void addTaskbarIfNecessary() {
         if (mTaskbarController != null) {
             mTaskbarController.cleanup();
+            if (mTisBinder != null) {
+                mTisBinder.setTaskbarOverviewProxyDelegate(null);
+            }
             mTaskbarController = null;
         }
         if (mDeviceProfile.isTaskbarPresent) {
@@ -256,6 +288,9 @@
             mTaskbarController = new TaskbarController(this,
                     taskbarActivityContext.getTaskbarContainerView(), taskbarViewOnHome);
             mTaskbarController.init();
+            if (mTisBinder != null) {
+                mTisBinder.setTaskbarOverviewProxyDelegate(mTaskbarController);
+            }
         }
     }
 
@@ -343,7 +378,7 @@
     @Override
     public float[] getNormalOverviewScaleAndOffset() {
         return SysUINavigationMode.getMode(this).hasGestures
-                ? new float[] {1, 1} : new float[] {1.1f, 0};
+                ? new float[] {1, NO_OFFSET, 1} : new float[] {1.1f, NO_OFFSET, NO_OFFSET};
     }
 
     @Override
@@ -460,7 +495,8 @@
             case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
             case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
             case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
-                // Fall through and continue if it's an app or shortcut
+            case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
+                // Fall through and continue if it's an app, shortcut, or widget
                 break;
             default:
                 return;
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index fb67645..36764a1 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -60,6 +60,7 @@
 import android.os.Looper;
 import android.os.SystemProperties;
 import android.util.Pair;
+import android.util.Size;
 import android.view.View;
 import android.view.animation.Interpolator;
 import android.view.animation.PathInterpolator;
@@ -780,7 +781,9 @@
         final float finalWindowRadius = mDeviceProfile.isMultiWindowMode
                 ? 0 : getWindowCornerRadius(mLauncher.getResources());
         final FloatingWidgetView floatingView = FloatingWidgetView.getFloatingWidgetView(mLauncher,
-                v, widgetBackgroundBounds, windowTargetBounds, finalWindowRadius);
+                v, widgetBackgroundBounds,
+                new Size(windowTargetBounds.width(), windowTargetBounds.height()),
+                finalWindowRadius);
         final float initialWindowRadius = supportsRoundedCornersOnWindows(mLauncher.getResources())
                 ? floatingView.getInitialCornerRadius() : 0;
 
diff --git a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
index cc3ccea..6d5975f 100644
--- a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -33,7 +33,6 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
 import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
 import com.android.launcher3.allapps.FloatingHeaderRow;
 import com.android.launcher3.allapps.FloatingHeaderView;
@@ -80,9 +79,9 @@
         setOrientation(LinearLayout.HORIZONTAL);
 
         mFocusHelper = new SimpleFocusIndicatorHelper(this);
-        mNumPredictedAppsPerRow = LauncherAppState.getIDP(context).numAllAppsColumns;
         mLauncher = Launcher.getLauncher(context);
         mLauncher.addOnDeviceProfileChangeListener(this);
+        mNumPredictedAppsPerRow = mLauncher.getDeviceProfile().numShownAllAppsColumns;
         updateVisibility();
     }
 
@@ -174,7 +173,7 @@
 
     @Override
     public void onDeviceProfileChanged(DeviceProfile dp) {
-        mNumPredictedAppsPerRow = dp.inv.numAllAppsColumns;
+        mNumPredictedAppsPerRow = dp.numShownAllAppsColumns;
         removeAllViews();
         applyPredictionApps();
     }
diff --git a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
index 8c68872..a9c2a5e 100644
--- a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
+++ b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
@@ -18,7 +18,6 @@
 import static android.text.format.DateUtils.DAY_IN_MILLIS;
 import static android.text.format.DateUtils.formatElapsedTime;
 
-import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_GRID;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_PREDICTION;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION;
@@ -104,8 +103,8 @@
         // TODO: Implement caching and preloading
         super.loadItems(ums, pinnedShortcuts);
 
-        WorkspaceItemFactory allAppsFactory =
-                new WorkspaceItemFactory(mApp, ums, pinnedShortcuts, mIDP.numAllAppsColumns);
+        WorkspaceItemFactory allAppsFactory = new WorkspaceItemFactory(
+                mApp, ums, pinnedShortcuts, mIDP.numDatabaseAllAppsColumns);
         mAllAppsState.items.setItems(
                 mAllAppsState.storage.read(mApp.getContext(), allAppsFactory, ums.allUsers::get));
         mDataModel.extraItems.put(CONTAINER_PREDICTION, mAllAppsState.items);
@@ -204,7 +203,7 @@
         registerPredictor(mAllAppsState, apm.createAppPredictionSession(
                 new AppPredictionContext.Builder(context)
                         .setUiSurface("home")
-                        .setPredictedTargetCount(mIDP.numAllAppsColumns)
+                        .setPredictedTargetCount(mIDP.numDatabaseAllAppsColumns)
                         .build()));
 
         // TODO: get bundle
@@ -252,11 +251,9 @@
     }
 
     @Override
-    public void onIdpChanged(int changeFlags, InvariantDeviceProfile profile) {
-        if ((changeFlags & CHANGE_FLAG_GRID) != 0) {
-            // Reinitialize everything
-            Executors.MODEL_EXECUTOR.execute(this::recreatePredictors);
-        }
+    public void onIdpChanged(InvariantDeviceProfile profile) {
+        // Reinitialize everything
+        Executors.MODEL_EXECUTOR.execute(this::recreatePredictors);
     }
 
     private void onAppTargetEvent(AppTargetEvent event, int client) {
diff --git a/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
index a29ac1a..22a8c9b 100644
--- a/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
+++ b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
@@ -16,16 +16,17 @@
 package com.android.launcher3.model;
 
 import android.app.prediction.AppTarget;
+import android.content.ComponentName;
+import android.text.TextUtils;
 
 import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.model.BgDataModel.FixedContainerItems;
 import com.android.launcher3.model.QuickstepModelDelegate.PredictorState;
-import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.widget.PendingAddWidgetInfo;
 
-import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -56,25 +57,43 @@
         Map<PackageUserKey, List<WidgetItem>> allWidgets =
                 dataModel.widgetsModel.getAllWidgetsWithoutShortcuts();
 
-        ArrayList<ItemInfo> recommendedWidgetsInDescendingOrder = new ArrayList<>();
-        for (AppTarget app : mTargets) {
-            PackageUserKey packageUserKey = new PackageUserKey(app.getPackageName(), app.getUser());
-            if (allWidgets.containsKey(packageUserKey)) {
-                List<WidgetItem> notAddedWidgets = allWidgets.get(packageUserKey).stream()
-                        .filter(item ->
-                                !widgetsInWorkspace.contains(
-                                        new ComponentKey(item.componentName, item.user)))
-                        .collect(Collectors.toList());
-                if (notAddedWidgets.size() > 0) {
-                    // Even an apps have more than one widgets, we only include one widget.
-                    recommendedWidgetsInDescendingOrder.add(
-                            new PendingAddWidgetInfo(notAddedWidgets.get(0).widgetInfo));
+        FixedContainerItems fixedContainerItems = mPredictorState.items;
+        fixedContainerItems.items.clear();
+
+        if (FeatureFlags.ENABLE_LOCAL_RECOMMENDED_WIDGETS_FILTER.get()) {
+            for (AppTarget app : mTargets) {
+                PackageUserKey packageUserKey = new PackageUserKey(app.getPackageName(),
+                        app.getUser());
+                if (allWidgets.containsKey(packageUserKey)) {
+                    List<WidgetItem> notAddedWidgets = allWidgets.get(packageUserKey).stream()
+                            .filter(item ->
+                                    !widgetsInWorkspace.contains(
+                                            new ComponentKey(item.componentName, item.user)))
+                            .collect(Collectors.toList());
+                    if (notAddedWidgets.size() > 0) {
+                        // Even an apps have more than one widgets, we only include one widget.
+                        fixedContainerItems.items.add(
+                                new PendingAddWidgetInfo(notAddedWidgets.get(0).widgetInfo));
+                    }
+                }
+            }
+        } else {
+            Map<ComponentKey, WidgetItem> widgetItems =
+                    allWidgets.values().stream().flatMap(List::stream)
+                            .collect(Collectors.toMap(widget -> (ComponentKey) widget,
+                                    widget -> widget));
+            for (AppTarget app : mTargets) {
+                if (TextUtils.isEmpty(app.getClassName())) {
+                    continue;
+                }
+                ComponentKey targetWidget = new ComponentKey(
+                        new ComponentName(app.getPackageName(), app.getClassName()), app.getUser());
+                if (widgetItems.containsKey(targetWidget)) {
+                    fixedContainerItems.items.add(
+                            new PendingAddWidgetInfo(widgetItems.get(targetWidget).widgetInfo));
                 }
             }
         }
-        FixedContainerItems fixedContainerItems = mPredictorState.items;
-        fixedContainerItems.items.clear();
-        fixedContainerItems.items.addAll(recommendedWidgetsInDescendingOrder);
         bindExtraContainerItems(fixedContainerItems);
 
         // Don't store widgets prediction to disk because it is not used frequently.
diff --git a/quickstep/src/com/android/launcher3/taskbar/ButtonProvider.java b/quickstep/src/com/android/launcher3/taskbar/ButtonProvider.java
new file mode 100644
index 0000000..0d4130d
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/ButtonProvider.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.taskbar;
+
+import android.annotation.DrawableRes;
+import android.content.Context;
+import android.view.View;
+import android.widget.ImageView;
+
+import com.android.launcher3.R;
+
+/**
+ * Creates Buttons for Taskbar for 3 button nav.
+ * Can add animations and state management for buttons in this class as things progress.
+ */
+public class ButtonProvider {
+
+    private int mMarginLeftRight;
+    private final Context mContext;
+
+    public ButtonProvider(Context context) {
+        mContext = context;
+    }
+
+    public void setMarginLeftRight(int margin) {
+        mMarginLeftRight = margin;
+    }
+
+    public View getBack() {
+        // Back button
+        return getButtonForDrawable(R.drawable.ic_sysbar_back);
+    }
+
+    public View getDown() {
+        // Ime down button
+        return getButtonForDrawable(R.drawable.ic_sysbar_back);
+    }
+
+    public View getHome() {
+        // Home button
+        return getButtonForDrawable(R.drawable.ic_sysbar_home);
+    }
+
+    public View getRecents() {
+        // Recents button
+        return getButtonForDrawable(R.drawable.ic_sysbar_recent);
+    }
+
+    public View getImeSwitcher() {
+        // IME Switcher Button
+        return getButtonForDrawable(R.drawable.ic_ime_switcher);
+    }
+
+    private View getButtonForDrawable(@DrawableRes int drawableId) {
+        ImageView buttonView = new ImageView(mContext);
+        buttonView.setImageResource(drawableId);
+        buttonView.setBackgroundResource(R.drawable.taskbar_icon_click_feedback_roundrect);
+        buttonView.setPadding(mMarginLeftRight, 0, mMarginLeftRight, 0);
+        return buttonView;
+    }
+
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/ImeBarView.java b/quickstep/src/com/android/launcher3/taskbar/ImeBarView.java
new file mode 100644
index 0000000..bb3669b
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/ImeBarView.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.taskbar;
+
+import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_BACK;
+import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_IME_SWITCH;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.RelativeLayout;
+
+import com.android.launcher3.views.ActivityContext;
+
+public class ImeBarView extends RelativeLayout {
+
+    private ButtonProvider mButtonProvider;
+    private TaskbarController.TaskbarViewCallbacks mControllerCallbacks;
+    private View mImeView;
+
+    public ImeBarView(Context context) {
+        this(context, null);
+    }
+
+    public ImeBarView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public ImeBarView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public void construct(ButtonProvider buttonProvider) {
+        mButtonProvider = buttonProvider;
+    }
+
+    public void init(TaskbarController.TaskbarViewCallbacks taskbarCallbacks) {
+        mControllerCallbacks = taskbarCallbacks;
+        ActivityContext context = getActivityContext();
+        RelativeLayout.LayoutParams imeParams = new RelativeLayout.LayoutParams(
+                context.getDeviceProfile().iconSizePx,
+                context.getDeviceProfile().iconSizePx
+        );
+        RelativeLayout.LayoutParams downParams = new RelativeLayout.LayoutParams(imeParams);
+
+        imeParams.addRule(ALIGN_PARENT_END);
+        imeParams.setMarginEnd(context.getDeviceProfile().iconSizePx);
+        downParams.setMarginStart(context.getDeviceProfile().iconSizePx);
+        downParams.addRule(ALIGN_PARENT_START);
+
+        // Down Arrow
+        View downView = mButtonProvider.getDown();
+        downView.setOnClickListener(view -> mControllerCallbacks.onNavigationButtonClick(
+                BUTTON_BACK));
+        downView.setLayoutParams(downParams);
+        downView.setRotation(-90);
+        addView(downView);
+
+        // IME switcher button
+        mImeView = mButtonProvider.getImeSwitcher();
+        mImeView.setOnClickListener(view -> mControllerCallbacks.onNavigationButtonClick(
+                BUTTON_IME_SWITCH));
+        mImeView.setLayoutParams(imeParams);
+        addView(mImeView);
+    }
+
+    public void cleanup() {
+        removeAllViews();
+    }
+
+    public void setImeSwitcherVisibility(boolean show) {
+        mImeView.setVisibility(show ? VISIBLE : GONE);
+    }
+
+    private <T extends Context & ActivityContext> T getActivityContext() {
+        return ActivityContext.lookupContext(getContext());
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarAnimationController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarAnimationController.java
index 46e4506..29f6935 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarAnimationController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarAnimationController.java
@@ -44,7 +44,7 @@
     private final AnimatedFloat mTaskbarVisibilityAlphaForLauncherState = new AnimatedFloat(
             this::updateVisibilityAlpha);
     private final AnimatedFloat mTaskbarVisibilityAlphaForIme = new AnimatedFloat(
-            this::updateVisibilityAlpha);
+            this::updateVisibilityAlphaForIme);
 
     // Scale.
     private final AnimatedFloat mTaskbarScaleForLauncherState = new AnimatedFloat(
@@ -110,16 +110,22 @@
         // We use mTaskbarBackgroundAlpha as a proxy for whether Launcher is resumed/paused, the
         // assumption being that Taskbar should always be visible regardless of the current
         // LauncherState if Launcher is paused.
+        float alphaDueToIme = mTaskbarVisibilityAlphaForIme.value;
         float alphaDueToLauncher = Math.max(mTaskbarBackgroundAlpha.value,
                 mTaskbarVisibilityAlphaForLauncherState.value);
-        float alphaDueToOther = mTaskbarVisibilityAlphaForIme.value;
-        float taskbarAlpha = alphaDueToLauncher * alphaDueToOther;
+        float taskbarAlpha = alphaDueToLauncher * alphaDueToIme;
         mTaskbarCallbacks.updateTaskbarVisibilityAlpha(taskbarAlpha);
 
         // Make the nav bar invisible if taskbar is visible.
         setNavBarButtonAlpha(1f - taskbarAlpha);
     }
 
+    private void updateVisibilityAlphaForIme() {
+        updateVisibilityAlpha();
+        float taskbarAlphaDueToIme = mTaskbarVisibilityAlphaForIme.value;
+        mTaskbarCallbacks.updateImeBarVisibilityAlpha(1f - taskbarAlphaDueToIme);
+    }
+
     private void updateScale() {
         // We use mTaskbarBackgroundAlpha as a proxy for whether Launcher is resumed/paused, the
         // assumption being that Taskbar should always be at scale 1f regardless of the current
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
index c93de00..6084e10 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
@@ -15,6 +15,9 @@
  */
 package com.android.launcher3.taskbar;
 
+import static android.view.View.GONE;
+import static android.view.View.INVISIBLE;
+import static android.view.View.VISIBLE;
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
@@ -28,6 +31,7 @@
 import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.inputmethodservice.InputMethodService;
 import android.view.Gravity;
 import android.view.MotionEvent;
 import android.view.View;
@@ -48,9 +52,12 @@
 import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.states.StateAnimationConfig;
+import com.android.launcher3.taskbar.TaskbarNavButtonController.TaskbarButton;
 import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.views.ActivityContext;
 import com.android.quickstep.AnimatedFloat;
+import com.android.quickstep.SysUINavigationMode;
+import com.android.quickstep.TouchInteractionService.TaskbarOverviewProxyDelegate;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.WindowManagerWrapper;
@@ -58,13 +65,15 @@
 /**
  * Interfaces with Launcher/WindowManager/SystemUI to determine what to show in TaskbarView.
  */
-public class TaskbarController {
+public class TaskbarController implements TaskbarOverviewProxyDelegate {
 
     private static final String WINDOW_TITLE = "Taskbar";
 
     private final TaskbarContainerView mTaskbarContainerView;
     private final TaskbarView mTaskbarViewInApp;
     private final TaskbarView mTaskbarViewOnHome;
+    private final ImeBarView mImeBarView;
+
     private final BaseQuickstepLauncher mLauncher;
     private final WindowManager mWindowManager;
     // Layout width and height of the Taskbar in the default state.
@@ -73,9 +82,13 @@
     private final TaskbarAnimationController mTaskbarAnimationController;
     private final TaskbarHotseatController mHotseatController;
     private final TaskbarDragController mDragController;
+    private final TaskbarNavButtonController mNavButtonController;
 
     // Initialized in init().
     private WindowManager.LayoutParams mWindowLayoutParams;
+    private SysUINavigationMode.Mode mNavMode = SysUINavigationMode.Mode.NO_BUTTON;
+    private final SysUINavigationMode.NavigationModeChangeListener mNavigationModeChangeListener =
+            this::onNavModeChanged;
 
     private @Nullable Animator mAnimator;
     private boolean mIsAnimatingToLauncher;
@@ -85,10 +98,14 @@
         mLauncher = launcher;
         mTaskbarContainerView = taskbarContainerView;
         mTaskbarContainerView.construct(createTaskbarContainerViewCallbacks());
+        ButtonProvider buttonProvider = new ButtonProvider(launcher);
         mTaskbarViewInApp = mTaskbarContainerView.findViewById(R.id.taskbar_view);
-        mTaskbarViewInApp.construct(createTaskbarViewCallbacks());
+        mTaskbarViewInApp.construct(createTaskbarViewCallbacks(), buttonProvider);
         mTaskbarViewOnHome = taskbarViewOnHome;
-        mTaskbarViewOnHome.construct(createTaskbarViewCallbacks());
+        mTaskbarViewOnHome.construct(createTaskbarViewCallbacks(), buttonProvider);
+        mImeBarView = mTaskbarContainerView.findViewById(R.id.ime_bar_view);
+        mImeBarView.construct(buttonProvider);
+        mNavButtonController = new TaskbarNavButtonController(launcher);
         mWindowManager = mLauncher.getWindowManager();
         mTaskbarSize = new Point(MATCH_PARENT, mLauncher.getDeviceProfile().taskbarSize);
         mTaskbarStateHandler = mLauncher.getTaskbarStateHandler();
@@ -108,11 +125,21 @@
 
             @Override
             public void updateTaskbarVisibilityAlpha(float alpha) {
-                mTaskbarContainerView.setAlpha(alpha);
+                mTaskbarViewInApp.setAlpha(alpha);
                 mTaskbarViewOnHome.setAlpha(alpha);
             }
 
             @Override
+            public void updateImeBarVisibilityAlpha(float alpha) {
+                if (mNavMode != SysUINavigationMode.Mode.THREE_BUTTONS) {
+                    // TODO Remove sysui IME bar for gesture nav as well
+                    return;
+                }
+                mImeBarView.setAlpha(alpha);
+                mImeBarView.setVisibility(alpha == 0 ? GONE : VISIBLE);
+            }
+
+            @Override
             public void updateTaskbarScale(float scale) {
                 mTaskbarViewInApp.setScaleX(scale);
                 mTaskbarViewInApp.setScaleY(scale);
@@ -136,16 +163,21 @@
         return new TaskbarContainerViewCallbacks() {
             @Override
             public void onViewRemoved() {
-                if (mTaskbarContainerView.getChildCount() == 1) {
-                    // Only TaskbarView remains.
-                    setTaskbarWindowFullscreen(false);
+                // Ensure no other children present (like Folders, etc)
+                for (int i = 0; i < mTaskbarContainerView.getChildCount(); i++) {
+                    View v = mTaskbarContainerView.getChildAt(i);
+                    if (!((v instanceof TaskbarView) || (v instanceof ImeBarView))){
+                        return;
+                    }
                 }
+                setTaskbarWindowFullscreen(false);
             }
 
             @Override
             public boolean isTaskbarTouchable() {
                 return mTaskbarContainerView.getAlpha() > AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD
-                        && mTaskbarViewInApp.getVisibility() == View.VISIBLE
+                        && (mTaskbarViewInApp.getVisibility() == VISIBLE
+                            || mImeBarView.getVisibility() == VISIBLE)
                         && !mIsAnimatingToLauncher;
             }
         };
@@ -198,7 +230,7 @@
                 // space so that the others line up with the home screen hotseat.
                 boolean isOnHomeScreen = taskbarView == mTaskbarViewOnHome
                         || mLauncher.hasBeenResumed() || mIsAnimatingToLauncher;
-                return isOnHomeScreen ? View.INVISIBLE : View.GONE;
+                return isOnHomeScreen ? INVISIBLE : GONE;
             }
 
             @Override
@@ -212,6 +244,11 @@
                     alignRealHotseatWithTaskbar();
                 }
             }
+
+            @Override
+            public void onNavigationButtonClick(@TaskbarButton int buttonType) {
+                mNavButtonController.onButtonClick(buttonType);
+            }
         };
     }
 
@@ -228,9 +265,12 @@
      * Initializes the Taskbar, including adding it to the screen.
      */
     public void init() {
-        mTaskbarViewInApp.init(mHotseatController.getNumHotseatIcons());
-        mTaskbarViewOnHome.init(mHotseatController.getNumHotseatIcons());
+        mNavMode = SysUINavigationMode.INSTANCE.get(mLauncher)
+                .addModeChangeListener(mNavigationModeChangeListener);
+        mTaskbarViewInApp.init(mHotseatController.getNumHotseatIcons(), mNavMode);
+        mTaskbarViewOnHome.init(mHotseatController.getNumHotseatIcons(), mNavMode);
         mTaskbarContainerView.init(mTaskbarViewInApp);
+        mImeBarView.init(createTaskbarViewCallbacks());
         addToWindowManager();
         mTaskbarStateHandler.setTaskbarCallbacks(createTaskbarStateHandlerCallbacks());
         mTaskbarAnimationController.init();
@@ -272,12 +312,15 @@
         mTaskbarViewInApp.cleanup();
         mTaskbarViewOnHome.cleanup();
         mTaskbarContainerView.cleanup();
+        mImeBarView.cleanup();
         removeFromWindowManager();
         mTaskbarStateHandler.setTaskbarCallbacks(null);
         mTaskbarAnimationController.cleanup();
         mHotseatController.cleanup();
 
         setWhichTaskbarViewIsVisible(null);
+        SysUINavigationMode.INSTANCE.get(mLauncher)
+                .removeModeChangeListener(mNavigationModeChangeListener);
     }
 
     private void removeFromWindowManager() {
@@ -315,6 +358,12 @@
         mWindowManager.addView(mTaskbarContainerView, mWindowLayoutParams);
     }
 
+    private void onNavModeChanged(SysUINavigationMode.Mode newMode) {
+        mNavMode = newMode;
+        cleanup();
+        init();
+    }
+
     /**
      * Should be called from onResume() and onPause(), and animates the Taskbar accordingly.
      */
@@ -387,6 +436,28 @@
      */
     public void setIsImeVisible(boolean isImeVisible) {
         mTaskbarAnimationController.animateToVisibilityForIme(isImeVisible ? 0 : 1);
+        blockTaskbarTouchesForIme(isImeVisible);
+    }
+
+    /**
+     * When in 3 button nav, the above doesn't get called since we prevent sysui nav bar from
+     * instantiating at all, which is what's responsible for sending sysui state flags over.
+     *
+     * @param vis IME visibility flag
+     * @param backDisposition Used to determine back button behavior for software keyboard
+     *                        See BACK_DISPOSITION_* constants in {@link InputMethodService}
+     */
+    public void updateImeStatus(int displayId, int vis, int backDisposition,
+            boolean showImeSwitcher) {
+        if (displayId != mTaskbarContainerView.getContext().getDisplayId() ||
+                mNavMode != SysUINavigationMode.Mode.THREE_BUTTONS) {
+            return;
+        }
+
+        boolean imeVisible = (vis & InputMethodService.IME_VISIBLE) != 0;
+        mTaskbarAnimationController.animateToVisibilityForIme(imeVisible ? 0 : 1);
+        mImeBarView.setImeSwitcherVisibility(showImeSwitcher);
+        blockTaskbarTouchesForIme(imeVisible);
     }
 
     /**
@@ -436,12 +507,17 @@
 
     private void setWhichTaskbarViewIsVisible(@Nullable TaskbarView visibleTaskbar) {
         mTaskbarViewInApp.setVisibility(visibleTaskbar == mTaskbarViewInApp
-                ? View.VISIBLE : View.INVISIBLE);
+                ? VISIBLE : INVISIBLE);
         mTaskbarViewOnHome.setVisibility(visibleTaskbar == mTaskbarViewOnHome
-                ? View.VISIBLE : View.INVISIBLE);
+                ? VISIBLE : INVISIBLE);
         mLauncher.getHotseat().setIconsAlpha(visibleTaskbar != mTaskbarViewInApp ? 1f : 0f);
     }
 
+    private void blockTaskbarTouchesForIme(boolean block) {
+        mTaskbarViewOnHome.setTouchesEnabled(!block);
+        mTaskbarViewInApp.setTouchesEnabled(!block);
+    }
+
     /**
      * Returns the ratio of the taskbar icon size on home vs in an app.
      */
@@ -485,6 +561,7 @@
     protected interface TaskbarAnimationControllerCallbacks {
         void updateTaskbarBackgroundAlpha(float alpha);
         void updateTaskbarVisibilityAlpha(float alpha);
+        void updateImeBarVisibilityAlpha(float alpha);
         void updateTaskbarScale(float scale);
         void updateTaskbarTranslationY(float translationY);
     }
@@ -507,6 +584,7 @@
         /** Returns how much to scale non-icon elements such as spacing and dividers. */
         float getNonIconScale(TaskbarView taskbarView);
         void onItemPositionsChanged(TaskbarView taskbarView);
+        void onNavigationButtonClick(@TaskbarButton int buttonType);
     }
 
     /**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
new file mode 100644
index 0000000..54e1610
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.taskbar;
+
+import android.content.Context;
+import android.content.Intent;
+import android.view.inputmethod.InputMethodManager;
+
+import androidx.annotation.IntDef;
+
+import com.android.quickstep.OverviewCommandHelper;
+import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.TouchInteractionService;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Controller for 3 button mode in the taskbar.
+ * Handles all the functionality of the various buttons, making/routing the right calls into
+ * launcher or sysui/system.
+ *
+ * TODO: Create callbacks to hook into UI layer since state will change for more context buttons/
+ *       assistant invocation.
+ */
+public class TaskbarNavButtonController {
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            BUTTON_BACK,
+            BUTTON_HOME,
+            BUTTON_RECENTS,
+            BUTTON_IME_SWITCH
+    })
+
+    public @interface TaskbarButton {}
+
+    static final int BUTTON_BACK = 1;
+    static final int BUTTON_HOME = BUTTON_BACK << 1;
+    static final int BUTTON_RECENTS = BUTTON_HOME << 1;
+    static final int BUTTON_IME_SWITCH = BUTTON_RECENTS << 1;
+
+
+    private final Context mContext;
+
+    public TaskbarNavButtonController(Context context) {
+        mContext = context;
+    }
+
+    public void onButtonClick(@TaskbarButton int buttonType) {
+        switch (buttonType) {
+            case BUTTON_BACK:
+                executeBack();
+                break;
+            case BUTTON_HOME:
+                navigateHome();
+                break;
+            case BUTTON_RECENTS:
+                navigateToOverview();;
+                break;
+            case BUTTON_IME_SWITCH:
+                showIMESwitcher();
+                break;
+        }
+    }
+
+    private void navigateHome() {
+        mContext.startActivity(new Intent(Intent.ACTION_MAIN)
+                .addCategory(Intent.CATEGORY_HOME)
+                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+    }
+
+    private void navigateToOverview() {
+        TouchInteractionService.getInstance().getOverviewCommandHelper()
+                .addCommand(OverviewCommandHelper.TYPE_SHOW);
+    }
+
+    private void executeBack() {
+        SystemUiProxy.INSTANCE.getNoCreate().onBackPressed();
+    }
+
+    private void showIMESwitcher() {
+        mContext.getSystemService(InputMethodManager.class).showInputMethodPickerFromSystem(
+                true /* showAuxiliarySubtypes */, mContext.getDisplayId());
+    }
+
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index 60a7add..9e8013e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -15,6 +15,10 @@
  */
 package com.android.launcher3.taskbar;
 
+import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_BACK;
+import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_HOME;
+import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_RECENTS;
+
 import android.animation.Animator;
 import android.animation.AnimatorSet;
 import android.animation.LayoutTransition;
@@ -24,8 +28,10 @@
 import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.graphics.RectF;
+import android.os.SystemProperties;
 import android.util.AttributeSet;
 import android.view.DragEvent;
+import android.view.Gravity;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewConfiguration;
@@ -45,12 +51,17 @@
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.views.ActivityContext;
+import com.android.quickstep.SysUINavigationMode;
 
 /**
  * Hosts the Taskbar content such as Hotseat and Recent Apps. Drawn on top of other apps.
  */
 public class TaskbarView extends LinearLayout implements FolderIcon.FolderIconParent, Insettable {
 
+
+    private static final boolean ENABLE_THREE_BUTTON_TASKBAR =
+            SystemProperties.getBoolean("persist.debug.taskbar_three_button", false);
+
     private final int mIconTouchSize;
     private final boolean mIsRtl;
     private final int mTouchSlop;
@@ -68,15 +79,22 @@
     private LayoutTransition mLayoutTransition;
     private int mHotseatStartIndex;
     private int mHotseatEndIndex;
+    private LinearLayout mButtonRegion;
 
     // Delegate touches to the closest view if within mIconTouchSize.
     private boolean mDelegateTargeted;
     private View mDelegateView;
+    // Prevents dispatching touches to children if true
+    private boolean mTouchEnabled = true;
 
     private boolean mIsDraggingItem;
     // Only non-null when the corresponding Folder is open.
     private @Nullable FolderIcon mLeaveBehindFolderIcon;
 
+    private int mNavButtonStartIndex;
+    /** Provider of buttons added to taskbar in 3 button nav */
+    private ButtonProvider mButtonProvider;
+
     public TaskbarView(@NonNull Context context) {
         this(context, null);
     }
@@ -100,15 +118,28 @@
         mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
     }
 
-    protected void construct(TaskbarController.TaskbarViewCallbacks taskbarViewCallbacks) {
+    protected void construct(TaskbarController.TaskbarViewCallbacks taskbarViewCallbacks,
+            ButtonProvider buttonProvider) {
         mControllerCallbacks = taskbarViewCallbacks;
         mNonIconScale = mControllerCallbacks.getNonIconScale(this);
         mItemMarginLeftRight = getResources().getDimensionPixelSize(R.dimen.taskbar_icon_spacing);
         mItemMarginLeftRight = Math.round(mItemMarginLeftRight * mNonIconScale);
+        mButtonProvider = buttonProvider;
+        mButtonProvider.setMarginLeftRight(mItemMarginLeftRight);
     }
 
-    protected void init(int numHotseatIcons) {
-        mHotseatStartIndex = 0;
+    protected void init(int numHotseatIcons, SysUINavigationMode.Mode newMode) {
+        // TODO: check if buttons on left
+        if (newMode == SysUINavigationMode.Mode.THREE_BUTTONS && ENABLE_THREE_BUTTON_TASKBAR) {
+            // 3 button
+            mNavButtonStartIndex = 0;
+            createNavButtons();
+        } else {
+            mNavButtonStartIndex = -1;
+            removeNavButtons();
+        }
+
+        mHotseatStartIndex = mNavButtonStartIndex + 1;
         mHotseatEndIndex = mHotseatStartIndex + numHotseatIcons - 1;
         updateHotseatItems(new ItemInfo[numHotseatIcons]);
 
@@ -185,11 +216,11 @@
             if (hotseatView == null || hotseatView.getSourceLayoutResId() != expectedLayoutResId
                     || needsReinflate) {
                 removeView(hotseatView);
-                ActivityContext activityContext = ActivityContext.lookupContext(getContext());
+                ActivityContext activityContext = getActivityContext();
                 if (isFolder) {
                     FolderInfo folderInfo = (FolderInfo) hotseatItemInfo;
                     FolderIcon folderIcon = FolderIcon.inflateFolderAndIcon(expectedLayoutResId,
-                            ActivityContext.lookupContext(getContext()), this, folderInfo);
+                            getActivityContext(), this, folderInfo);
                     folderIcon.setTextVisible(false);
                     hotseatView = folderIcon;
                 } else {
@@ -244,11 +275,23 @@
     }
 
     @Override
+    public boolean dispatchTouchEvent(MotionEvent ev) {
+        if (!mTouchEnabled) {
+            return true;
+        }
+        return super.dispatchTouchEvent(ev);
+    }
+
+    @Override
     public boolean onTouchEvent(MotionEvent event) {
         boolean handled = delegateTouchIfNecessary(event);
         return super.onTouchEvent(event) || handled;
     }
 
+    public void setTouchesEnabled(boolean touchEnabled) {
+        this.mTouchEnabled = touchEnabled;
+    }
+
     /**
      * User touched the Taskbar background. Determine whether the touch is close enough to a view
      * that we should forward the touches to it.
@@ -335,12 +378,57 @@
         return findDelegateView(xInOurCoordinates, yInOurCoorindates) != null;
     }
 
+    private void removeNavButtons() {
+        if (mButtonRegion != null) {
+            mButtonRegion.removeAllViews();
+            removeView(mButtonRegion);
+        } // else We've never been in 3 button. Woah Scoob!
+    }
+
+    /**
+     * Add back/home/recents buttons into a single ViewGroup that will be inserted at
+     * {@param navButtonStartIndex}
+     */
+    private void createNavButtons() {
+        ActivityContext context = getActivityContext();
+        if (mButtonRegion == null) {
+            mButtonRegion = new LinearLayout(getContext());
+        } else {
+            mButtonRegion.removeAllViews();
+        }
+        mButtonRegion.setVisibility(VISIBLE);
+
+        LinearLayout.LayoutParams buttonParams = new LinearLayout.LayoutParams(
+                context.getDeviceProfile().iconSizePx,
+                context.getDeviceProfile().iconSizePx
+        );
+        buttonParams.gravity = Gravity.CENTER;
+
+        View backButton = mButtonProvider.getBack();
+        backButton.setOnClickListener(view -> mControllerCallbacks.onNavigationButtonClick(
+                BUTTON_BACK));
+        mButtonRegion.addView(backButton, buttonParams);
+
+        // Home button
+        View homeButton = mButtonProvider.getHome();
+        homeButton.setOnClickListener(view -> mControllerCallbacks.onNavigationButtonClick(
+                BUTTON_HOME));
+        mButtonRegion.addView(homeButton, buttonParams);
+
+        View recentsButton = mButtonProvider.getRecents();
+        recentsButton.setOnClickListener(view -> mControllerCallbacks.onNavigationButtonClick(
+                BUTTON_RECENTS));
+        mButtonRegion.addView(recentsButton, buttonParams);
+
+        addView(mButtonRegion, mNavButtonStartIndex);
+    }
+
     @Override
     public boolean onDragEvent(DragEvent event) {
         switch (event.getAction()) {
             case DragEvent.ACTION_DRAG_STARTED:
                 mIsDraggingItem = true;
-                AbstractFloatingView.closeAllOpenViews(ActivityContext.lookupContext(getContext()));
+                AbstractFloatingView.closeAllOpenViews(getActivityContext());
                 return true;
             case DragEvent.ACTION_DRAG_ENDED:
                 mIsDraggingItem = false;
@@ -407,12 +495,15 @@
     }
 
     private View inflate(@LayoutRes int layoutResId) {
-        return ActivityContext.lookupContext(getContext()).getLayoutInflater()
-                .inflate(layoutResId, this, false);
+        return getActivityContext().getLayoutInflater().inflate(layoutResId, this, false);
     }
 
     @Override
     public void setInsets(Rect insets) {
         // Ignore, we just implement Insettable to draw behind system insets.
     }
+
+    private <T extends Context & ActivityContext> T getActivityContext() {
+        return ActivityContext.lookupContext(getContext());
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
index 1e03b05..76a5782 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
@@ -18,13 +18,23 @@
 
 import android.app.Person;
 import android.content.pm.ShortcutInfo;
+import android.view.Display;
 
 import com.android.launcher3.Utilities;
 
 public class ApiWrapper {
 
+    public static final boolean TASKBAR_DRAWN_IN_PROCESS = true;
+
     public static Person[] getPersons(ShortcutInfo si) {
         Person[] persons = si.getPersons();
         return persons == null ? Utilities.EMPTY_PERSON_ARRAY : persons;
     }
+
+    /**
+     * Returns true if the display is an internal displays
+     */
+    public static boolean isInternalDisplay(Display display) {
+        return display.getType() == Display.TYPE_INTERNAL;
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
index 01616d4..e508690 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
@@ -24,10 +24,12 @@
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y;
 import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
-import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET;
+import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
+import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_VERTICAL_OFFSET;
 import static com.android.quickstep.views.RecentsView.RECENTS_GRID_PROGRESS;
 import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
-import static com.android.quickstep.views.RecentsView.TASK_PRIMARY_TRANSLATION;
+import static com.android.quickstep.views.RecentsView.TASK_PRIMARY_SPLIT_TRANSLATION;
+import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_SPLIT_TRANSLATION;
 import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION;
 
 import android.util.FloatProperty;
@@ -62,7 +64,8 @@
     public void setState(@NonNull LauncherState state) {
         float[] scaleAndOffset = state.getOverviewScaleAndOffset(mLauncher);
         RECENTS_SCALE_PROPERTY.set(mRecentsView, scaleAndOffset[0]);
-        ADJACENT_PAGE_OFFSET.set(mRecentsView, scaleAndOffset[1]);
+        ADJACENT_PAGE_HORIZONTAL_OFFSET.set(mRecentsView, scaleAndOffset[1]);
+        ADJACENT_PAGE_VERTICAL_OFFSET.set(mRecentsView, scaleAndOffset[2]);
         TASK_SECONDARY_TRANSLATION.set(mRecentsView, 0f);
 
         getContentAlphaProperty().set(mRecentsView, state.overviewUi ? 1f : 0);
@@ -92,15 +95,17 @@
         float[] scaleAndOffset = toState.getOverviewScaleAndOffset(mLauncher);
         setter.setFloat(mRecentsView, RECENTS_SCALE_PROPERTY, scaleAndOffset[0],
                 config.getInterpolator(ANIM_OVERVIEW_SCALE, LINEAR));
-        setter.setFloat(mRecentsView, ADJACENT_PAGE_OFFSET, scaleAndOffset[1],
+        setter.setFloat(mRecentsView, ADJACENT_PAGE_HORIZONTAL_OFFSET, scaleAndOffset[1],
                 config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_X, LINEAR));
+        setter.setFloat(mRecentsView, ADJACENT_PAGE_VERTICAL_OFFSET, scaleAndOffset[2],
+                config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, LINEAR));
         PagedOrientationHandler orientationHandler =
                 ((RecentsView) mLauncher.getOverviewPanel()).getPagedOrientationHandler();
         FloatProperty taskViewsFloat = orientationHandler.getSplitSelectTaskOffset(
-                TASK_PRIMARY_TRANSLATION, TASK_SECONDARY_TRANSLATION, mLauncher.getDeviceProfile());
+                TASK_PRIMARY_SPLIT_TRANSLATION, TASK_SECONDARY_SPLIT_TRANSLATION,
+                mLauncher.getDeviceProfile());
         setter.setFloat(mRecentsView, taskViewsFloat,
-                toState.getOverviewSecondaryTranslation(mLauncher),
-                config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, LINEAR));
+                toState.getOverviewSecondaryTranslation(mLauncher), LINEAR);
 
         setter.setFloat(mRecentsView, getContentAlphaProperty(), toState.overviewUi ? 1 : 0,
                 config.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT));
diff --git a/quickstep/src/com/android/launcher3/uioverrides/DeviceFlag.java b/quickstep/src/com/android/launcher3/uioverrides/DeviceFlag.java
index bb1f6fc..c115bbb 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/DeviceFlag.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/DeviceFlag.java
@@ -68,6 +68,12 @@
         mListeners.remove(r);
     }
 
+    @Override
+    public boolean get() {
+        // Override this method in order to let Robolectric ShadowDeviceFlag to stub it.
+        return super.get();
+    }
+
     private void registerDeviceConfigChangedListener(Context context) {
         DeviceConfig.addOnPropertiesChangedListener(
                 NAMESPACE_LAUNCHER,
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
index 66e4f4c..a54f963 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
@@ -23,6 +23,7 @@
 import android.view.View;
 import android.widget.RemoteViews;
 
+import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.util.ActivityOptionsWrapper;
 import com.android.launcher3.widget.LauncherAppWidgetHostView;
 
@@ -49,6 +50,11 @@
         Pair<Intent, ActivityOptions> options = remoteResponse.getLaunchOptions(hostView);
         ActivityOptionsWrapper activityOptions = mLauncher.getAppTransitionManager()
                 .getActivityLaunchOptions(mLauncher, hostView);
+        activityOptions.options.setPendingIntentLaunchFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        Object itemInfo = hostView.getTag();
+        if (itemInfo instanceof ItemInfo) {
+            mLauncher.addLaunchCookie((ItemInfo) itemInfo, activityOptions.options);
+        }
         options = Pair.create(options.first, activityOptions.options);
         return RemoteViews.startPendingIntent(hostView, pendingIntent, options);
     }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index f09d9e0..d07cc35 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -26,6 +26,7 @@
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK;
 import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_WIDGET_APP_START;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
 import static com.android.launcher3.testing.TestProtocol.HINT_STATE_ORDINAL;
 import static com.android.launcher3.testing.TestProtocol.HINT_STATE_TWO_BUTTON_ORDINAL;
@@ -36,7 +37,6 @@
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.content.res.Configuration;
-import android.os.SystemProperties;
 import android.view.HapticFeedbackConstants;
 import android.view.View;
 
@@ -90,9 +90,6 @@
 
 public class QuickstepLauncher extends BaseQuickstepLauncher {
 
-    private static final boolean ENABLE_APP_WIDGET_LAUNCH_ANIMATION =
-            SystemProperties.getBoolean("persist.debug.quickstep_app_widget_launch", false);
-
     public static final boolean GO_LOW_RAM_RECENTS_ENABLED = false;
     /**
      * Reusable command for applying the shelf height on the background thread.
@@ -327,7 +324,7 @@
 
     protected LauncherAppWidgetHost createAppWidgetHost() {
         LauncherAppWidgetHost appWidgetHost = super.createAppWidgetHost();
-        if (ENABLE_APP_WIDGET_LAUNCH_ANIMATION) {
+        if (ENABLE_QUICKSTEP_WIDGET_APP_START.get()) {
             appWidgetHost.setInteractionHandler(new QuickstepInteractionHandler(this));
         }
         return appWidgetHost;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
index a81bdd5..d822c8c 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -84,11 +84,6 @@
     }
 
     @Override
-    public float[] getOverviewScaleAndOffset(Launcher launcher) {
-        return new float[] {0.9f, 1};
-    }
-
-    @Override
     public LauncherState getHistoryForState(LauncherState previousState) {
         return previousState == OVERVIEW ? OVERVIEW : NORMAL;
     }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index 77c2611..06ffae4 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -96,6 +96,6 @@
             BaseDraggingActivity activity) {
         return new float[] {
                 ((RecentsView) activity.getOverviewPanel()).getMaxScaleForFullScreen(),
-                NO_OFFSET};
+                NO_OFFSET, NO_OFFSET};
     }
 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
index 6f084a1..1fc288f 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
@@ -78,6 +78,6 @@
         float scale = Math.min((float) modalTaskSize.height() / taskSize.y,
                 (float) modalTaskSize.width() / taskSize.x);
 
-        return new float[] {scale, NO_OFFSET};
+        return new float[] {scale, NO_OFFSET, NO_OFFSET};
     }
 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
index 135c478..c9cfad3 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -17,7 +17,6 @@
 
 import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
-import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
 
 import android.content.Context;
 import android.graphics.Rect;
@@ -60,9 +59,8 @@
 
     @Override
     public int getTransitionDuration(Context context) {
-        // In no-button mode, overview comes in all the way from the left, so give it more time.
-        boolean isNoButtonMode = SysUINavigationMode.INSTANCE.get(context).getMode() == NO_BUTTON;
-        return isNoButtonMode ? 380 : 250;
+        // In gesture modes, overview comes in all the way from the bottom, so give it more time.
+        return SysUINavigationMode.INSTANCE.get(context).getMode().hasGestures ? 380 : 250;
     }
 
     @Override
@@ -80,7 +78,7 @@
 
     @Override
     public float[] getOverviewScaleAndOffset(Launcher launcher) {
-        return new float[] {NO_SCALE, NO_OFFSET};
+        return new float[] {NO_SCALE, NO_OFFSET, NO_OFFSET};
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
index ba61923..adc6b18 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
@@ -23,34 +23,34 @@
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.WorkspaceStateTransitionAnimation.getSpringScaleAnimator;
 import static com.android.launcher3.anim.Interpolators.ACCEL;
-import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
+import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE_IN_OUT;
 import static com.android.launcher3.anim.Interpolators.DEACCEL;
 import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
 import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
 import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
 import static com.android.launcher3.anim.Interpolators.INSTANT;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
 import static com.android.launcher3.anim.Interpolators.clampToProgress;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_DEPTH;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_ACTIONS_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_SCRIM_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_SCALE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE;
-import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
 import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
 
 import android.animation.ValueAnimator;
-import android.view.View;
 
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.Hotseat;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.Workspace;
-import com.android.launcher3.allapps.AllAppsContainerView;
 import com.android.launcher3.states.StateAnimationConfig;
 import com.android.launcher3.uioverrides.QuickstepLauncher;
 import com.android.quickstep.SysUINavigationMode;
@@ -65,6 +65,12 @@
 
     // Scale recents takes before animating in
     private static final float RECENTS_PREPARE_SCALE = 1.33f;
+    // Scale workspace takes before animating in
+    private static final float WORKSPACE_PREPARE_SCALE_GESTURES = 0.97f;
+    private static final float WORKSPACE_PREPARE_SCALE_BUTTONS = 0.92f;
+    // When the overview to home transition reaches this percentage, immediately hide overview and
+    // start animating away the scrim and animating in workspace.
+    private static final float OVERVIEW_TO_HOME_HARD_HAND_OFF = 0.4f;
 
     // Due to use of physics, duration may differ between devices so we need to calculate and
     // cache the value.
@@ -77,17 +83,33 @@
     @Override
     public void prepareForAtomicAnimation(LauncherState fromState, LauncherState toState,
             StateAnimationConfig config) {
+        RecentsView overview = mActivity.getOverviewPanel();
         if (toState == NORMAL && fromState == OVERVIEW) {
-            config.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL);
-            config.setInterpolator(ANIM_WORKSPACE_FADE, ACCEL);
-            config.setInterpolator(ANIM_ALL_APPS_FADE, ACCEL);
-            config.setInterpolator(ANIM_OVERVIEW_SCALE, clampToProgress(ACCEL, 0, 0.9f));
-            config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, ACCEL_DEACCEL);
-
-            if (SysUINavigationMode.getMode(mActivity) == NO_BUTTON) {
-                config.setInterpolator(ANIM_OVERVIEW_FADE, FINAL_FRAME);
+            final float workspacePrepareScale;
+            if (SysUINavigationMode.getMode(mActivity).hasGestures
+                    && overview.getTaskViewCount() > 0) {
+                workspacePrepareScale = WORKSPACE_PREPARE_SCALE_GESTURES;
+                // Overview is going offscreen, so keep it at its current scale and opacity.
+                config.setInterpolator(ANIM_OVERVIEW_SCALE, FINAL_FRAME);
+                config.setInterpolator(ANIM_OVERVIEW_FADE, clampToProgress(
+                        FINAL_FRAME, 0f, OVERVIEW_TO_HOME_HARD_HAND_OFF));
+                config.setInterpolator(ANIM_OVERVIEW_ACTIONS_FADE, clampToProgress(
+                        DEACCEL, 0f, OVERVIEW_TO_HOME_HARD_HAND_OFF));
+                config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, AGGRESSIVE_EASE_IN_OUT);
+                config.setInterpolator(ANIM_SCRIM_FADE, clampToProgress(
+                        DEACCEL, OVERVIEW_TO_HOME_HARD_HAND_OFF, 1f));
+                config.setInterpolator(ANIM_WORKSPACE_SCALE, clampToProgress(
+                        DEACCEL, OVERVIEW_TO_HOME_HARD_HAND_OFF, 1f));
+                config.setInterpolator(ANIM_WORKSPACE_FADE, clampToProgress(
+                        INSTANT, OVERVIEW_TO_HOME_HARD_HAND_OFF, 1f));
             } else {
+                workspacePrepareScale = WORKSPACE_PREPARE_SCALE_BUTTONS;
+                config.setInterpolator(ANIM_OVERVIEW_SCALE, clampToProgress(ACCEL, 0, 0.9f));
                 config.setInterpolator(ANIM_OVERVIEW_FADE, DEACCEL_1_7);
+                config.setInterpolator(ANIM_OVERVIEW_ACTIONS_FADE, LINEAR);
+                config.setInterpolator(ANIM_SCRIM_FADE, LINEAR);
+                config.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL);
+                config.setInterpolator(ANIM_WORKSPACE_FADE, ACCEL);
             }
 
             Workspace workspace = mActivity.getWorkspace();
@@ -100,35 +122,33 @@
                         && currentChild.getShortcutsAndWidgets().getAlpha() > 0;
             }
             if (!isWorkspaceVisible) {
-                workspace.setScaleX(0.92f);
-                workspace.setScaleY(0.92f);
+                workspace.setScaleX(workspacePrepareScale);
+                workspace.setScaleY(workspacePrepareScale);
             }
             Hotseat hotseat = mActivity.getHotseat();
             boolean isHotseatVisible = hotseat.getVisibility() == VISIBLE && hotseat.getAlpha() > 0;
             if (!isHotseatVisible) {
-                hotseat.setScaleX(0.92f);
-                hotseat.setScaleY(0.92f);
-                AllAppsContainerView qsbContainer = mActivity.getAppsView();
-                View qsb = qsbContainer.getSearchView();
-                boolean qsbVisible = qsb.getVisibility() == VISIBLE && qsb.getAlpha() > 0;
-                if (!qsbVisible) {
-                    qsbContainer.setScaleX(0.92f);
-                    qsbContainer.setScaleY(0.92f);
-                }
+                hotseat.setScaleX(workspacePrepareScale);
+                hotseat.setScaleY(workspacePrepareScale);
             }
         } else if ((fromState == NORMAL || fromState == HINT_STATE
                 || fromState == HINT_STATE_TWO_BUTTON) && toState == OVERVIEW) {
-            if (SysUINavigationMode.getMode(mActivity) == NO_BUTTON) {
+            if (SysUINavigationMode.getMode(mActivity).hasGestures) {
                 config.setInterpolator(ANIM_WORKSPACE_SCALE,
                         fromState == NORMAL ? ACCEL : OVERSHOOT_1_2);
                 config.setInterpolator(ANIM_WORKSPACE_TRANSLATE, ACCEL);
-                config.setInterpolator(ANIM_OVERVIEW_FADE, INSTANT);
+
+                // Scrolling in tasks, so show straight away
+                if (overview.getTaskViewCount() > 0) {
+                    config.setInterpolator(ANIM_OVERVIEW_FADE, INSTANT);
+                } else {
+                    config.setInterpolator(ANIM_OVERVIEW_FADE, OVERSHOOT_1_2);
+                }
             } else {
                 config.setInterpolator(ANIM_WORKSPACE_SCALE, OVERSHOOT_1_2);
                 config.setInterpolator(ANIM_OVERVIEW_FADE, OVERSHOOT_1_2);
 
                 // Scale up the recents, if it is not coming from the side
-                RecentsView overview = mActivity.getOverviewPanel();
                 if (overview.getVisibility() != VISIBLE || overview.getContentAlpha() == 0) {
                     RECENTS_SCALE_PROPERTY.set(overview, RECENTS_PREPARE_SCALE);
                 }
@@ -137,6 +157,10 @@
             config.setInterpolator(ANIM_ALL_APPS_FADE, OVERSHOOT_1_2);
             config.setInterpolator(ANIM_OVERVIEW_SCALE, OVERSHOOT_1_2);
             config.setInterpolator(ANIM_DEPTH, OVERSHOOT_1_2);
+            config.setInterpolator(ANIM_SCRIM_FADE, t -> {
+                // Animate at the same rate until reaching progress 1, and skip the overshoot.
+                return Math.min(1, OVERSHOOT_1_2.getInterpolation(t));
+            });
             config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, OVERSHOOT_1_2);
             config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, OVERSHOOT_1_2);
         } else if (fromState == HINT_STATE && toState == NORMAL) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
index 2d95ce2..729e2cb 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
@@ -18,20 +18,11 @@
 
 import static com.android.launcher3.LauncherAnimUtils.VIEW_BACKGROUND_COLOR;
 import static com.android.launcher3.LauncherAnimUtils.newCancelListener;
-import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.HINT_STATE;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
-import static com.android.launcher3.anim.Interpolators.ACCEL;
 import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
-import static com.android.launcher3.anim.Interpolators.DEACCEL;
-import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_HEADER_FADE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_SCALE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE;
 import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
 
@@ -45,7 +36,6 @@
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.states.StateAnimationConfig;
 import com.android.launcher3.util.VibratorWrapper;
 import com.android.quickstep.SystemUiProxy;
@@ -269,39 +259,4 @@
     private float dpiFromPx(float pixels) {
         return Utilities.dpiFromPx(pixels, mLauncher.getResources().getDisplayMetrics().densityDpi);
     }
-
-    @Override
-    protected StateAnimationConfig getConfigForStates(
-            LauncherState fromState, LauncherState toState) {
-        if (fromState == NORMAL && toState == ALL_APPS) {
-            StateAnimationConfig builder = new StateAnimationConfig();
-            builder.setInterpolator(ANIM_ALL_APPS_HEADER_FADE, Interpolators.clampToProgress(
-                    ACCEL,
-                    0,
-                    ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD));
-            builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(
-                    ACCEL,
-                    ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD,
-                    ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD));
-
-            // Get workspace out of the way quickly, to prepare for potential pause.
-            builder.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL_3);
-            builder.setInterpolator(ANIM_WORKSPACE_TRANSLATE, DEACCEL_3);
-            builder.setInterpolator(ANIM_WORKSPACE_FADE, DEACCEL_3);
-            return builder;
-        } else if (fromState == ALL_APPS && toState == NORMAL) {
-            StateAnimationConfig builder = new StateAnimationConfig();
-
-            builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(
-                    DEACCEL,
-                    1 - ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD,
-                    1 - ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD));
-            builder.setInterpolator(ANIM_ALL_APPS_HEADER_FADE, Interpolators.clampToProgress(
-                    DEACCEL,
-                    1 - ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD,
-                    1));
-            return builder;
-        }
-        return super.getConfigForStates(fromState, toState);
-    }
 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
index 12de4a6..0f64abc 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
@@ -36,11 +36,14 @@
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_SCALE;
 import static com.android.launcher3.states.StateAnimationConfig.SKIP_ALL_ANIMATIONS;
 import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
+import static com.android.launcher3.states.StateAnimationConfig.SKIP_SCRIM;
 import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_RIGHT;
 import static com.android.launcher3.touch.BothAxesSwipeDetector.DIRECTION_UP;
 import static com.android.launcher3.util.DisplayController.getSingleFrameMs;
 import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
-import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET;
+import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
+import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_VERTICAL_OFFSET;
+import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
 import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
 import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
 import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION;
@@ -207,7 +210,7 @@
     /** Create state animation to control non-overview components. */
     private void updateNonOverviewAnim(LauncherState toState, StateAnimationConfig config) {
         config.duration = (long) (Math.max(mXRange, mYRange) * 2);
-        config.animFlags |= SKIP_OVERVIEW;
+        config.animFlags |= SKIP_OVERVIEW | SKIP_SCRIM;
         mNonOverviewAnim = mLauncher.getStateManager()
                 .createAnimationToNewWorkspace(toState, config);
         mNonOverviewAnim.getTarget().addListener(mClearStateOnCancelListener);
@@ -219,7 +222,8 @@
 
         // Set RecentView's initial properties.
         RECENTS_SCALE_PROPERTY.set(mRecentsView, fromState.getOverviewScaleAndOffset(mLauncher)[0]);
-        ADJACENT_PAGE_OFFSET.set(mRecentsView, 1f);
+        ADJACENT_PAGE_HORIZONTAL_OFFSET.set(mRecentsView, 1f);
+        ADJACENT_PAGE_VERTICAL_OFFSET.set(mRecentsView, 0f);
         mRecentsView.setContentAlpha(1);
         mRecentsView.setFullscreenProgress(fromState.getOverviewFullscreenProgress());
         mLauncher.getActionsView().getVisibilityAlpha().setValue(
@@ -229,10 +233,14 @@
         // As we drag right, animate the following properties:
         //   - RecentsView translationX
         //   - OverviewScrim
+        //   - RecentsView fade (if it's empty)
         PendingAnimation xAnim = new PendingAnimation((long) (mXRange * 2));
-        xAnim.setFloat(mRecentsView, ADJACENT_PAGE_OFFSET, scaleAndOffset[1], LINEAR);
+        xAnim.setFloat(mRecentsView, ADJACENT_PAGE_HORIZONTAL_OFFSET, scaleAndOffset[1], LINEAR);
         xAnim.setViewBackgroundColor(mLauncher.getScrimView(),
                 toState.getWorkspaceScrimColor(mLauncher), LINEAR);
+        if (mRecentsView.getTaskViewCount() == 0) {
+            xAnim.addFloat(mRecentsView, CONTENT_ALPHA, 0f, 1f, LINEAR);
+        }
         mXOverviewAnim = xAnim.createPlaybackController();
         mXOverviewAnim.dispatchOnStart();
 
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
index b8f9452..3c83d25 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
@@ -24,6 +24,7 @@
 import static com.android.launcher3.anim.Interpolators.ACCEL;
 import static com.android.launcher3.anim.Interpolators.DEACCEL;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_SCRIM_FADE;
 
 import android.view.MotionEvent;
 
@@ -56,13 +57,17 @@
     /**
      * Minimum clamping progress for fading in all apps content
      */
-    protected static final float ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD = 0.4f;
-
+    protected static final float ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD = 0.5f;
 
     /**
-     * The progress at which recents will begin fading out when swiping up from overview.
+     * Minimum clamping progress for fading in all apps scrim
      */
-    private static final float RECENTS_FADE_THRESHOLD = 0.88f;
+    protected static final float ALL_APPS_SCRIM_VISIBLE_THRESHOLD = .1f;
+
+    /**
+     * Maximum clamping progress for opaque all apps scrim
+     */
+    protected static final float ALL_APPS_SCRIM_OPAQUE_THRESHOLD = .5f;
 
     private final PortraitOverviewStateTouchHelper mOverviewPortraitStateTouchHelper;
 
@@ -122,14 +127,22 @@
     private StateAnimationConfig getNormalToAllAppsAnimation() {
         StateAnimationConfig builder = new StateAnimationConfig();
         builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(ACCEL,
-                0, ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD));
+                ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD,
+                ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD));
+        builder.setInterpolator(ANIM_SCRIM_FADE, Interpolators.clampToProgress(ACCEL,
+                ALL_APPS_SCRIM_VISIBLE_THRESHOLD,
+                ALL_APPS_SCRIM_OPAQUE_THRESHOLD));
         return builder;
     }
 
     private StateAnimationConfig getAllAppsToNormalAnimation() {
         StateAnimationConfig builder = new StateAnimationConfig();
         builder.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(DEACCEL,
-                1 - ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD, 1));
+                1 - ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD,
+                1 - ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD));
+        builder.setInterpolator(ANIM_SCRIM_FADE, Interpolators.clampToProgress(DEACCEL,
+                1 - ALL_APPS_SCRIM_OPAQUE_THRESHOLD,
+                1 - ALL_APPS_SCRIM_VISIBLE_THRESHOLD));
         return builder;
     }
 
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
index 3953e42..5891d5f 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
@@ -30,6 +30,9 @@
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE;
 import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK;
+import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
+import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_VERTICAL_OFFSET;
+import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
 import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
@@ -105,6 +108,14 @@
         StateAnimationConfig config = new StateAnimationConfig();
         setupInterpolators(config);
         config.duration = (long) (getShiftRange() * 2);
+
+        // Set RecentView's initial properties for coming in from the side.
+        RECENTS_SCALE_PROPERTY.set(mOverviewPanel,
+                QUICK_SWITCH.getOverviewScaleAndOffset(mLauncher)[0] * 0.85f);
+        ADJACENT_PAGE_HORIZONTAL_OFFSET.set(mOverviewPanel, 1f);
+        ADJACENT_PAGE_VERTICAL_OFFSET.set(mOverviewPanel, 0f);
+        mOverviewPanel.setContentAlpha(1);
+
         mCurrentAnimation = mLauncher.getStateManager()
                 .createAnimationToNewWorkspace(mToState, config);
         mCurrentAnimation.getTarget().addListener(mClearStateOnCancelListener);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
index c2e5cda..70b3870 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
@@ -167,6 +167,7 @@
                                 mAllowGoingDown = true;
                                 directionsToDetectScroll = DIRECTION_BOTH;
                             } else {
+                                mAllowGoingDown = false;
                                 directionsToDetectScroll = upDirection;
                             }
                         }
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index ecaac94..fe10908 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -47,7 +47,6 @@
 import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE;
 import static com.android.quickstep.util.SwipePipToHomeAnimator.FRACTION_END;
 import static com.android.quickstep.util.SwipePipToHomeAnimator.FRACTION_START;
-import static com.android.quickstep.views.RecentsView.RECENTS_GRID_PROGRESS;
 import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
@@ -55,7 +54,6 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.annotation.TargetApi;
 import android.app.ActivityManager;
@@ -340,6 +338,10 @@
     }
 
     protected boolean onActivityInit(Boolean alreadyOnHome) {
+        if (mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)) {
+            return false;
+        }
+
         T createdActivity = mActivityInterface.getCreatedActivity();
         if (createdActivity != null) {
             initTransitionEndpoints(createdActivity.getDeviceProfile());
@@ -464,8 +466,6 @@
                     mDeviceState.getRotationTouchHelper()
                             .onEndTargetCalculated(mGestureState.getEndTarget(),
                                     mActivityInterface);
-
-                    mRecentsView.onGestureEndTargetCalculated(mGestureState.getEndTarget());
                 });
 
         notifyGestureStartedAsync();
@@ -571,6 +571,8 @@
                 }
             });
             reapplyWindowTransformAnim.setDuration(RECENTS_ATTACH_DURATION).start();
+            mStateCallback.runOnceAtState(STATE_HANDLER_INVALIDATED,
+                    reapplyWindowTransformAnim::cancel);
         } else {
             applyWindowTransform();
         }
@@ -1133,6 +1135,10 @@
             }
             homeAnimFactory.playAtomicAnimation(velocityPxPerMs.y);
             mLauncherTransitionController = null;
+
+            if (mRecentsView != null) {
+                mRecentsView.onPrepareGestureEndAnimation(null, mGestureState.getEndTarget());
+            }
         } else {
             AnimatorSet animatorSet = new AnimatorSet();
             ValueAnimator windowAnim = mCurrentShift.animateToValue(start, end);
@@ -1171,9 +1177,9 @@
                 }
             });
             animatorSet.play(windowAnim);
-            S state = mActivityInterface.stateFromGestureEndTarget(mGestureState.getEndTarget());
-            if (mRecentsView != null && state.displayOverviewTasksAsGrid(mDp)) {
-                animatorSet.play(ObjectAnimator.ofFloat(mRecentsView, RECENTS_GRID_PROGRESS, 1));
+            if (mRecentsView != null) {
+                mRecentsView.onPrepareGestureEndAnimation(
+                        animatorSet, mGestureState.getEndTarget());
             }
             animatorSet.setDuration(duration).setInterpolator(interpolator);
             animatorSet.start();
@@ -1376,12 +1382,6 @@
         mActivityInitListener.unregister();
         ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mActivityRestartListener);
         mTaskSnapshot = null;
-        mHandler.post(() -> {
-            // Defer clearing the activity since invalidation can happen over multiple callbacks
-            // ie. invalidateHandlerWithLauncher()
-            mActivity = null;
-            mRecentsView = null;
-        });
     }
 
     private void invalidateHandlerWithLauncher() {
@@ -1392,6 +1392,12 @@
 
         mRecentsView.removeOnScrollChangedListener(mOnRecentsScrollListener);
         resetLauncherListeners();
+
+        mHandler.post(() -> {
+            // Defer clearing the activity since invalidation can happen over multiple callbacks.
+            mActivity = null;
+            mRecentsView = null;
+        });
     }
 
     private void endLauncherTransitionController() {
@@ -1701,7 +1707,7 @@
         // No need to apply any transform if there is ongoing swipe-pip-to-home animator since
         // that animator handles the leash solely.
         if (mRecentsAnimationTargets != null && !mIsSwipingPipToHome) {
-            if (mRecentsViewScrollLinked) {
+            if (mRecentsViewScrollLinked && mRecentsView != null) {
                 mTaskViewSimulator.setScroll(mRecentsView.getScrollOffset());
             }
             mTaskViewSimulator.apply(mTransformParams);
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 7aa81d4..b60b1be 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -23,8 +23,8 @@
 import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
 import static com.android.quickstep.SysUINavigationMode.getMode;
 import static com.android.quickstep.util.RecentsAtomicAnimationFactory.INDEX_RECENTS_FADE_ANIM;
-import static com.android.quickstep.util.RecentsAtomicAnimationFactory.INDEX_RECENTS_TRANSLATE_X_ANIM;
-import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET;
+import static com.android.quickstep.util.RecentsAtomicAnimationFactory.INDEX_RECENTS_TRANSLATE_Y_ANIM;
+import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_VERTICAL_OFFSET;
 import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
 import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
 import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION;
@@ -469,17 +469,17 @@
             float fromTranslation = attached ? 1 : 0;
             float toTranslation = attached ? 0 : 1;
             mActivity.getStateManager()
-                    .cancelStateElementAnimation(INDEX_RECENTS_TRANSLATE_X_ANIM);
+                    .cancelStateElementAnimation(INDEX_RECENTS_TRANSLATE_Y_ANIM);
             if (!recentsView.isShown() && animate) {
-                ADJACENT_PAGE_OFFSET.set(recentsView, fromTranslation);
+                ADJACENT_PAGE_VERTICAL_OFFSET.set(recentsView, fromTranslation);
             } else {
-                fromTranslation = ADJACENT_PAGE_OFFSET.get(recentsView);
+                fromTranslation = ADJACENT_PAGE_VERTICAL_OFFSET.get(recentsView);
             }
             if (!animate) {
-                ADJACENT_PAGE_OFFSET.set(recentsView, toTranslation);
+                ADJACENT_PAGE_VERTICAL_OFFSET.set(recentsView, toTranslation);
             } else {
                 mActivity.getStateManager().createStateElementAnimation(
-                        INDEX_RECENTS_TRANSLATE_X_ANIM,
+                        INDEX_RECENTS_TRANSLATE_Y_ANIM,
                         fromTranslation, toTranslation).start();
             }
 
diff --git a/quickstep/src/com/android/quickstep/FallbackActivityInterface.java b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
index 9fa65d9..3c81d1b 100644
--- a/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
@@ -163,7 +163,9 @@
 
     @Override
     public boolean isInLiveTileMode() {
-        return false;
+        RecentsActivity activity = getCreatedActivity();
+        return activity != null && activity.getStateManager().getState() == DEFAULT &&
+                activity.isStarted();
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
index 2ea34d7..46cd8a2 100644
--- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
+++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java
@@ -30,9 +30,11 @@
 import android.animation.AnimatorSet;
 import android.animation.ValueAnimator;
 import android.content.Context;
+import android.graphics.Rect;
 import android.graphics.RectF;
 import android.os.IBinder;
 import android.os.UserHandle;
+import android.util.Size;
 import android.view.View;
 
 import androidx.annotation.NonNull;
@@ -50,9 +52,11 @@
 import com.android.launcher3.util.DynamicResource;
 import com.android.launcher3.util.ObjectWrapper;
 import com.android.launcher3.views.FloatingIconView;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
 import com.android.quickstep.util.AppCloseConfig;
 import com.android.quickstep.util.RectFSpringAnim;
 import com.android.quickstep.util.StaggeredWorkspaceAnim;
+import com.android.quickstep.views.FloatingWidgetView;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskView;
 import com.android.systemui.plugins.ResourceProvider;
@@ -77,155 +81,207 @@
     @Override
     protected HomeAnimationFactory createHomeAnimationFactory(ArrayList<IBinder> launchCookies,
             long duration) {
-        HomeAnimationFactory homeAnimFactory;
-        if (mActivity != null) {
-            final View workspaceView = findWorkspaceView(launchCookies,
-                    mRecentsView.getRunningTaskView());
-            boolean canUseWorkspaceView =
-                    workspaceView != null && workspaceView.isAttachedToWindow();
-
-            mActivity.getRootView().setForceHideBackArrow(true);
-            mActivity.setHintUserWillBeActive();
-
-            if (canUseWorkspaceView) {
-                final ResourceProvider rp = DynamicResource.provider(mActivity);
-                final float transY = dpToPx(rp.getFloat(R.dimen.swipe_up_trans_y_dp));
-                float dpPerSecond = dpToPx(rp.getFloat(R.dimen.swipe_up_trans_y_dp_per_s));
-                final float launcherAlphaMax =
-                        rp.getFloat(R.dimen.swipe_up_launcher_alpha_max_progress);
-
-                RectF iconLocation = new RectF();
-                FloatingIconView floatingIconView = getFloatingIconView(mActivity, workspaceView,
-                        true /* hideOriginal */, iconLocation, false /* isOpening */);
-
-                // We want the window alpha to be 0 once this threshold is met, so that the
-                // FolderIconView can be seen morphing into the icon shape.
-                float windowAlphaThreshold = 1f - SHAPE_PROGRESS_DURATION;
-                homeAnimFactory = new LauncherHomeAnimationFactory() {
-
-                    // There is a delay in loading the icon, so we need to keep the window
-                    // opaque until it is ready.
-                    private boolean mIsFloatingIconReady = false;
-
-                    private @Nullable ValueAnimator mBounceBackAnimator;
-
-                    @Override
-                    public RectF getWindowTargetRect() {
-                        if (PROTOTYPE_APP_CLOSE.get()) {
-                            // We want the target rect to be at this offset position, so that all
-                            // launcher content can spring back upwards.
-                            floatingIconView.setPositionOffsetY(transY);
-                        }
-                        return iconLocation;
-                    }
-
-                    @Override
-                    public void setAnimation(RectFSpringAnim anim) {
-                        anim.addAnimatorListener(floatingIconView);
-                        floatingIconView.setOnTargetChangeListener(anim::onTargetPositionChanged);
-                        floatingIconView.setFastFinishRunnable(anim::end);
-                        if (PROTOTYPE_APP_CLOSE.get()) {
-                            mBounceBackAnimator = bounceBackToRestingPosition();
-                            // Use a spring to put drag layer translation back to 0.
-                            anim.addAnimatorListener(new AnimatorListenerAdapter() {
-                                @Override
-                                public void onAnimationEnd(Animator animation) {
-                                    floatingIconView.setPositionOffsetY(0);
-                                    mBounceBackAnimator.start();
-                                }
-                            });
-
-                            Workspace workspace = mActivity.getWorkspace();
-                            workspace.setPivotToScaleWithSelf(mActivity.getHotseat());
-                        }
-                    }
-
-                    private ValueAnimator bounceBackToRestingPosition() {
-                        DragLayer dl = mActivity.getDragLayer();
-                        Workspace workspace = mActivity.getWorkspace();
-                        Hotseat hotseat = mActivity.getHotseat();
-
-                        final float startValue = transY;
-                        final float endValue = 0;
-                        // Ensures the velocity is always aligned with the direction.
-                        float pixelPerSecond = Math.abs(dpPerSecond)
-                                * Math.signum(endValue - transY);
-
-                        ValueAnimator springTransY = new SpringAnimationBuilder(dl.getContext())
-                                .setStiffness(rp.getFloat(R.dimen.swipe_up_trans_y_stiffness))
-                                .setDampingRatio(rp.getFloat(R.dimen.swipe_up_trans_y_damping))
-                                .setMinimumVisibleChange(1f)
-                                .setStartValue(startValue)
-                                .setEndValue(endValue)
-                                .setStartVelocity(pixelPerSecond)
-                                .build(dl, VIEW_TRANSLATE_Y);
-                        springTransY.addListener(new AnimatorListenerAdapter() {
-                            @Override
-                            public void onAnimationEnd(Animator animation) {
-                                dl.setTranslationY(0f);
-                                dl.setAlpha(1f);
-                                SCALE_PROPERTY.set(workspace, 1f);
-                                SCALE_PROPERTY.set(hotseat, 1f);
-                            }
-                        });
-                        return springTransY;
-                    }
-
-                    @Override
-                    public boolean keepWindowOpaque() {
-                        if (mIsFloatingIconReady || floatingIconView.isVisibleToUser()) {
-                            mIsFloatingIconReady = true;
-                            return false;
-                        }
-                        return true;
-                    }
-
-                    @Override
-                    public void update(@Nullable AppCloseConfig config, RectF currentRect,
-                            float progress, float radius) {
-                        int fgAlpha = 255;
-                        if (config != null && PROTOTYPE_APP_CLOSE.get()) {
-                            DragLayer dl = mActivity.getDragLayer();
-                            float translationY = config.getWorkspaceTransY();
-                            dl.setTranslationY(translationY);
-
-                            float alpha = mapToRange(progress, 0, launcherAlphaMax, 0, 1f, LINEAR);
-                            dl.setAlpha(Math.min(alpha, 1f));
-
-                            float scale = Math.min(1f, config.getWorkspaceScale());
-                            SCALE_PROPERTY.set(mActivity.getWorkspace(), scale);
-                            SCALE_PROPERTY.set(mActivity.getHotseat(), scale);
-                            SCALE_PROPERTY.set(mActivity.getAppsView(), scale);
-
-                            progress = config.getInterpolatedProgress();
-                            fgAlpha = config.getFgAlpha();
-                        }
-                        floatingIconView.update(1f, fgAlpha, currentRect, progress,
-                                windowAlphaThreshold, radius, false);
-                    }
-
-                    @Override
-                    public void onCancel() {
-                        floatingIconView.fastFinish();
-                        if (mBounceBackAnimator != null) {
-                            mBounceBackAnimator.cancel();
-                        }
-                    }
-                };
-            } else {
-                homeAnimFactory = new LauncherHomeAnimationFactory();
-            }
-        } else {
-            homeAnimFactory = new HomeAnimationFactory() {
+        if (mActivity == null) {
+            mStateCallback.addChangeListener(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
+                    isPresent -> mRecentsView.startHome());
+            return new HomeAnimationFactory() {
                 @Override
                 public AnimatorPlaybackController createActivityAnimationToHome() {
                     return AnimatorPlaybackController.wrap(new AnimatorSet(), duration);
                 }
             };
-            mStateCallback.addChangeListener(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
-                    isPresent -> mRecentsView.startHome());
         }
-        return homeAnimFactory;
+
+        final View workspaceView = findWorkspaceView(launchCookies,
+                mRecentsView.getRunningTaskView());
+        boolean canUseWorkspaceView = workspaceView != null && workspaceView.isAttachedToWindow();
+
+        mActivity.getRootView().setForceHideBackArrow(true);
+        mActivity.setHintUserWillBeActive();
+
+        if (!canUseWorkspaceView) {
+            return new LauncherHomeAnimationFactory();
+        }
+        if (workspaceView instanceof LauncherAppWidgetHostView) {
+            return createWidgetHomeAnimationFactory((LauncherAppWidgetHostView) workspaceView);
+        }
+        return createIconHomeAnimationFactory(workspaceView);
+    }
+
+    private HomeAnimationFactory createIconHomeAnimationFactory(View workspaceView) {
+        final ResourceProvider rp = DynamicResource.provider(mActivity);
+        final float transY = dpToPx(rp.getFloat(R.dimen.swipe_up_trans_y_dp));
+        float dpPerSecond = dpToPx(rp.getFloat(R.dimen.swipe_up_trans_y_dp_per_s));
+        final float launcherAlphaMax =
+                rp.getFloat(R.dimen.swipe_up_launcher_alpha_max_progress);
+
+        RectF iconLocation = new RectF();
+        FloatingIconView floatingIconView = getFloatingIconView(mActivity, workspaceView,
+                true /* hideOriginal */, iconLocation, false /* isOpening */);
+
+        // We want the window alpha to be 0 once this threshold is met, so that the
+        // FolderIconView can be seen morphing into the icon shape.
+        float windowAlphaThreshold = 1f - SHAPE_PROGRESS_DURATION;
+        return new LauncherHomeAnimationFactory() {
+
+            // There is a delay in loading the icon, so we need to keep the window
+            // opaque until it is ready.
+            private boolean mIsFloatingIconReady = false;
+
+            private @Nullable ValueAnimator mBounceBackAnimator;
+
+            @Override
+            public RectF getWindowTargetRect() {
+                if (PROTOTYPE_APP_CLOSE.get()) {
+                    // We want the target rect to be at this offset position, so that all
+                    // launcher content can spring back upwards.
+                    floatingIconView.setPositionOffsetY(transY);
+                }
+                return iconLocation;
+            }
+
+            @Override
+            public void setAnimation(RectFSpringAnim anim) {
+                anim.addAnimatorListener(floatingIconView);
+                floatingIconView.setOnTargetChangeListener(anim::onTargetPositionChanged);
+                floatingIconView.setFastFinishRunnable(anim::end);
+                if (PROTOTYPE_APP_CLOSE.get()) {
+                    mBounceBackAnimator = bounceBackToRestingPosition();
+                    // Use a spring to put drag layer translation back to 0.
+                    anim.addAnimatorListener(new AnimatorListenerAdapter() {
+                        @Override
+                        public void onAnimationEnd(Animator animation) {
+                            floatingIconView.setPositionOffsetY(0);
+                            mBounceBackAnimator.start();
+                        }
+                    });
+
+                    Workspace workspace = mActivity.getWorkspace();
+                    workspace.setPivotToScaleWithSelf(mActivity.getHotseat());
+                }
+            }
+
+            private ValueAnimator bounceBackToRestingPosition() {
+                DragLayer dl = mActivity.getDragLayer();
+                Workspace workspace = mActivity.getWorkspace();
+                Hotseat hotseat = mActivity.getHotseat();
+
+                final float startValue = transY;
+                final float endValue = 0;
+                // Ensures the velocity is always aligned with the direction.
+                float pixelPerSecond = Math.abs(dpPerSecond) * Math.signum(endValue - transY);
+
+                ValueAnimator springTransY = new SpringAnimationBuilder(dl.getContext())
+                        .setStiffness(rp.getFloat(R.dimen.swipe_up_trans_y_stiffness))
+                        .setDampingRatio(rp.getFloat(R.dimen.swipe_up_trans_y_damping))
+                        .setMinimumVisibleChange(1f)
+                        .setStartValue(startValue)
+                        .setEndValue(endValue)
+                        .setStartVelocity(pixelPerSecond)
+                        .build(dl, VIEW_TRANSLATE_Y);
+                springTransY.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        dl.setTranslationY(0f);
+                        dl.setAlpha(1f);
+                        SCALE_PROPERTY.set(workspace, 1f);
+                        SCALE_PROPERTY.set(hotseat, 1f);
+                    }
+                });
+                return springTransY;
+            }
+
+            @Override
+            public boolean keepWindowOpaque() {
+                if (mIsFloatingIconReady || floatingIconView.isVisibleToUser()) {
+                    mIsFloatingIconReady = true;
+                    return false;
+                }
+                return true;
+            }
+
+            @Override
+            public void update(@Nullable AppCloseConfig config, RectF currentRect,
+                    float progress, float radius) {
+                int fgAlpha = 255;
+                if (config != null && PROTOTYPE_APP_CLOSE.get()) {
+                    DragLayer dl = mActivity.getDragLayer();
+                    float translationY = config.getWorkspaceTransY();
+                    dl.setTranslationY(translationY);
+
+                    float alpha = mapToRange(progress, 0, launcherAlphaMax, 0, 1f, LINEAR);
+                    dl.setAlpha(Math.min(alpha, 1f));
+
+                    float scale = Math.min(1f, config.getWorkspaceScale());
+                    SCALE_PROPERTY.set(mActivity.getWorkspace(), scale);
+                    SCALE_PROPERTY.set(mActivity.getHotseat(), scale);
+                    SCALE_PROPERTY.set(mActivity.getAppsView(), scale);
+
+                    progress = config.getInterpolatedProgress();
+                    fgAlpha = config.getFgAlpha();
+                }
+                floatingIconView.update(1f, fgAlpha, currentRect, progress,
+                        windowAlphaThreshold, radius, false);
+            }
+
+            @Override
+            public void onCancel() {
+                floatingIconView.fastFinish();
+                if (mBounceBackAnimator != null) {
+                    mBounceBackAnimator.cancel();
+                }
+            }
+        };
+    }
+
+    private HomeAnimationFactory createWidgetHomeAnimationFactory(
+            LauncherAppWidgetHostView hostView) {
+
+        RectF backgroundLocation = new RectF();
+        Rect crop = new Rect();
+        mTaskViewSimulator.getCurrentCropRect().roundOut(crop);
+        Size windowSize = new Size(crop.width(), crop.height());
+        FloatingWidgetView floatingWidgetView = FloatingWidgetView.getFloatingWidgetView(mActivity,
+                hostView, backgroundLocation, windowSize,
+                mTaskViewSimulator.getCurrentCornerRadius());
+
+        return new LauncherHomeAnimationFactory() {
+
+            @Override
+            public RectF getWindowTargetRect() {
+                return backgroundLocation;
+            }
+
+            @Override
+            public float getEndRadius(RectF cropRectF) {
+                return floatingWidgetView.getInitialCornerRadius();
+            }
+
+            @Override
+            public void setAnimation(RectFSpringAnim anim) {
+                anim.addAnimatorListener(floatingWidgetView);
+                floatingWidgetView.setOnTargetChangeListener(anim::onTargetPositionChanged);
+                floatingWidgetView.setFastFinishRunnable(anim::end);
+            }
+
+            @Override
+            public boolean keepWindowOpaque() {
+                return false;
+            }
+
+            @Override
+            public void update(@Nullable AppCloseConfig config, RectF currentRect,
+                    float progress, float radius) {
+                floatingWidgetView.update(currentRect, 1 /* floatingWidgetAlpha */,
+                        config != null ? config.getFgAlpha() : 1f /* foregroundAlpha */,
+                        0 /* fallbackBackgroundAlpha */, 1 - progress);
+            }
+
+            @Override
+            public void onCancel() {
+                floatingWidgetView.fastFinish();
+            }
+        };
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
index c47300c..62b821c 100644
--- a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
+++ b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
@@ -191,7 +191,7 @@
      * @see #enableMultipleRegions(boolean, Info)
      */
     void createOrAddTouchRegion(Info info) {
-        mCurrentDisplay = new CurrentDisplay(info.realSize, info.rotation);
+        mCurrentDisplay = new CurrentDisplay(info.currentSize, info.rotation);
 
         if (mQuickStepStartingRotation > QUICKSTEP_ROTATION_UNINITIALIZED
                 && mCurrentDisplay.rotation == mQuickStepStartingRotation) {
@@ -256,7 +256,7 @@
             Log.d(TAG, "clearing all regions except rotation: " + mCurrentDisplay.rotation);
         }
 
-        mCurrentDisplay = new CurrentDisplay(region.realSize, region.rotation);
+        mCurrentDisplay = new CurrentDisplay(region.currentSize, region.rotation);
         OrientationRectF regionToKeep = mSwipeTouchRegions.get(mCurrentDisplay);
         if (DEBUG) {
             Log.d(TestProtocol.NO_SWIPE_TO_HOME, "cached region: " + regionToKeep
@@ -289,7 +289,7 @@
             + " with mode: " + mMode + " displayRotation: " + display.rotation);
         }
 
-        Point size = display.realSize;
+        Point size = display.currentSize;
         int rotation = display.rotation;
         int touchHeight = mNavBarGesturalHeight;
         OrientationRectF orientationRectF =
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 3b92779..0e9e3ad 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -22,10 +22,13 @@
 import static com.android.launcher3.QuickstepTransitionManager.STATUS_BAR_TRANSITION_DURATION;
 import static com.android.launcher3.QuickstepTransitionManager.STATUS_BAR_TRANSITION_PRE_DELAY;
 import static com.android.launcher3.Utilities.createHomeIntent;
+import static com.android.launcher3.graphics.SysUiScrim.SYSUI_PROGRESS;
 import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
 import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
 import static com.android.quickstep.TaskViewUtils.createRecentsWindowAnimator;
+import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -35,6 +38,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
+import android.view.SurfaceControl.Transaction;
 import android.view.View;
 
 import androidx.annotation.Nullable;
@@ -47,6 +51,7 @@
 import com.android.launcher3.R;
 import com.android.launcher3.WrappedAnimationRunnerImpl;
 import com.android.launcher3.WrappedLauncherAnimationRunner;
+import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.compat.AccessibilityManagerCompat;
@@ -69,6 +74,7 @@
 import com.android.quickstep.util.RecentsAtomicAnimationFactory;
 import com.android.quickstep.util.SplitSelectStateController;
 import com.android.quickstep.views.OverviewActionsView;
+import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.SplitPlaceholderView;
 import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.system.ActivityOptionsCompat;
@@ -90,6 +96,8 @@
 
     private Handler mUiHandler = new Handler(Looper.getMainLooper());
 
+    private static final long HOME_APPEAR_DURATION = 250;
+
     private RecentsDragLayer mDragLayer;
     private ScrimView mScrimView;
     private FallbackRecentsView mFallbackRecentsView;
@@ -112,6 +120,7 @@
         mScrimView = findViewById(R.id.scrim_view);
         mFallbackRecentsView = findViewById(R.id.overview_panel);
         mActionsView = findViewById(R.id.overview_actions_view);
+        SYSUI_PROGRESS.set(getRootView().getSysUiScrim(), 0f);
 
         SplitPlaceholderView splitPlaceholderView = findViewById(R.id.split_placeholder);
         splitPlaceholderView.init(
@@ -291,6 +300,16 @@
         super.onConfigurationChanged(newConfig);
     }
 
+    @Override
+    public void onStateSetEnd(RecentsState state) {
+        super.onStateSetEnd(state);
+
+        if (state == RecentsState.DEFAULT) {
+            AccessibilityManagerCompat.sendStateEventToTest(getBaseContext(),
+                    OVERVIEW_STATE_ORDINAL);
+        }
+    }
+
     /**
      * Initialize/update the device profile.
      */
@@ -329,7 +348,42 @@
     }
 
     public void startHome() {
-        startActivity(createHomeIntent());
+        if (LIVE_TILE.get()) {
+            RecentsView recentsView = getOverviewPanel();
+            recentsView.switchToScreenshot(() -> recentsView.finishRecentsAnimation(true,
+                    this::startHomeInternal));
+        } else {
+            startHomeInternal();
+        }
+    }
+
+    private void startHomeInternal() {
+        WrappedLauncherAnimationRunner runner = new WrappedLauncherAnimationRunner(
+                getMainThreadHandler(), this::onCreateAnimationToHome, true);
+        RemoteAnimationAdapterCompat adapterCompat =
+                new RemoteAnimationAdapterCompat(runner, HOME_APPEAR_DURATION, 0);
+        startActivity(createHomeIntent(),
+                ActivityOptionsCompat.makeRemoteAnimation(adapterCompat).toBundle());
+    }
+
+    private void onCreateAnimationToHome(
+            int transit, RemoteAnimationTargetCompat[] appTargets,
+            RemoteAnimationTargetCompat[] wallpaperTargets,
+            RemoteAnimationTargetCompat[] nonAppTargets, AnimationResult result) {
+        AnimatorPlaybackController controller = getStateManager()
+                .createAnimationToNewWorkspace(RecentsState.BG_LAUNCHER, HOME_APPEAR_DURATION);
+        controller.dispatchOnStart();
+
+        RemoteAnimationTargets targets = new RemoteAnimationTargets(
+                appTargets, wallpaperTargets, nonAppTargets, MODE_OPENING);
+        for (RemoteAnimationTargetCompat app : targets.apps) {
+            new Transaction().setAlpha(app.leash.getSurfaceControl(), 1).apply();
+        }
+        AnimatorSet anim = new AnimatorSet();
+        anim.play(controller.getAnimationPlayer());
+        anim.setDuration(HOME_APPEAR_DURATION);
+        result.setAnimation(anim, this,
+                () -> getStateManager().goToState(RecentsState.HOME, false));
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index ef09957..e271203 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -18,7 +18,7 @@
 import static android.content.Intent.ACTION_USER_UNLOCKED;
 
 import static com.android.launcher3.util.DisplayController.CHANGE_ALL;
-import static com.android.launcher3.util.DisplayController.CHANGE_FRAME_DELAY;
+import static com.android.launcher3.util.DisplayController.CHANGE_ROTATION;
 import static com.android.launcher3.util.SettingsCache.ONE_HANDED_ENABLED;
 import static com.android.launcher3.util.SettingsCache.ONE_HANDED_SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED;
 import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
@@ -271,15 +271,9 @@
 
     @Override
     public void onDisplayInfoChanged(Context context, Info info, int flags) {
-        if (info.id != getDisplayId() || flags == CHANGE_FRAME_DELAY) {
-            // ignore displays that aren't running launcher and frame refresh rate changes
-            return;
+        if ((flags & CHANGE_ROTATION) != 0) {
+            mNavBarPosition = new NavBarPosition(mMode, info);
         }
-
-        if (!mMode.hasGestures) {
-            return;
-        }
-        mNavBarPosition = new NavBarPosition(mMode, info);
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index c786167..2eb9dd8 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -31,6 +31,7 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.launcher3.icons.IconProvider;
+import com.android.launcher3.icons.IconProvider.IconChangeListener;
 import com.android.launcher3.util.Executors.SimpleThreadFactory;
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.systemui.shared.recents.model.Task;
@@ -49,7 +50,7 @@
  * Singleton class to load and manage recents model.
  */
 @TargetApi(Build.VERSION_CODES.O)
-public class RecentsModel extends TaskStackChangeListener {
+public class RecentsModel extends TaskStackChangeListener implements IconChangeListener {
 
     // We do not need any synchronization for this variable as its only written on UI thread.
     public static final MainThreadInitializedObject<RecentsModel> INSTANCE =
@@ -69,12 +70,13 @@
         mContext = context;
         mTaskList = new RecentTasksList(MAIN_EXECUTOR,
                 new KeyguardManagerCompat(context), ActivityManagerWrapper.getInstance());
-        mIconCache = new TaskIconCache(context, RECENTS_MODEL_EXECUTOR);
+
+        IconProvider iconProvider = new IconProvider(context);
+        mIconCache = new TaskIconCache(context, RECENTS_MODEL_EXECUTOR, iconProvider);
         mThumbnailCache = new TaskThumbnailCache(context, RECENTS_MODEL_EXECUTOR);
 
         ActivityManagerWrapper.getInstance().registerTaskStackListener(this);
-        IconProvider.registerIconChangeListener(context,
-                this::onPackageIconChanged, MAIN_EXECUTOR.getHandler());
+        iconProvider.registerIconChangeListener(this, MAIN_EXECUTOR.getHandler());
     }
 
     public TaskIconCache getIconCache() {
@@ -183,17 +185,23 @@
         if (level == ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL) {
             // Clear everything once we reach a low-mem situation
             mThumbnailCache.clear();
-            mIconCache.clear();
+            mIconCache.clearCache();
         }
     }
 
-    private void onPackageIconChanged(String pkg, UserHandle user) {
-        mIconCache.invalidateCacheEntries(pkg, user);
+    @Override
+    public void onAppIconChanged(String packageName, UserHandle user) {
+        mIconCache.invalidateCacheEntries(packageName, user);
         for (int i = mThumbnailChangeListeners.size() - 1; i >= 0; i--) {
-            mThumbnailChangeListeners.get(i).onTaskIconChanged(pkg, user);
+            mThumbnailChangeListeners.get(i).onTaskIconChanged(packageName, user);
         }
     }
 
+    @Override
+    public void onSystemIconStateChanged(String iconState) {
+        mIconCache.clearCache();
+    }
+
     /**
      * Adds a listener for visuals changes
      */
diff --git a/quickstep/src/com/android/quickstep/RotationTouchHelper.java b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
index fd0de42..7629862 100644
--- a/quickstep/src/com/android/quickstep/RotationTouchHelper.java
+++ b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
@@ -17,8 +17,9 @@
 
 import static android.view.Surface.ROTATION_0;
 
+import static com.android.launcher3.util.DisplayController.CHANGE_ACTIVE_SCREEN;
 import static com.android.launcher3.util.DisplayController.CHANGE_ALL;
-import static com.android.launcher3.util.DisplayController.CHANGE_FRAME_DELAY;
+import static com.android.launcher3.util.DisplayController.CHANGE_ROTATION;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.quickstep.SysUINavigationMode.Mode.THREE_BUTTONS;
 
@@ -274,8 +275,7 @@
 
     @Override
     public void onDisplayInfoChanged(Context context, Info info, int flags) {
-        if (info.id != mDisplayId|| flags == CHANGE_FRAME_DELAY) {
-            // ignore displays that aren't running launcher and frame refresh rate changes
+        if ((flags & (CHANGE_ROTATION | CHANGE_ACTIVE_SCREEN)) == 0) {
             return;
         }
 
diff --git a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
index 0f34a72..29a00d1 100644
--- a/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
+++ b/quickstep/src/com/android/quickstep/SwipeUpAnimationLogic.java
@@ -145,6 +145,11 @@
                     targetX + halfIconSize, targetY + halfIconSize);
         }
 
+        /** Returns the corner radius of the window at the end of the animation. */
+        public float getEndRadius(RectF cropRectF) {
+            return cropRectF.width() / 2f;
+        }
+
         public abstract @NonNull AnimatorPlaybackController createActivityAnimationToHome();
 
         public void playAtomicAnimation(float velocity) {
@@ -197,8 +202,7 @@
         final RectF targetRect = homeAnimationFactory.getWindowTargetRect();
 
         Matrix homeToWindowPositionMap = new Matrix();
-        final RectF startRect = updateProgressForStartRect(
-                homeToWindowPositionMap, startProgress);
+        final RectF startRect = updateProgressForStartRect(homeToWindowPositionMap, startProgress);
         RectF cropRectF = new RectF(mTaskViewSimulator.getCurrentCropRect());
 
         // Move the startRect to Launcher space as floatingIconView runs in Launcher
@@ -210,7 +214,7 @@
         if (PROTOTYPE_APP_CLOSE.get()) {
             anim = new RectFSpringAnim2(startRect, targetRect, mContext,
                     mTaskViewSimulator.getCurrentCornerRadius(),
-                    cropRectF.width() / 2f);
+                    homeAnimationFactory.getEndRadius(cropRectF));
         } else {
             anim = new RectFSpringAnim(startRect, targetRect, mContext);
         }
@@ -269,7 +273,7 @@
             // End on a "round-enough" radius so that the shape reveal doesn't have to do too much
             // rounding at the end of the animation.
             mStartRadius = mTaskViewSimulator.getCurrentCornerRadius();
-            mEndRadius = cropRectF.width() / 2f;
+            mEndRadius = factory.getEndRadius(cropRectF);
         }
 
         @Override
diff --git a/quickstep/src/com/android/quickstep/SysUINavigationMode.java b/quickstep/src/com/android/quickstep/SysUINavigationMode.java
index efec037..74f4bea 100644
--- a/quickstep/src/com/android/quickstep/SysUINavigationMode.java
+++ b/quickstep/src/com/android/quickstep/SysUINavigationMode.java
@@ -34,6 +34,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
 
 /**
  * Observer for the resource config that specifies the navigation bar mode.
@@ -75,7 +76,8 @@
     private int mNavBarGesturalHeight;
     private int mNavBarLargerGesturalHeight;
 
-    private final List<NavigationModeChangeListener> mChangeListeners = new ArrayList<>();
+    private final List<NavigationModeChangeListener> mChangeListeners =
+            new CopyOnWriteArrayList<>();
     private final List<OneHandedModeChangeListener> mOneHandedOverlayChangeListeners =
             new ArrayList<>();
 
diff --git a/quickstep/src/com/android/quickstep/TaskIconCache.java b/quickstep/src/com/android/quickstep/TaskIconCache.java
index ba1c413..fa61fff 100644
--- a/quickstep/src/com/android/quickstep/TaskIconCache.java
+++ b/quickstep/src/com/android/quickstep/TaskIconCache.java
@@ -16,6 +16,7 @@
 package com.android.quickstep;
 
 import static com.android.launcher3.uioverrides.QuickstepLauncher.GO_LOW_RAM_RECENTS_ENABLED;
+import static com.android.launcher3.util.DisplayController.CHANGE_DENSITY;
 
 import android.app.ActivityManager.TaskDescription;
 import android.content.Context;
@@ -35,9 +36,12 @@
 
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.icons.BaseIconFactory;
 import com.android.launcher3.icons.BitmapInfo;
 import com.android.launcher3.icons.IconProvider;
-import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.util.DisplayController;
+import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
+import com.android.launcher3.util.DisplayController.Info;
 import com.android.launcher3.util.Preconditions;
 import com.android.quickstep.util.CancellableTask;
 import com.android.quickstep.util.TaskKeyLruCache;
@@ -52,7 +56,7 @@
 /**
  * Manages the caching of task icons and related data.
  */
-public class TaskIconCache {
+public class TaskIconCache implements DisplayInfoChangeListener {
 
     private final Executor mBgExecutor;
     private final AccessibilityManager mAccessibilityManager;
@@ -62,15 +66,27 @@
     private final SparseArray<BitmapInfo> mDefaultIcons = new SparseArray<>();
     private final IconProvider mIconProvider;
 
-    public TaskIconCache(Context context, Executor bgExecutor) {
+    private BaseIconFactory mIconFactory;
+
+    public TaskIconCache(Context context, Executor bgExecutor, IconProvider iconProvider) {
         mContext = context;
         mBgExecutor = bgExecutor;
         mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
+        mIconProvider = iconProvider;
 
         Resources res = context.getResources();
         int cacheSize = res.getInteger(R.integer.recentsIconCacheSize);
+
         mIconCache = new TaskKeyLruCache<>(cacheSize);
-        mIconProvider = new IconProvider(context);
+
+        DisplayController.INSTANCE.get(mContext).addChangeListener(this);
+    }
+
+    @Override
+    public void onDisplayInfoChanged(Context context, Info info, int flags) {
+        if ((flags & CHANGE_DENSITY) != 0) {
+            clearCache();
+        }
     }
 
     /**
@@ -104,8 +120,11 @@
         return request;
     }
 
-    public void clear() {
-        mIconCache.evictAll();
+    /**
+     * Clears the icon cache
+     */
+    public void clearCache() {
+        mBgExecutor.execute(this::resetFactory);
     }
 
     void onTaskRemoved(TaskKey taskKey) {
@@ -193,8 +212,8 @@
         synchronized (mDefaultIcons) {
             BitmapInfo info = mDefaultIcons.get(userId);
             if (info == null) {
-                try (LauncherIcons la = LauncherIcons.obtain(mContext)) {
-                    info = la.makeDefaultIcon(UserHandle.of(userId));
+                try (BaseIconFactory bif = getIconFactory()) {
+                    info = bif.makeDefaultIcon(UserHandle.of(userId));
                 }
                 mDefaultIcons.put(userId, info);
             }
@@ -205,16 +224,32 @@
     @WorkerThread
     private BitmapInfo getBitmapInfo(Drawable drawable, int userId,
             int primaryColor, boolean isInstantApp) {
-        try (LauncherIcons la = LauncherIcons.obtain(mContext)) {
-            la.disableColorExtraction();
-            la.setWrapperBackgroundColor(primaryColor);
+        try (BaseIconFactory bif = getIconFactory()) {
+            bif.disableColorExtraction();
+            bif.setWrapperBackgroundColor(primaryColor);
 
             // User version code O, so that the icon is always wrapped in an adaptive icon container
-            return la.createBadgedIconBitmap(drawable, UserHandle.of(userId),
+            return bif.createBadgedIconBitmap(drawable, UserHandle.of(userId),
                     Build.VERSION_CODES.O, isInstantApp);
         }
     }
 
+    @WorkerThread
+    private BaseIconFactory getIconFactory() {
+        if (mIconFactory == null) {
+            mIconFactory = new BaseIconFactory(mContext,
+                    DisplayController.INSTANCE.get(mContext).getInfo().densityDpi,
+                    mContext.getResources().getDimensionPixelSize(R.dimen.taskbar_icon_size));
+        }
+        return mIconFactory;
+    }
+
+    @WorkerThread
+    private void resetFactory() {
+        mIconFactory = null;
+        mIconCache.evictAll();
+    }
+
     private static class TaskCacheEntry {
         public Drawable icon;
         public String contentDescription = "";
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 4fc9770..5fe0fc7 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -52,6 +52,7 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.Looper;
+import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.util.Log;
@@ -145,7 +146,22 @@
     @Nullable
     private OverscrollPlugin mOverscrollPlugin;
 
-    private final IBinder mMyBinder = new IOverviewProxy.Stub() {
+    /**
+     * Extension of OverviewProxy aidl interface without needing to modify the actual interface.
+     * This is for methods that need only need local access and not intended to make IPC calls.
+     */
+    public abstract static class TISBinder extends IOverviewProxy.Stub {
+        public abstract void setTaskbarOverviewProxyDelegate(
+                @Nullable TaskbarOverviewProxyDelegate i);
+    }
+
+
+    private final TISBinder mMyBinder = new TISBinder() {
+
+        public void setTaskbarOverviewProxyDelegate(
+                @Nullable TaskbarOverviewProxyDelegate delegate) {
+            mTaskbarOverviewProxyDelegate = delegate;
+        }
 
         @BinderThread
         public void onInitialize(Bundle bundle) {
@@ -252,20 +268,49 @@
             MAIN_EXECUTOR.execute(() -> mDeviceState.setDeferredGestureRegion(region));
         }
 
+        @Override
         public void onSplitScreenSecondaryBoundsChanged(Rect bounds, Rect insets)  {
             WindowBounds wb = new WindowBounds(bounds, insets);
             MAIN_EXECUTOR.execute(() -> SplitScreenBounds.INSTANCE.setSecondaryWindowBounds(wb));
         }
+
+        @Override
+        public void onImeWindowStatusChanged(int displayId, IBinder token, int vis,
+                int backDisposition, boolean showImeSwitcher) throws RemoteException {
+            if (mTaskbarOverviewProxyDelegate == null) {
+                return;
+            }
+            MAIN_EXECUTOR.execute(() -> {
+                if (mTaskbarOverviewProxyDelegate == null) {
+                    return;
+                }
+                mTaskbarOverviewProxyDelegate
+                        .updateImeStatus(displayId, vis, backDisposition, showImeSwitcher);
+            });
+        }
     };
 
+    public interface TaskbarOverviewProxyDelegate {
+        void updateImeStatus(int displayId, int vis, int backDisposition,
+                boolean showImeSwitcher);
+    }
+
     private static boolean sConnected = false;
+    private static TouchInteractionService sInstance;
     private static boolean sIsInitialized = false;
     private RotationTouchHelper mRotationTouchHelper;
+    @Nullable
+    private TaskbarOverviewProxyDelegate mTaskbarOverviewProxyDelegate;
 
     public static boolean isConnected() {
         return sConnected;
     }
 
+    @Nullable
+    public static TouchInteractionService getInstance() {
+        return sInstance;
+    }
+
     public static boolean isInitialized() {
         return sIsInitialized;
     }
@@ -293,6 +338,10 @@
 
     private DisplayManager mDisplayManager;
 
+    public TouchInteractionService() {
+        sInstance = this;
+    }
+
     @Override
     public void onCreate() {
         super.onCreate();
@@ -389,6 +438,10 @@
         onOverviewTargetChange(mOverviewComponentObserver.isHomeAndOverviewSame());
     }
 
+    public OverviewCommandHelper getOverviewCommandHelper() {
+        return mOverviewCommandHelper;
+    }
+
     private void resetHomeBounceSeenOnQuickstepEnabledFirstTime() {
         if (!mDeviceState.isUserUnlocked() || mDeviceState.isButtonNavMode()) {
             // Skip if not yet unlocked (can't read user shared prefs) or if the current navigation
@@ -496,6 +549,13 @@
             Point sz = new Point();
             display.getRealSize(sz);
             if (rotation != Surface.ROTATION_0) {
+                if ((rotation % 2) != 0) {
+                    // via display-manager, the display size is unrotated, so "rotate" its size
+                    // to match the rotation we are transforming the event into.
+                    final int tmpX = sz.x;
+                    sz.x = sz.y;
+                    sz.y = tmpX;
+                }
                 event.transform(InputChannelCompat.createRotationMatrix(rotation, sz.x, sz.y));
             }
         }
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
index 7067dbc..4d776ba 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
@@ -20,9 +20,10 @@
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_SCRIM_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_SCRIM_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
-import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET;
+import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
+import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_VERTICAL_OFFSET;
 import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
 import static com.android.quickstep.views.RecentsView.RECENTS_GRID_PROGRESS;
 import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
@@ -86,8 +87,10 @@
         float[] scaleAndOffset = state.getOverviewScaleAndOffset(mActivity);
         setter.setFloat(mRecentsView, RECENTS_SCALE_PROPERTY, scaleAndOffset[0],
                 config.getInterpolator(ANIM_OVERVIEW_SCALE, LINEAR));
-        setter.setFloat(mRecentsView, ADJACENT_PAGE_OFFSET, scaleAndOffset[1],
+        setter.setFloat(mRecentsView, ADJACENT_PAGE_HORIZONTAL_OFFSET, scaleAndOffset[1],
                 config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_X, LINEAR));
+        setter.setFloat(mRecentsView, ADJACENT_PAGE_VERTICAL_OFFSET, scaleAndOffset[2],
+                config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, LINEAR));
         setter.setFloat(mRecentsView, TASK_SECONDARY_TRANSLATION, 0f,
                 config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, LINEAR));
 
@@ -98,6 +101,6 @@
                 state.displayOverviewTasksAsGrid(mActivity.getDeviceProfile()) ? 1f : 0f, LINEAR);
 
         setter.setViewBackgroundColor(mActivity.getScrimView(), state.getScrimColor(mActivity),
-                config.getInterpolator(ANIM_WORKSPACE_SCRIM_FADE, LINEAR));
+                config.getInterpolator(ANIM_SCRIM_FADE, LINEAR));
     }
 }
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index 4d4b6e1..1bb8e96 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -20,6 +20,7 @@
 import static com.android.quickstep.fallback.RecentsState.HOME;
 import static com.android.quickstep.fallback.RecentsState.MODAL_TASK;
 
+import android.animation.AnimatorSet;
 import android.annotation.TargetApi;
 import android.app.ActivityManager.RunningTaskInfo;
 import android.content.Context;
@@ -27,6 +28,9 @@
 import android.util.AttributeSet;
 import android.util.Log;
 
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.statemanager.StateManager.StateListener;
 import com.android.launcher3.testing.TestProtocol;
@@ -67,7 +71,6 @@
     @Override
     public void startHome() {
         mActivity.startHome();
-        mActivity.getStateManager().goToState(RecentsState.HOME);
     }
 
     /**
@@ -86,14 +89,17 @@
      * to the center.
      */
     @Override
-    public void onGestureEndTargetCalculated(GestureState.GestureEndTarget endTarget) {
-        super.onGestureEndTargetCalculated(endTarget);
-        if (mHomeTaskInfo != null && endTarget == RECENTS) {
+    public void onPrepareGestureEndAnimation(
+            @Nullable AnimatorSet animatorSet, GestureState.GestureEndTarget endTarget) {
+        super.onPrepareGestureEndAnimation(animatorSet, endTarget);
+        if (mHomeTaskInfo != null && endTarget == RECENTS && animatorSet != null) {
             TaskView tv = getTaskView(mHomeTaskInfo.taskId);
             if (tv != null) {
                 PendingAnimation pa = createTaskDismissAnimation(tv, true, false, 150);
                 pa.addEndListener(e -> setCurrentTask(-1));
-                runDismissAnimation(pa);
+                AnimatorPlaybackController controller = pa.createPlaybackController();
+                controller.dispatchOnStart();
+                animatorSet.play(controller.getAnimationPlayer());
             }
         }
     }
diff --git a/quickstep/src/com/android/quickstep/fallback/RecentsState.java b/quickstep/src/com/android/quickstep/fallback/RecentsState.java
index b3d6cfa..532f219 100644
--- a/quickstep/src/com/android/quickstep/fallback/RecentsState.java
+++ b/quickstep/src/com/android/quickstep/fallback/RecentsState.java
@@ -39,15 +39,18 @@
     private static final int FLAG_OVERVIEW_ACTIONS = BaseState.getFlag(3);
     private static final int FLAG_SHOW_AS_GRID = BaseState.getFlag(4);
     private static final int FLAG_SCRIM = BaseState.getFlag(5);
+    private static final int FLAG_LIVE_TILE = BaseState.getFlag(6);
 
     public static final RecentsState DEFAULT = new RecentsState(0,
-            FLAG_CLEAR_ALL_BUTTON | FLAG_OVERVIEW_ACTIONS | FLAG_SHOW_AS_GRID | FLAG_SCRIM);
+            FLAG_CLEAR_ALL_BUTTON | FLAG_OVERVIEW_ACTIONS | FLAG_SHOW_AS_GRID | FLAG_SCRIM
+                    | FLAG_LIVE_TILE);
     public static final RecentsState MODAL_TASK = new ModalState(1,
             FLAG_DISABLE_RESTORE | FLAG_CLEAR_ALL_BUTTON | FLAG_OVERVIEW_ACTIONS | FLAG_MODAL
-                    | FLAG_SHOW_AS_GRID | FLAG_SCRIM);
+                    | FLAG_SHOW_AS_GRID | FLAG_SCRIM | FLAG_LIVE_TILE);
     public static final RecentsState BACKGROUND_APP = new BackgroundAppState(2,
             FLAG_DISABLE_RESTORE | FLAG_NON_INTERACTIVE | FLAG_FULL_SCREEN);
     public static final RecentsState HOME = new RecentsState(3, 0);
+    public static final RecentsState BG_LAUNCHER = new LauncherState(4, 0);
 
     public final int ordinal;
     private final int mFlags;
@@ -108,6 +111,13 @@
     }
 
     /**
+     * For this state, whether live tile should be shown.
+     */
+    public boolean hasLiveTile() {
+        return hasFlag(FLAG_LIVE_TILE);
+    }
+
+    /**
      * For this state, what color scrim should be drawn behind overview.
      */
     public int getScrimColor(RecentsActivity activity) {
@@ -116,7 +126,7 @@
     }
 
     public float[] getOverviewScaleAndOffset(RecentsActivity activity) {
-        return new float[] { NO_SCALE, NO_OFFSET };
+        return new float[] { NO_SCALE, NO_OFFSET, NO_OFFSET };
     }
 
     /**
@@ -152,4 +162,15 @@
             return getOverviewScaleAndOffsetForBackgroundState(activity);
         }
     }
+
+    private static class LauncherState extends RecentsState {
+        LauncherState(int id, int flags) {
+            super(id, flags);
+        }
+
+        @Override
+        public float[] getOverviewScaleAndOffset(RecentsActivity activity) {
+            return new float[] { NO_SCALE, NO_OFFSET, 1 };
+        }
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/fallback/RecentsTaskController.java b/quickstep/src/com/android/quickstep/fallback/RecentsTaskController.java
index d7458d2..273d1f6 100644
--- a/quickstep/src/com/android/quickstep/fallback/RecentsTaskController.java
+++ b/quickstep/src/com/android/quickstep/fallback/RecentsTaskController.java
@@ -15,6 +15,8 @@
  */
 package com.android.quickstep.fallback;
 
+import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE;
+
 import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController;
 import com.android.quickstep.RecentsActivity;
 
@@ -26,7 +28,8 @@
 
     @Override
     protected boolean isRecentsInteractive() {
-        return mActivity.hasWindowFocus();
+        return mActivity.hasWindowFocus() || (LIVE_TILE.get()
+                && mActivity.getStateManager().getState().hasLiveTile());
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
index 9c64794..fcc0217 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
@@ -116,7 +116,7 @@
                 R.dimen.device_locked_y_offset);
 
         // Do not use DeviceProfile as the user data might be locked
-        mDisplaySize = DisplayController.INSTANCE.get(context).getInfo().realSize;
+        mDisplaySize = DisplayController.INSTANCE.get(context).getInfo().currentSize;
 
         // Init states
         mStateCallback = new MultiStateCallback(STATE_NAMES);
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java
index d82d43d..8d9c524 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OneHandedModeInputConsumer.java
@@ -70,7 +70,7 @@
         mDragDistThreshold = context.getResources().getDimensionPixelSize(
                 R.dimen.gestures_onehanded_drag_threshold);
         mSquaredSlop = Utilities.squaredTouchSlop(context);
-        mDisplaySize = DisplayController.INSTANCE.get(mContext).getInfo().realSize;
+        mDisplaySize = DisplayController.INSTANCE.get(mContext).getInfo().currentSize;
         mNavBarSize = ResourceUtils.getNavbarSize(NAVBAR_BOTTOM_GESTURE_SIZE,
                 mContext.getResources());
     }
diff --git a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
new file mode 100644
index 0000000..76f43c9
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.interaction;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.graphics.Typeface;
+import android.os.Bundle;
+import android.text.TextPaint;
+import android.text.method.LinkMovementMethod;
+import android.util.Log;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.R;
+
+import java.net.URISyntaxException;
+
+/**
+ * A page shows after SUW flow to hint users to swipe up from the bottom of the screen to go home
+ * for the gestural system navigation.
+ */
+public class AllSetActivity extends Activity {
+
+    private static final String LOG_TAG = "AllSetActivity";
+    private static final String URI_SYSTEM_NAVIGATION_SETTING =
+            "#Intent;action=com.android.settings.SEARCH_RESULT_TRAMPOLINE;S.:settings:fragment_args_key=gesture_system_navigation_input_summary;S.:settings:show_fragment=com.android.settings.gestures.SystemNavigationGestureSettings;end";
+    private static final String EXTRA_ACCENT_COLOR_DARK_MODE = "accent_color_dark_mode";
+    private static final String EXTRA_ACCENT_COLOR_LIGHT_MODE = "accent_color_light_mode";
+
+    private int mAccentColor;
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_allset);
+        setTitle(R.string.allset_title);
+
+        final int mode =
+                getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
+        mAccentColor = getIntent().getIntExtra(
+                mode == Configuration.UI_MODE_NIGHT_YES
+                        ? EXTRA_ACCENT_COLOR_DARK_MODE : EXTRA_ACCENT_COLOR_LIGHT_MODE,
+                /* defValue= */ Color.BLACK);
+
+        ((ImageView) findViewById(R.id.icon)).getDrawable().mutate().setTint(mAccentColor);
+
+        TextView navigationSettings = findViewById(R.id.navigation_settings);
+        navigationSettings.setMovementMethod(LinkMovementMethod.getInstance());
+        AnnotationSpan.LinkInfo linkInfo = new AnnotationSpan.LinkInfo(
+                AnnotationSpan.LinkInfo.DEFAULT_ANNOTATION,
+                new AllSetLinkSpan(
+                        /* context= */ this,
+                        view -> {
+                            try {
+                                startActivityForResult(
+                                        Intent.parseUri(URI_SYSTEM_NAVIGATION_SETTING, 0), 0);
+                            } catch (URISyntaxException e) {
+                                Log.e(LOG_TAG, "Failed to parse system nav settings intent", e);
+                            }
+                            finish();
+                        }));
+        navigationSettings.setText(
+                AnnotationSpan.linkify(getText(R.string.allset_navigation_settings), linkInfo));
+    }
+
+    @Override
+    public void onWindowFocusChanged(boolean hasFocus) {
+        super.onWindowFocusChanged(hasFocus);
+        if (hasFocus) {
+            hideSystemUI();
+        }
+    }
+
+    private void hideSystemUI() {
+        getWindow().getDecorView().setSystemUiVisibility(
+                View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+                        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+                        | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+                        | View.SYSTEM_UI_FLAG_FULLSCREEN);
+        getWindow().setNavigationBarColor(Color.TRANSPARENT);
+    }
+
+    private final class AllSetLinkSpan extends AnnotationSpan {
+
+        private final String mFontFamily;
+        private final int mTextSize;
+
+        AllSetLinkSpan(Context context, View.OnClickListener listener) {
+            super(listener);
+            TypedArray typedArray =
+                    context.obtainStyledAttributes(R.style.TextAppearance_GestureTutorial_LinkText,
+                            R.styleable.AllSetLinkSpan);
+            mFontFamily = typedArray.getString(R.styleable.AllSetLinkSpan_android_fontFamily);
+            mTextSize =
+                    typedArray.getDimensionPixelSize(
+                            R.styleable.AllSetLinkSpan_android_textSize, /* defValue= */ -1);
+            typedArray.recycle();
+        }
+
+        @Override
+        public void updateDrawState(TextPaint ds) {
+            super.updateDrawState(ds);
+            ds.setColor(mAccentColor);
+            ds.setTypeface(Typeface.create(mFontFamily, Typeface.NORMAL));
+            ds.setUnderlineText(false);
+            if (mTextSize != -1) {
+                ds.setTextSize(mTextSize);
+            }
+        }
+    }
+
+}
diff --git a/quickstep/src/com/android/quickstep/interaction/AnnotationSpan.java b/quickstep/src/com/android/quickstep/interaction/AnnotationSpan.java
new file mode 100644
index 0000000..fea5078
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/interaction/AnnotationSpan.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2018 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.quickstep.interaction;
+
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.text.Annotation;
+import android.text.SpannableString;
+import android.text.SpannableStringBuilder;
+import android.text.style.URLSpan;
+import android.util.Log;
+import android.view.View;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+/**
+ * This class is used to add {@link View.OnClickListener} for the text been wrapped by
+ * annotation.
+ *
+ * Copied from packages/apps/Settings/src/com/android/settings/utils/AnnotationSpan.java.
+ */
+public class AnnotationSpan extends URLSpan {
+
+    private final View.OnClickListener mClickListener;
+
+    AnnotationSpan(View.OnClickListener lsn) {
+        super((String) null);
+        mClickListener = lsn;
+    }
+
+    @Override
+    public void onClick(View widget) {
+        if (mClickListener != null) {
+            mClickListener.onClick(widget);
+        }
+    }
+
+    public static CharSequence linkify(CharSequence rawText, LinkInfo... linkInfos) {
+        SpannableString msg = new SpannableString(rawText);
+        Annotation[] spans = msg.getSpans(0, msg.length(), Annotation.class);
+        SpannableStringBuilder builder = new SpannableStringBuilder(msg);
+        for (Annotation annotation : spans) {
+            final String key = annotation.getValue();
+            int start = msg.getSpanStart(annotation);
+            int end = msg.getSpanEnd(annotation);
+            AnnotationSpan link = null;
+            for (LinkInfo linkInfo : linkInfos) {
+                if (linkInfo.mAnnotation.equals(key)) {
+                    link = linkInfo.mCustomizedSpan != null ? linkInfo.mCustomizedSpan
+                            : new AnnotationSpan(linkInfo.mClickListener);
+                    break;
+                }
+            }
+            if (link != null) {
+                builder.setSpan(link, start, end, msg.getSpanFlags(link));
+            }
+        }
+        return builder;
+    }
+
+    /**
+     * get the text part without having text for link part
+     */
+    public static CharSequence textWithoutLink(CharSequence encodedText) {
+        SpannableString msg = new SpannableString(encodedText);
+        Annotation[] spans = msg.getSpans(0, msg.length(), Annotation.class);
+        if (spans == null) {
+            return encodedText;
+        }
+        Arrays.sort(spans, Comparator.comparingInt(span -> -msg.getSpanStart(span)));
+        StringBuilder msgWithoutLink = new StringBuilder(msg.toString());
+        for (Annotation span : spans) {
+            msgWithoutLink.delete(msg.getSpanStart(span), msg.getSpanEnd(span));
+        }
+        return msgWithoutLink.toString();
+    }
+
+    /** Data class to store the annotation and the click action. */
+    public static class LinkInfo {
+        public static final String DEFAULT_ANNOTATION = "link";
+        private static final String TAG = "AnnotationSpan.LinkInfo";
+        private final String mAnnotation;
+        private final Boolean mActionable;
+        private final View.OnClickListener mClickListener;
+        private final AnnotationSpan mCustomizedSpan;
+
+        public LinkInfo(String annotation, View.OnClickListener listener) {
+            mAnnotation = annotation;
+            mClickListener = listener;
+            mActionable = true; // assume actionable
+            mCustomizedSpan = null;
+        }
+
+        public LinkInfo(String annotation, AnnotationSpan customizedSpan) {
+            mAnnotation = annotation;
+            mClickListener = null;
+            mActionable = customizedSpan != null;
+            mCustomizedSpan = customizedSpan;
+        }
+
+        public LinkInfo(Context context, String annotation, Intent intent) {
+            mAnnotation = annotation;
+            mCustomizedSpan = null;
+            if (intent != null) {
+                mActionable = context.getPackageManager().resolveActivity(intent, 0) != null;
+            } else {
+                mActionable = false;
+            }
+            if (mActionable) {
+                mClickListener =
+                        view -> {
+                            try {
+                                context.startActivity(intent);
+                            } catch (ActivityNotFoundException e) {
+                                Log.w(TAG, "Activity was not found for intent, " + intent);
+                            }
+                        };
+            } else {
+                mClickListener = null;
+            }
+        }
+
+        public boolean isActionable() {
+            return mActionable;
+        }
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
index c396eec..14c3a92 100644
--- a/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/BackGestureTutorialController.java
@@ -20,9 +20,7 @@
 import static com.android.quickstep.interaction.TutorialController.TutorialType.RIGHT_EDGE_BACK_NAVIGATION;
 
 import android.graphics.PointF;
-import android.view.View;
 
-import androidx.annotation.Nullable;
 import androidx.appcompat.content.res.AppCompatResources;
 
 import com.android.launcher3.R;
@@ -36,11 +34,6 @@
         super(fragment, tutorialType);
     }
 
-    @Nullable
-    public View getMockLauncherView() {
-        return null;
-    }
-
     @Override
     public Integer getIntroductionTitle() {
         return mTutorialType == LEFT_EDGE_BACK_NAVIGATION
diff --git a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
index 3c59ed3..06610e6 100644
--- a/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/OverviewGestureTutorialController.java
@@ -16,15 +16,11 @@
 package com.android.quickstep.interaction;
 
 import static com.android.launcher3.anim.Interpolators.ACCEL;
-import static com.android.quickstep.interaction.TutorialController.TutorialType.OVERVIEW_NAVIGATION_COMPLETE;
 
 import android.animation.AnimatorSet;
 import android.annotation.TargetApi;
 import android.graphics.PointF;
 import android.os.Build;
-import android.view.View;
-
-import androidx.annotation.Nullable;
 
 import com.android.launcher3.R;
 import com.android.launcher3.anim.PendingAnimation;
@@ -52,12 +48,6 @@
         return R.string.overview_gesture_intro_subtitle;
     }
 
-    @Nullable
-    @Override
-    public View getMockLauncherView() {
-        return null;
-    }
-
     @Override
     public void onBackGestureAttempted(BackGestureResult result) {
         switch (mTutorialType) {
diff --git a/quickstep/src/com/android/quickstep/interaction/SandboxLauncherRenderer.java b/quickstep/src/com/android/quickstep/interaction/SandboxLauncherRenderer.java
deleted file mode 100644
index 80ffe66..0000000
--- a/quickstep/src/com/android/quickstep/interaction/SandboxLauncherRenderer.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep.interaction;
-
-import android.content.Context;
-import android.view.View;
-
-import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.graphics.LauncherPreviewRenderer;
-
-/** Renders a fake Launcher for use in the Sandbox. */
-class SandboxLauncherRenderer extends LauncherPreviewRenderer {
-    SandboxLauncherRenderer(Context context, InvariantDeviceProfile idp, boolean migrated) {
-        super(context, idp, migrated);
-    }
-
-    @Override
-    public boolean shouldShowRealLauncherPreview() {
-        return false;
-    }
-
-    @Override
-    public boolean shouldShowQsb() {
-        return false;
-    }
-
-    @Override
-    public View.OnLongClickListener getWorkspaceChildOnLongClickListener() {
-        return null;
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialController.java b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
index 6f82f85..55972ad 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
@@ -37,7 +37,6 @@
 import androidx.appcompat.app.AlertDialog;
 import androidx.appcompat.content.res.AppCompatResources;
 
-import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.R;
 import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.views.ClipIconView;
@@ -129,13 +128,6 @@
         return R.drawable.default_sandbox_app_previous_task_thumbnail;
     }
 
-    @Nullable
-    public View getMockLauncherView() {
-        InvariantDeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext);
-
-        return new SandboxLauncherRenderer(mContext, dp, true).getRenderedView();
-    }
-
     @DrawableRes
     public int getMockAppIconResId() {
         return R.drawable.default_sandbox_app_icon;
diff --git a/quickstep/src/com/android/quickstep/util/AssistContentRequester.java b/quickstep/src/com/android/quickstep/util/AssistContentRequester.java
index 3730284..71c6382 100644
--- a/quickstep/src/com/android/quickstep/util/AssistContentRequester.java
+++ b/quickstep/src/com/android/quickstep/util/AssistContentRequester.java
@@ -28,6 +28,10 @@
 
 import com.android.launcher3.util.Executors;
 
+import java.lang.ref.WeakReference;
+import java.util.Collections;
+import java.util.Map;
+import java.util.WeakHashMap;
 import java.util.concurrent.Executor;
 
 /**
@@ -51,6 +55,10 @@
     private final String mPackageName;
     private final Executor mCallbackExecutor;
 
+    // If system loses the callback, our internal cache of original callback will also get cleared.
+    private final Map<Object, Callback> mPendingCallbacks =
+            Collections.synchronizedMap(new WeakHashMap<>());
+
     public AssistContentRequester(Context context) {
         mActivityTaskManager = ActivityTaskManager.getService();
         mPackageName = context.getApplicationContext().getPackageName();
@@ -66,20 +74,28 @@
     public void requestAssistContent(int taskId, Callback callback) {
         try {
             mActivityTaskManager.requestAssistDataForTask(
-                    new AssistDataReceiver(callback, mCallbackExecutor), taskId, mPackageName);
+                    new AssistDataReceiver(callback, this), taskId, mPackageName);
         } catch (RemoteException e) {
             Log.e(TAG, "Requesting assist content failed for task: " + taskId, e);
         }
     }
 
+    private void executeOnMainExecutor(Runnable callback) {
+        mCallbackExecutor.execute(callback);
+    }
+
     private static final class AssistDataReceiver extends IAssistDataReceiver.Stub {
 
-        private final Executor mExecutor;
-        private final Callback mCallback;
+        // The AssistDataReceiver binder callback object is passed to a system server, that may
+        // keep hold of it for longer than the lifetime of the AssistContentRequester object,
+        // potentially causing a memory leak. In the callback passed to the system server, only
+        // keep a weak reference to the parent object and lookup its callback if it still exists.
+        private final WeakReference<AssistContentRequester> mParentRef;
+        private final Object mCallbackKey = new Object();
 
-        AssistDataReceiver(Callback callback, Executor callbackExecutor) {
-            mCallback = callback;
-            mExecutor = callbackExecutor;
+        AssistDataReceiver(Callback callback, AssistContentRequester parent) {
+            parent.mPendingCallbacks.put(mCallbackKey, callback);
+            mParentRef = new WeakReference<>(parent);
         }
 
         @Override
@@ -94,7 +110,18 @@
                 return;
             }
 
-            mExecutor.execute(() -> mCallback.onAssistContentAvailable(content));
+            AssistContentRequester requester = mParentRef.get();
+            if (requester != null) {
+                Callback callback = requester.mPendingCallbacks.get(mCallbackKey);
+                if (callback != null) {
+                    requester.executeOnMainExecutor(
+                            () -> callback.onAssistContentAvailable(content));
+                } else {
+                    Log.d(TAG, "Callback received after calling UI was disposed of");
+                }
+            } else {
+                Log.d(TAG, "Callback received after Requester was collected");
+            }
         }
 
         @Override
diff --git a/quickstep/src/com/android/quickstep/util/NavigationModeFeatureFlag.java b/quickstep/src/com/android/quickstep/util/NavigationModeFeatureFlag.java
index c527be3..60c7add 100644
--- a/quickstep/src/com/android/quickstep/util/NavigationModeFeatureFlag.java
+++ b/quickstep/src/com/android/quickstep/util/NavigationModeFeatureFlag.java
@@ -20,8 +20,6 @@
 
 import android.content.Context;
 
-import com.android.quickstep.OverviewComponentObserver;
-import com.android.quickstep.RecentsAnimationDeviceState;
 import com.android.quickstep.SysUINavigationMode;
 
 import java.util.function.Predicate;
@@ -37,7 +35,6 @@
     private final Supplier<Boolean> mBasePredicate;
     private final Predicate<SysUINavigationMode.Mode> mModePredicate;
     private boolean mSupported;
-    private OverviewComponentObserver mObserver;
 
     private NavigationModeFeatureFlag(Supplier<Boolean> basePredicate,
             Predicate<SysUINavigationMode.Mode> modePredicate) {
@@ -46,17 +43,12 @@
     }
 
     public boolean get() {
-        return mBasePredicate.get() && mSupported && mObserver != null
-                && mObserver.isHomeAndOverviewSame();
+        return mBasePredicate.get() && mSupported;
     }
 
     public void initialize(Context context) {
         onNavigationModeChanged(SysUINavigationMode.INSTANCE.get(context).getMode());
         SysUINavigationMode.INSTANCE.get(context).addModeChangeListener(this);
-
-        // Temporary solution to disable live tile for the fallback launcher
-        RecentsAnimationDeviceState rads = new RecentsAnimationDeviceState(context);
-        mObserver = new OverviewComponentObserver(context, rads);
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/util/OverviewToHomeAnim.java b/quickstep/src/com/android/quickstep/util/OverviewToHomeAnim.java
index 42be9bb..10b7662 100644
--- a/quickstep/src/com/android/quickstep/util/OverviewToHomeAnim.java
+++ b/quickstep/src/com/android/quickstep/util/OverviewToHomeAnim.java
@@ -17,44 +17,26 @@
 
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.anim.Interpolators.DEACCEL;
-import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
-import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.anim.Interpolators.clampToProgress;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_ACTIONS_FADE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_Y;
 
 import android.animation.Animator;
 import android.animation.AnimatorSet;
 import android.util.Log;
-import android.view.animation.Interpolator;
 
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.statemanager.StateManager;
 import com.android.launcher3.states.StateAnimationConfig;
-import com.android.quickstep.views.RecentsView;
 
 /**
  * Runs an animation from overview to home. Currently, this animation is just a wrapper around the
- * normal state transition, in order to keep RecentsView at the same scale and translationY that
- * it started out at as it translates offscreen. It also scrolls RecentsView to page 0 and may play
- * a {@link StaggeredWorkspaceAnim} if we're starting from an upward fling.
+ * normal state transition and may play a {@link StaggeredWorkspaceAnim} if we're starting from an
+ * upward fling.
  */
 public class OverviewToHomeAnim {
 
     private static final String TAG = "OverviewToHomeAnim";
 
-    // Constants to specify how to scroll RecentsView to the default page if it's not already there.
-    private static final int DEFAULT_PAGE = 0;
-    private static final int PER_PAGE_SCROLL_DURATION = 150;
-    private static final int MAX_PAGE_SCROLL_DURATION = 750;
-
     private final Launcher mLauncher;
     private final Runnable mOnReachedHome;
 
@@ -95,24 +77,8 @@
             mIsHomeStaggeredAnimFinished = true;
         }
 
-        RecentsView recentsView = mLauncher.getOverviewPanel();
-        int numPagesToScroll = recentsView.getNextPage() - DEFAULT_PAGE;
-        int scrollDuration = Math.min(MAX_PAGE_SCROLL_DURATION,
-                numPagesToScroll * PER_PAGE_SCROLL_DURATION);
-        int duration = Math.max(scrollDuration, startState.getTransitionDuration(mLauncher));
-
-        StateAnimationConfig config = new UseFirstInterpolatorStateAnimConfig();
-        config.duration = duration;
-        boolean isLayoutNaturalToLauncher = recentsView.getPagedOrientationHandler()
-                .isLayoutNaturalToLauncher();
-        config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, isLayoutNaturalToLauncher
-                ? clampToProgress(FAST_OUT_SLOW_IN, 0, 0.75f) : FINAL_FRAME);
-        config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, FINAL_FRAME);
-        config.setInterpolator(ANIM_OVERVIEW_SCALE, FINAL_FRAME);
-        config.setInterpolator(ANIM_OVERVIEW_ACTIONS_FADE, LINEAR);
-        if (!isLayoutNaturalToLauncher) {
-            config.setInterpolator(ANIM_OVERVIEW_FADE, DEACCEL);
-        }
+        StateAnimationConfig config = new StateAnimationConfig();
+        config.duration = startState.getTransitionDuration(mLauncher);
         AnimatorSet stateAnim = stateManager.createAtomicAnimation(
                 startState, NORMAL, config);
         stateAnim.addListener(new AnimationSuccessListener() {
@@ -125,7 +91,6 @@
         anim.play(stateAnim);
         stateManager.setCurrentAnimation(anim, NORMAL);
         anim.start();
-        recentsView.snapToPage(DEFAULT_PAGE, duration);
     }
 
     private void maybeOverviewToHomeAnimComplete() {
@@ -133,17 +98,4 @@
             mOnReachedHome.run();
         }
     }
-
-    /**
-     * Wrapper around StateAnimationConfig that doesn't allow interpolators to be set if they are
-     * already set. This ensures they aren't overridden before being used.
-     */
-    private static class UseFirstInterpolatorStateAnimConfig extends StateAnimationConfig {
-        @Override
-        public void setInterpolator(int animId, Interpolator interpolator) {
-            if (mInterpolators[animId] == null || interpolator == null) {
-                super.setInterpolator(animId, interpolator);
-            }
-        }
-    }
 }
diff --git a/quickstep/src/com/android/quickstep/util/RecentsAtomicAnimationFactory.java b/quickstep/src/com/android/quickstep/util/RecentsAtomicAnimationFactory.java
index ba70bf7..c1ca060 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsAtomicAnimationFactory.java
+++ b/quickstep/src/com/android/quickstep/util/RecentsAtomicAnimationFactory.java
@@ -15,11 +15,13 @@
  */
 package com.android.quickstep.util;
 
-import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET;
+import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_VERTICAL_OFFSET;
 
 import android.animation.Animator;
 import android.animation.ObjectAnimator;
 
+import androidx.dynamicanimation.animation.DynamicAnimation;
+
 import com.android.launcher3.anim.SpringAnimationBuilder;
 import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory;
 import com.android.launcher3.statemanager.StatefulActivity;
@@ -29,7 +31,7 @@
         extends AtomicAnimationFactory<STATE_TYPE> {
 
     public static final int INDEX_RECENTS_FADE_ANIM = AtomicAnimationFactory.NEXT_INDEX + 0;
-    public static final int INDEX_RECENTS_TRANSLATE_X_ANIM = AtomicAnimationFactory.NEXT_INDEX + 1;
+    public static final int INDEX_RECENTS_TRANSLATE_Y_ANIM = AtomicAnimationFactory.NEXT_INDEX + 1;
 
     private static final int MY_ANIM_COUNT = 2;
 
@@ -46,14 +48,14 @@
             case INDEX_RECENTS_FADE_ANIM:
                 return ObjectAnimator.ofFloat(mActivity.getOverviewPanel(),
                         RecentsView.CONTENT_ALPHA, values);
-            case INDEX_RECENTS_TRANSLATE_X_ANIM: {
+            case INDEX_RECENTS_TRANSLATE_Y_ANIM: {
                 RecentsView rv = mActivity.getOverviewPanel();
                 return new SpringAnimationBuilder(mActivity)
-                        .setMinimumVisibleChange(1f / rv.getPageOffsetScale())
+                        .setMinimumVisibleChange(DynamicAnimation.MIN_VISIBLE_CHANGE_SCALE)
                         .setDampingRatio(0.8f)
                         .setStiffness(250)
                         .setValues(values)
-                        .build(rv, ADJACENT_PAGE_OFFSET);
+                        .build(rv, ADJACENT_PAGE_VERTICAL_OFFSET);
             }
             default:
                 return super.createStateElementAnimation(index, values);
diff --git a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
index 188efad..d164c8c 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
+++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
@@ -33,6 +33,7 @@
 import android.content.SharedPreferences;
 import android.content.res.Resources;
 import android.graphics.Matrix;
+import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.util.Log;
@@ -49,6 +50,7 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.touch.PagedOrientationHandler;
+import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.SettingsCache;
 import com.android.launcher3.util.WindowBounds;
 import com.android.quickstep.BaseActivityInterface;
@@ -561,11 +563,27 @@
      */
     public DeviceProfile getLauncherDeviceProfile() {
         InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(mContext);
-        // TODO also check the natural orientation is landscape or portrait
-        return  (mRecentsActivityRotation == ROTATION_90
-                || mRecentsActivityRotation == ROTATION_270)
-                ? idp.landscapeProfile
-                : idp.portraitProfile;
+        Point currentSize = DisplayController.INSTANCE.get(mContext).getInfo().currentSize;
+
+        int width, height;
+        if ((mRecentsActivityRotation == ROTATION_90 || mRecentsActivityRotation == ROTATION_270)) {
+            width = Math.max(currentSize.x, currentSize.y);
+            height = Math.min(currentSize.x, currentSize.y);
+        } else {
+            width = Math.min(currentSize.x, currentSize.y);
+            height = Math.max(currentSize.x, currentSize.y);
+        }
+
+        DeviceProfile bestMatch = idp.supportedProfiles.get(0);
+        float minDiff = Float.MAX_VALUE;
+        for (DeviceProfile profile : idp.supportedProfiles) {
+            float diff = Math.abs(profile.widthPx - width) + Math.abs(profile.heightPx - height);
+            if (diff < minDiff) {
+                minDiff = diff;
+                bestMatch = profile;
+            }
+        }
+        return bestMatch;
     }
 
     private static String nameAndAddress(Object obj) {
diff --git a/quickstep/src/com/android/quickstep/util/SplitScreenBounds.java b/quickstep/src/com/android/quickstep/util/SplitScreenBounds.java
index 88cc650..483a1c6 100644
--- a/quickstep/src/com/android/quickstep/util/SplitScreenBounds.java
+++ b/quickstep/src/com/android/quickstep/util/SplitScreenBounds.java
@@ -20,10 +20,7 @@
 
 import android.annotation.TargetApi;
 import android.content.Context;
-import android.graphics.Insets;
-import android.graphics.Rect;
 import android.os.Build;
-import android.view.WindowInsets.Type;
 import android.view.WindowManager;
 import android.view.WindowMetrics;
 
@@ -73,10 +70,8 @@
      */
     private static WindowBounds createDefaultWindowBounds(Context context) {
         WindowMetrics wm = context.getSystemService(WindowManager.class).getMaximumWindowMetrics();
-        Insets insets = wm.getWindowInsets().getInsets(Type.systemBars());
+        WindowBounds bounds = WindowBounds.fromWindowMetrics(wm);
 
-        WindowBounds bounds = new WindowBounds(wm.getBounds(),
-                new Rect(insets.left, insets.top, insets.right, insets.bottom));
         int rotation = DisplayController.INSTANCE.get(context).getInfo().rotation;
         int halfDividerSize = context.getResources()
                 .getDimensionPixelSize(R.dimen.multi_window_task_divider_size) / 2;
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index 01d51f8..3d33e57 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -25,6 +25,7 @@
 import android.animation.AnimatorSet;
 import android.app.ActivityOptions;
 import android.content.res.Resources;
+import android.graphics.Rect;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -64,6 +65,7 @@
     private final SystemUiProxy mSystemUiProxy;
     private TaskView mInitialTaskView;
     private SplitPositionOption mInitialPosition;
+    private Rect mInitialBounds;
     private final Handler mHandler;
 
     public SplitSelectStateController(Handler handler, SystemUiProxy systemUiProxy) {
@@ -74,9 +76,11 @@
     /**
      * To be called after first task selected
      */
-    public void setInitialTaskSelect(TaskView taskView, SplitPositionOption positionOption) {
+    public void setInitialTaskSelect(TaskView taskView, SplitPositionOption positionOption,
+            Rect initialBounds) {
         mInitialTaskView = taskView;
         mInitialPosition = positionOption;
+        mInitialBounds = initialBounds;
     }
 
     /**
@@ -220,9 +224,14 @@
     public void resetState() {
         mInitialTaskView = null;
         mInitialPosition = null;
+        mInitialBounds = null;
     }
 
     public boolean isSplitSelectActive() {
         return mInitialTaskView != null;
     }
+
+    public Rect getInitialBounds() {
+        return mInitialBounds;
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index e63f8bb..f578ad1 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -36,6 +36,7 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.util.TraceHelper;
 import com.android.quickstep.AnimatedFloat;
 import com.android.quickstep.BaseActivityInterface;
 import com.android.quickstep.views.TaskThumbnailView.PreviewPositionHelper;
@@ -95,7 +96,9 @@
         mContext = context;
         mSizeStrategy = sizeStrategy;
 
-        mOrientationState = new RecentsOrientedState(context, sizeStrategy, i -> { });
+        // TODO(b/187074722): Don't create this per-TaskViewSimulator
+        mOrientationState = TraceHelper.allowIpcs("",
+                () -> new RecentsOrientedState(context, sizeStrategy, i -> { }));
         mOrientationState.setGestureActive(true);
         mCurrentFullscreenParams = new FullscreenDrawParams(context);
         mOrientationStateId = mOrientationState.getStateId();
diff --git a/quickstep/src/com/android/quickstep/views/FloatingWidgetBackgroundView.java b/quickstep/src/com/android/quickstep/views/FloatingWidgetBackgroundView.java
index f74aa55..9ea2369 100644
--- a/quickstep/src/com/android/quickstep/views/FloatingWidgetBackgroundView.java
+++ b/quickstep/src/com/android/quickstep/views/FloatingWidgetBackgroundView.java
@@ -100,8 +100,8 @@
     /** Restores the drawables to the source view. */
     void finish() {
         if (isUninitialized()) return;
-        mSourceView.setForeground(mOriginalForeground);
-        mSourceView.setBackground(mOriginalBackground);
+        if (mOriginalForeground != null) mSourceView.setForeground(mOriginalForeground);
+        if (mOriginalBackground != null) mSourceView.setBackground(mOriginalBackground);
     }
 
     void recycle() {
diff --git a/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java b/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java
index d23884c..ed54f10 100644
--- a/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java
+++ b/quickstep/src/com/android/quickstep/views/FloatingWidgetView.java
@@ -20,13 +20,14 @@
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.graphics.Matrix;
-import android.graphics.Rect;
 import android.graphics.RectF;
 import android.os.Build;
 import android.util.AttributeSet;
+import android.util.Size;
 import android.view.GhostView;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
 import android.widget.FrameLayout;
 
 import com.android.launcher3.Launcher;
@@ -39,7 +40,8 @@
 
 /** A view that mimics an App Widget through a launch animation. */
 @TargetApi(Build.VERSION_CODES.S)
-public class FloatingWidgetView extends FrameLayout implements AnimatorListener {
+public class FloatingWidgetView extends FrameLayout implements AnimatorListener,
+        OnGlobalLayoutListener {
     private static final Matrix sTmpMatrix = new Matrix();
 
     private final Launcher mLauncher;
@@ -54,6 +56,7 @@
 
     private Runnable mEndRunnable;
     private Runnable mFastFinishRunnable;
+    private Runnable mOnTargetChangeRunnable;
 
     public FloatingWidgetView(Context context) {
         this(context, null);
@@ -93,6 +96,32 @@
     public void onAnimationRepeat(Animator animator) {
     }
 
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        getViewTreeObserver().addOnGlobalLayoutListener(this);
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        getViewTreeObserver().removeOnGlobalLayoutListener(this);
+        super.onDetachedFromWindow();
+    }
+
+    @Override
+    public void onGlobalLayout() {
+        if (isUninitialized()) return;
+        positionViews();
+        if (mOnTargetChangeRunnable != null) {
+            mOnTargetChangeRunnable.run();
+        }
+    }
+
+    /** Sets a runnable that is called on global layout change. */
+    public void setOnTargetChangeListener(Runnable onTargetChangeListener) {
+        mOnTargetChangeRunnable = onTargetChangeListener;
+    }
+
     /** Sets a runnable that is called after a call to {@link #fastFinish()}. */
     public void setFastFinishRunnable(Runnable runnable) {
         mFastFinishRunnable = runnable;
@@ -113,7 +142,7 @@
     }
 
     private void init(DragLayer dragLayer, LauncherAppWidgetHostView originalView,
-            RectF widgetBackgroundPosition, Rect windowTargetBounds, float windowCornerRadius) {
+            RectF widgetBackgroundPosition, Size windowSize, float windowCornerRadius) {
         mAppWidgetView = originalView;
         mAppWidgetView.beginDeferringUpdates();
         mBackgroundPosition = widgetBackgroundPosition;
@@ -128,7 +157,7 @@
         getRelativePosition(mAppWidgetBackgroundView, mAppWidgetView, mBackgroundOffset);
         mBackgroundView.init(mAppWidgetView, mAppWidgetBackgroundView, windowCornerRadius);
         // Layout call before GhostView creation so that the overlaid view isn't clipped
-        layout(0, 0, windowTargetBounds.width(), windowTargetBounds.height());
+        layout(0, 0, windowSize.getWidth(), windowSize.getHeight());
         mForegroundOverlayView = GhostView.addGhost(mAppWidgetView, this);
         positionViews();
 
@@ -205,6 +234,7 @@
     private void recycle() {
         mEndRunnable = null;
         mFastFinishRunnable = null;
+        mOnTargetChangeRunnable = null;
         mBackgroundPosition = null;
         mListenerView.setListener(null);
         mAppWidgetView = null;
@@ -219,19 +249,19 @@
      *
      * @param widgetBackgroundPosition a {@link RectF} that will be updated with the widget's
      *                                 background bounds
-     * @param windowTargetBounds       the bounds of the window when launched
+     * @param windowSize               the size of the window when launched
      * @param windowCornerRadius       the corner radius of the window
      */
     public static FloatingWidgetView getFloatingWidgetView(Launcher launcher,
             LauncherAppWidgetHostView originalView, RectF widgetBackgroundPosition,
-            Rect windowTargetBounds, float windowCornerRadius) {
+            Size windowSize, float windowCornerRadius) {
         final DragLayer dragLayer = launcher.getDragLayer();
         ViewGroup parent = (ViewGroup) dragLayer.getParent();
         FloatingWidgetView floatingView =
                 launcher.getViewCache().getView(R.layout.floating_widget_view, launcher, parent);
         floatingView.recycle();
 
-        floatingView.init(dragLayer, originalView, widgetBackgroundPosition, windowTargetBounds,
+        floatingView.init(dragLayer, originalView, widgetBackgroundPosition, windowSize,
                 windowCornerRadius);
         parent.addView(floatingView);
         return floatingView;
@@ -240,7 +270,7 @@
     private static void getRelativePosition(View descendant, View ancestor, RectF position) {
         float[] points = new float[]{0, 0, descendant.getWidth(), descendant.getHeight()};
         Utilities.getDescendantCoordRelativeToAncestor(descendant, ancestor, points,
-                false /* includeRootScroll */);
+                false /* includeRootScroll */, true /* ignoreTransform */);
         position.set(
                 Math.min(points[0], points[2]),
                 Math.min(points[1], points[3]),
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index f5a8ff8..65956d5 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -21,7 +21,6 @@
 import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK;
 import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
 import static com.android.launcher3.LauncherState.SPRING_LOADED;
-import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE;
 
 import android.annotation.TargetApi;
 import android.content.Context;
@@ -39,7 +38,6 @@
 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
 import com.android.launcher3.util.SplitConfigurationOptions;
 import com.android.quickstep.LauncherActivityInterface;
-import com.android.quickstep.util.OverviewToHomeAnim;
 import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.plugins.RecentsExtraCard;
 
@@ -90,15 +88,7 @@
 
     @Override
     public void startHome() {
-        Runnable onReachedHome = () -> mActivity.getStateManager().goToState(NORMAL, false);
-        OverviewToHomeAnim overviewToHomeAnim = new OverviewToHomeAnim(mActivity, onReachedHome);
-        if (LIVE_TILE.get()) {
-            switchToScreenshot(null,
-                    () -> finishRecentsAnimation(true /* toRecents */,
-                            () -> overviewToHomeAnim.animateWithVelocity(0)));
-        } else {
-            overviewToHomeAnim.animateWithVelocity(0);
-        }
+        mActivity.getStateManager().goToState(NORMAL);
     }
 
     @Override
@@ -245,8 +235,8 @@
         if (mActivity.isInState(OVERVIEW_SPLIT_SELECT)) {
             // We want to keep the tasks translations in this temporary state
             // after resetting the rest above
-            setTaskViewsResistanceTranslation(mTaskViewsSecondaryTranslation);
-            setTaskViewsPrimaryTranslation(mTaskViewsPrimaryTranslation);
+            setTaskViewsPrimarySplitTranslation(mTaskViewsPrimarySplitTranslation);
+            setTaskViewsSecondarySplitTranslation(mTaskViewsSecondarySplitTranslation);
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index c9e7a73..a04b886 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -23,7 +23,6 @@
 import static com.android.launcher3.AbstractFloatingView.TYPE_TASK_MENU;
 import static com.android.launcher3.AbstractFloatingView.getTopOpenViewWithType;
 import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
-import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_ICON_PARAMS;
 import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS;
 import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
 import static com.android.launcher3.LauncherState.BACKGROUND_APP;
@@ -171,8 +170,7 @@
 public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_TYPE>,
         STATE_TYPE extends BaseState<STATE_TYPE>> extends PagedView implements Insettable,
         TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback,
-        InvariantDeviceProfile.OnIDPChangeListener, TaskVisualsChangeListener,
-        SplitScreenBounds.OnChangeListener {
+        TaskVisualsChangeListener, SplitScreenBounds.OnChangeListener {
 
     public static final FloatProperty<RecentsView> CONTENT_ALPHA =
             new FloatProperty<RecentsView>("contentAlpha") {
@@ -213,19 +211,35 @@
                 }
             };
 
-    public static final FloatProperty<RecentsView> ADJACENT_PAGE_OFFSET =
-            new FloatProperty<RecentsView>("adjacentPageOffset") {
+    public static final FloatProperty<RecentsView> ADJACENT_PAGE_HORIZONTAL_OFFSET =
+            new FloatProperty<RecentsView>("adjacentPageHorizontalOffset") {
                 @Override
                 public void setValue(RecentsView recentsView, float v) {
-                    if (recentsView.mAdjacentPageOffset != v) {
-                        recentsView.mAdjacentPageOffset = v;
+                    if (recentsView.mAdjacentPageHorizontalOffset != v) {
+                        recentsView.mAdjacentPageHorizontalOffset = v;
                         recentsView.updatePageOffsets();
                     }
                 }
 
                 @Override
                 public Float get(RecentsView recentsView) {
-                    return recentsView.mAdjacentPageOffset;
+                    return recentsView.mAdjacentPageHorizontalOffset;
+                }
+            };
+
+    public static final FloatProperty<RecentsView> ADJACENT_PAGE_VERTICAL_OFFSET =
+            new FloatProperty<RecentsView>("adjacentPageVerticalOffset") {
+                @Override
+                public void setValue(RecentsView recentsView, float v) {
+                    if (recentsView.mAdjacentPageVerticalOffset != v) {
+                        recentsView.mAdjacentPageVerticalOffset = v;
+                        recentsView.updateVerticalPageOffsets();
+                    }
+                }
+
+                @Override
+                public Float get(RecentsView recentsView) {
+                    return recentsView.mAdjacentPageVerticalOffset;
                 }
             };
 
@@ -240,6 +254,8 @@
                 @Override
                 public void setValue(RecentsView recentsView, float v) {
                     recentsView.setTaskViewsResistanceTranslation(v);
+                    recentsView.mLastComputedTaskBottomPushOutDistance = null;
+                    recentsView.updateVerticalPageOffsets();
                 }
 
                 @Override
@@ -254,16 +270,29 @@
      * more specific, we'd want to create a similar FloatProperty just for a TaskView's
      * offsetX/Y property
      */
-    public static final FloatProperty<RecentsView> TASK_PRIMARY_TRANSLATION =
-            new FloatProperty<RecentsView>("taskPrimaryTranslation") {
+    public static final FloatProperty<RecentsView> TASK_PRIMARY_SPLIT_TRANSLATION =
+            new FloatProperty<RecentsView>("taskPrimarySplitTranslation") {
                 @Override
                 public void setValue(RecentsView recentsView, float v) {
-                    recentsView.setTaskViewsPrimaryTranslation(v);
+                    recentsView.setTaskViewsPrimarySplitTranslation(v);
                 }
 
                 @Override
                 public Float get(RecentsView recentsView) {
-                    return recentsView.mTaskViewsPrimaryTranslation;
+                    return recentsView.mTaskViewsPrimarySplitTranslation;
+                }
+            };
+
+    public static final FloatProperty<RecentsView> TASK_SECONDARY_SPLIT_TRANSLATION =
+            new FloatProperty<RecentsView>("taskSecondarySplitTranslation") {
+                @Override
+                public void setValue(RecentsView recentsView, float v) {
+                    recentsView.setTaskViewsSecondarySplitTranslation(v);
+                }
+
+                @Override
+                public Float get(RecentsView recentsView) {
+                    return recentsView.mTaskViewsSecondarySplitTranslation;
                 }
             };
 
@@ -276,10 +305,11 @@
                     view.setScaleY(scale);
                     view.mLastComputedTaskStartPushOutDistance = null;
                     view.mLastComputedTaskEndPushOutDistance = null;
+                    view.mLastComputedTaskBottomPushOutDistance = null;
                     view.mLiveTileTaskViewSimulator.recentsViewScale.value = scale;
-                    view.updatePageOffsets();
                     view.setTaskViewsResistanceTranslation(view.mTaskViewsSecondaryTranslation);
-                    view.setTaskViewsPrimaryTranslation(view.mTaskViewsPrimaryTranslation);
+                    view.updatePageOffsets();
+                    view.updateVerticalPageOffsets();
                 }
 
                 @Override
@@ -318,6 +348,7 @@
     // How much a task that is directly offscreen will be pushed out due to RecentsView scale/pivot.
     protected Float mLastComputedTaskStartPushOutDistance = null;
     protected Float mLastComputedTaskEndPushOutDistance = null;
+    protected Float mLastComputedTaskBottomPushOutDistance = null;
     protected boolean mEnableDrawingLiveTile = false;
     protected final Rect mTempRect = new Rect();
     protected final RectF mTempRectF = new RectF();
@@ -361,9 +392,11 @@
     private boolean mOverviewGridEnabled;
     private boolean mOverviewFullscreenEnabled;
 
-    private float mAdjacentPageOffset = 0;
+    private float mAdjacentPageHorizontalOffset = 0;
+    private float mAdjacentPageVerticalOffset = 0;
     protected float mTaskViewsSecondaryTranslation = 0;
-    protected float mTaskViewsPrimaryTranslation = 0;
+    protected float mTaskViewsPrimarySplitTranslation = 0;
+    protected float mTaskViewsSecondarySplitTranslation = 0;
     // Progress from 0 to 1 where 0 is a carousel and 1 is a 2 row grid.
     private float mGridProgress = 0;
     private final IntSet mTopRowIdSet = new IntSet();
@@ -609,28 +642,9 @@
         // Draw overscroll
         if (mAllowOverScroll && (!mEdgeGlowRight.isFinished() || !mEdgeGlowLeft.isFinished())) {
             final int restoreCount = canvas.save();
-            final int width = getWidth();
-            final int height = getHeight();
-            int primarySize = mOrientationHandler.getPrimaryValue(width, height);
-            int secondarySize = mOrientationHandler.getSecondaryValue(width, height);
 
-            float effectiveShift = 0;
-            if (!mEdgeGlowLeft.isFinished()) {
-                mEdgeGlowLeft.setSize(secondarySize, primarySize);
-                if (((TranslateEdgeEffect) mEdgeGlowLeft).getTranslationShift(mTempFloat)) {
-                    effectiveShift = mTempFloat[0];
-                    postInvalidateOnAnimation();
-                }
-            }
-            if (!mEdgeGlowRight.isFinished()) {
-                mEdgeGlowRight.setSize(secondarySize, primarySize);
-                if (((TranslateEdgeEffect) mEdgeGlowRight).getTranslationShift(mTempFloat)) {
-                    effectiveShift -= mTempFloat[0];
-                    postInvalidateOnAnimation();
-                }
-            }
-
-            int scroll = OverScroll.dampedScroll(effectiveShift * primarySize, primarySize);
+            int primarySize = mOrientationHandler.getPrimaryValue(getWidth(), getHeight());
+            int scroll = OverScroll.dampedScroll(getUndampedOverScrollShift(), primarySize);
             mOrientationHandler.set(canvas, CANVAS_TRANSLATE, scroll);
 
             if (mOverScrollShift != scroll) {
@@ -652,6 +666,31 @@
         }
     }
 
+    private float getUndampedOverScrollShift() {
+        final int width = getWidth();
+        final int height = getHeight();
+        int primarySize = mOrientationHandler.getPrimaryValue(width, height);
+        int secondarySize = mOrientationHandler.getSecondaryValue(width, height);
+
+        float effectiveShift = 0;
+        if (!mEdgeGlowLeft.isFinished()) {
+            mEdgeGlowLeft.setSize(secondarySize, primarySize);
+            if (((TranslateEdgeEffect) mEdgeGlowLeft).getTranslationShift(mTempFloat)) {
+                effectiveShift = mTempFloat[0];
+                postInvalidateOnAnimation();
+            }
+        }
+        if (!mEdgeGlowRight.isFinished()) {
+            mEdgeGlowRight.setSize(secondarySize, primarySize);
+            if (((TranslateEdgeEffect) mEdgeGlowRight).getTranslationShift(mTempFloat)) {
+                effectiveShift -= mTempFloat[0];
+                postInvalidateOnAnimation();
+            }
+        }
+
+        return effectiveShift * primarySize;
+    }
+
     /**
      * Returns the view shift due to overscroll
      */
@@ -705,16 +744,6 @@
         updateTaskStackListenerState();
     }
 
-    @Override
-    public void onIdpChanged(int changeFlags, InvariantDeviceProfile idp) {
-        if ((changeFlags & CHANGE_FLAG_ICON_PARAMS) == 0) {
-            return;
-        }
-        mModel.getIconCache().clear();
-        unloadVisibleTaskData(TaskView.FLAG_UPDATE_ICON);
-        loadVisibleTaskData(TaskView.FLAG_UPDATE_ICON);
-    }
-
     public void init(OverviewActionsView actionsView, SplitPlaceholderView splitPlaceholderView) {
         mActionsView = actionsView;
         mActionsView.updateHiddenFlags(HIDDEN_NO_TASKS, getTaskViewCount() == 0);
@@ -739,7 +768,6 @@
         mSyncTransactionApplier = new SurfaceTransactionApplier(this);
         mLiveTileParams.setSyncTransactionApplier(mSyncTransactionApplier);
         RecentsModel.INSTANCE.get(getContext()).addThumbnailChangeListener(this);
-        mIdp.addOnChangeListener(this);
         mIPipAnimationListener.setActivity(mActivity);
         SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener(
                 mIPipAnimationListener);
@@ -758,7 +786,6 @@
         mSyncTransactionApplier = null;
         mLiveTileParams.setSyncTransactionApplier(null);
         RecentsModel.INSTANCE.get(getContext()).removeThumbnailChangeListener(this);
-        mIdp.removeOnChangeListener(this);
         SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener(null);
         SplitScreenBounds.INSTANCE.removeOnChangeListener(this);
         mIPipAnimationListener.setActivity(null);
@@ -1175,6 +1202,7 @@
         // Update the set of visible task's data
         loadVisibleTaskData(TaskView.FLAG_UPDATE_ALL);
         setTaskModalness(0);
+        updateVerticalPageOffsets();
     }
 
     public void setFullscreenProgress(float fullscreenProgress) {
@@ -1631,13 +1659,21 @@
     /**
      * Called when a gesture from an app has finished, and an end target has been determined.
      */
-    public void onGestureEndTargetCalculated(GestureState.GestureEndTarget endTarget) {
+    public void onPrepareGestureEndAnimation(
+            @Nullable AnimatorSet animatorSet, GestureState.GestureEndTarget endTarget) {
+        if (mSizeStrategy.stateFromGestureEndTarget(endTarget)
+                .displayOverviewTasksAsGrid(mActivity.getDeviceProfile())) {
+            if (animatorSet == null) {
+                setGridProgress(1);
+            } else {
+                animatorSet.play(ObjectAnimator.ofFloat(this, RECENTS_GRID_PROGRESS, 1));
+            }
+        }
         mCurrentGestureEndTarget = endTarget;
         if (endTarget == GestureState.GestureEndTarget.NEW_TASK
                 || endTarget == GestureState.GestureEndTarget.LAST_TASK) {
             // When switching to tasks in quick switch, ensures the snapped page's scroll maintain
-            // invariant between quick switch and overview grid, to ensure a smooth animation
-            // transition.
+            // invariant between quick switch and overview, to ensure a smooth animation transition.
             updateGridProperties();
         }
     }
@@ -1839,15 +1875,28 @@
         final int boxLength = Math.max(mLastComputedGridTaskSize.width(),
                 mLastComputedGridTaskSize.height());
         int taskTopMargin = mActivity.getDeviceProfile().overviewTaskThumbnailTopMarginPx;
-        float heightOffset = (boxLength + taskTopMargin) + mRowSpacing;
-        float taskGridVerticalDiff = mLastComputedGridTaskSize.top - mLastComputedTaskSize.top;
+
+        /*
+         * taskGridVerticalDiff is used to position the top of a task in the top row of the grid
+         * heightOffset is the vertical space one grid task takes + space between top and
+         *   bottom row
+         * Summed together they provide the top position for bottom row of grid tasks
+         */
+        final float taskGridVerticalDiff =
+                mLastComputedGridTaskSize.top - mLastComputedTaskSize.top;
+        final float heightOffset = (boxLength + taskTopMargin) + mRowSpacing;
 
         int topRowWidth = 0;
         int bottomRowWidth = 0;
         float topAccumulatedTranslationX = 0;
         float bottomAccumulatedTranslationX = 0;
+
+        // Contains whether the child index is in top or bottom of grid (for non-focused task)
+        // Different from mTopRowIdSet, which contains the taskId of what task is in top row
         IntSet topSet = new IntSet();
         IntSet bottomSet = new IntSet();
+
+        // Horizontal grid translation for each task
         float[] gridTranslations = new float[taskCount];
 
         int focusedTaskIndex = Integer.MAX_VALUE;
@@ -1901,7 +1950,6 @@
                 boolean isTopRow = isTaskDismissal ? mTopRowIdSet.contains(taskId)
                         : topRowWidth <= bottomRowWidth;
                 if (isTopRow) {
-                    gridTranslations[i] += topAccumulatedTranslationX;
                     topRowWidth += taskWidthAndSpacing;
                     topSet.add(i);
                     mTopRowIdSet.add(taskId);
@@ -1917,11 +1965,10 @@
                         widthOffset += getTaskViewAt(j).getLayoutParams().width + mPageSpacing;
                     }
 
-                    float gridTranslationX = mIsRtl ? widthOffset : -widthOffset;
-                    gridTranslations[i] += gridTranslationX;
-                    topAccumulatedTranslationX += gridTranslationX;
+                    float currentTaskTranslationX = mIsRtl ? widthOffset : -widthOffset;
+                    gridTranslations[i] += topAccumulatedTranslationX + currentTaskTranslationX;
+                    topAccumulatedTranslationX += currentTaskTranslationX;
                 } else {
-                    gridTranslations[i] += bottomAccumulatedTranslationX;
                     bottomRowWidth += taskWidthAndSpacing;
                     bottomSet.add(i);
 
@@ -1937,9 +1984,9 @@
                         widthOffset += getTaskViewAt(j).getLayoutParams().width + mPageSpacing;
                     }
 
-                    float gridTranslationX = mIsRtl ? widthOffset : -widthOffset;
-                    gridTranslations[i] += gridTranslationX;
-                    bottomAccumulatedTranslationX += gridTranslationX;
+                    float currentTaskTranslationX = mIsRtl ? widthOffset : -widthOffset;
+                    gridTranslations[i] += bottomAccumulatedTranslationX + currentTaskTranslationX;
+                    bottomAccumulatedTranslationX += currentTaskTranslationX;
                 }
                 if (taskView == snappedTaskView) {
                     snappedTaskRowWidth = isTopRow ? topRowWidth : bottomRowWidth;
@@ -2130,18 +2177,43 @@
         // Use setFloat instead of setViewAlpha as we want to keep the view visible even when it's
         // alpha is set to 0 so that it can be recycled in the view pool properly
         anim.setFloat(taskView, VIEW_ALPHA, 0, ACCEL_2);
-        FloatProperty<TaskView> secondaryViewTranslate =
-                taskView.getSecondaryDissmissTranslationProperty();
-        int secondaryTaskDimension = mOrientationHandler.getSecondaryDimension(taskView);
-        int verticalFactor = mOrientationHandler.getSecondaryTranslationDirectionFactor();
+        SplitSelectStateController splitController = mSplitPlaceholderView.getSplitController();
 
         ResourceProvider rp = DynamicResource.provider(mActivity);
         SpringProperty sp = new SpringProperty(SpringProperty.FLAG_CAN_SPRING_ON_START)
                 .setDampingRatio(rp.getFloat(R.dimen.dismiss_task_trans_y_damping_ratio))
                 .setStiffness(rp.getFloat(R.dimen.dismiss_task_trans_y_stiffness));
+        FloatProperty<TaskView> dismissingTaskViewTranslate =
+                taskView.getSecondaryDissmissTranslationProperty();;
+        // TODO(b/186800707) translate entire grid size distance
+        int translateDistance = mOrientationHandler.getSecondaryDimension(taskView);
+        int positiveNegativeFactor = mOrientationHandler.getSecondaryTranslationDirectionFactor();
+        if (splitController.isSplitSelectActive()) {
+            // Have the task translate towards whatever side was just pinned
+            int dir = mOrientationHandler.getSplitTaskViewDismissDirection(splitController
+                    .getActiveSplitPositionOption(), mActivity.getDeviceProfile());
+            switch (dir) {
+                case PagedOrientationHandler.SPLIT_TRANSLATE_SECONDARY_NEGATIVE:
+                    dismissingTaskViewTranslate = taskView
+                            .getSecondaryDissmissTranslationProperty();
+                    positiveNegativeFactor = -1;
+                    break;
 
-        anim.add(ObjectAnimator.ofFloat(taskView, secondaryViewTranslate,
-                verticalFactor * secondaryTaskDimension).setDuration(duration), LINEAR, sp);
+                case PagedOrientationHandler.SPLIT_TRANSLATE_PRIMARY_POSITIVE:
+                    dismissingTaskViewTranslate = taskView.getPrimaryDismissTranslationProperty();
+                    positiveNegativeFactor = 1;
+                    break;
+
+                case PagedOrientationHandler.SPLIT_TRANSLATE_PRIMARY_NEGATIVE:
+                    dismissingTaskViewTranslate = taskView.getPrimaryDismissTranslationProperty();
+                    positiveNegativeFactor = -1;
+                    break;
+                default:
+                    throw new IllegalStateException("Invalid split task translation: " + dir);
+            }
+        }
+        anim.add(ObjectAnimator.ofFloat(taskView, dismissingTaskViewTranslate,
+                positiveNegativeFactor * translateDistance).setDuration(duration), LINEAR, sp);
 
         if (LIVE_TILE.get() && taskView.isRunningTask()) {
             anim.addOnFrameCallback(() -> {
@@ -2206,18 +2278,6 @@
                     }
                 }
 
-                // Additional offset for fake landscape, if the pinning happens to the right or
-                // left, we need to scroll all the tasks away from the direction of the splaceholder
-                // view
-                if (isSplitSelectionActive()) {
-                    int splitPosition = getSplitPlaceholder().getSplitController()
-                            .getActiveSplitPositionOption().mStagePosition;
-                    int direction = mOrientationHandler
-                            .getSplitTranslationDirectionFactor(splitPosition);
-                    int splitOffset = mOrientationHandler.getSplitAnimationTranslation(
-                            mSplitPlaceholderView.getHeight(), mActivity.getDeviceProfile());
-                    offset += direction * splitOffset;
-                }
                 int scrollDiff = newScroll[i] - oldScroll[i] + offset;
                 if (scrollDiff != 0) {
                     FloatProperty translationProperty = child instanceof TaskView
@@ -2249,7 +2309,8 @@
         mPendingAnimation.addEndListener(new Consumer<Boolean>() {
             @Override
             public void accept(Boolean success) {
-                if (LIVE_TILE.get() && taskView.isRunningTask() && success) {
+                if (LIVE_TILE.get() && mEnableDrawingLiveTile && taskView.isRunningTask()
+                        && success) {
                     finishRecentsAnimation(true /* toHome */, () -> onEnd(success));
                 } else {
                     onEnd(success);
@@ -2347,7 +2408,7 @@
         return true;
     }
 
-    protected void runDismissAnimation(PendingAnimation pendingAnim) {
+    private void runDismissAnimation(PendingAnimation pendingAnim) {
         AnimatorPlaybackController controller = pendingAnim.createPlaybackController();
         controller.dispatchOnStart();
         controller.getAnimationPlayer().setInterpolator(FAST_OUT_SLOW_IN);
@@ -2596,13 +2657,15 @@
         setTaskModalness(mTaskModalness);
         mLastComputedTaskStartPushOutDistance = null;
         mLastComputedTaskEndPushOutDistance = null;
+        mLastComputedTaskBottomPushOutDistance = null;
         updatePageOffsets();
+        updateVerticalPageOffsets();
         setImportantForAccessibility(isModal() ? IMPORTANT_FOR_ACCESSIBILITY_NO
                 : IMPORTANT_FOR_ACCESSIBILITY_AUTO);
     }
 
     private void updatePageOffsets() {
-        float offset = mAdjacentPageOffset;
+        float offset = mAdjacentPageHorizontalOffset;
         float modalOffset = ACCEL_0_75.getInterpolation(mTaskModalness);
         int count = getChildCount();
 
@@ -2613,10 +2676,10 @@
 
         float midpointOffsetSize = 0;
         float leftOffsetSize = midpoint - 1 >= 0
-                ? -getOffsetSize(midpoint - 1, midpoint, offset)
+                ? -getHorizontalOffsetSize(midpoint - 1, midpoint, offset)
                 : 0;
         float rightOffsetSize = midpoint + 1 < count
-                ? getOffsetSize(midpoint + 1, midpoint, offset)
+                ? getHorizontalOffsetSize(midpoint + 1, midpoint, offset)
                 : 0;
 
         boolean showAsGrid = showAsGrid();
@@ -2630,14 +2693,14 @@
             // calculation is the task directly next to the focus task in the grid.
             int referenceIndex = modalMidpoint == 0 ? 1 : 0;
             gridOffsetSize = referenceIndex < count
-                    ? getOffsetSize(referenceIndex, modalMidpoint, modalOffset)
+                    ? getHorizontalOffsetSize(referenceIndex, modalMidpoint, modalOffset)
                     : 0;
         } else {
             modalLeftOffsetSize = modalMidpoint - 1 >= 0
-                    ? getOffsetSize(modalMidpoint - 1, modalMidpoint, modalOffset)
+                    ? getHorizontalOffsetSize(modalMidpoint - 1, modalMidpoint, modalOffset)
                     : 0;
             modalRightOffsetSize = modalMidpoint + 1 < count
-                    ? getOffsetSize(modalMidpoint + 1, modalMidpoint, modalOffset)
+                    ? getHorizontalOffsetSize(modalMidpoint + 1, modalMidpoint, modalOffset)
                     : 0;
         }
 
@@ -2688,7 +2751,7 @@
      * translating away from the given midpoint.
      * @param offsetProgress From 0 to 1 where 0 means no offset and 1 means offset offscreen.
      */
-    private float getOffsetSize(int childIndex, int midpointIndex, float offsetProgress) {
+    private float getHorizontalOffsetSize(int childIndex, int midpointIndex, float offsetProgress) {
         if (offsetProgress == 0) {
             // Don't bother calculating everything below if we won't offset anyway.
             return 0;
@@ -2750,6 +2813,64 @@
         return distanceToOffscreen * offsetProgress;
     }
 
+    private void updateVerticalPageOffsets() {
+        float offset = mAdjacentPageVerticalOffset;
+        int count = getTaskViewCount();
+
+        TaskView runningTask = mRunningTaskId == -1 || !mRunningTaskTileHidden
+                ? null : getTaskView(mRunningTaskId);
+        int midpoint = runningTask == null ? -1 : indexOfChild(runningTask);
+
+        float offsetSize = getVerticalOffsetSize(offset);
+        float midpointOffsetSize = 0;
+
+        for (int i = 0; i < count; i++) {
+            float translation = i == midpoint
+                    ? midpointOffsetSize
+                    : offsetSize;
+            int directionFactor = mOrientationHandler.getSecondaryTranslationDirectionFactor() * -1;
+            translation *= directionFactor;
+            TaskView child = getTaskViewAt(i);
+            FloatProperty translationProperty = child.getSecondaryTaskOffsetTranslationProperty();
+            translationProperty.set(child, translation);
+            if (LIVE_TILE.get() && mEnableDrawingLiveTile && i == getRunningTaskIndex()) {
+                mLiveTileTaskViewSimulator.taskSecondaryTranslation.value = translation;
+                redrawLiveTile();
+            }
+        }
+    }
+
+    /**
+     * Computes the distance to offset the given child such that it is completely offscreen when
+     * translating away from its position in overview.
+     * @param offsetProgress From 0 to 1 where 0 means no offset and 1 means offset offscreen.
+     */
+    private float getVerticalOffsetSize(float offsetProgress) {
+        if (offsetProgress == 0) {
+            // Don't bother calculating everything below if we won't offset anyway.
+            return 0;
+        }
+        // First, find the distance to offscreen from the normal (centered) task position.
+        mTempRectF.set(mLastComputedTaskSize);
+        RectF taskPosition = mTempRectF;
+        float desiredTop = getHeight();
+        float distanceToOffscreen = desiredTop - taskPosition.top;
+        // Next, we need to account for the resistance translation if any (e.g. long swipe up).
+        float translationY = mTaskViewsSecondaryTranslation;
+        distanceToOffscreen -= translationY;
+        // Finally, we need to account for RecentsView scale, because it moves tasks based on its
+        // pivot. To do this, we move the task position to where it would be offscreen at scale = 1
+        // (computed above), then we apply the scale via getMatrix() to determine how much that
+        // moves the task from its desired position, and adjust the computed distance accordingly.
+        if (mLastComputedTaskBottomPushOutDistance == null) {
+            taskPosition.offsetTo(0, desiredTop + translationY);
+            getMatrix().mapRect(taskPosition);
+            mLastComputedTaskBottomPushOutDistance = (taskPosition.top - desiredTop) / getScaleY();
+        }
+        distanceToOffscreen -= mLastComputedTaskBottomPushOutDistance;
+        return distanceToOffscreen * offsetProgress;
+    }
+
     protected void setTaskViewsResistanceTranslation(float translation) {
         mTaskViewsSecondaryTranslation = translation;
         for (int i = 0; i < getTaskViewCount(); i++) {
@@ -2759,20 +2880,20 @@
         mLiveTileTaskViewSimulator.recentsViewSecondaryTranslation.value = translation;
     }
 
-    protected void setTaskViewsPrimaryTranslation(float translation) {
-        mTaskViewsPrimaryTranslation = translation;
+    protected void setTaskViewsPrimarySplitTranslation(float translation) {
+        mTaskViewsPrimarySplitTranslation = translation;
         for (int i = 0; i < getTaskViewCount(); i++) {
             TaskView task = getTaskViewAt(i);
-            task.getPrimaryDismissTranslationProperty().set(task, translation / getScaleY());
+            task.getPrimarySplitTranslationProperty().set(task, translation);
         }
-        mLiveTileTaskViewSimulator.recentsViewPrimaryTranslation.value = translation;
     }
 
-    /**
-     * TODO: Do not assume motion across X axis for adjacent page
-     */
-    public float getPageOffsetScale() {
-        return Math.max(getWidth(), 1);
+    protected void setTaskViewsSecondarySplitTranslation(float translation) {
+        mTaskViewsSecondarySplitTranslation = translation;
+        for (int i = 0; i < getTaskViewCount(); i++) {
+            TaskView task = getTaskViewAt(i);
+            task.getSecondarySplitTranslationProperty().set(task, translation);
+        }
     }
 
     /**
@@ -2788,8 +2909,9 @@
     public void initiateSplitSelect(TaskView taskView, SplitPositionOption splitPositionOption) {
         mSplitHiddenTaskView = taskView;
         SplitSelectStateController splitController = mSplitPlaceholderView.getSplitController();
-        splitController.setInitialTaskSelect(taskView,
-                splitPositionOption);
+        Rect initialBounds = new Rect(taskView.getLeft(), taskView.getTop(), taskView.getRight(),
+                taskView.getBottom());
+        splitController.setInitialTaskSelect(taskView, splitPositionOption, initialBounds);
         mSplitHiddenTaskViewIndex = indexOfChild(taskView);
         mSplitPlaceholderView.setLayoutParams(
                 splitController.getLayoutParamsForActivePosition(getResources(),
@@ -2809,7 +2931,10 @@
     }
 
     public PendingAnimation cancelSplitSelect(boolean animate) {
-        mSplitPlaceholderView.getSplitController().resetState();
+        SplitSelectStateController splitController = mSplitPlaceholderView.getSplitController();
+        SplitPositionOption splitOption = splitController.getActiveSplitPositionOption();
+        Rect initialBounds = splitController.getInitialBounds();
+        splitController.resetState();
         int duration = mActivity.getStateManager().getState().getTransitionDuration(getContext());
         PendingAnimation pendingAnim = new PendingAnimation(duration);
         if (!animate) {
@@ -2824,8 +2949,6 @@
         getPageScrolls(oldScroll, false,
                 view -> view.getVisibility() != GONE && view != mSplitHiddenTaskView);
 
-        // x is correct, y is before tasks move up
-        int[] locationOnScreen = mSplitHiddenTaskView.getLocationOnScreen();
         int[] newScroll = new int[getChildCount()];
         getPageScrolls(newScroll, false, SIMPLE_SCROLL_LOGIC);
 
@@ -2833,20 +2956,42 @@
         for (int i = mSplitHiddenTaskViewIndex; i >= 0; i--) {
             View child = getChildAt(i);
             if (child == mSplitHiddenTaskView) {
+                TaskView taskView = (TaskView) child;
 
-                int left = newScroll[i] + getPaddingStart();
-                int topMargin = mActivity.getDeviceProfile().overviewTaskThumbnailTopMarginPx;
-                int top = -mSplitHiddenTaskView.getHeight() - locationOnScreen[1];
-                mSplitHiddenTaskView.layout(left, top,
-                        left + mSplitHiddenTaskView.getWidth(),
-                        top + mSplitHiddenTaskView.getHeight());
-                pendingAnim.add(ObjectAnimator.ofFloat(mSplitHiddenTaskView, TRANSLATION_Y,
-                        -top + mSplitPlaceholderView.getHeight() - topMargin));
+                int dir = mOrientationHandler.getSplitTaskViewDismissDirection(splitOption,
+                        mActivity.getDeviceProfile());
+                FloatProperty<TaskView> dismissingTaskViewTranslate;
+                Rect hiddenBounds = new Rect(taskView.getLeft(), taskView.getTop(),
+                        taskView.getRight(), taskView.getBottom());
+                int distanceDelta = 0;
+                if (dir == PagedOrientationHandler.SPLIT_TRANSLATE_SECONDARY_NEGATIVE) {
+                    dismissingTaskViewTranslate = taskView
+                            .getSecondaryDissmissTranslationProperty();
+                    distanceDelta = initialBounds.top - hiddenBounds.top;
+                    taskView.layout(initialBounds.left, hiddenBounds.top, initialBounds.right,
+                            hiddenBounds.bottom);
+                } else {
+                    dismissingTaskViewTranslate = taskView
+                            .getPrimaryDismissTranslationProperty();
+                    distanceDelta = initialBounds.left - hiddenBounds.left;
+                    taskView.layout(hiddenBounds.left, initialBounds.top, hiddenBounds.right,
+                            initialBounds.bottom);
+                    if (dir == PagedOrientationHandler.SPLIT_TRANSLATE_PRIMARY_POSITIVE) {
+                        distanceDelta *= -1;
+                    }
+                }
+                pendingAnim.add(ObjectAnimator.ofFloat(mSplitHiddenTaskView,
+                        dismissingTaskViewTranslate,
+                        distanceDelta));
                 pendingAnim.add(ObjectAnimator.ofFloat(mSplitHiddenTaskView, ALPHA, 1));
             } else {
                 // If insertion is on last index (furthest from clear all), we directly add the view
                 // else we translate all views to the right of insertion index further right,
                 // ignore views to left
+                if (showAsGrid()) {
+                    // TODO(b/186800707) handle more elegantly for grid
+                    continue;
+                }
                 int scrollDiff = newScroll[i] - oldScroll[i];
                 if (scrollDiff != 0) {
                     FloatProperty translationProperty = child instanceof TaskView
@@ -2872,6 +3017,12 @@
         pendingAnim.addListener(new AnimationSuccessListener() {
             @Override
             public void onAnimationSuccess(Animator animator) {
+                // TODO(b/186800707) Figure out how to undo for grid view
+                //  Need to handle cases where dismissed task is
+                //  * Top Row
+                //  * Bottom Row
+                //  * Focused Task
+                updateGridProperties();
                 resetFromSplitSelectionState();
             }
         });
@@ -2881,13 +3032,16 @@
 
     private void resetFromSplitSelectionState() {
         mSplitHiddenTaskView.setTranslationY(0);
-        int pageToSnapTo = mCurrentPage;
-        if (mSplitHiddenTaskViewIndex <= pageToSnapTo) {
-            pageToSnapTo += 1;
-        } else {
-            pageToSnapTo = mSplitHiddenTaskViewIndex;
+        if (!showAsGrid()) {
+            // TODO(b/186800707)
+            int pageToSnapTo = mCurrentPage;
+            if (mSplitHiddenTaskViewIndex <= pageToSnapTo) {
+                pageToSnapTo += 1;
+            } else {
+                pageToSnapTo = mSplitHiddenTaskViewIndex;
+            }
+            snapToPageImmediately(pageToSnapTo);
         }
-        snapToPageImmediately(pageToSnapTo);
         onLayout(false /*  changed */, getLeft(), getTop(), getRight(), getBottom());
         resetTaskVisuals();
         mSplitHiddenTaskView = null;
@@ -3027,6 +3181,11 @@
             return new PendingAnimation(duration);
         }
 
+        // When swiping down from overview to tasks, ensures the snapped page's scroll maintain
+        // invariant between quick switch and overview, to ensure a smooth animation transition.
+        updateGridProperties();
+        updateScrollSynchronously();
+
         int targetSysUiFlags = tv.getThumbnail().getSysUiStatusNavFlags();
         final boolean[] passedOverviewThreshold = new boolean[] {false};
         ValueAnimator progressAnim = ValueAnimator.ofFloat(0, 1);
@@ -3349,8 +3508,16 @@
         if (pageIndex == -1) {
             return 0;
         }
+
+        int overScrollShift = getOverScrollShift();
+        if (mAdjacentPageVerticalOffset > 0) {
+            // Don't dampen the scroll (due to overscroll) if the adjacent tasks are offscreen, so
+            // that the page can move freely given there's no visual indication why it shouldn't.
+            overScrollShift = (int) Utilities.mapRange(mAdjacentPageVerticalOffset, overScrollShift,
+                    getUndampedOverScrollShift());
+        }
         return getScrollForPage(pageIndex) - mOrientationHandler.getPrimaryScroll(this)
-                + getOverScrollShift();
+                + overScrollShift;
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 3349b74..6f3aade 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -172,6 +172,32 @@
                 }
             };
 
+    private static final FloatProperty<TaskView> SPLIT_SELECT_TRANSLATION_X =
+            new FloatProperty<TaskView>("splitSelectTranslationX") {
+                @Override
+                public void setValue(TaskView taskView, float v) {
+                    taskView.setSplitSelectTranslationX(v);
+                }
+
+                @Override
+                public Float get(TaskView taskView) {
+                    return taskView.mSplitSelectTranslationX;
+                }
+            };
+
+    private static final FloatProperty<TaskView> SPLIT_SELECT_TRANSLATION_Y =
+            new FloatProperty<TaskView>("splitSelectTranslationY") {
+                @Override
+                public void setValue(TaskView taskView, float v) {
+                    taskView.setSplitSelectTranslationY(v);
+                }
+
+                @Override
+                public Float get(TaskView taskView) {
+                    return taskView.mSplitSelectTranslationY;
+                }
+            };
+
     private static final FloatProperty<TaskView> DISMISS_TRANSLATION_X =
             new FloatProperty<TaskView>("dismissTranslationX") {
                 @Override
@@ -345,6 +371,9 @@
     // The following grid translations scales with mGridProgress.
     private float mGridTranslationX;
     private float mGridTranslationY;
+    // Used when in SplitScreenSelectState
+    private float mSplitSelectTranslationY;
+    private float mSplitSelectTranslationX;
 
     private ObjectAnimator mIconAndDimAnimator;
     private float mIconScaleAnimStartProgress = 0;
@@ -553,6 +582,7 @@
 
                 @Override
                 public void onAnimationEnd(Animator animator) {
+                    recentsView.getLiveTileTaskViewSimulator().setDrawsBelowRecents(true);
                     mIsClickableAsLiveTile = true;
                 }
             });
@@ -777,8 +807,7 @@
         float upperClamp = invert ? 1 : iconScalePercentage;
         float scale = Interpolators.clampToProgress(FAST_OUT_SLOW_IN, lowerClamp, upperClamp)
                 .getInterpolation(progress);
-        mIconView.setScaleX(scale);
-        mIconView.setScaleY(scale);
+        mIconView.setAlpha(scale);
         if (mContextualChipWrapper != null && mContextualChipWrapper != null) {
             mContextualChipWrapper.setAlpha(scale);
             mContextualChipWrapper.setScaleX(Math.min(scale, comp(mModalness)));
@@ -826,8 +855,10 @@
     protected void resetViewTransforms() {
         // fullscreenTranslation and accumulatedTranslation should not be reset, as
         // resetViewTransforms is called during Quickswitch scrolling.
-        mDismissTranslationX = mTaskOffsetTranslationX = mTaskResistanceTranslationX = 0f;
-        mDismissTranslationY = mTaskOffsetTranslationY = mTaskResistanceTranslationY = 0f;
+        mDismissTranslationX = mTaskOffsetTranslationX = mTaskResistanceTranslationX =
+                mSplitSelectTranslationX = 0f;
+        mDismissTranslationY = mTaskOffsetTranslationY = mTaskResistanceTranslationY =
+                mSplitSelectTranslationY = 0f;
         applyTranslationX();
         applyTranslationY();
         setTranslationZ(0);
@@ -957,6 +988,15 @@
         setScaleY(scale);
     }
 
+    private void setSplitSelectTranslationX(float x) {
+        mSplitSelectTranslationX = x;
+        applyTranslationX();
+    }
+
+    private void setSplitSelectTranslationY(float y) {
+        mSplitSelectTranslationY = y;
+        applyTranslationY();
+    }
     private void setDismissTranslationX(float x) {
         mDismissTranslationX = x;
         applyTranslationX();
@@ -1057,12 +1097,12 @@
 
     private void applyTranslationX() {
         setTranslationX(mDismissTranslationX + mTaskOffsetTranslationX + mTaskResistanceTranslationX
-                + getPersistentTranslationX());
+                + mSplitSelectTranslationX + getPersistentTranslationX());
     }
 
     private void applyTranslationY() {
         setTranslationY(mDismissTranslationY + mTaskOffsetTranslationY + mTaskResistanceTranslationY
-                + getPersistentTranslationY());
+                + mSplitSelectTranslationY + getPersistentTranslationY());
     }
 
     /**
@@ -1086,6 +1126,16 @@
                 + getGridTrans(mGridTranslationY);
     }
 
+    public FloatProperty<TaskView> getPrimarySplitTranslationProperty() {
+        return getPagedOrientationHandler().getPrimaryValue(
+                SPLIT_SELECT_TRANSLATION_X, SPLIT_SELECT_TRANSLATION_Y);
+    }
+
+    public FloatProperty<TaskView> getSecondarySplitTranslationProperty() {
+        return getPagedOrientationHandler().getSecondaryValue(
+                SPLIT_SELECT_TRANSLATION_X, SPLIT_SELECT_TRANSLATION_Y);
+    }
+
     public FloatProperty<TaskView> getPrimaryDismissTranslationProperty() {
         return getPagedOrientationHandler().getPrimaryValue(
                 DISMISS_TRANSLATION_X, DISMISS_TRANSLATION_Y);
@@ -1101,6 +1151,11 @@
                 TASK_OFFSET_TRANSLATION_X, TASK_OFFSET_TRANSLATION_Y);
     }
 
+    public FloatProperty<TaskView> getSecondaryTaskOffsetTranslationProperty() {
+        return getPagedOrientationHandler().getSecondaryValue(
+                TASK_OFFSET_TRANSLATION_X, TASK_OFFSET_TRANSLATION_Y);
+    }
+
     public FloatProperty<TaskView> getTaskResistanceTranslationProperty() {
         return getPagedOrientationHandler().getSecondaryValue(
                 TASK_RESISTANCE_TRANSLATION_X, TASK_RESISTANCE_TRANSLATION_Y);
@@ -1418,9 +1473,10 @@
     }
 
     private void setColorTint(float amount) {
-        mSnapshotView.setDimAlpha(amount);
-        mIconView.setIconColorTint(mTintingColor, amount);
-        mDigitalWellBeingToast.setBannerColorTint(mTintingColor, amount);
+        mTintAmount = amount;
+        mSnapshotView.setDimAlpha(mTintAmount);
+        mIconView.setIconColorTint(mTintingColor, mTintAmount);
+        mDigitalWellBeingToast.setBannerColorTint(mTintingColor, mTintAmount);
     }
 
     private float getColorTint() {
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index 88f1850..f93d87c 100644
--- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -61,6 +61,7 @@
 import com.android.launcher3.util.rule.FailureWatcher;
 import com.android.quickstep.views.RecentsView;
 
+import org.junit.After;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.RuleChain;
@@ -141,6 +142,12 @@
         }
     }
 
+    @After
+    public void verifyLauncherState() {
+        // Limits UI tests affecting tests running after them.
+        AbstractQuickStepTest.checkDetectedLeaks(mLauncher);
+    }
+
     // b/143488140
     //@NavigationModeSwitch
     @Test
diff --git a/res/color/cell_layout_bg_color_active.xml b/res/color/cell_layout_bg_color_active.xml
index e826489..d1a3d7c 100644
--- a/res/color/cell_layout_bg_color_active.xml
+++ b/res/color/cell_layout_bg_color_active.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:alpha="0.4"
+    <item android:alpha="0.3"
       android:color="?android:attr/colorAccent"/>
 </selector>
diff --git a/res/color/cell_layout_bg_color_inactive.xml b/res/color/cell_layout_bg_color_inactive.xml
index d60a27a..0632100 100644
--- a/res/color/cell_layout_bg_color_inactive.xml
+++ b/res/color/cell_layout_bg_color_inactive.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:alpha="0.3"
+    <item android:alpha="0.25"
         android:color="?android:attr/colorAccent"/>
 </selector>
\ No newline at end of file
diff --git a/res/layout/widgets_bottom_sheet_content.xml b/res/layout/widgets_bottom_sheet_content.xml
index a9d523a..85c6488 100644
--- a/res/layout/widgets_bottom_sheet_content.xml
+++ b/res/layout/widgets_bottom_sheet_content.xml
@@ -15,10 +15,12 @@
 -->
 <merge xmlns:android="http://schemas.android.com/apk/res/android">
     <View
+        android:id="@+id/collapse_handle"
         android:layout_width="48dp"
         android:layout_height="2dp"
         android:layout_gravity="center_horizontal"
         android:layout_marginBottom="16dp"
+        android:visibility="gone"
         android:background="?android:attr/textColorSecondary"/>
     <TextView
         style="@style/TextHeadline"
diff --git a/res/layout/widgets_full_sheet_search_and_recommendations.xml b/res/layout/widgets_full_sheet_search_and_recommendations.xml
index bfce01d..ce7a682 100644
--- a/res/layout/widgets_full_sheet_search_and_recommendations.xml
+++ b/res/layout/widgets_full_sheet_search_and_recommendations.xml
@@ -13,7 +13,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<LinearLayout
+<com.android.launcher3.widget.picker.SearchAndRecommendationsView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/search_and_recommendations_container"
     android:layout_width="match_parent"
@@ -49,4 +49,4 @@
         android:paddingVertical="@dimen/recommended_widgets_table_vertical_padding"
         android:layout_marginTop="16dp"
         android:visibility="gone"/>
-</LinearLayout>
+</com.android.launcher3.widget.picker.SearchAndRecommendationsView>
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index e516468..c6d372a 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Dubbeltik en hou om \'n legstuk te skuif of gebruik gepasmaakte handelinge."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d breed by %2$d hoog"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Raak en hou die legstuk om dit op die Tuisskerm rond te beweeg"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Voeg by Tuisskerm"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> legstukke</item>
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> legstuk</item>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 5241acd..7123a8b 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"ምግብርን ለማንቀሳቀስ ወይም ብጁ እርምጃዎችን ለመጠቀም ሁለቴ መታ ያድርጉ እና ይያዙ።"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d ስፋት በ%2$d ከፍታ"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"በመነሻ ገጽ አካባቢ ላይ ለማንቀሳቀስ ነክተው ይያዙት"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"ወደ መነሻ ገጽ አክል"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> ንዑስ ፕሮግራሞች</item>
       <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> ንዑስ ፕሮግራሞች</item>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 669aa91..c6ab52a 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"انقر مرتين مع تثبيت إصبعك لنقل أداة أو استخدام الإجراءات المخصّصة."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"‏العرض %1$d الطول %2$d"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"يمكنك النقر على الأداة مع الاستمرار لتحريكها على الشاشة الرئيسية."</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"إضافة إلى الشاشة الرئيسية"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="zero"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> أداة</item>
       <item quantity="two">أداتان (<xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g>)</item>
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index 931b3ab..5e80e65 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"কোনো ৱিজেট স্থানান্তৰ কৰিবলৈ দুবাৰ টিপি ধৰি ৰাখক অথবা কাষ্টম কাৰ্য ব্যৱহাৰ কৰক।"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d বহল x %2$d ওখ"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"ৱিজেটটো গৃহ স্ক্ৰীনৰ আশে-পাশে নিবলৈ সেইটোত স্পৰ্শ কৰি ধৰি ৰাখক"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"গৃহ স্ক্ৰীনত যোগ কৰক"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> টা ৱিজেট</item>
       <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> টা ৱিজেট</item>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index 8b525f2..17f600c 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Vidceti daşımaq üçün iki dəfə toxunub saxlayın və ya fərdi əməliyyatlardan istifadə edin."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%2$d hündürlük %1$d enində"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Əsas ekranda hərəkət etdirmək üçün vidcetə toxunub saxlayın"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Əsas ekrana əlavə edin"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> vidcet</item>
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> vidcet</item>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index d4f013a..dc04fed 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Dvaput dodirnite i zadržite da biste pomerali vidžet ili koristite prilagođene radnje."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d×%2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"širina od %1$d i visina od %2$d"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Dodirnite i zadržite vidžet da biste ga pomerali po početnom ekranu"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Dodaj na početni ekran"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> vidžet</item>
       <item quantity="few"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> vidžeta</item>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index cf02d48..9bb2c00 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Дакраніцеся двойчы і ўтрымлівайце, каб перамясціць віджэт або выкарыстоўваць спецыяльныя дзеянні."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Шырына: %1$d, вышыня: %2$d"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Утрымліваючы віджэт націснутым, перамяшчайце яго па Галоўным экране"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Дадаць на Галоўны экран"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> віджэт</item>
       <item quantity="few"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> віджэты</item>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index b3c5ca4..0d398ee 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Докоснете двукратно и задръжте за преместване на приспособление или използвайте персонал. действия."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Ширина %1$d и височина %2$d"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Докоснете приспособлението и го задръжте, за да го местите на началния екран"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Добавяне към началния екран"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> приспособления</item>
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> приспособление</item>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index fcd6f3f..50aff80 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Dvaput dodirnite i zadržite da pomjerite vidžet ili da koristite prilagođene radnje."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Širina %1$d, visina %2$d"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Dodirnite i držite vidžet da ga pomjerate po Početnom ekranu"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Dodaj na početni ekran"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> vidžet</item>
       <item quantity="few"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> vidžeta</item>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index d54670b..9ea5fc7 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Fes doble toc i mantén premut per moure un widget o per utilitzar accions personalitzades."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d d\'amplada per %2$d d\'alçada"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Mantén premut el widget per moure\'l per la pantalla d\'inici"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Afegeix a la pantalla d\'inici"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widgets</item>
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> widget</item>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index a9ea587..9ae37cc 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Dvojitým klepnutím a podržením přesunete widget, případně použijte vlastní akce."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"šířka %1$d, výška %2$d"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Pokud widgetem chcete pohybovat po ploše, dotkněte se ho a podržte ho"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Přidat na plochu"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="few"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widgety</item>
       <item quantity="many"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widgetu</item>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index e5dacac..3b4e07b 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Tryk to gange, og hold en widget nede for at flytte den eller bruge tilpassede handlinger."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d i bredden og %2$d i højden"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Hold widgetten nede for at flytte den rundt på startskærmen"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Føj til startskærm"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widget</item>
       <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widgets</item>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index a9deedb..1d93bfc 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Πατήστε δύο φορές παρατεταμένα για μετακίνηση γραφικού στοιχείου ή χρήση προσαρμοσμένων ενεργειών."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Πλάτος %1$d επί ύψος %2$d"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Αγγίξτε παρατεταμένα το γραφικό στοιχείο για να το μετακινήσετε στην Αρχική οθόνη"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Προσθήκη στην Αρχική οθόνη"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> γραφικά στοιχεία</item>
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> γραφικό στοιχείο</item>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 0330795..79d33fd 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Presiona dos veces y mantén presionado para mover un widget o usar acciones personalizadas."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de ancho por %2$d de alto"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Mantén presionado el widget para moverlo por la pantalla principal"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Agregar a pantalla principal"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widgets</item>
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> widget</item>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 888436f..1bc4c60 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Toca dos veces y mantén pulsado un widget para moverlo o usar acciones personalizadas."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de ancho por %2$d de alto"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Mantén pulsado el widget para moverlo por la pantalla de inicio"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Añadir a la pantalla de inicio"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="other">Widgets: <xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g></item>
       <item quantity="one">Widget: <xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g></item>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index 15b6177..916aafc 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Vidina teisaldamiseks või kohandatud toimingute kasutamiseks topeltpuudutage ja hoidke all."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d lai ja %2$d kõrge"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Vidina avakuval liigutamiseks puudutage vidinat ja hoidke all"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Lisa avakuvale"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> vidinat</item>
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> vidin</item>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index 5309052..901d3cb 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Sakatu birritan eta eduki sakatuta widget bat mugitzeko edo ekintza pertsonalizatuak erabiltzeko."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d zabal eta %2$d luze"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Eduki sakatuta widgeta hasierako pantailan zehar mugitzeko"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Gehitu hasierako pantailan"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widget</item>
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> widget</item>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index b0c300c..194ea0d 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"برای جابه‌جا کردن ابزارک یا استفاده از کنش‌های سفارشی، دوضربه بزنید و نگه دارید."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"‏%1$d عرض در %2$d طول"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"ابزارک را لمس و کنید و نگه دارید تا آن را در صفحه اصلی حرکت دهید"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"افزودن به صفحه اصلی"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> ابزارک</item>
       <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> ابزارک</item>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 47466cf..6cffafc 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Kaksoisnapauta ja paina pitkään, niin voit siirtää widgetiä tai käyttää muokattuja toimintoja."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Leveys: %1$d, korkeus: %2$d"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Voit siirtää widgetiä aloitusnäytöllä koskettamalla sitä pitkään"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Lisää aloitusnäytölle"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widgetiä</item>
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> widget</item>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index f50d837..3ebd61a 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Touchez 2x un widget et maintenez le doigt dessus pour le déplacer ou utiliser des actions personnalisées."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de largeur sur %2$d de hauteur"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Maintenez le doigt sur le widget pour le déplacer sur l\'écran d\'accueil"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Ajouter à l\'écran d\'accueil"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widget</item>
       <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widgets</item>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index f51c3d9..bb56bac 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Appuyez deux fois et maintenez la pression pour déplacer widget ou utiliser actions personnalisées."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d x %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de largeur et %2$d de hauteur"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Appuyez de manière prolongée sur le widget pour le déplacer sur l\'écran d\'accueil"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Ajouter à l\'écran d\'accueil"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widget</item>
       <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widgets</item>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index c3e3596..d3f3f0b 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Toca dúas veces un widget e manteno premido para movelo ou utiliza accións personalizadas."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de largo por %2$d de alto"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Mantén premido o widget para movelo pola pantalla de inicio"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Engadir á pantalla de inicio"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widgets</item>
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> widget</item>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 61776d6..9a3c56c 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"किसी विजेट को एक से दूसरी जगह ले जाने के लिए, उस पर दो बार टैप करके दबाकर रखें या पसंद के मुताबिक कार्रवाइयां इस्तेमाल करें."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d चौड़ाई गुणा %2$d ऊंचाई"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"होम स्क्रीन पर यहां-वहां ले जाने के लिए विजेट को दबाकर रखें"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"होम स्क्रीन पर जोड़ें"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> विजेट</item>
       <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> विजेट</item>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 171f5d0..d1cb5bf 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Dvaput dodirnite i zadržite pritisak da biste premjestili widget ili upotrijebite prilagođene radnje"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d širine i %2$d visine"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Dodirnite i zadržite widget da biste ga pomicali po početnom zaslonu"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Dodaj na početni zaslon"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widget</item>
       <item quantity="few"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widgeta</item>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index bbb24e9..9e2c358 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Modul áthelyezéséhez koppintson duplán, tartsa nyomva az ujját, vagy használjon egyéni műveleteket."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d széles és %2$d magas"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Tartsa lenyomva a modult a kezdőképernyőn való mozgatáshoz"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Hozzáadás a kezdőképernyőhöz"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> modul</item>
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> modul</item>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index c1889ff..d42e4f9 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Ketuk dua kali &amp; tahan untuk memindahkan widget atau gunakan tindakan khusus."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"lebar %1$d x tinggi %2$d"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Sentuh lama widget untuk memindahkannya di sekitar Layar utama"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Tambahkan ke Layar utama"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widget</item>
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> widget</item>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index 1dce7c7..30e73b4 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Ýttu tvisvar og haltu fingri á græju til að færa hana eða notaðu sérsniðnar aðgerðir."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d á breidd og %2$d á hæð"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Haltu fingri á græjunni til að hreyfa hana um heimaskjáinn"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Bæta á heimaskjá"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> græja</item>
       <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> græjur</item>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 3d1cb5b..1d420a9 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Tocca due volte e tieni premuto per spostare un widget o per usare le azioni personalizzate."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d di larghezza per %2$d di altezza"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Tocca e tieni premuto il widget per spostarlo nella schermata Home"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Aggiungi a schermata Home"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widget</item>
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> widget</item>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 4cf0eb2..7cd53fa 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"כדי להעביר ווידג\'ט למקום אחר או להשתמש בפעולות מותאמות אישית, יש ללחוץ פעמיים ולא להרפות."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"‏רוחב %1$d על גובה %2$d"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"יש ללחוץ לחיצה ארוכה על הווידג\'ט כדי להזיז אותו ברחבי מסך הבית"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"הוספה למסך הבית"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="two"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> ווידג\'טים</item>
       <item quantity="many"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> ווידג\'טים</item>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 77efbad..c1136a4 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"ウィジェットをダブルタップして長押ししながら移動するか、カスタム操作を使用してください。"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$dx%2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"幅 %1$d、高さ %2$d"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"ウィジェットを押し続けた状態で、ホーム画面上に移動させます。"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"ホーム画面に追加"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> 件のウィジェット</item>
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> 件のウィジェット</item>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index e3be57f..c701795 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"ორმაგი შეხებით აირჩიეთ და გეჭიროთ ვიჯეტის გადასაადგილებლად ან მორგებული მოქმედებების გამოსაყენებლად."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"სიგრძე: %1$d, სიგანე: %2$d"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"ხანგრძლივად შეეხეთ ვიჯეტს მთავარ ეკრანზე მის გადასაადგილებლად"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"მთავარ ეკრანზე დამატება"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> ვიჯეტი</item>
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> ვიჯეტი</item>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index 392dc8b..8f92db0 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Виджетті жылжыту үшін екі рет түртіңіз де, ұстап тұрыңыз немесе арнаулы әрекеттерді пайдаланыңыз."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Ені: %1$d, биіктігі: %2$d"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Негізгі экран бойымен қозғалту үшін виджетті басып, ұстап тұрыңыз."</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Негізгі экранға қосу"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> виджет</item>
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> виджет</item>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index dd04c01..8a82bda 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"ចុចពីរដង រួចសង្កត់ឱ្យជាប់ ដើម្បីផ្លាស់ទី​ធាតុក្រាហ្វិក ឬប្រើ​សកម្មភាព​តាមបំណង​។"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"ទទឺង %1$d គុណនឹងកម្ពស់ %2$d"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"ចុចធាតុក្រាហ្វិក​ឱ្យជាប់ ដើម្បីផ្លាស់ទីវា​ជុំវិញអេក្រង់ដើម"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"បញ្ចូល​ទៅអេក្រង់ដើម"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="other">ធាតុ​ក្រាហ្វិក <xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g></item>
       <item quantity="one">ធាតុ​ក្រាហ្វិក <xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g></item>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index 7e24dc3..b98ee5c 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"두 번 탭한 다음 길게 터치하여 위젯을 이동하거나 맞춤 작업을 사용하세요."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d×%2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"너비 %1$d, 높이 %2$d"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"홈 화면에서 위젯을 이동하려면 길게 터치하세요."</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"홈 화면에 추가"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="other">위젯 <xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g>개</item>
       <item quantity="one">위젯 <xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g>개</item>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index a4f54bf..62fb75c 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Виджетти жылдыруу үчүн эки жолу таптап, кармап туруңуз же ыңгайлаштырылган аракеттерди колдонуңуз."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Туурасы: %1$d, бийиктиги: %2$d"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Башкы экранга жылдыруу үчүн виджетти коё бербей басып туруңуз"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Башкы экранга кошуу"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> виджет</item>
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> виджет</item>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 43055a5..0677e88 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Dukart palieskite ir palaikykite, kad perkeltumėte valdiklį ar naudotumėte tinkintus veiksmus."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d plotis ir %2$d aukštis"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Paliesdami ir palaikydami valdiklį galite judėti pagrindiniame ekrane"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Pridėti prie pagrindinio ekrano"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> valdiklis</item>
       <item quantity="few"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> valdikliai</item>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 8bcf250..6310375 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Lai pārvietotu logrīku, uz tā veiciet dubultskārienu un turiet. Varat arī veikt pielāgotas darbības."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d plats un %2$d augsts"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Pieskarieties logrīkam un turiet to, lai to pārvietotu pa sākuma ekrānu."</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Pievienot sākuma ekrānam"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="zero"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> logrīku</item>
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> logrīks</item>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index b512dc2..3e99d67 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Жижиг хэрэгслийг зөөх эсвэл захиалгат үйлдлийг ашиглахын тулд хоёр товшоод, удаан дарна уу."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d өргөн %2$d өндөр"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Жижиг хэрэгслийг Үндсэн нүүрний эргэн тойронд зөөхийн тулд түүнд хүрээд, удаан дарна уу"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Үндсэн нүүрэнд нэмэх"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> жижиг хэрэгсэл</item>
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> жижиг хэрэгсэл</item>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index 91f2a4d..9401040 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Ketik dua kali &amp; tahan untuk menggerakkan widget atau menggunakan tindakan tersuai."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Lebar %1$d kali tinggi %2$d"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Sentuh &amp; tahan widget untuk menggerakkan widget di sekitar Skrin utama"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Tambah pada Skrin utama"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widget</item>
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> widget</item>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index f042629..4dcd6c1 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"ဝိဂျက်ကို ရွှေ့ရန် (သို့) စိတ်ကြိုက်လုပ်ဆောင်ချက်များကို သုံးရန် နှစ်ချက်တို့ပြီး ဖိထားပါ။"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"အလျား %1$d နှင့် အမြင့် %2$d"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"ပင်မစာမျက်နှာအနီးတွင် ဝိဂျက်ကိုရွှေ့ရန် ၎င်းကို တို့ထိ၍ဖိထားပါ"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"ပင်မစာမျက်နှာသို့ ထည့်ရန်"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="other">ဝိဂျက် <xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> ခု</item>
       <item quantity="one">ဝိဂျက် <xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> ခု</item>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 540d726..ad5affb 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Dobbelttrykk og hold inne for å flytte en modul eller bruke tilpassede handlinger."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d bredde x %2$d høyde"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Trykk og hold på modulen for å bevege den rundt på startskjermen"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Legg til på startskjermen"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> moduler</item>
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> modul</item>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index d4478b7..cc16a89 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Dubbeltik en houd vast om een widget te verplaatsen of aangepaste acties te gebruiken."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d breed en %2$d hoog"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Houd de widget ingedrukt om deze te verplaatsen op het startscherm"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Toevoegen aan startscherm"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widgets</item>
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> widget</item>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index 3595f33..7f3495a 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"ଏକ ୱିଜେଟକୁ ମୁଭ୍ କରିବା ପାଇଁ ଦୁଇଥର-ଟାପ୍ କରି ଧରି ରଖନ୍ତୁ କିମ୍ବା କଷ୍ଟମ୍ କାର୍ଯ୍ୟଗୁଡ଼ିକୁ ବ୍ୟବହାର କରନ୍ତୁ।"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d ଓସାର ଓ %2$d ଉଚ୍ଚ"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"ମୂଳସ୍କ୍ରିନର ଆଖପାଖରେ ୱିଜେଟକୁ ମୁଭ୍ କରିବା ପାଇଁ ଏହାକୁ ସ୍ପର୍ଶ କରି ଧରି ରଖନ୍ତୁ"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"ମୂଳସ୍କ୍ରିନରେ ଯୋଗ କରନ୍ତୁ"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g>ଟି ୱିଜେଟ୍</item>
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g>ଟି ୱିଜେଟ୍</item>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 306a9a0..ceb8046 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Naciśnij dwukrotnie i przytrzymaj, aby przenieść widżet lub użyć działań niestandardowych."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Szerokość %1$d, wysokość %2$d"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Kliknij i przytrzymaj widżet, by poruszać nim po ekranie głównym."</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Dodaj do ekranu głównego"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="few"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widżety</item>
       <item quantity="many"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widżetów</item>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index a104931..e40accf 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Toque duas vezes sem soltar para mover um widget ou utilizar ações personalizadas."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de largura por %2$d de altura"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Toque sem soltar no widget para o mover à volta do ecrã principal"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Adicionar ao ecrã principal"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widgets</item>
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> widget</item>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 35d1e5d..8b18329 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Toque duas vezes e mantenha a tela pressionada para mover um widget ou usar ações personalizadas."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d de largura por %2$d de altura"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Toque no widget e mantenha-o pressionado para movê-lo pela tela inicial"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Adicionar à tela inicial"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widget</item>
       <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widgets</item>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index f614b18..a71f999 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Atingeți de două ori și țineți apăsat pentru a muta un widget sau folosiți acțiuni personalizate."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d lățime și %2$d înălțime"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Atingeți lung widgetul pentru a-l muta pe ecranul de pornire"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Adăugați pe ecranul de pornire"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="few"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widgeturi</item>
       <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> de widgeturi</item>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 3336b0b..0c1acbc 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Чтобы использовать специальные действия или перенести виджет, нажмите на него дважды и удерживайте."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d x %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Ширина %1$d, высота %2$d"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Нажмите на виджет и удерживайте его, чтобы переместить в нужное место на главном экране."</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Добавить на главный экран"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> виджет</item>
       <item quantity="few"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> виджета</item>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index fa3287d..6b27168 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"විජට් එකක් ගෙන යාමට හෝ අභිරුචි ක්‍රියා භාවිත කිරීමට දෙවරක් තට්ටු කර අල්ලා ගෙන සිටින්න."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"පළල %1$d උස %2$d"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"එය මුල් පිටු තිරය වටා ගෙන යාමට විජට් එක ස්පර්ශ කර අල්ලා ගන්න"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"මුල් පිටු තිරය වෙත එක් කරන්න"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="one">විජට් <xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g></item>
       <item quantity="other">විජට් <xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g></item>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index bd90155..7ea59d6 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Dvojitým klepnutím a pridržaním presuňte miniaplikáciu alebo použite vlastné akcie."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"šírka %1$d, výška %2$d"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Po pridržaní môžete miniaplikáciu môžete posúvať po ploche"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Pridať na plochu"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="few"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> miniaplikácie</item>
       <item quantity="many"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widgets</item>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index df1db7e..f74bdcc 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Dvakrat se dotaknite pripomočka in ga pridržite, da ga premaknete, ali pa uporabite dejanja po meri."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Širina %1$d, višina %2$d"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Pridržite pripomoček, če ga želite premikati po začetnem zaslonu."</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Dodaj na začetni zaslon"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> pripomoček</item>
       <item quantity="two"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> pripomočka</item>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index 4d97676..9a53936 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Trokit dy herë dhe mbaje shtypur një miniapliikacion për ta zhvendosur atë ose për të përdorur veprimet e personalizuara."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d i gjerë me %2$d i lartë"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Prek dhe mbaj miniaplikacionin për ta lëvizur nëpër \"Ekranin bazë\""</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Shto në \"Ekranin bazë\""</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> miniaplikacione</item>
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> miniaplikacion</item>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index 4103b00..4f67b23 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Двапут додирните и задржите да бисте померали виџет или користите прилагођене радње."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d×%2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"ширина од %1$d и висина од %2$d"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Додирните и задржите виџет да бисте га померали по почетном екрану"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Додај на почетни екран"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> виџет</item>
       <item quantity="few"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> виџета</item>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index fb3e8c1..91b9b88 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Tryck snabbt två gånger och håll kvar för att flytta en widget eller använda anpassade åtgärder."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d bred gånger %2$d hög"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Tryck länge på widgeten om du vill flytta den på startskärmen"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Lägg till på startskärmen"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widgetar</item>
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> widget</item>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 6caadaa..6227239 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Gusa mara mbili na ushikilie ili usogeze wijeti au utumie vitendo maalum."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Upana wa %1$d na kimo cha %2$d"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Gusa na ushikilie wijeti ili uisogeze kwenye Skrini ya kwanza"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Weka kwenye Skrini ya kwanza"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="other">Wijeti <xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g></item>
       <item quantity="one">Wijeti <xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g></item>
diff --git a/res/values-sw600dp/config.xml b/res/values-sw600dp/config.xml
index d50115b..09bdaaf 100644
--- a/res/values-sw600dp/config.xml
+++ b/res/values-sw600dp/config.xml
@@ -1,6 +1,3 @@
 <resources>
     <bool name="allow_rotation">true</bool>
-
-    <!-- Hotseat -->
-    <bool name="hotseat_transpose_layout_with_orientation">false</bool>
 </resources>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index a2bc2c5..cae7e24 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"விட்ஜெட்டை நகர்த்த இருமுறை தட்டிப் பிடிக்கவும் அல்லது பிரத்தியேகச் செயல்களைப் பயன்படுத்தவும்."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d அகலத்திற்கு %2$d உயரம்"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"முகப்புத் திரைக்கு விட்ஜெட்டை நகர்த்த அதைத் தொட்டுப் பிடிக்கவும்"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"முகப்புத் திரையில் சேர்"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> விட்ஜெட்டுகள்</item>
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> விட்ஜெட்</item>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 1b79b9c..4f98cd2 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"แตะสองครั้งค้างไว้เพื่อย้ายวิดเจ็ตหรือใช้การดำเนินการที่กำหนดเอง"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"กว้าง %1$d x สูง %2$d"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"แตะวิดเจ็ตค้างไว้เพื่อย้ายไปรอบๆ หน้าจอหลัก"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"เพิ่มลงในหน้าจอหลัก"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="other">วิดเจ็ต <xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> รายการ</item>
       <item quantity="one">วิดเจ็ต <xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> รายการ</item>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 58e8c8a..a3504fe 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"I-double tap at pindutin nang matagal para ilipat ang widget o gumamit ng mga custom na pagkilos."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d ang lapad at %2$d ang taas"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Pindutin nang matagal ang widget para ilipat-lipat ito sa Home screen"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Idagdag sa Home screen"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widget</item>
       <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> na widget</item>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index bc6ad44..8029aeb 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Widget\'ı taşımak veya özel işlemleri kullanmak için iki kez dokunup basılı tutun."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"genişlik: %1$d, yükseklik: %2$d"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Ana ekranda taşımak için widget\'a dokunup basılı tutun"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Ana ekrana ekle"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> widget</item>
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> widget</item>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index fdba5e61..5e179aa 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Двічі натисніть і втримуйте віджет, щоб перемістити його або виконати інші дії."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Ширина – %1$d, висота – %2$d"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Натисніть і втримуйте віджет, щоб переміщувати його головним екраном"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Додати на головний екран"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> віджет</item>
       <item quantity="few"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> віджети</item>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index 1ab449d..9c8f7ed 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Ikki marta bosib va bosib turgan holatda vidjetni tanlang yoki maxsus amaldan foydalaning."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Eni %1$d, bo‘yi %2$d"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Bosh ekranda surish uchun vidjet ustiga bosib turing"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Bosh ekranga chiqarish"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> ta vidjet</item>
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> ta vidjet</item>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index acb101b..a026dbc 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Nhấn đúp và giữ để di chuyển một tiện ích hoặc sử dụng các thao tác tùy chỉnh."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"Rộng %1$d x cao %2$d"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Chạm và giữ để di chuyển tiện ích xung quanh Màn hình chính"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Thêm vào Màn hình chính"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> tiện ích</item>
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> tiện ích</item>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index 5b06896..d32611d 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"㩒兩下之後㩒住,就可以郁小工具或者用自訂操作。"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d 闊,%2$d 高"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"按住小工具即可將其移至主畫面上任何位置"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"新增至主畫面"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> 個小工具</item>
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> 個小工具</item>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 9566a8c..58e8f19 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"輕觸兩下並按住即可移動小工具或使用自訂操作。"</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"寬度為 %1$d,高度為 %2$d"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"按住小工具即可將小工具移到主畫面的任一位置"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"新增到主畫面"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="other"><xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g> 項小工具</item>
       <item quantity="one"><xliff:g id="WIDGETS_COUNT_0">%1$d</xliff:g> 項小工具</item>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 0da9ed7..3892f6b 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -32,10 +32,8 @@
     <string name="long_accessible_way_to_add" msgid="2733588281439571974">"Thepha kabili uphinde ubambe ukuze uhambise iwijethi noma usebenzise izindlela ezingokwezifiso."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d ububanzi ngokungu-%2$d ukuya phezulu"</string>
-    <!-- no translation found for add_item_request_drag_hint (5653291305078645405) -->
-    <skip />
-    <!-- no translation found for add_to_home_screen (8631549138215492708) -->
-    <skip />
+    <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Thinta uphinde ubambe iwijethi ukuyihambisa Kusikrini sasekhaya"</string>
+    <string name="add_to_home_screen" msgid="8631549138215492708">"Engeza kusikrini sasekhaya"</string>
     <plurals name="widgets_count" formatted="false" msgid="656794749266073027">
       <item quantity="one">Amawijethi angu-<xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g></item>
       <item quantity="other">Amawijethi angu-<xliff:g id="WIDGETS_COUNT_1">%1$d</xliff:g></item>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 93d88c2..a81802d 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -127,17 +127,24 @@
         <!-- numFolderRows & numFolderColumns defaults to numRows & numColumns, if not specified -->
         <attr name="numFolderRows" format="integer" />
         <attr name="numFolderColumns" format="integer" />
+        <!-- numAllAppsColumns defaults to numColumns, if not specified -->
+        <attr name="numAllAppsColumns" format="integer" />
+        <!-- Number of columns to use when extending the all-apps size,
+         defaults to 2 * numAllAppsColumns -->
+        <attr name="numExtendedAllAppsColumns" format="integer" />
+
         <!-- numHotseatIcons defaults to numColumns, if not specified -->
         <attr name="numHotseatIcons" format="integer" />
+        <!-- Number of icons to use when extending the hotseat size,
+         defaults to 2 * numHotseatIcons -->
+        <attr name="numExtendedHotseatIcons" format="integer" />
+
         <attr name="dbFile" format="string" />
         <attr name="defaultLayoutId" format="reference" />
         <attr name="demoModeLayoutId" format="reference" />
         <attr name="isScalable" format="boolean" />
         <attr name="devicePaddingId" format="reference" />
 
-        <!-- whether the grid option is shown to the user -->
-        <attr name="visible" format="boolean" />
-
     </declare-styleable>
 
     <declare-styleable name="DevicePadding">
@@ -167,8 +174,12 @@
         <attr name="iconTextSize" format="float" />
         <!-- landscapeIconTextSize defaults to iconTextSize, if not specified -->
         <attr name="landscapeIconTextSize" format="float" />
-        <!-- If true, this display option is used to determine the default grid -->
-        <attr name="canBeDefault" format="boolean" />
+
+        <!-- If set, this display option is used to determine the default grid -->
+        <attr name="canBeDefault" format="boolean|integer" >
+            <!-- The profile can be default on split display devices -->
+            <flag name="split_display" value="0x2" />
+        </attr>
 
         <!-- The following values are only enabled if grid is supported. -->
         <!-- allAppsIconSize defaults to iconSize, if not specified -->
@@ -176,11 +187,6 @@
         <!-- allAppsIconTextSize defaults to iconTextSize, if not specified -->
         <attr name="allAppsIconTextSize" format="float" />
 
-        <!-- numAllAppsColumns defaults to GridDisplayOption.numColumns, if not specified -->
-        <attr name="numAllAppsColumns" format="integer" />
-
-        <!-- numShownHotseatIcons defaults to GridDisplayOption.numHotseatIcons, if not specified -->
-        <attr name="numShownHotseatIcons" format="integer" />
     </declare-styleable>
 
     <declare-styleable name="CellLayout">
diff --git a/res/values/config.xml b/res/values/config.xml
index f8a517d..9ad1224 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -35,7 +35,7 @@
     <!-- Workspace -->
     <!-- The duration (in ms) of the fade animation on the object outlines, used when
          we are dragging objects around on the home screen. -->
-    <integer name="config_dragOutlineFadeTime">900</integer>
+    <integer name="config_dragOutlineFadeTime">500</integer>
 
     <!-- The alpha value at which to show the most recent drop visualization outline. -->
     <integer name="config_dragOutlineMaxAlpha">255</integer>
@@ -55,9 +55,6 @@
     <!-- The duration of the caret animation -->
     <integer name="config_caretAnimationDuration">200</integer>
 
-    <!-- Hotseat -->
-    <bool name="hotseat_transpose_layout_with_orientation">true</bool>
-
     <!-- Various classes overriden by projects/build flavors. -->
     <string name="folder_name_provider_class" translatable="false"></string>
     <string name="stats_log_manager_class" translatable="false"></string>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index b967091..fe0b11b 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -294,7 +294,7 @@
     <dimen name="overview_task_margin">0dp</dimen>
 
 <!-- Workspace grid visualization parameters -->
-    <dimen name="grid_visualization_rounding_radius">28dp</dimen>
+    <dimen name="grid_visualization_rounding_radius">22dp</dimen>
     <dimen name="grid_visualization_cell_spacing">6dp</dimen>
 
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index d6936ab..eae32b7 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -221,7 +221,7 @@
     <!-- Text for wallpaper change button [CHAR LIMIT=30]-->
     <string name="wallpaper_button_text">Wallpapers</string>
     <!-- Text for wallpaper change button [CHAR LIMIT=30]-->
-    <string name="styles_wallpaper_button_text">Styles &amp; wallpapers</string>
+    <string name="styles_wallpaper_button_text">Wallpaper &amp; style</string>
     <!-- Text for settings button [CHAR LIMIT=30]-->
     <string name="settings_button_text">Home settings</string>
     <!-- Message shown when a feature is disabled by the administrator -->
diff --git a/robolectric_tests/src/com/android/launcher3/shadows/ShadowDeviceFlag.java b/robolectric_tests/src/com/android/launcher3/shadows/ShadowDeviceFlag.java
index 344f532..b58e4b7 100644
--- a/robolectric_tests/src/com/android/launcher3/shadows/ShadowDeviceFlag.java
+++ b/robolectric_tests/src/com/android/launcher3/shadows/ShadowDeviceFlag.java
@@ -18,11 +18,15 @@
 
 import android.content.Context;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.uioverrides.DeviceFlag;
 import com.android.launcher3.util.LooperExecutor;
 
 import org.robolectric.annotation.Implementation;
 import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.RealObject;
+import org.robolectric.shadow.api.Shadow;
 
 /**
  * Shadow for {@link LooperExecutor} to provide reset functionality for static executors.
@@ -30,6 +34,9 @@
 @Implements(value = DeviceFlag.class, isInAndroidSdk = false)
 public class ShadowDeviceFlag {
 
+    @RealObject private DeviceFlag mRealObject;
+    @Nullable private Boolean mValue;
+
     /**
      * Mock change listener as it uses internal system classes not available to robolectric
      */
@@ -40,4 +47,16 @@
     protected static boolean getDeviceValue(String key, boolean defaultValue) {
         return defaultValue;
     }
+
+    @Implementation
+    public boolean get() {
+        if (mValue != null) {
+            return mValue;
+        }
+        return Shadow.directlyOn(mRealObject, DeviceFlag.class, "get");
+    }
+
+    public void setValue(boolean value) {
+        mValue = new Boolean(value);
+    }
 }
diff --git a/robolectric_tests/src/com/android/launcher3/ui/LauncherUIScrollTest.java b/robolectric_tests/src/com/android/launcher3/ui/LauncherUIScrollTest.java
index 4d151f1..ea75548 100644
--- a/robolectric_tests/src/com/android/launcher3/ui/LauncherUIScrollTest.java
+++ b/robolectric_tests/src/com/android/launcher3/ui/LauncherUIScrollTest.java
@@ -161,7 +161,7 @@
 
     private static MotionEvent createScrollEvent(int scroll) {
         DeviceProfile dp = InvariantDeviceProfile.INSTANCE
-                .get(RuntimeEnvironment.application).portraitProfile;
+                .get(RuntimeEnvironment.application).supportedProfiles.get(0);
 
         final PointerProperties[] pointerProperties = new PointerProperties[1];
         pointerProperties[0] = new PointerProperties();
diff --git a/robolectric_tests/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfoTest.java b/robolectric_tests/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfoTest.java
index 92f77f2..d977011 100644
--- a/robolectric_tests/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfoTest.java
+++ b/robolectric_tests/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfoTest.java
@@ -17,6 +17,9 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+
 import android.content.Context;
 import android.graphics.Point;
 
@@ -109,10 +112,14 @@
 
     private InvariantDeviceProfile createIDP() {
         DeviceProfile profile = Mockito.mock(DeviceProfile.class);
+        doAnswer(i -> {
+            ((Point) i.getArgument(0)).set(CELL_SIZE, CELL_SIZE);
+            return null;
+        }).when(profile).getCellSize(any(Point.class));
         Mockito.when(profile.getCellSize()).thenReturn(new Point(CELL_SIZE, CELL_SIZE));
 
         InvariantDeviceProfile idp = new InvariantDeviceProfile();
-        idp.landscapeProfile = idp.portraitProfile = profile;
+        idp.supportedProfiles.add(profile);
         return idp;
     }
 
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index 5d41bb5..c1f3ac5 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -1,5 +1,7 @@
 package com.android.launcher3;
 
+import static android.appwidget.AppWidgetHostView.getDefaultPaddingForWidget;
+
 import static com.android.launcher3.LauncherAnimUtils.LAYOUT_HEIGHT;
 import static com.android.launcher3.LauncherAnimUtils.LAYOUT_WIDTH;
 import static com.android.launcher3.Utilities.ATLEAST_S;
@@ -10,7 +12,9 @@
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
 import android.appwidget.AppWidgetHostView;
+import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
 import android.content.Context;
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -25,16 +29,14 @@
 import android.widget.ImageButton;
 import android.widget.ImageView;
 
-import androidx.annotation.Nullable;
-
 import com.android.launcher3.accessibility.DragViewStateAnnouncer;
 import com.android.launcher3.dragndrop.DragLayer;
-import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.widget.LauncherAppWidgetHostView;
 import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.stream.Collectors;
 
 public class AppWidgetResizeFrame extends AbstractFloatingView implements View.OnKeyListener {
     private static final int SNAP_DURATION = 150;
@@ -43,22 +45,6 @@
 
     private static final Rect sTmpRect = new Rect();
 
-    // Represents the cell size on the grid in the two orientations.
-    public static final MainThreadInitializedObject<Point[]> CELL_SIZE =
-            new MainThreadInitializedObject<>(c -> {
-                InvariantDeviceProfile inv = LauncherAppState.getIDP(c);
-                return new Point[] {inv.landscapeProfile.getCellSize(),
-                        inv.portraitProfile.getCellSize()};
-            });
-
-    // Represents the border spacing size on the grid in the two orientations.
-    public static final MainThreadInitializedObject<int[]> BORDER_SPACING_SIZE =
-            new MainThreadInitializedObject<>(c -> {
-                InvariantDeviceProfile inv = LauncherAppState.getIDP(c);
-                return new int[] {inv.landscapeProfile.cellLayoutBorderSpacingPx,
-                        inv.portraitProfile.cellLayoutBorderSpacingPx};
-            });
-
     private static final int HANDLE_COUNT = 4;
     private static final int INDEX_LEFT = 0;
     private static final int INDEX_TOP = 1;
@@ -71,6 +57,22 @@
 
     private final View[] mDragHandles = new View[HANDLE_COUNT];
     private final List<Rect> mSystemGestureExclusionRects = new ArrayList<>(HANDLE_COUNT);
+    private final OnAttachStateChangeListener mWidgetViewAttachStateChangeListener =
+            new OnAttachStateChangeListener() {
+                @Override
+                public void onViewAttachedToWindow(View view) {
+                    // Do nothing
+                }
+
+                @Override
+                public void onViewDetachedFromWindow(View view) {
+                    // When the app widget view is detached, we should close the resize frame.
+                    // An example is when the dragging starts, the widget view is detached from
+                    // CellLayout and then reattached to DragLayout.
+                    close(false);
+                }
+            };
+
 
     private LauncherAppWidgetHostView mWidgetView;
     private CellLayout mCellLayout;
@@ -191,7 +193,11 @@
     private void setupForWidget(LauncherAppWidgetHostView widgetView, CellLayout cellLayout,
             DragLayer dragLayer) {
         mCellLayout = cellLayout;
+        if (mWidgetView != null) {
+            mWidgetView.removeOnAttachStateChangeListener(mWidgetViewAttachStateChangeListener);
+        }
         mWidgetView = widgetView;
+        mWidgetView.addOnAttachStateChangeListener(mWidgetViewAttachStateChangeListener);
         LauncherAppWidgetProviderInfo info = (LauncherAppWidgetProviderInfo)
                 widgetView.getAppWidgetInfo();
         mResizeMode = info.resizeMode;
@@ -202,7 +208,7 @@
         mMaxHSpan = info.maxSpanX;
         mMaxVSpan = info.maxSpanY;
 
-        mWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(getContext(),
+        mWidgetPadding = getDefaultPaddingForWidget(getContext(),
                 widgetView.getAppWidgetInfo().provider, null);
 
         if (mResizeMode == AppWidgetProviderInfo.RESIZE_HORIZONTAL) {
@@ -392,81 +398,82 @@
         mWidgetView.requestLayout();
     }
 
-    public static void updateWidgetSizeRanges(AppWidgetHostView widgetView, Launcher launcher,
-                                              int spanX, int spanY) {
-        List<SizeF> sizes = getWidgetSizes(launcher, spanX, spanY);
+    public static void updateWidgetSizeRanges(
+            AppWidgetHostView widgetView, Context context, int spanX, int spanY) {
+        List<SizeF> sizes = getWidgetSizes(context, spanX, spanY);
         if (ATLEAST_S) {
             widgetView.updateAppWidgetSize(new Bundle(), sizes);
         } else {
-            Rect bounds = getMinMaxSizes(sizes, null /* outRect */);
+            Rect bounds = getMinMaxSizes(sizes);
             widgetView.updateAppWidgetSize(new Bundle(), bounds.left, bounds.top, bounds.right,
                     bounds.bottom);
         }
     }
 
-    private static SizeF getWidgetSize(Context context, Point cellSize, int spanX, int spanY,
-            int borderSpacing) {
-        final float density = context.getResources().getDisplayMetrics().density;
-        final float hBorderSpacing = (spanX - 1) * borderSpacing;
-        final float vBorderSpacing = (spanY - 1) * borderSpacing;
-
-        return new SizeF(((spanX * cellSize.x) + hBorderSpacing) / density,
-                ((spanY * cellSize.y) + vBorderSpacing) / density);
-    }
-
     /** Returns the list of sizes for a widget of given span, in dp. */
     public static ArrayList<SizeF> getWidgetSizes(Context context, int spanX, int spanY) {
-        final Point[] cellSize = CELL_SIZE.get(context);
-        final int[] borderSpacing = BORDER_SPACING_SIZE.get(context);
-
-        SizeF landSize = getWidgetSize(context, cellSize[0], spanX, spanY, borderSpacing[0]);
-        SizeF portSize = getWidgetSize(context, cellSize[1], spanX, spanY, borderSpacing[1]);
-
         ArrayList<SizeF> sizes = new ArrayList<>(2);
-        sizes.add(landSize);
-        sizes.add(portSize);
+        final float density = context.getResources().getDisplayMetrics().density;
+        Point cellSize = new Point();
+
+        for (DeviceProfile profile : LauncherAppState.getIDP(context).supportedProfiles) {
+            final float hBorderSpacing = (spanX - 1) * profile.cellLayoutBorderSpacingPx;
+            final float vBorderSpacing = (spanY - 1) * profile.cellLayoutBorderSpacingPx;
+            profile.getCellSize(cellSize);
+            sizes.add(new SizeF(
+                    ((spanX * cellSize.x) + hBorderSpacing) / density,
+                    ((spanY * cellSize.y) + vBorderSpacing) / density));
+        }
         return sizes;
     }
 
     /**
+     * Returns the bundle to be used as the default options for a widget with provided size
+     */
+    public static Bundle getWidgetSizeOptions(
+            Context context, ComponentName provider, int spanX, int spanY) {
+        ArrayList<SizeF> sizes = getWidgetSizes(context, spanX, spanY);
+        Rect padding = getDefaultPaddingForWidget(context, provider, null);
+        float density = context.getResources().getDisplayMetrics().density;
+        float xPaddingDips = (padding.left + padding.right) / density;
+        float yPaddingDips = (padding.top + padding.bottom) / density;
+
+        ArrayList<SizeF> paddedSizes = sizes.stream()
+                .map(size -> new SizeF(
+                        Math.max(0.f, size.getWidth() - xPaddingDips),
+                        Math.max(0.f, size.getHeight() - yPaddingDips)))
+                .collect(Collectors.toCollection(ArrayList::new));
+
+        Rect rect = AppWidgetResizeFrame.getMinMaxSizes(paddedSizes);
+        Bundle options = new Bundle();
+        options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, rect.left);
+        options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, rect.top);
+        options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, rect.right);
+        options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, rect.bottom);
+        options.putParcelableArrayList(AppWidgetManager.OPTION_APPWIDGET_SIZES, paddedSizes);
+        return options;
+    }
+
+    /**
      * Returns the min and max widths and heights given a list of sizes, in dp.
      *
      * @param sizes List of sizes to get the min/max from.
-     * @param outRect Rectangle in which the result can be stored, to avoid extra allocations. If
-     *               null, a new rectangle will be allocated.
      * @return A rectangle with the left (resp. top) is used for the min width (resp. height) and
      * the right (resp. bottom) for the max. The returned rectangle is set with 0s if the list is
      * empty.
      */
-    public static Rect getMinMaxSizes(List<SizeF> sizes, @Nullable Rect outRect) {
-        if (outRect == null) {
-            outRect = new Rect();
-        }
+    private static Rect getMinMaxSizes(List<SizeF> sizes) {
         if (sizes.isEmpty()) {
-            outRect.set(0, 0, 0, 0);
+            return new Rect();
         } else {
             SizeF first = sizes.get(0);
-            outRect.set((int) first.getWidth(), (int) first.getHeight(), (int) first.getWidth(),
-                    (int) first.getHeight());
+            Rect result = new Rect((int) first.getWidth(), (int) first.getHeight(),
+                    (int) first.getWidth(), (int) first.getHeight());
             for (int i = 1; i < sizes.size(); i++) {
-                outRect.union((int) sizes.get(i).getWidth(), (int) sizes.get(i).getHeight());
+                result.union((int) sizes.get(i).getWidth(), (int) sizes.get(i).getHeight());
             }
+            return result;
         }
-        return outRect;
-    }
-
-    /**
-     * Returns the range of sizes a widget may be displayed, given its span.
-     *
-     * @param context Context in which the View is rendered.
-     * @param spanX Width of the widget, in cells.
-     * @param spanY Height of the widget, in cells.
-     * @param outRect Rectangle in which the result can be stored, to avoid extra allocations. If
-     *               null, a new rectangle will be allocated.
-     */
-    public static Rect getWidgetSizeRanges(Context context, int spanX, int spanY,
-            @Nullable Rect outRect) {
-        return getMinMaxSizes(getWidgetSizes(context, spanX, spanY), outRect);
     }
 
     @Override
@@ -641,6 +648,9 @@
     @Override
     protected void handleClose(boolean animate) {
         mDragLayer.removeView(this);
+        if (mWidgetView != null) {
+            mWidgetView.removeOnAttachStateChangeListener(mWidgetViewAttachStateChangeListener);
+        }
     }
 
     @Override
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 2ace796..3d044d6 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -317,7 +317,8 @@
 
     @UiThread
     protected void applyIconAndLabel(ItemInfoWithIcon info) {
-        FastBitmapDrawable iconDrawable = info.newIcon(getContext());
+        boolean useTheme = mDisplay == DISPLAY_WORKSPACE || mDisplay == DISPLAY_FOLDER;
+        FastBitmapDrawable iconDrawable = info.newIcon(getContext(), useTheme);
         mDotParams.color = IconPalette.getMutedColor(info.bitmap.color, 0.54f);
 
         setIcon(iconDrawable);
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index a037675..1df9df6 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -195,6 +195,7 @@
 
     private final Rect mTempRect = new Rect();
     private final RectF mTempRectF = new RectF();
+    private final float[] mTmpFloatArray = new float[4];
 
     private static final Paint sPaint = new Paint();
 
@@ -504,7 +505,7 @@
     }
 
     private void updateBgAlpha() {
-        mBackground.setAlpha((int) (mSpringLoadedProgress * mScrollProgress * 255));
+        mBackground.setAlpha((int) (mSpringLoadedProgress * 255));
     }
 
     /**
@@ -527,9 +528,12 @@
     }
 
     protected void visualizeGrid(Canvas canvas) {
-        mVisualizeGridRect.set(mGridVisualizationPadding, mGridVisualizationPadding,
-                mCellWidth - mGridVisualizationPadding,
-                mCellHeight - mGridVisualizationPadding);
+        DeviceProfile dp = mActivity.getDeviceProfile();
+        int paddingX = (int) Math.min((mCellWidth - dp.iconSizePx) / 2, mGridVisualizationPadding);
+        int paddingY = (int) Math.min((mCellHeight - dp.iconSizePx) / 2, mGridVisualizationPadding);
+        mVisualizeGridRect.set(paddingX, paddingY,
+                mCellWidth - paddingX,
+                mCellHeight - paddingY);
 
         mVisualizeGridPaint.setStrokeWidth(8);
         int paintAlpha = (int) (120 * mGridAlpha);
@@ -539,9 +543,9 @@
             for (int i = 0; i < mCountX; i++) {
                 for (int j = 0; j < mCountY; j++) {
                     int transX = i * mCellWidth + (i * mBorderSpacing) + getPaddingLeft()
-                            + mGridVisualizationPadding;
+                            + paddingX;
                     int transY = j * mCellHeight + (j * mBorderSpacing) + getPaddingTop()
-                            + mGridVisualizationPadding;
+                            + paddingY;
 
                     mVisualizeGridRect.offsetTo(transX, transY);
                     mVisualizeGridPaint.setStyle(Paint.Style.FILL);
@@ -562,14 +566,14 @@
                 int spanX = mDragOutlines[i].cellHSpan;
                 int spanY = mDragOutlines[i].cellVSpan;
 
-                mVisualizeGridRect.set(mGridVisualizationPadding, mGridVisualizationPadding,
-                        mCellWidth * spanX - mGridVisualizationPadding,
-                        mCellHeight * spanY - mGridVisualizationPadding);
+                mVisualizeGridRect.set(paddingX, paddingY,
+                        mCellWidth * spanX - paddingX,
+                        mCellHeight * spanY - paddingY);
 
                 int transX = x * mCellWidth + (x * mBorderSpacing)
-                        + getPaddingLeft() + mGridVisualizationPadding;
+                        + getPaddingLeft() + paddingX;
                 int transY = y * mCellHeight + (y * mBorderSpacing)
-                        + getPaddingTop() + mGridVisualizationPadding;
+                        + getPaddingTop() + paddingY;
 
                 mVisualizeGridRect.offsetTo(transX, transY);
 
@@ -1077,7 +1081,7 @@
 
             // Now get the rect in drag layer coordinates.
             getBoundsForViewInDragLayer(launcher.getDragLayer(), workspace, mTempRect, false,
-                    mTempRectF);
+                    mTmpFloatArray, mTempRectF);
             Utilities.setRect(mTempRectF, mTempRect);
             ((LauncherAppWidgetHostView) view).handleDrag(mTempRect, pageId);
         }
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index fd97936..d5860dc 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -31,7 +31,6 @@
 import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
 import android.util.DisplayMetrics;
-import android.util.Log;
 import android.view.Surface;
 import android.view.WindowInsets;
 import android.view.WindowManager;
@@ -42,7 +41,6 @@
 import com.android.launcher3.icons.DotRenderer;
 import com.android.launcher3.icons.GraphicsUtils;
 import com.android.launcher3.icons.IconNormalizer;
-import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.DisplayController.Info;
 import com.android.launcher3.util.WindowBounds;
@@ -52,9 +50,6 @@
 @SuppressLint("NewApi")
 public class DeviceProfile {
 
-    private static final float TABLET_MIN_DPS = 600;
-    private static final float LARGE_TABLET_MIN_DPS = 720;
-
     private static final int DEFAULT_DOT_SIZE = 100;
 
     public final InvariantDeviceProfile inv;
@@ -63,9 +58,9 @@
 
     // Device properties
     public final boolean isTablet;
-    public final boolean isLargeTablet;
     public final boolean isPhone;
     public final boolean transposeLayoutWithOrientation;
+    public final boolean isTwoPanels;
 
     // Device properties in current orientation
     public final boolean isLandscape;
@@ -164,6 +159,7 @@
     public int allAppsCellWidthPx;
     public int allAppsIconSizePx;
     public int allAppsIconDrawablePaddingPx;
+    public final int numShownAllAppsColumns;
     public float allAppsIconTextSizePx;
 
     // Overview
@@ -194,42 +190,30 @@
     // How much of the bottom inset is due to Taskbar rather than other system elements.
     public int nonOverlappingTaskbarInset;
 
-    DeviceProfile(Context context, InvariantDeviceProfile inv, Info info,
-            Point minSize, Point maxSize, int width, int height, boolean isLandscape,
+    DeviceProfile(Context context, InvariantDeviceProfile inv, Info info, WindowBounds windowBounds,
             boolean isMultiWindowMode, boolean transposeLayoutWithOrientation,
-            Point windowPosition) {
+            boolean useTwoPanels) {
 
         this.inv = inv;
-        this.isLandscape = isLandscape;
+        this.isLandscape = windowBounds.isLandscape();
         this.isMultiWindowMode = isMultiWindowMode;
         this.transposeLayoutWithOrientation = transposeLayoutWithOrientation;
-        windowX = windowPosition.x;
-        windowY = windowPosition.y;
+        windowX = windowBounds.bounds.left;
+        windowY = windowBounds.bounds.top;
 
         isScalableGrid = inv.isScalable && !isVerticalBarLayout() && !isMultiWindowMode;
 
         // Determine sizes.
-        widthPx = width;
-        heightPx = height;
-        int nonFinalAvailableHeightPx;
-        if (isLandscape) {
-            availableWidthPx = maxSize.x;
-            nonFinalAvailableHeightPx = minSize.y;
-        } else {
-            availableWidthPx = minSize.x;
-            nonFinalAvailableHeightPx = maxSize.y;
-        }
+        widthPx = windowBounds.bounds.width();
+        heightPx = windowBounds.bounds.height();
+        availableWidthPx = windowBounds.availableSize.x;
+        int nonFinalAvailableHeightPx = windowBounds.availableSize.y;
 
         mInfo = info;
+        isTablet = info.isTablet(windowBounds);
+        isPhone = !isTablet;
+        isTwoPanels = isTablet && useTwoPanels;
 
-        // Constants from resources
-        float swDPs = dpiFromPx(Math.min(info.smallestSize.x, info.smallestSize.y),
-                info.densityDpi);
-        boolean allowRotation = context.getResources().getBoolean(R.bool.allow_rotation);
-        // Tablet UI is built with assumption that simulated landscape is disabled.
-        isTablet = allowRotation && swDPs >= TABLET_MIN_DPS;
-        isLargeTablet = isTablet && swDPs >= LARGE_TABLET_MIN_DPS;
-        isPhone = !isTablet && !isLargeTablet;
         aspectRatio = ((float) Math.max(widthPx, heightPx)) / Math.min(widthPx, heightPx);
         boolean isTallDevice = Float.compare(aspectRatio, TALL_DEVICE_ASPECT_RATIO_THRESHOLD) >= 0;
 
@@ -284,7 +268,7 @@
                 ? 0
                 : res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_layout_padding);
 
-        if (FeatureFlags.ENABLE_TWO_PANEL_HOME.get() && isTablet) {
+        if (isTwoPanels) {
             cellLayoutPaddingLeftRightPx =
                     res.getDimensionPixelSize(R.dimen.two_panel_home_side_padding);
             cellLayoutBottomPaddingPx = 0;
@@ -309,7 +293,10 @@
 
         workspaceCellPaddingXPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_padding_x);
 
-        numShownHotseatIcons = inv.numShownHotseatIcons;
+        numShownHotseatIcons =
+                isTwoPanels ? inv.numDatabaseHotseatIcons : inv.numShownHotseatIcons;
+        numShownAllAppsColumns =
+                isTwoPanels ? inv.numDatabaseAllAppsColumns : inv.numAllAppsColumns;
         hotseatBarTopPaddingPx =
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_top_padding);
         hotseatBarBottomPaddingPx = (isTallDevice ? 0
@@ -393,11 +380,12 @@
     }
 
     public Builder toBuilder(Context context) {
-        Point size = new Point(availableWidthPx, availableHeightPx);
+        WindowBounds bounds =
+                new WindowBounds(widthPx, heightPx, availableWidthPx, availableHeightPx);
+        bounds.bounds.offsetTo(windowX, windowY);
         return new Builder(context, inv, mInfo)
-                .setSizeRange(size, size)
-                .setSize(widthPx, heightPx)
-                .setWindowPosition(windowX, windowY)
+                .setWindowBounds(bounds)
+                .setUseTwoPanels(isTwoPanels)
                 .setMultiWindowMode(isMultiWindowMode);
     }
 
@@ -409,15 +397,8 @@
      * TODO: Move this to the builder as part of setMultiWindowMode
      */
     public DeviceProfile getMultiWindowProfile(Context context, WindowBounds windowBounds) {
-        // We take the minimum sizes of this profile and it's multi-window variant to ensure that
-        // the system decor is always excluded.
-        Point mwSize = new Point(Math.min(availableWidthPx, windowBounds.availableSize.x),
-                Math.min(availableHeightPx, windowBounds.availableSize.y));
-
         DeviceProfile profile = toBuilder(context)
-                .setSizeRange(mwSize, mwSize)
-                .setSize(windowBounds.bounds.width(), windowBounds.bounds.height())
-                .setWindowPosition(windowBounds.bounds.left, windowBounds.bounds.top)
+                .setWindowBounds(windowBounds)
                 .setMultiWindowMode(true)
                 .build();
 
@@ -434,14 +415,6 @@
     }
 
     /**
-     * Inverse of {@link #getMultiWindowProfile(Context, WindowBounds)}
-     * @return device profile corresponding to the current orientation in non multi-window mode.
-     */
-    public DeviceProfile getFullScreenProfile() {
-        return isLandscape ? inv.landscapeProfile : inv.portraitProfile;
-    }
-
-    /**
      * Checks if there is enough space for labels on the workspace.
      * If there is not, labels on the Workspace are hidden.
      * It is important to call this method after the All Apps variables have been set.
@@ -553,7 +526,7 @@
         }
 
         // All apps
-        if (allAppsHasDifferentNumColumns()) {
+        if (numShownAllAppsColumns != inv.numColumns) {
             allAppsIconSizePx = pxFromDp(inv.allAppsIconSize, mMetrics);
             allAppsIconTextSizePx = Utilities.pxFromSp(inv.allAppsIconTextSize, mMetrics);
             allAppsIconDrawablePaddingPx = iconDrawablePaddingOriginalPx;
@@ -667,18 +640,20 @@
     }
 
     public Point getCellSize() {
-        return getCellSize(inv.numColumns, inv.numRows);
+        return getCellSize(null);
     }
 
-    private Point getCellSize(int numColumns, int numRows) {
-        Point result = new Point();
+    public Point getCellSize(Point result) {
+        if (result == null) {
+            result = new Point();
+        }
         // Since we are only concerned with the overall padding, layout direction does
         // not matter.
         Point padding = getTotalWorkspacePadding();
         result.x = calculateCellWidth(availableWidthPx - padding.x
-                - cellLayoutPaddingLeftRightPx * 2, cellLayoutBorderSpacingPx, numColumns);
+                - cellLayoutPaddingLeftRightPx * 2, cellLayoutBorderSpacingPx, inv.numColumns);
         result.y = calculateCellHeight(availableHeightPx - padding.y
-                - cellLayoutBottomPaddingPx, cellLayoutBorderSpacingPx, numRows);
+                - cellLayoutBottomPaddingPx, cellLayoutBorderSpacingPx, inv.numRows);
         return result;
     }
 
@@ -723,7 +698,7 @@
                 padding.set(availablePaddingX / 2, edgeMarginPx + availablePaddingY / 2,
                         availablePaddingX / 2, paddingBottom + availablePaddingY / 2);
 
-                if (FeatureFlags.ENABLE_TWO_PANEL_HOME.get()) {
+                if (isTwoPanels) {
                     padding.set(0, padding.top, 0, padding.bottom);
                 }
             } else {
@@ -751,7 +726,7 @@
             // for this, we pad the left and right of the hotseat with half of the difference of a
             // workspace cell vs a hotseat cell.
             float workspaceCellWidth = (float) widthPx / inv.numColumns;
-            float hotseatCellWidth = (float) widthPx / inv.numShownHotseatIcons;
+            float hotseatCellWidth = (float) widthPx / numShownHotseatIcons;
             int hotseatAdjustment = Math.round((workspaceCellWidth - hotseatCellWidth) / 2);
             mHotseatPadding.set(
                     hotseatAdjustment + workspacePadding.left + cellLayoutPaddingLeftRightPx
@@ -802,13 +777,6 @@
     }
 
     /**
-     * Returns true when the number of workspace columns and all apps columns differs.
-     */
-    private boolean allAppsHasDifferentNumColumns() {
-        return inv.numAllAppsColumns != inv.numColumns;
-    }
-
-    /**
      * Updates orientation information and returns true if it has changed from the previous value.
      */
     public boolean updateIsSeascape(Context context) {
@@ -828,7 +796,7 @@
     }
 
     public boolean shouldFadeAdjacentWorkspaceScreens() {
-        return isVerticalBarLayout() || isLargeTablet;
+        return isVerticalBarLayout();
     }
 
     public int getCellHeight(@ContainerType int containerType) {
@@ -854,13 +822,13 @@
         writer.println(prefix + "\t1 dp = " + mMetrics.density + " px");
 
         writer.println(prefix + "\tisTablet:" + isTablet);
-        writer.println(prefix + "\tisLargeTablet:" + isLargeTablet);
         writer.println(prefix + "\tisPhone:" + isPhone);
         writer.println(prefix + "\ttransposeLayoutWithOrientation:"
                 + transposeLayoutWithOrientation);
 
         writer.println(prefix + "\tisLandscape:" + isLandscape);
         writer.println(prefix + "\tisMultiWindowMode:" + isMultiWindowMode);
+        writer.println(prefix + "\tisTwoPanels:" + isTwoPanels);
 
         writer.println(prefix + pxToDpStr("windowX", windowX));
         writer.println(prefix + pxToDpStr("windowY", windowY));
@@ -907,6 +875,7 @@
         writer.println(prefix + pxToDpStr("allAppsIconDrawablePaddingPx",
                 allAppsIconDrawablePaddingPx));
         writer.println(prefix + pxToDpStr("allAppsCellHeightPx", allAppsCellHeightPx));
+        writer.println(prefix + "\tnumShownAllAppsColumns: " + numShownAllAppsColumns);
 
         writer.println(prefix + pxToDpStr("hotseatBarSizePx", hotseatBarSizePx));
         writer.println(prefix + pxToDpStr("hotseatCellHeightPx", hotseatCellHeightPx));
@@ -916,6 +885,7 @@
                 hotseatBarSidePaddingStartPx));
         writer.println(prefix + pxToDpStr("hotseatBarSidePaddingEndPx",
                 hotseatBarSidePaddingEndPx));
+        writer.println(prefix + "\tnumShownHotseatIcons: " + numShownHotseatIcons);
 
         writer.println(prefix + "\tisTaskbarPresent:" + isTaskbarPresent);
 
@@ -967,41 +937,16 @@
         private InvariantDeviceProfile mInv;
         private Info mInfo;
 
-        private final Point mWindowPosition = new Point();
-        private Point mMinSize, mMaxSize;
-        private int mWidth, mHeight;
+        private WindowBounds mWindowBounds;
+        private boolean mUseTwoPanels;
 
-        private boolean mIsLandscape;
         private boolean mIsMultiWindowMode = false;
-        private boolean mTransposeLayoutWithOrientation;
+        private Boolean mTransposeLayoutWithOrientation;
 
         public Builder(Context context, InvariantDeviceProfile inv, Info info) {
             mContext = context;
             mInv = inv;
             mInfo = info;
-            mTransposeLayoutWithOrientation = context.getResources()
-                    .getBoolean(R.bool.hotseat_transpose_layout_with_orientation);
-            if (TestProtocol.sDebugTracing) {
-                Log.d(TestProtocol.LAUNCHER_NOT_TRANSPOSED,
-                        "transposeLayout=" + mTransposeLayoutWithOrientation);
-            }
-        }
-
-        public Builder setSizeRange(Point minSize, Point maxSize) {
-            mMinSize = minSize;
-            mMaxSize = maxSize;
-            return this;
-        }
-
-        public Builder setSize(int width, int height) {
-            mWidth = width;
-            mHeight = height;
-            mIsLandscape = mWidth > mHeight;
-            if (TestProtocol.sDebugTracing) {
-                Log.d(TestProtocol.LAUNCHER_NOT_TRANSPOSED,
-                        "isLandscape=" + mIsLandscape + " w=" + mWidth + " h=" + mHeight);
-            }
-            return this;
         }
 
         public Builder setMultiWindowMode(boolean isMultiWindowMode) {
@@ -1009,11 +954,14 @@
             return this;
         }
 
-        /**
-         * Sets the window position if not full-screen
-         */
-        public Builder setWindowPosition(int x, int y) {
-            mWindowPosition.set(x, y);
+        public Builder setUseTwoPanels(boolean useTwoPanels) {
+            mUseTwoPanels = useTwoPanels;
+            return this;
+        }
+
+
+        public Builder setWindowBounds(WindowBounds bounds) {
+            mWindowBounds = bounds;
             return this;
         }
 
@@ -1023,9 +971,14 @@
         }
 
         public DeviceProfile build() {
-            return new DeviceProfile(mContext, mInv, mInfo, mMinSize, mMaxSize,
-                    mWidth, mHeight, mIsLandscape, mIsMultiWindowMode,
-                    mTransposeLayoutWithOrientation, mWindowPosition);
+            if (mWindowBounds == null) {
+                throw new IllegalArgumentException("Window bounds not set");
+            }
+            if (mTransposeLayoutWithOrientation == null) {
+                mTransposeLayoutWithOrientation = !mInfo.isTablet(mWindowBounds);
+            }
+            return new DeviceProfile(mContext, mInv, mInfo, mWindowBounds,
+                    mIsMultiWindowMode, mTransposeLayoutWithOrientation, mUseTwoPanels);
         }
     }
 
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 8496fd5..b2a9e75 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -90,11 +90,11 @@
     public void resetLayout(boolean hasVerticalHotseat) {
         removeAllViewsInLayout();
         mHasVerticalHotseat = hasVerticalHotseat;
-        InvariantDeviceProfile idp = mActivity.getDeviceProfile().inv;
+        DeviceProfile dp = mActivity.getDeviceProfile();
         if (hasVerticalHotseat) {
-            setGridSize(1, idp.numShownHotseatIcons);
+            setGridSize(1, dp.numShownHotseatIcons);
         } else {
-            setGridSize(idp.numShownHotseatIcons, 1);
+            setGridSize(dp.numShownHotseatIcons, 1);
         }
     }
 
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 1332e14..7836fa3 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -16,20 +16,17 @@
 
 package com.android.launcher3;
 
-import static com.android.launcher3.Utilities.getDevicePrefs;
+import static com.android.launcher3.Utilities.dpiFromPx;
 import static com.android.launcher3.Utilities.getPointString;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_TWO_PANEL_HOME;
 import static com.android.launcher3.util.DisplayController.CHANGE_DENSITY;
-import static com.android.launcher3.util.DisplayController.CHANGE_SIZE;
+import static com.android.launcher3.util.DisplayController.CHANGE_SUPPORTED_BOUNDS;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.launcher3.util.PackageManagerHelper.getPackageFilter;
 
 import android.annotation.TargetApi;
 import android.appwidget.AppWidgetHostView;
-import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
@@ -48,13 +45,13 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
-import com.android.launcher3.graphics.IconShape;
 import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.DisplayController.Info;
 import com.android.launcher3.util.IntArray;
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.Themes;
+import com.android.launcher3.util.WindowBounds;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -62,6 +59,7 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.List;
 
 public class InvariantDeviceProfile {
 
@@ -73,15 +71,13 @@
     public static final String KEY_MIGRATION_SRC_WORKSPACE_SIZE = "migration_src_workspace_size";
     public static final String KEY_MIGRATION_SRC_HOTSEAT_COUNT = "migration_src_hotseat_count";
 
+    private static final int DEFAULT_TRUE = -1;
+    private static final int DEFAULT_SPLIT_DISPLAY = 2;
+
     private static final String KEY_IDP_GRID_NAME = "idp_grid_name";
 
     private static final float ICON_SIZE_DEFINED_IN_APP_DP = 48;
 
-    public static final int CHANGE_FLAG_GRID = 1 << 0;
-    public static final int CHANGE_FLAG_ICON_PARAMS = 1 << 1;
-
-    public static final String KEY_ICON_PATH_REF = "pref_icon_shape_path";
-
     // Constants that affects the interpolation curve between statically defined device profile
     // buckets.
     private static final float KNEARESTNEIGHBOR = 3;
@@ -90,9 +86,6 @@
     // used to offset float not being able to express extremely small weights in extreme cases.
     private static final float WEIGHT_EFFICIENT = 100000f;
 
-    private static final int CONFIG_ICON_MASK_RES_ID = Resources.getSystem().getIdentifier(
-            "config_icon_mask", "string", "android");
-
     /**
      * Number of icons per row and column in the workspace.
      */
@@ -105,7 +98,6 @@
     public int numFolderRows;
     public int numFolderColumns;
     public float iconSize;
-    public String iconShapePath;
     public float landscapeIconSize;
     public float landscapeIconTextSize;
     public int iconBitmapSize;
@@ -136,6 +128,7 @@
      * Number of columns in the all apps list.
      */
     public int numAllAppsColumns;
+    public int numDatabaseAllAppsColumns;
 
     /**
      * Do not query directly. see {@link DeviceProfile#isScalableGrid}.
@@ -147,8 +140,7 @@
     public int defaultLayoutId;
     int demoModeLayoutId;
 
-    public DeviceProfile landscapeProfile;
-    public DeviceProfile portraitProfile;
+    public final List<DeviceProfile> supportedProfiles = new ArrayList<>();
 
     @Nullable public DevicePaddings devicePaddings;
 
@@ -156,7 +148,6 @@
     public Rect defaultWidgetPadding;
 
     private final ArrayList<OnIDPChangeListener> mChangeListeners = new ArrayList<>();
-    private OverlayMonitor mOverlayMonitor;
 
     @VisibleForTesting
     public InvariantDeviceProfile() {}
@@ -167,7 +158,6 @@
         numFolderRows = p.numFolderRows;
         numFolderColumns = p.numFolderColumns;
         iconSize = p.iconSize;
-        iconShapePath = p.iconShapePath;
         landscapeIconSize = p.landscapeIconSize;
         iconBitmapSize = p.iconBitmapSize;
         iconTextSize = p.iconTextSize;
@@ -175,6 +165,7 @@
         numShownHotseatIcons = p.numShownHotseatIcons;
         numDatabaseHotseatIcons = p.numDatabaseHotseatIcons;
         numAllAppsColumns = p.numAllAppsColumns;
+        numDatabaseAllAppsColumns = p.numDatabaseAllAppsColumns;
         isScalable = p.isScalable;
         devicePaddingId = p.devicePaddingId;
         minCellHeight = p.minCellHeight;
@@ -186,7 +177,6 @@
         defaultLayoutId = p.defaultLayoutId;
         demoModeLayoutId = p.demoModeLayoutId;
         mExtraAttrs = p.mExtraAttrs;
-        mOverlayMonitor = p.mOverlayMonitor;
         devicePaddings = p.devicePaddings;
     }
 
@@ -204,11 +194,10 @@
 
         DisplayController.INSTANCE.get(context).addChangeListener(
                 (displayContext, info, flags) -> {
-                    if ((flags & (CHANGE_SIZE | CHANGE_DENSITY)) != 0) {
+                    if ((flags & (CHANGE_DENSITY | CHANGE_SUPPORTED_BOUNDS)) != 0) {
                         onConfigChanged(displayContext);
                     }
                 });
-        mOverlayMonitor = new OverlayMonitor(context);
     }
 
     /**
@@ -232,66 +221,62 @@
         // Get the display info based on default display and interpolate it to existing display
         DisplayOption defaultDisplayOption = invDistWeightedInterpolate(
                 DisplayController.INSTANCE.get(context).getInfo(),
-                getPredefinedDeviceProfiles(context, gridName));
+                getPredefinedDeviceProfiles(context, gridName, false), false);
 
         Info myInfo = new Info(context, display);
         DisplayOption myDisplayOption = invDistWeightedInterpolate(
-                myInfo, getPredefinedDeviceProfiles(context, gridName));
+                myInfo, getPredefinedDeviceProfiles(context, gridName, false), false);
 
         DisplayOption result = new DisplayOption(defaultDisplayOption.grid)
                 .add(myDisplayOption);
         result.iconSize = defaultDisplayOption.iconSize;
         result.landscapeIconSize = defaultDisplayOption.landscapeIconSize;
-        result.numShownHotseatIcons = myDisplayOption.numShownHotseatIcons;
         if (defaultDisplayOption.allAppsIconSize < myDisplayOption.allAppsIconSize) {
             result.allAppsIconSize = defaultDisplayOption.allAppsIconSize;
-            result.numAllAppsColumns = defaultDisplayOption.numAllAppsColumns;
         } else {
             result.allAppsIconSize = myDisplayOption.allAppsIconSize;
-            result.numAllAppsColumns = myDisplayOption.numAllAppsColumns;
         }
         result.minCellHeight = defaultDisplayOption.minCellHeight;
         result.minCellWidth = defaultDisplayOption.minCellWidth;
         result.borderSpacing = defaultDisplayOption.borderSpacing;
 
-        initGrid(context, myInfo, result);
+        initGrid(context, myInfo, result, false);
     }
 
     public static String getCurrentGridName(Context context) {
-        if (ENABLE_TWO_PANEL_HOME.get()) {
-            return ENABLE_TWO_PANEL_HOME.key;
-        }
         return Utilities.isGridOptionsEnabled(context)
                 ? Utilities.getPrefs(context).getString(KEY_IDP_GRID_NAME, null) : null;
     }
 
-    /**
-     * Retrieve system defined or RRO overriden icon shape.
-     */
-    private static String getIconShapePath(Context context) {
-        if (CONFIG_ICON_MASK_RES_ID == 0) {
-            Log.e(TAG, "Icon mask res identifier failed to retrieve.");
-            return "";
-        }
-        return context.getResources().getString(CONFIG_ICON_MASK_RES_ID);
-    }
-
     private String initGrid(Context context, String gridName) {
         Info displayInfo = DisplayController.INSTANCE.get(context).getInfo();
-        ArrayList<DisplayOption> allOptions = getPredefinedDeviceProfiles(context, gridName);
+        // Determine if we have split display
 
-        DisplayOption displayOption = invDistWeightedInterpolate(displayInfo, allOptions);
-        initGrid(context, displayInfo, displayOption);
+        boolean isTablet = false, isPhone = false;
+        for (WindowBounds bounds : displayInfo.supportedBounds) {
+            if (displayInfo.isTablet(bounds)) {
+                isTablet = true;
+            } else {
+                isPhone = true;
+            }
+        }
+        boolean isSplitDisplay = isPhone && isTablet && ENABLE_TWO_PANEL_HOME.get();
+
+        ArrayList<DisplayOption> allOptions =
+                getPredefinedDeviceProfiles(context, gridName, isSplitDisplay);
+        DisplayOption displayOption =
+                invDistWeightedInterpolate(displayInfo, allOptions, isSplitDisplay);
+        initGrid(context, displayInfo, displayOption, isSplitDisplay);
         return displayOption.grid.name;
     }
 
     private void initGrid(
-            Context context, Info displayInfo, DisplayOption displayOption) {
+            Context context, Info displayInfo, DisplayOption displayOption,
+            boolean isSplitDisplay) {
         DisplayMetrics metrics = context.getResources().getDisplayMetrics();
         GridOption closestProfile = displayOption.grid;
         numRows = closestProfile.numRows;
         numColumns = closestProfile.numColumns;
-        numDatabaseHotseatIcons = closestProfile.numDatabaseHotseatIcons;
         dbFile = closestProfile.dbFile;
         defaultLayoutId = closestProfile.defaultLayoutId;
         demoModeLayoutId = closestProfile.demoModeLayoutId;
@@ -303,7 +288,6 @@
         mExtraAttrs = closestProfile.extraAttrs;
 
         iconSize = displayOption.iconSize;
-        iconShapePath = getIconShapePath(context);
         landscapeIconSize = displayOption.landscapeIconSize;
         iconBitmapSize = ResourceUtils.pxFromDp(iconSize, metrics);
         iconTextSize = displayOption.iconTextSize;
@@ -313,8 +297,14 @@
         minCellHeight = displayOption.minCellHeight;
         minCellWidth = displayOption.minCellWidth;
         borderSpacing = displayOption.borderSpacing;
-        numShownHotseatIcons = Math.round(displayOption.numShownHotseatIcons);
-        numAllAppsColumns = Math.round(displayOption.numAllAppsColumns);
+
+        numShownHotseatIcons = closestProfile.numHotseatIcons;
+        numDatabaseHotseatIcons = isSplitDisplay
+                ? closestProfile.numDatabaseHotseatIcons : closestProfile.numHotseatIcons;
+
+        numAllAppsColumns = closestProfile.numAllAppsColumns;
+        numDatabaseAllAppsColumns = isSplitDisplay
+                ? closestProfile.numDatabaseAllAppsColumns : closestProfile.numAllAppsColumns;
 
         if (Utilities.isGridOptionsEnabled(context)) {
             allAppsIconSize = displayOption.allAppsIconSize;
@@ -332,31 +322,26 @@
         // Supported overrides: numRows, numColumns, iconSize
         applyPartnerDeviceProfileOverrides(context, metrics);
 
-        Point realSize = new Point(displayInfo.realSize);
-        // The real size never changes. smallSide and largeSide will remain the
-        // same in any orientation.
-        int smallSide = Math.min(realSize.x, realSize.y);
-        int largeSide = Math.max(realSize.x, realSize.y);
+        supportedProfiles.clear();
+        defaultWallpaperSize = new Point(displayInfo.currentSize);
+        for (WindowBounds bounds : displayInfo.supportedBounds) {
+            supportedProfiles.add(new DeviceProfile.Builder(context, this, displayInfo)
+                    .setUseTwoPanels(isSplitDisplay)
+                    .setWindowBounds(bounds).build());
 
-        DeviceProfile.Builder builder = new DeviceProfile.Builder(context, this, displayInfo)
-                .setSizeRange(new Point(displayInfo.smallestSize),
-                        new Point(displayInfo.largestSize));
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.LAUNCHER_NOT_TRANSPOSED,
-                    "largeSide=" + largeSide + " smallSide=" + smallSide);
-        }
+            // Wallpaper size should be the maximum of the all possible sizes Launcher expects
+            int displayWidth = bounds.bounds.width();
+            int displayHeight = bounds.bounds.height();
+            defaultWallpaperSize.y = Math.max(defaultWallpaperSize.y, displayHeight);
 
-        landscapeProfile = builder.setSize(largeSide, smallSide).build();
-        portraitProfile = builder.setSize(smallSide, largeSide).build();
-
-        // We need to ensure that there is enough extra space in the wallpaper
-        // for the intended parallax effects
-        if (context.getResources().getConfiguration().smallestScreenWidthDp >= 720) {
-            defaultWallpaperSize = new Point(
-                    (int) (largeSide * wallpaperTravelToScreenWidthRatio(largeSide, smallSide)),
-                    largeSide);
-        } else {
-            defaultWallpaperSize = new Point(Math.max(smallSide * 2, largeSide), largeSide);
+            // We need to ensure that there is enough extra space in the wallpaper
+            // for the intended parallax effects
+            float parallaxFactor =
+                    dpiFromPx(Math.min(displayWidth, displayHeight), displayInfo.densityDpi) < 720
+                            ? 2
+                            : wallpaperTravelToScreenWidthRatio(displayWidth, displayHeight);
+            defaultWallpaperSize.x =
+                    Math.max(defaultWallpaperSize.x, Math.round(parallaxFactor * displayWidth));
         }
 
         ComponentName cn = new ComponentName(context.getPackageName(), getClass().getName());
@@ -376,18 +361,6 @@
         mChangeListeners.remove(listener);
     }
 
-    public void verifyConfigChangedInBackground(final Context context) {
-        String savedIconMaskPath = getDevicePrefs(context).getString(KEY_ICON_PATH_REF, "");
-        // Good place to check if grid size changed in themepicker when launcher was dead.
-        if (savedIconMaskPath.isEmpty()) {
-            getDevicePrefs(context).edit().putString(KEY_ICON_PATH_REF, getIconShapePath(context))
-                    .apply();
-        } else if (!savedIconMaskPath.equals(getIconShapePath(context))) {
-            getDevicePrefs(context).edit().putString(KEY_ICON_PATH_REF, getIconShapePath(context))
-                    .apply();
-            apply(context, CHANGE_FLAG_ICON_PARAMS);
-        }
-    }
 
     public void setCurrentGrid(Context context, String gridName) {
         Context appContext = context.getApplicationContext();
@@ -399,40 +372,18 @@
         if (TestProtocol.sDebugTracing) {
             Log.d(TestProtocol.LAUNCHER_NOT_TRANSPOSED, "IDP.onConfigChanged");
         }
-        // Config changes, what shall we do?
-        InvariantDeviceProfile oldProfile = new InvariantDeviceProfile(this);
 
         // Re-init grid
         String gridName = getCurrentGridName(context);
         initGrid(context, gridName);
 
-        int changeFlags = 0;
-        if (numRows != oldProfile.numRows ||
-                numColumns != oldProfile.numColumns ||
-                numFolderColumns != oldProfile.numFolderColumns ||
-                numFolderRows != oldProfile.numFolderRows ||
-                numDatabaseHotseatIcons != oldProfile.numDatabaseHotseatIcons) {
-            changeFlags |= CHANGE_FLAG_GRID;
-        }
-
-        if (iconSize != oldProfile.iconSize || iconBitmapSize != oldProfile.iconBitmapSize ||
-                !iconShapePath.equals(oldProfile.iconShapePath)) {
-            changeFlags |= CHANGE_FLAG_ICON_PARAMS;
-        }
-        if (!iconShapePath.equals(oldProfile.iconShapePath)) {
-            IconShape.init(context);
-        }
-
-        apply(context, changeFlags);
-    }
-
-    private void apply(Context context, int changeFlags) {
         for (OnIDPChangeListener listener : mChangeListeners) {
-            listener.onIdpChanged(changeFlags, this);
+            listener.onIdpChanged(this);
         }
     }
 
-    static ArrayList<DisplayOption> getPredefinedDeviceProfiles(Context context, String gridName) {
+    private static ArrayList<DisplayOption> getPredefinedDeviceProfiles(
+            Context context, String gridName, boolean isSplitDisplay) {
         ArrayList<DisplayOption> profiles = new ArrayList<>();
         try (XmlResourceParser parser = context.getResources().getXml(R.xml.device_profiles)) {
             final int depth = parser.getDepth();
@@ -449,8 +400,9 @@
                             && type != XmlPullParser.END_DOCUMENT) {
                         if ((type == XmlPullParser.START_TAG) && "display-option".equals(
                                 parser.getName())) {
-                            profiles.add(new DisplayOption(
-                                    gridOption, context, Xml.asAttributeSet(parser)));
+                            profiles.add(new DisplayOption(gridOption, context,
+                                    Xml.asAttributeSet(parser),
+                                    isSplitDisplay ? DEFAULT_SPLIT_DISPLAY : DEFAULT_TRUE));
                         }
                     }
                 }
@@ -521,17 +473,29 @@
         return (float) Math.hypot(x1 - x0, y1 - y0);
     }
 
-    @VisibleForTesting
-    static DisplayOption invDistWeightedInterpolate(
-            Info displayInfo, ArrayList<DisplayOption> points) {
-        Point smallestSize = new Point(displayInfo.smallestSize);
-        Point largestSize = new Point(displayInfo.largestSize);
+    private static DisplayOption invDistWeightedInterpolate(
+            Info displayInfo, ArrayList<DisplayOption> points, boolean isSplitDisplay) {
+        int minWidthPx = Integer.MAX_VALUE;
+        int minHeightPx = Integer.MAX_VALUE;
+        for (WindowBounds bounds : displayInfo.supportedBounds) {
+            boolean isTablet = displayInfo.isTablet(bounds);
+            if (isTablet && isSplitDisplay) {
+                // For split displays, take half width per page
+                minWidthPx = Math.min(minWidthPx, bounds.availableSize.x / 2);
+                minHeightPx = Math.min(minHeightPx, bounds.availableSize.y);
 
-        // This guarantees that width < height
-        float width = Utilities.dpiFromPx((float) Math.min(smallestSize.x, smallestSize.y),
-                displayInfo.densityDpi);
-        float height = Utilities.dpiFromPx((float) Math.min(largestSize.x, largestSize.y),
-                displayInfo.densityDpi);
+            } else if (!isTablet && bounds.isLandscape()) {
+                // We will use transposed layout in this case
+                minWidthPx = Math.min(minWidthPx, bounds.availableSize.y);
+                minHeightPx = Math.min(minHeightPx, bounds.availableSize.x);
+            } else {
+                minWidthPx = Math.min(minWidthPx, bounds.availableSize.x);
+                minHeightPx = Math.min(minHeightPx, bounds.availableSize.y);
+            }
+        }
+
+        float width = dpiFromPx(minWidthPx, displayInfo.densityDpi);
+        float height = dpiFromPx(minHeightPx, displayInfo.densityDpi);
 
         // Sort the profiles based on the closeness to the device size
         Collections.sort(points, (a, b) ->
@@ -556,33 +520,30 @@
         return out.multiply(1.0f / weights);
     }
 
-    @VisibleForTesting
-    static DisplayOption invDistWeightedInterpolate(float width, float height,
-            ArrayList<DisplayOption> points) {
-        float weights = 0;
-
-        DisplayOption p = points.get(0);
-        if (dist(width, height, p.minWidthDps, p.minHeightDps) == 0) {
-            return p;
-        }
-
-        DisplayOption out = new DisplayOption();
-        for (int i = 0; i < points.size() && i < KNEARESTNEIGHBOR; ++i) {
-            p = points.get(i);
-            float w = weight(width, height, p.minWidthDps, p.minHeightDps, WEIGHT_POWER);
-            weights += w;
-            out.add(new DisplayOption().add(p).multiply(w));
-        }
-        return out.multiply(1.0f / weights);
-    }
-
     public DeviceProfile getDeviceProfile(Context context) {
+        Resources res = context.getResources();
+        Configuration config = context.getResources().getConfiguration();
+
+        float availableWidth = config.screenWidthDp * res.getDisplayMetrics().density;
+        float availableHeight = config.screenHeightDp * res.getDisplayMetrics().density;
+
         if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.LAUNCHER_NOT_TRANSPOSED, "getDeviceProfile: orientation="
-                    + context.getResources().getConfiguration().orientation);
+            Log.d(TestProtocol.LAUNCHER_NOT_TRANSPOSED,
+                    "getDeviceProfile: orientation=" + config.orientation
+                            + " size=" + availableWidth + "x" + availableHeight);
         }
-        return context.getResources().getConfiguration().orientation
-                == Configuration.ORIENTATION_LANDSCAPE ? landscapeProfile : portraitProfile;
+        DeviceProfile bestMatch = supportedProfiles.get(0);
+        float minDiff = Float.MAX_VALUE;
+
+        for (DeviceProfile profile : supportedProfiles) {
+            float diff = Math.abs(profile.availableWidthPx - availableWidth)
+                    + Math.abs(profile.availableHeightPx - availableHeight);
+            if (diff < minDiff) {
+                minDiff = diff;
+                bestMatch = profile;
+            }
+        }
+        return bestMatch;
     }
 
     private static float weight(float x0, float y0, float x1, float y1, float pow) {
@@ -624,7 +585,10 @@
 
     public interface OnIDPChangeListener {
 
-        void onIdpChanged(int changeFlags, InvariantDeviceProfile profile);
+        /**
+         * Called when the device provide changes
+         */
+        void onIdpChanged(InvariantDeviceProfile profile);
     }
 
 
@@ -639,6 +603,9 @@
         private final int numFolderRows;
         private final int numFolderColumns;
 
+        private final int numAllAppsColumns;
+        private final int numDatabaseAllAppsColumns;
+        private final int numHotseatIcons;
         private final int numDatabaseHotseatIcons;
 
         private final String dbFile;
@@ -649,8 +616,6 @@
         private final boolean isScalable;
         private final int devicePaddingId;
 
-        public final boolean visible;
-
         private final SparseArray<TypedValue> extraAttrs;
 
         public GridOption(Context context, AttributeSet attrs) {
@@ -665,8 +630,17 @@
                     R.styleable.GridDisplayOption_defaultLayoutId, 0);
             demoModeLayoutId = a.getResourceId(
                     R.styleable.GridDisplayOption_demoModeLayoutId, defaultLayoutId);
-            numDatabaseHotseatIcons = a.getInt(
+
+            numAllAppsColumns = a.getInt(
+                    R.styleable.GridDisplayOption_numAllAppsColumns, numColumns);
+            numDatabaseAllAppsColumns = a.getInt(
+                    R.styleable.GridDisplayOption_numExtendedAllAppsColumns, 2 * numAllAppsColumns);
+
+            numHotseatIcons = a.getInt(
                     R.styleable.GridDisplayOption_numHotseatIcons, numColumns);
+            numDatabaseHotseatIcons = a.getInt(
+                    R.styleable.GridDisplayOption_numExtendedHotseatIcons, 2 * numHotseatIcons);
+
             numFolderRows = a.getInt(
                     R.styleable.GridDisplayOption_numFolderRows, numRows);
             numFolderColumns = a.getInt(
@@ -677,24 +651,21 @@
             devicePaddingId = a.getResourceId(
                     R.styleable.GridDisplayOption_devicePaddingId, 0);
 
-            visible = a.getBoolean(R.styleable.GridDisplayOption_visible, true);
-
             a.recycle();
-
             extraAttrs = Themes.createValueMap(context, attrs,
                     IntArray.wrap(R.styleable.GridDisplayOption));
         }
     }
 
-    private static final class DisplayOption {
-        private final GridOption grid;
+    @VisibleForTesting
+    static final class DisplayOption {
+
+        public final GridOption grid;
 
         private final float minWidthDps;
         private final float minHeightDps;
         private final boolean canBeDefault;
 
-        private float numShownHotseatIcons;
-        private float numAllAppsColumns;
         private float minCellHeight;
         private float minCellWidth;
         private float borderSpacing;
@@ -706,7 +677,7 @@
         private float allAppsIconSize;
         private float allAppsIconTextSize;
 
-        DisplayOption(GridOption grid, Context context, AttributeSet attrs) {
+        DisplayOption(GridOption grid, Context context, AttributeSet attrs, int defaultFlagValue) {
             this.grid = grid;
 
             TypedArray a = context.obtainStyledAttributes(
@@ -714,12 +685,9 @@
 
             minWidthDps = a.getFloat(R.styleable.ProfileDisplayOption_minWidthDps, 0);
             minHeightDps = a.getFloat(R.styleable.ProfileDisplayOption_minHeightDps, 0);
-            canBeDefault = a.getBoolean(
-                    R.styleable.ProfileDisplayOption_canBeDefault, false);
-            numShownHotseatIcons = a.getInt(R.styleable.ProfileDisplayOption_numShownHotseatIcons,
-                    grid.numDatabaseHotseatIcons);
-            numAllAppsColumns = a.getInt(R.styleable.ProfileDisplayOption_numAllAppsColumns,
-                    grid.numColumns);
+
+            canBeDefault = a.getInt(R.styleable.ProfileDisplayOption_canBeDefault, 0)
+                    == defaultFlagValue;
 
             minCellHeight = a.getFloat(R.styleable.ProfileDisplayOption_minCellHeightDps, 0);
             minCellWidth = a.getFloat(R.styleable.ProfileDisplayOption_minCellWidthDps, 0);
@@ -748,16 +716,12 @@
             minWidthDps = 0;
             minHeightDps = 0;
             canBeDefault = false;
-            numShownHotseatIcons = 0;
-            numAllAppsColumns = 0;
             minCellHeight = 0;
             minCellWidth = 0;
             borderSpacing = 0;
         }
 
         private DisplayOption multiply(float w) {
-            numShownHotseatIcons *= w;
-            numAllAppsColumns *= w;
             iconSize *= w;
             landscapeIconSize *= w;
             allAppsIconSize *= w;
@@ -771,8 +735,6 @@
         }
 
         private DisplayOption add(DisplayOption p) {
-            numShownHotseatIcons += p.numShownHotseatIcons;
-            numAllAppsColumns += p.numAllAppsColumns;
             iconSize += p.iconSize;
             landscapeIconSize += p.landscapeIconSize;
             allAppsIconSize += p.allAppsIconSize;
@@ -785,18 +747,4 @@
             return this;
         }
     }
-
-    private class OverlayMonitor extends BroadcastReceiver {
-
-        private final String ACTION_OVERLAY_CHANGED = "android.intent.action.OVERLAY_CHANGED";
-
-        OverlayMonitor(Context context) {
-            context.registerReceiver(this, getPackageFilter("android", ACTION_OVERLAY_CHANGED));
-        }
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            onConfigChanged(context);
-        }
-    }
 }
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index f640118..deb1147 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -189,7 +189,6 @@
 import com.android.launcher3.widget.PendingAddWidgetInfo;
 import com.android.launcher3.widget.PendingAppWidgetHostView;
 import com.android.launcher3.widget.WidgetAddFlowHandler;
-import com.android.launcher3.widget.WidgetHostViewLoader;
 import com.android.launcher3.widget.WidgetManagerHelper;
 import com.android.launcher3.widget.custom.CustomWidgetManager;
 import com.android.launcher3.widget.model.WidgetsListBaseEntry;
@@ -566,11 +565,7 @@
     }
 
     @Override
-    public void onIdpChanged(int changeFlags, InvariantDeviceProfile idp) {
-        onIdpChanged(idp);
-    }
-
-    private void onIdpChanged(InvariantDeviceProfile idp) {
+    public void onIdpChanged(InvariantDeviceProfile idp) {
         if (TestProtocol.sDebugTracing) {
             Log.d(TestProtocol.LAUNCHER_NOT_TRANSPOSED, "onIdpChanged");
         }
@@ -2332,8 +2327,7 @@
                         pendingInfo.spanY = item.spanY;
                         pendingInfo.minSpanX = item.minSpanX;
                         pendingInfo.minSpanY = item.minSpanY;
-                        Bundle options = WidgetHostViewLoader.getDefaultOptionsForWidget(this,
-                                pendingInfo);
+                        Bundle options = pendingInfo.getDefaultSizeOptions(this);
 
                         boolean isDirectConfig =
                                 item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_DIRECT_CONFIG);
@@ -2782,7 +2776,7 @@
      * @see LauncherState#getOverviewScaleAndOffset(Launcher)
      */
     public float[] getNormalOverviewScaleAndOffset() {
-        return new float[] {NO_SCALE, NO_OFFSET};
+        return new float[] {NO_SCALE, NO_OFFSET, NO_OFFSET};
     }
 
     /**
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 11585f9..834b5a7 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -16,7 +16,8 @@
 
 package com.android.launcher3;
 
-import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_ICON_PARAMS;
+import static com.android.launcher3.Utilities.getDevicePrefs;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_THEMED_ICONS;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 import static com.android.launcher3.util.SettingsCache.NOTIFICATION_BADGING_URI;
 
@@ -24,12 +25,13 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.LauncherApps;
-import android.os.Handler;
+import android.os.UserHandle;
 import android.util.Log;
 
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.graphics.IconShape;
 import com.android.launcher3.icons.IconCache;
 import com.android.launcher3.icons.IconProvider;
 import com.android.launcher3.icons.LauncherIcons;
@@ -39,6 +41,7 @@
 import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.Preconditions;
+import com.android.launcher3.util.RunnableList;
 import com.android.launcher3.util.SafeCloseable;
 import com.android.launcher3.util.SettingsCache;
 import com.android.launcher3.util.SimpleBroadcastReceiver;
@@ -47,6 +50,7 @@
 public class LauncherAppState {
 
     public static final String ACTION_FORCE_ROLOAD = "force-reload-launcher";
+    private static final String KEY_ICON_STATE = "pref_icon_shape_path";
 
     // We do not need any synchronization for this variable as its only written on UI thread.
     public static final MainThreadInitializedObject<LauncherAppState> INSTANCE =
@@ -54,16 +58,11 @@
 
     private final Context mContext;
     private final LauncherModel mModel;
+    private final IconProvider mIconProvider;
     private final IconCache mIconCache;
     private final WidgetPreviewLoader mWidgetCache;
     private final InvariantDeviceProfile mInvariantDeviceProfile;
-    private SettingsCache.OnChangeListener mNotificationSettingsChangedListener;
-
-    private SettingsCache mSettingsCache;
-    private InstallSessionTracker mInstallSessionTracker;
-    private SimpleBroadcastReceiver mModelChangeReceiver;
-    private SafeCloseable mCalendarChangeTracker;
-    private SafeCloseable mUserChangeListener;
+    private final RunnableList mOnTerminateCallback = new RunnableList();
 
     public static LauncherAppState getInstance(final Context context) {
         return INSTANCE.get(context);
@@ -80,40 +79,47 @@
     public LauncherAppState(Context context) {
         this(context, LauncherFiles.APP_ICONS_DB);
 
-        mModelChangeReceiver = new SimpleBroadcastReceiver(mModel::onBroadcastIntent);
+        mInvariantDeviceProfile.addOnChangeListener(idp -> refreshAndReloadLauncher());
 
         mContext.getSystemService(LauncherApps.class).registerCallback(mModel);
-        mModelChangeReceiver.register(mContext, Intent.ACTION_LOCALE_CHANGED,
+
+        SimpleBroadcastReceiver modelChangeReceiver =
+                new SimpleBroadcastReceiver(mModel::onBroadcastIntent);
+        modelChangeReceiver.register(mContext, Intent.ACTION_LOCALE_CHANGED,
                 Intent.ACTION_MANAGED_PROFILE_AVAILABLE,
                 Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE,
                 Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
         if (FeatureFlags.IS_STUDIO_BUILD) {
-            mModelChangeReceiver.register(mContext, ACTION_FORCE_ROLOAD);
+            modelChangeReceiver.register(mContext, ACTION_FORCE_ROLOAD);
         }
-
-        mCalendarChangeTracker = IconProvider.registerIconChangeListener(mContext,
-                mModel::onAppIconChanged, MODEL_EXECUTOR.getHandler());
+        mOnTerminateCallback.add(() -> mContext.unregisterReceiver(modelChangeReceiver));
 
         // TODO: remove listener on terminate
         FeatureFlags.APP_SEARCH_IMPROVEMENTS.addChangeListener(context, mModel::forceReload);
         CustomWidgetManager.INSTANCE.get(mContext)
                 .setWidgetRefreshCallback(mModel::refreshAndBindWidgetsAndShortcuts);
 
-        mUserChangeListener = UserCache.INSTANCE.get(mContext)
+        SafeCloseable userChangeListener = UserCache.INSTANCE.get(mContext)
                 .addUserChangeListener(mModel::forceReload);
+        mOnTerminateCallback.add(userChangeListener::close);
 
-        mInvariantDeviceProfile.addOnChangeListener(this::onIdpChanged);
-        new Handler().post( () -> mInvariantDeviceProfile.verifyConfigChangedInBackground(context));
+        IconObserver observer = new IconObserver();
+        SafeCloseable iconChangeTracker = mIconProvider.registerIconChangeListener(
+                observer, MODEL_EXECUTOR.getHandler());
+        mOnTerminateCallback.add(iconChangeTracker::close);
+        MODEL_EXECUTOR.execute(observer::verifyIconChanged);
 
-        mInstallSessionTracker = InstallSessionHelper.INSTANCE.get(context)
-                .registerInstallTracker(mModel);
+        InstallSessionTracker installSessionTracker =
+                InstallSessionHelper.INSTANCE.get(context).registerInstallTracker(mModel);
+        mOnTerminateCallback.add(installSessionTracker::unregister);
 
         // Register an observer to rebind the notification listener when dots are re-enabled.
-        mSettingsCache = SettingsCache.INSTANCE.get(mContext);
-        mNotificationSettingsChangedListener = this::onNotificationSettingsChanged;
-        mSettingsCache.register(NOTIFICATION_BADGING_URI,
-                mNotificationSettingsChangedListener);
-        onNotificationSettingsChanged(mSettingsCache.getValue(NOTIFICATION_BADGING_URI));
+        SettingsCache settingsCache = SettingsCache.INSTANCE.get(mContext);
+        SettingsCache.OnChangeListener notificationLister = this::onNotificationSettingsChanged;
+        settingsCache.register(NOTIFICATION_BADGING_URI, notificationLister);
+        onNotificationSettingsChanged(settingsCache.getValue(NOTIFICATION_BADGING_URI));
+        mOnTerminateCallback.add(() ->
+                settingsCache.unregister(NOTIFICATION_BADGING_URI, notificationLister));
     }
 
     public LauncherAppState(Context context, @Nullable String iconCacheFileName) {
@@ -122,30 +128,25 @@
         mContext = context;
 
         mInvariantDeviceProfile = InvariantDeviceProfile.INSTANCE.get(context);
-
-        mIconCache = new IconCache(mContext, mInvariantDeviceProfile, iconCacheFileName);
+        mIconProvider =  new IconProvider(context, ENABLE_THEMED_ICONS.get());
+        mIconCache = new IconCache(mContext, mInvariantDeviceProfile,
+                iconCacheFileName, mIconProvider);
         mWidgetCache = new WidgetPreviewLoader(mContext, mIconCache);
         mModel = new LauncherModel(context, this, mIconCache, new AppFilter(mContext));
     }
 
-    protected void onNotificationSettingsChanged(boolean areNotificationDotsEnabled) {
+    private void onNotificationSettingsChanged(boolean areNotificationDotsEnabled) {
         if (areNotificationDotsEnabled) {
             NotificationListener.requestRebind(new ComponentName(
                     mContext, NotificationListener.class));
         }
     }
 
-    private void onIdpChanged(int changeFlags, InvariantDeviceProfile idp) {
-        if (changeFlags == 0) {
-            return;
-        }
-
-        if ((changeFlags & CHANGE_FLAG_ICON_PARAMS) != 0) {
-            LauncherIcons.clearPool();
-            mIconCache.updateIconParams(idp.fillResIconDpi, idp.iconBitmapSize);
-            mWidgetCache.refresh();
-        }
-
+    private void refreshAndReloadLauncher() {
+        LauncherIcons.clearPool();
+        mIconCache.updateIconParams(
+                mInvariantDeviceProfile.fillResIconDpi, mInvariantDeviceProfile.iconBitmapSize);
+        mWidgetCache.refresh();
         mModel.forceReload();
     }
 
@@ -154,25 +155,13 @@
      */
     public void onTerminate() {
         mModel.destroy();
-        if (mModelChangeReceiver != null) {
-            mContext.unregisterReceiver(mModelChangeReceiver);
-        }
         mContext.getSystemService(LauncherApps.class).unregisterCallback(mModel);
-        if (mInstallSessionTracker != null) {
-            mInstallSessionTracker.unregister();
-        }
-        if (mCalendarChangeTracker != null) {
-            mCalendarChangeTracker.close();
-        }
-        if (mUserChangeListener != null) {
-            mUserChangeListener.close();
-        }
         CustomWidgetManager.INSTANCE.get(mContext).setWidgetRefreshCallback(null);
+        mOnTerminateCallback.executeAllAndDestroy();
+    }
 
-        if (mSettingsCache != null) {
-            mSettingsCache.unregister(NOTIFICATION_BADGING_URI,
-                    mNotificationSettingsChangedListener);
-        }
+    public IconProvider getIconProvider() {
+        return mIconProvider;
     }
 
     public IconCache getIconCache() {
@@ -197,4 +186,26 @@
     public static InvariantDeviceProfile getIDP(Context context) {
         return InvariantDeviceProfile.INSTANCE.get(context);
     }
+
+    private class IconObserver implements IconProvider.IconChangeListener {
+
+        @Override
+        public void onAppIconChanged(String packageName, UserHandle user) {
+            mModel.onAppIconChanged(packageName, user);
+        }
+
+        @Override
+        public void onSystemIconStateChanged(String iconState) {
+            IconShape.init(mContext);
+            refreshAndReloadLauncher();
+            getDevicePrefs(mContext).edit().putString(KEY_ICON_STATE, iconState).apply();
+        }
+
+        void verifyIconChanged() {
+            String iconState = mIconProvider.getSystemIconState();
+            if (!iconState.equals(getDevicePrefs(mContext).getString(KEY_ICON_STATE, ""))) {
+                onSystemIconStateChanged(iconState);
+            }
+        }
+    }
 }
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 4c11725..9d50edd 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -172,10 +172,12 @@
     }
 
     /**
-     * Returns an array of two elements.
+     * Returns an array of three elements.
      * The first specifies the scale for the overview
      * The second is the factor ([0, 1], 0 => center-screen; 1 => offscreen) by which overview
      * should be shifted horizontally.
+     * The third is the factor ([0, 1], 0 => center-screen; 1 => offscreen) by which overview
+     * should be shifted vertically.
      */
     public float[] getOverviewScaleAndOffset(Launcher launcher) {
         return launcher.getNormalOverviewScaleAndOffset();
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 073a46c..7c00362 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -75,9 +75,9 @@
 import com.android.launcher3.graphics.GridCustomizationsProvider;
 import com.android.launcher3.graphics.TintedDrawableSpan;
 import com.android.launcher3.icons.FastBitmapDrawable;
-import com.android.launcher3.icons.IconProvider;
 import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.icons.ShortcutCachingLogic;
+import com.android.launcher3.icons.ThemedIconDrawable.ThemedAdaptiveIcon;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.pm.ShortcutConfigActivityInfo;
@@ -242,8 +242,8 @@
      * @param outRect The out rect where we return the bounds of {@param view} in drag layer coords.
      */
     public static void getBoundsForViewInDragLayer(BaseDragLayer dragLayer, View view,
-            Rect viewBounds, boolean ignoreTransform, RectF outRect) {
-        float[] points = sTmpFloatArray;
+            Rect viewBounds, boolean ignoreTransform, float[] recycle, RectF outRect) {
+        float[] points = recycle == null ? new float[4] : recycle;
         points[0] = viewBounds.left;
         points[1] = viewBounds.top;
         points[2] = viewBounds.right;
@@ -646,13 +646,23 @@
      */
     public static Drawable getFullDrawable(Launcher launcher, ItemInfo info, int width, int height,
             Object[] outObj) {
+        Drawable icon = loadFullDrawableWithoutTheme(launcher, info, width, height, outObj);
+        if (icon instanceof ThemedAdaptiveIcon) {
+            icon = ((ThemedAdaptiveIcon) icon).getThemedDrawable(launcher);
+        }
+        return icon;
+    }
+
+    private static Drawable loadFullDrawableWithoutTheme(Launcher launcher, ItemInfo info,
+            int width, int height, Object[] outObj) {
         LauncherAppState appState = LauncherAppState.getInstance(launcher);
         if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
             LauncherActivityInfo activityInfo = launcher.getSystemService(LauncherApps.class)
                     .resolveActivity(info.getIntent(), info.user);
             outObj[0] = activityInfo;
-            return activityInfo == null ? null : new IconProvider(launcher).getIcon(
-                    activityInfo, launcher.getDeviceProfile().inv.fillResIconDpi);
+            return activityInfo == null ? null : LauncherAppState.getInstance(launcher)
+                    .getIconProvider().getIcon(
+                            activityInfo, launcher.getDeviceProfile().inv.fillResIconDpi);
         } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
             if (info instanceof PendingAddShortcutInfo) {
                 ShortcutConfigActivityInfo activityInfo =
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 05d6e04..7c5f99e 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -456,7 +456,7 @@
     }
 
     private boolean isTwoPanelEnabled() {
-        return mLauncher.mDeviceProfile.isTablet && FeatureFlags.ENABLE_TWO_PANEL_HOME.get();
+        return mLauncher.mDeviceProfile.isTwoPanels;
     }
 
     @Override
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index aa99d52..c771e3e 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -34,9 +34,9 @@
 import static com.android.launcher3.graphics.SysUiScrim.SYSUI_PROGRESS;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_SCALE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_HOTSEAT_TRANSLATE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_SCRIM_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_SCALE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_SCRIM_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE;
 import static com.android.launcher3.states.StateAnimationConfig.SKIP_SCRIM;
 
@@ -166,10 +166,9 @@
         propertySetter.setFloat(sysUiScrim, SYSUI_PROGRESS,
                 state.hasFlag(FLAG_HAS_SYS_UI_SCRIM) ? 1 : 0, LINEAR);
 
-
         propertySetter.setViewBackgroundColor(mLauncher.getScrimView(),
                 state.getWorkspaceScrimColor(mLauncher),
-                config.getInterpolator(ANIM_WORKSPACE_SCRIM_FADE, LINEAR));
+                config.getInterpolator(ANIM_SCRIM_FADE, LINEAR));
     }
 
     public void applyChildState(LauncherState state, CellLayout cl, int childIndex) {
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index a5852ba..dbdfb2b 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -32,6 +32,7 @@
 import com.android.launcher3.Workspace;
 import com.android.launcher3.dragndrop.DragController.DragListener;
 import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.dragndrop.DragView;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.keyboard.KeyboardDragAndDropView;
 import com.android.launcher3.model.data.AppInfo;
@@ -290,7 +291,12 @@
             return actions;
         }
 
-        CellLayout layout = (CellLayout) host.getParent().getParent();
+        CellLayout layout;
+        if (host.getParent() instanceof DragView) {
+            layout = (CellLayout) ((DragView) host.getParent()).getContentViewParent().getParent();
+        } else {
+            layout = (CellLayout) host.getParent().getParent();
+        }
         if ((providerInfo.resizeMode & AppWidgetProviderInfo.RESIZE_HORIZONTAL) != 0) {
             if (layout.isRegionVacant(info.cellX + info.spanX, info.cellY, 1, info.spanY) ||
                     layout.isRegionVacant(info.cellX - 1, info.cellY, 1, info.spanY)) {
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 40f7ab1..b11b63e 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -75,7 +75,7 @@
 public class AllAppsContainerView extends SpringRelativeLayout implements DragSource,
         Insettable, OnDeviceProfileChangeListener, OnActivePageChangedListener {
 
-    private static final float FLING_VELOCITY_MULTIPLIER = 1800f;
+    private static final float FLING_VELOCITY_MULTIPLIER = 1000f;
 
     // Starts the springs after at least 25% of the animation has passed.
     private static final float FLING_ANIMATION_THRESHOLD = 0.25f;
@@ -157,7 +157,7 @@
     @Override
     public void onDeviceProfileChanged(DeviceProfile dp) {
         for (AdapterHolder holder : mAH) {
-            holder.adapter.setAppsPerRow(dp.inv.numAllAppsColumns);
+            holder.adapter.setAppsPerRow(dp.numShownAllAppsColumns);
             if (holder.recyclerView != null) {
                 // Remove all views and clear the pool, while keeping the data same. After this
                 // call, all the viewHolders will be recreated.
@@ -611,7 +611,7 @@
             public void onAnimationUpdate(ValueAnimator valueAnimator) {
                 if (shouldSpring
                         && valueAnimator.getAnimatedFraction() >= FLING_ANIMATION_THRESHOLD) {
-                    absorbSwipeUpVelocity(-Math.abs(
+                    absorbSwipeUpVelocity(Math.abs(
                             Math.round(velocity * FLING_VELOCITY_MULTIPLIER)));
                     shouldSpring = false;
                 }
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 5b4c4c5..70588ea 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -283,7 +283,7 @@
         mOnIconClickListener = launcher.getItemOnClickListener();
 
         mSearchAdapterProvider = searchAdapterProvider;
-        setAppsPerRow(mLauncher.getDeviceProfile().inv.numAllAppsColumns);
+        setAppsPerRow(mLauncher.getDeviceProfile().numShownAllAppsColumns);
     }
 
     public void setAppsPerRow(int appsPerRow) {
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index c61c0d6..c4c7891 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -15,13 +15,15 @@
  */
 package com.android.launcher3.allapps;
 
+import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.ALL_APPS_CONTENT;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
+import static com.android.launcher3.anim.Interpolators.ACCEL_0_75;
+import static com.android.launcher3.anim.Interpolators.ACCEL_2;
+import static com.android.launcher3.anim.Interpolators.DEACCEL;
+import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
 import static com.android.launcher3.util.SystemUiController.UI_STATE_ALLAPPS;
 
@@ -154,9 +156,9 @@
             return;
         }
 
-        Interpolator interpolator = config.userControlled ? LINEAR : toState == OVERVIEW
-                ? config.getInterpolator(ANIM_OVERVIEW_SCALE, FAST_OUT_SLOW_IN)
-                : FAST_OUT_SLOW_IN;
+        Interpolator interpolator = toState.equals(ALL_APPS)
+                ? (config.userControlled ? ACCEL_2 : ACCEL_0_75) :
+                        (config.userControlled ? DEACCEL_2 : DEACCEL);
 
         Animator anim = createSpringAnimation(mProgress, targetProgress);
         anim.setInterpolator(config.getInterpolator(ANIM_VERTICAL_PROGRESS, interpolator));
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index bad8704..3b88a0b 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -87,6 +87,10 @@
     public static final BooleanFlag ENABLE_QUICKSTEP_LIVE_TILE = getDebugFlag(
             "ENABLE_QUICKSTEP_LIVE_TILE", true, "Enable live tile in Quickstep overview");
 
+    public static final BooleanFlag ENABLE_QUICKSTEP_WIDGET_APP_START = getDebugFlag(
+            "ENABLE_QUICKSTEP_WIDGET_APP_START", false,
+            "Enable Quickstep animation when launching activities from an app widget");
+
     // Keep as DeviceFlag to allow remote disable in emergency.
     public static final BooleanFlag ENABLE_SUGGESTED_ACTIONS_OVERVIEW = new DeviceFlag(
             "ENABLE_SUGGESTED_ACTIONS_OVERVIEW", true, "Show chip hints on the overview screen");
@@ -137,8 +141,8 @@
     public static final BooleanFlag MULTI_DB_GRID_MIRATION_ALGO = getDebugFlag(
             "MULTI_DB_GRID_MIRATION_ALGO", true, "Use the multi-db grid migration algorithm");
 
-    public static final BooleanFlag ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER = getDebugFlag(
-            "ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER", true, "Show launcher preview in grid picker");
+    public static final BooleanFlag ENABLE_THEMED_ICONS = getDebugFlag(
+            "ENABLE_THEMED_ICONS", false, "Enable themed icons on workspace");
 
     // Keep as DeviceFlag for remote disable in emergency.
     public static final BooleanFlag ENABLE_OVERVIEW_SELECTIONS = new DeviceFlag(
@@ -226,6 +230,10 @@
     public static final BooleanFlag ENABLE_ENFORCED_ROUNDED_CORNERS = new DeviceFlag(
             "ENABLE_ENFORCED_ROUNDED_CORNERS", true, "Enforce rounded corners on all App Widgets");
 
+    public static final BooleanFlag ENABLE_LOCAL_RECOMMENDED_WIDGETS_FILTER = new DeviceFlag(
+            "ENABLE_LOCAL_RECOMMENDED_WIDGETS_FILTER", true,
+            "Enables a local filter for recommended widgets.");
+
     public static final BooleanFlag NOTIFY_CRASHES = getDebugFlag("NOTIFY_CRASHES", false,
             "Sends a notification whenever launcher encounters an uncaught exception.");
 
diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java
index d5a04a6..5ba36f2 100644
--- a/src/com/android/launcher3/dragndrop/AddItemActivity.java
+++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java
@@ -64,7 +64,6 @@
 import com.android.launcher3.widget.PendingAddWidgetInfo;
 import com.android.launcher3.widget.WidgetCell;
 import com.android.launcher3.widget.WidgetCellPreview;
-import com.android.launcher3.widget.WidgetHostViewLoader;
 import com.android.launcher3.widget.WidgetImageView;
 import com.android.launcher3.widget.WidgetManagerHelper;
 
@@ -234,7 +233,7 @@
         PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(widgetInfo);
         pendingInfo.spanX = Math.min(mIdp.numColumns, widgetInfo.spanX);
         pendingInfo.spanY = Math.min(mIdp.numRows, widgetInfo.spanY);
-        mWidgetOptions = WidgetHostViewLoader.getDefaultOptionsForWidget(this, pendingInfo);
+        mWidgetOptions = pendingInfo.getDefaultSizeOptions(this);
         mWidgetCell.getWidgetView().setTag(pendingInfo);
 
         applyWidgetItemAsync(() -> new WidgetItem(widgetInfo, mIdp, mApp.getIconCache()));
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index d7f6cdb..575b8fd 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -22,7 +22,6 @@
 import static com.android.launcher3.Utilities.ATLEAST_Q;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 
-import android.animation.ValueAnimator;
 import android.content.ComponentName;
 import android.content.res.Resources;
 import android.graphics.Point;
@@ -307,17 +306,12 @@
             mOptions.preDragCondition.onPreDragEnd(mDragObject, true /* dragStarted*/);
         }
         mIsInPreDrag = false;
+        mDragObject.dragView.onDragStart();
         for (DragListener listener : new ArrayList<>(mListeners)) {
             listener.onDragStart(mDragObject, mOptions);
         }
     }
 
-    public void addFirstFrameAnimationHelper(ValueAnimator anim) {
-        if (mDragObject != null && mDragObject.dragView != null) {
-            mDragObject.dragView.mFirstFrameAnimatorHelper.addTo(anim);
-        }
-    }
-
     public Optional<InstanceId> getLogInstanceId() {
         return Optional.ofNullable(mDragObject)
                 .map(dragObject -> dragObject.logInstanceId);
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index 68a8af2..30ee9ec 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -51,7 +51,6 @@
 import androidx.dynamicanimation.animation.SpringAnimation;
 import androidx.dynamicanimation.animation.SpringForce;
 
-import com.android.launcher3.FirstFrameAnimatorHelper;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.LauncherState;
@@ -62,6 +61,7 @@
 import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.statemanager.StateManager.StateListener;
+import com.android.launcher3.util.RunnableList;
 import com.android.launcher3.util.Thunk;
 import com.android.launcher3.views.BaseDragLayer;
 
@@ -85,12 +85,13 @@
     private final float mScaleOnDrop;
     private final int[] mTempLoc = new int[2];
 
+    private final RunnableList mOnDragStartCallback = new RunnableList();
+
     private Point mDragVisualizeOffset = null;
     private Rect mDragRegion = null;
     private final Launcher mLauncher;
     private final DragLayer mDragLayer;
     @Thunk final DragController mDragController;
-    final FirstFrameAnimatorHelper mFirstFrameAnimatorHelper;
     private boolean mHasDrawn = false;
 
     final ValueAnimator mAnim;
@@ -136,7 +137,6 @@
         mLauncher = launcher;
         mDragLayer = launcher.getDragLayer();
         mDragController = launcher.getDragController();
-        mFirstFrameAnimatorHelper = new FirstFrameAnimatorHelper(this);
 
         mContent = content;
         mWidth = width;
@@ -276,7 +276,8 @@
                 }
                 mFgSpringDrawable.setBounds(bounds);
 
-                new Handler(Looper.getMainLooper()).post(() -> {
+                new Handler(Looper.getMainLooper()).post(() -> mOnDragStartCallback.add(() -> {
+                    // TODO: Consider fade-in animation
                     // Assign the variable on the UI thread to avoid race conditions.
                     mScaledMaskPath = mask;
                     // Avoid relayout as we do not care about children affecting layout
@@ -290,11 +291,18 @@
                         mBadge.setColorFilter(d.getColorFilter());
                     }
                     invalidate();
-                });
+                }));
             }
         });
     }
 
+    /**
+     * Called when pre-drag finishes for an icon
+     */
+    public void onDragStart() {
+        mOnDragStartCallback.executeAllAndDestroy();
+    }
+
     // TODO(b/183609936): This is only for LauncherAppWidgetHostView that is rendered in a drawable.
     // Once LauncherAppWidgetHostView is directly rendered in this view, removes this method.
     @Override
diff --git a/src/com/android/launcher3/folder/PreviewItemManager.java b/src/com/android/launcher3/folder/PreviewItemManager.java
index 8244f01..6adef01 100644
--- a/src/com/android/launcher3/folder/PreviewItemManager.java
+++ b/src/com/android/launcher3/folder/PreviewItemManager.java
@@ -400,7 +400,7 @@
             drawable.setLevel(item.getProgressLevel());
             p.drawable = drawable;
         } else {
-            p.drawable = item.newIcon(mContext);
+            p.drawable = item.newIcon(mContext, true);
         }
         p.drawable.setBounds(0, 0, mIconSize, mIconSize);
         p.item = item;
diff --git a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
index 911f8c3..cb42e7a 100644
--- a/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
+++ b/src/com/android/launcher3/graphics/GridCustomizationsProvider.java
@@ -90,10 +90,7 @@
                     parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
                 if ((type == XmlPullParser.START_TAG)
                         && GridOption.TAG_NAME.equals(parser.getName())) {
-                    GridOption option = new GridOption(getContext(), Xml.asAttributeSet(parser));
-                    if (option.visible) {
-                        result.add(option);
-                    }
+                    result.add(new GridOption(getContext(), Xml.asAttributeSet(parser)));
                 }
             }
         } catch (IOException | XmlPullParserException e) {
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index 31764c5..f5b6890 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -20,7 +20,6 @@
 import static android.view.View.VISIBLE;
 
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER;
 import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems;
 import static com.android.launcher3.model.ModelUtils.getMissingHotseatRanks;
 import static com.android.launcher3.model.ModelUtils.sortWorkspaceItemsSpatially;
@@ -118,7 +117,7 @@
  *   4) Measure and draw the view on a canvas
  */
 @TargetApi(Build.VERSION_CODES.O)
-public class LauncherPreviewRenderer extends ContextThemeWrapper
+public class LauncherPreviewRenderer extends ContextWrapper
         implements ActivityContext, WorkspaceLayoutManager, LayoutInflater.Factory2 {
 
     private static final String TAG = "LauncherPreviewRenderer";
@@ -220,11 +219,11 @@
     private final CellLayout mWorkspace;
 
     public LauncherPreviewRenderer(Context context, InvariantDeviceProfile idp, boolean migrated) {
-        super(context, R.style.AppTheme);
+        super(context);
         mUiHandler = new Handler(Looper.getMainLooper());
         mContext = context;
         mIdp = idp;
-        mDp = idp.portraitProfile.copy(context);
+        mDp = idp.getDeviceProfile(context).copy(context);
         mMigrated = migrated;
 
         // TODO: get correct insets once display cutout API is available.
@@ -271,14 +270,6 @@
         return mRootView;
     }
 
-    public boolean shouldShowRealLauncherPreview() {
-        return ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER.get();
-    }
-
-    public boolean shouldShowQsb() {
-        return FeatureFlags.QSB_ON_FIRST_SCREEN;
-    }
-
     @Override
     public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
         if ("TextClock".equals(name)) {
@@ -402,107 +393,88 @@
     }
 
     private void populate() {
-        if (shouldShowRealLauncherPreview()) {
-            WorkspaceFetcher fetcher;
-            PreviewContext previewContext = null;
-            if (mMigrated) {
-                previewContext = new PreviewContext(mContext, mIdp);
-                LauncherAppState appForPreview = new LauncherAppState(
-                        previewContext, null /* iconCacheFileName */);
-                fetcher = new WorkspaceItemsInfoFromPreviewFetcher(appForPreview);
-                MODEL_EXECUTOR.execute(fetcher);
-            } else {
-                fetcher = new WorkspaceItemsInfoFetcher();
-                LauncherAppState.getInstance(mContext).getModel().enqueueModelUpdateTask(
-                        (LauncherModel.ModelUpdateTask) fetcher);
-            }
-            WorkspaceResult workspaceResult = fetcher.get();
-            if (previewContext != null) {
-                previewContext.onDestroy();
-            }
-
-            if (workspaceResult == null) {
-                return;
-            }
-
-            // Separate the items that are on the current screen, and the other remaining items.
-            ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<>();
-            ArrayList<ItemInfo> otherWorkspaceItems = new ArrayList<>();
-            ArrayList<LauncherAppWidgetInfo> currentAppWidgets = new ArrayList<>();
-            ArrayList<LauncherAppWidgetInfo> otherAppWidgets = new ArrayList<>();
-            filterCurrentWorkspaceItems(0 /* currentScreenId */,
-                    workspaceResult.mWorkspaceItems, currentWorkspaceItems,
-                    otherWorkspaceItems);
-            filterCurrentWorkspaceItems(0 /* currentScreenId */, workspaceResult.mAppWidgets,
-                    currentAppWidgets, otherAppWidgets);
-            sortWorkspaceItemsSpatially(mIdp, currentWorkspaceItems);
-            for (ItemInfo itemInfo : currentWorkspaceItems) {
-                switch (itemInfo.itemType) {
-                    case Favorites.ITEM_TYPE_APPLICATION:
-                    case Favorites.ITEM_TYPE_SHORTCUT:
-                    case Favorites.ITEM_TYPE_DEEP_SHORTCUT:
-                        inflateAndAddIcon((WorkspaceItemInfo) itemInfo);
-                        break;
-                    case Favorites.ITEM_TYPE_FOLDER:
-                        inflateAndAddFolder((FolderInfo) itemInfo);
-                        break;
-                    default:
-                        break;
-                }
-            }
-            for (ItemInfo itemInfo : currentAppWidgets) {
-                switch (itemInfo.itemType) {
-                    case Favorites.ITEM_TYPE_APPWIDGET:
-                    case Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
-                        if (mMigrated) {
-                            inflateAndAddWidgets((LauncherAppWidgetInfo) itemInfo,
-                                    workspaceResult.mWidgetProvidersMap);
-                        } else {
-                            inflateAndAddWidgets((LauncherAppWidgetInfo) itemInfo,
-                                    workspaceResult.mWidgetsModel);
-                        }
-                        break;
-                    default:
-                        break;
-                }
-            }
-            IntArray ranks = getMissingHotseatRanks(currentWorkspaceItems,
-                    mDp.numShownHotseatIcons);
-            List<ItemInfo> predictions = workspaceResult.mHotseatPredictions == null
-                    ? Collections.emptyList() : workspaceResult.mHotseatPredictions.items;
-            int count = Math.min(ranks.size(), predictions.size());
-            for (int i = 0; i < count; i++) {
-                int rank = ranks.get(i);
-                WorkspaceItemInfo itemInfo =
-                        new WorkspaceItemInfo((WorkspaceItemInfo) predictions.get(i));
-                itemInfo.container = CONTAINER_HOTSEAT_PREDICTION;
-                itemInfo.rank = rank;
-                itemInfo.cellX = mHotseat.getCellXFromOrder(rank);
-                itemInfo.cellY = mHotseat.getCellYFromOrder(rank);
-                itemInfo.screenId = rank;
-                inflateAndAddPredictedIcon(itemInfo);
-            }
+        WorkspaceFetcher fetcher;
+        PreviewContext previewContext = null;
+        if (mMigrated) {
+            previewContext = new PreviewContext(mContext, mIdp);
+            LauncherAppState appForPreview = new LauncherAppState(
+                    previewContext, null /* iconCacheFileName */);
+            fetcher = new WorkspaceItemsInfoFromPreviewFetcher(appForPreview);
+            MODEL_EXECUTOR.execute(fetcher);
         } else {
-            // Add hotseat icons
-            for (int i = 0; i < mDp.numShownHotseatIcons; i++) {
-                WorkspaceItemInfo info = new WorkspaceItemInfo(mWorkspaceItemInfo);
-                info.container = Favorites.CONTAINER_HOTSEAT;
-                info.screenId = i;
-                inflateAndAddIcon(info);
+            fetcher = new WorkspaceItemsInfoFetcher();
+            LauncherAppState.getInstance(mContext).getModel().enqueueModelUpdateTask(
+                    (LauncherModel.ModelUpdateTask) fetcher);
+        }
+        WorkspaceResult workspaceResult = fetcher.get();
+        if (previewContext != null) {
+            previewContext.onDestroy();
+        }
+
+        if (workspaceResult == null) {
+            return;
+        }
+
+        // Separate the items that are on the current screen, and the other remaining items.
+        ArrayList<ItemInfo> currentWorkspaceItems = new ArrayList<>();
+        ArrayList<ItemInfo> otherWorkspaceItems = new ArrayList<>();
+        ArrayList<LauncherAppWidgetInfo> currentAppWidgets = new ArrayList<>();
+        ArrayList<LauncherAppWidgetInfo> otherAppWidgets = new ArrayList<>();
+        filterCurrentWorkspaceItems(0 /* currentScreenId */,
+                workspaceResult.mWorkspaceItems, currentWorkspaceItems,
+                otherWorkspaceItems);
+        filterCurrentWorkspaceItems(0 /* currentScreenId */, workspaceResult.mAppWidgets,
+                currentAppWidgets, otherAppWidgets);
+        sortWorkspaceItemsSpatially(mIdp, currentWorkspaceItems);
+        for (ItemInfo itemInfo : currentWorkspaceItems) {
+            switch (itemInfo.itemType) {
+                case Favorites.ITEM_TYPE_APPLICATION:
+                case Favorites.ITEM_TYPE_SHORTCUT:
+                case Favorites.ITEM_TYPE_DEEP_SHORTCUT:
+                    inflateAndAddIcon((WorkspaceItemInfo) itemInfo);
+                    break;
+                case Favorites.ITEM_TYPE_FOLDER:
+                    inflateAndAddFolder((FolderInfo) itemInfo);
+                    break;
+                default:
+                    break;
             }
-            // Add workspace icons
-            for (int i = 0; i < mIdp.numColumns; i++) {
-                WorkspaceItemInfo info = new WorkspaceItemInfo(mWorkspaceItemInfo);
-                info.container = Favorites.CONTAINER_DESKTOP;
-                info.screenId = 0;
-                info.cellX = i;
-                info.cellY = mIdp.numRows - 1;
-                inflateAndAddIcon(info);
+        }
+        for (ItemInfo itemInfo : currentAppWidgets) {
+            switch (itemInfo.itemType) {
+                case Favorites.ITEM_TYPE_APPWIDGET:
+                case Favorites.ITEM_TYPE_CUSTOM_APPWIDGET:
+                    if (mMigrated) {
+                        inflateAndAddWidgets((LauncherAppWidgetInfo) itemInfo,
+                                workspaceResult.mWidgetProvidersMap);
+                    } else {
+                        inflateAndAddWidgets((LauncherAppWidgetInfo) itemInfo,
+                                workspaceResult.mWidgetsModel);
+                    }
+                    break;
+                default:
+                    break;
             }
         }
+        IntArray ranks = getMissingHotseatRanks(currentWorkspaceItems,
+                mDp.numShownHotseatIcons);
+        List<ItemInfo> predictions = workspaceResult.mHotseatPredictions == null
+                ? Collections.emptyList() : workspaceResult.mHotseatPredictions.items;
+        int count = Math.min(ranks.size(), predictions.size());
+        for (int i = 0; i < count; i++) {
+            int rank = ranks.get(i);
+            WorkspaceItemInfo itemInfo =
+                    new WorkspaceItemInfo((WorkspaceItemInfo) predictions.get(i));
+            itemInfo.container = CONTAINER_HOTSEAT_PREDICTION;
+            itemInfo.rank = rank;
+            itemInfo.cellX = mHotseat.getCellXFromOrder(rank);
+            itemInfo.cellY = mHotseat.getCellYFromOrder(rank);
+            itemInfo.screenId = rank;
+            inflateAndAddPredictedIcon(itemInfo);
+        }
 
         // Add first page QSB
-        if (shouldShowQsb()) {
+        if (FeatureFlags.QSB_ON_FIRST_SCREEN) {
             View qsb = mHomeElementInflater.inflate(
                     R.layout.search_container_workspace, mWorkspace, false);
             CellLayout.LayoutParams lp =
diff --git a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
index fdc3a94..6193570 100644
--- a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
+++ b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
@@ -20,6 +20,7 @@
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 
+import android.app.WallpaperColors;
 import android.content.Context;
 import android.hardware.display.DisplayManager;
 import android.os.Bundle;
@@ -28,18 +29,23 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.Messenger;
+import android.view.ContextThemeWrapper;
 import android.view.Display;
 import android.view.SurfaceControlViewHost;
 import android.view.View;
 import android.view.animation.AccelerateDecelerateInterpolator;
 
 import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.R;
 import com.android.launcher3.model.GridSizeMigrationTask;
 import com.android.launcher3.model.GridSizeMigrationTaskV2;
+import com.android.launcher3.util.Themes;
+import com.android.launcher3.widget.LocalColorExtractor;
 
 import java.util.concurrent.TimeUnit;
 
 /** Render preview using surface view. */
+@SuppressWarnings("NewApi")
 public class PreviewSurfaceRenderer implements IBinder.DeathRecipient {
 
     private static final int FADE_IN_ANIMATION_DURATION = 200;
@@ -50,6 +56,7 @@
     private static final String KEY_DISPLAY_ID = "display_id";
     private static final String KEY_SURFACE_PACKAGE = "surface_package";
     private static final String KEY_CALLBACK = "callback";
+    private static final String KEY_COLORS = "wallpaper_colors";
 
     private final Context mContext;
     private final InvariantDeviceProfile mIdp;
@@ -57,6 +64,7 @@
     private final int mWidth;
     private final int mHeight;
     private final Display mDisplay;
+    private final WallpaperColors mWallpaperColors;
 
     private SurfaceControlViewHost mSurfaceControlViewHost;
 
@@ -68,6 +76,8 @@
         if (gridName == null) {
             gridName = InvariantDeviceProfile.getCurrentGridName(context);
         }
+        mWallpaperColors = bundle.getParcelable(KEY_COLORS);
+
         mIdp = new InvariantDeviceProfile(context, gridName);
 
         mHostToken = bundle.getBinder(KEY_HOST_TOKEN);
@@ -100,6 +110,19 @@
         MODEL_EXECUTOR.post(() -> {
             final boolean success = doGridMigrationIfNecessary();
 
+            final Context inflationContext;
+            if (mWallpaperColors != null) {
+                // Workaround to create a themed context
+                Context context = mContext.createDisplayContext(mDisplay);
+                LocalColorExtractor.newInstance(mContext)
+                        .applyColorsOverride(context, mWallpaperColors);
+
+                inflationContext = new ContextThemeWrapper(context,
+                        Themes.getActivityThemeRes(context, mWallpaperColors.getColorHints()));
+            } else {
+                inflationContext = new ContextThemeWrapper(mContext,  R.style.AppTheme);
+            }
+
             MAIN_EXECUTOR.post(() -> {
                 // If mSurfaceControlViewHost is null due to any reason (e.g. binder died,
                 // happening when user leaves the preview screen before preview rendering finishes),
@@ -109,7 +132,8 @@
                     return;
                 }
 
-                View view = new LauncherPreviewRenderer(mContext, mIdp, success).getRenderedView();
+                View view = new LauncherPreviewRenderer(inflationContext, mIdp, success)
+                        .getRenderedView();
                 // This aspect scales the view to fit in the surface and centers it
                 final float scale = Math.min(mWidth / (float) view.getMeasuredWidth(),
                         mHeight / (float) view.getMeasuredHeight());
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index a2c0f5c..297325a 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -52,7 +52,6 @@
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.pm.UserCache;
 import com.android.launcher3.shortcuts.ShortcutKey;
-import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.InstantAppResolver;
 import com.android.launcher3.util.PackageUserKey;
 import com.android.launcher3.util.Preconditions;
@@ -82,10 +81,11 @@
     private int mPendingIconRequestCount = 0;
 
     public IconCache(Context context, InvariantDeviceProfile idp) {
-        this(context, idp, LauncherFiles.APP_ICONS_DB);
+        this(context, idp, LauncherFiles.APP_ICONS_DB, new IconProvider(context));
     }
 
-    public IconCache(Context context, InvariantDeviceProfile idp, String dbFileName) {
+    public IconCache(Context context, InvariantDeviceProfile idp, String dbFileName,
+            IconProvider iconProvider) {
         super(context, dbFileName, MODEL_EXECUTOR.getLooper(),
                 idp.fillResIconDpi, idp.iconBitmapSize, true /* inMemoryCache */);
         mComponentWithLabelCachingLogic = new ComponentCachingLogic(context, false);
@@ -94,7 +94,7 @@
         mLauncherApps = mContext.getSystemService(LauncherApps.class);
         mUserManager = UserCache.INSTANCE.get(mContext);
         mInstantAppResolver = InstantAppResolver.newInstance(mContext);
-        mIconProvider = new IconProvider(context);
+        mIconProvider = iconProvider;
     }
 
     @Override
@@ -108,7 +108,7 @@
     }
 
     @Override
-    protected BaseIconFactory getIconFactory() {
+    public BaseIconFactory getIconFactory() {
         return LauncherIcons.obtain(mContext);
     }
 
@@ -333,15 +333,6 @@
                 + ",flags_asi:" + FeatureFlags.APP_SEARCH_IMPROVEMENTS.get();
     }
 
-    @Override
-    protected boolean getEntryFromDB(ComponentKey cacheKey, CacheEntry entry, boolean lowRes) {
-        if (mIconProvider.isClockIcon(cacheKey)) {
-            // For clock icon, we always load the dynamic icon
-            return false;
-        }
-        return super.getEntryFromDB(cacheKey, entry, lowRes);
-    }
-
     /**
      * Interface for receiving itemInfo with high-res icon.
      */
diff --git a/src/com/android/launcher3/icons/LauncherActivityCachingLogic.java b/src/com/android/launcher3/icons/LauncherActivityCachingLogic.java
index 93de35a..e820ac4 100644
--- a/src/com/android/launcher3/icons/LauncherActivityCachingLogic.java
+++ b/src/com/android/launcher3/icons/LauncherActivityCachingLogic.java
@@ -20,6 +20,7 @@
 import android.content.pm.LauncherActivityInfo;
 import android.os.UserHandle;
 
+import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
 import com.android.launcher3.icons.cache.CachingLogic;
 import com.android.launcher3.util.ResourceBasedOverride;
@@ -56,8 +57,8 @@
     @Override
     public BitmapInfo loadIcon(Context context, LauncherActivityInfo object) {
         try (LauncherIcons li = LauncherIcons.obtain(context)) {
-            return li.createBadgedIconBitmap(new IconProvider(context)
-                            .getIcon(object, li.mFillResIconDpi),
+            return li.createBadgedIconBitmap(LauncherAppState.getInstance(context)
+                            .getIconProvider().getIcon(object, li.mFillResIconDpi),
                     object.getUser(), object.getApplicationInfo().targetSdkVersion);
         }
     }
diff --git a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
index 76b2ab0..6813b97 100644
--- a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
+++ b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
@@ -223,7 +223,15 @@
      * Returns a FastBitmapDrawable with the icon.
      */
     public FastBitmapDrawable newIcon(Context context) {
-        FastBitmapDrawable drawable = bitmap.newIcon(context);
+        return newIcon(context, false);
+    }
+
+    /**
+     * Returns a FastBitmapDrawable with the icon and context theme applied
+     */
+    public FastBitmapDrawable newIcon(Context context, boolean applyTheme) {
+        FastBitmapDrawable drawable = applyTheme
+                ? bitmap.newThemedIcon(context) : bitmap.newIcon(context);
         drawable.setIsDisabled(isDisabled());
         return drawable;
     }
diff --git a/src/com/android/launcher3/model/data/SearchActionItemInfo.java b/src/com/android/launcher3/model/data/SearchActionItemInfo.java
index b3057d5..0eea92c 100644
--- a/src/com/android/launcher3/model/data/SearchActionItemInfo.java
+++ b/src/com/android/launcher3/model/data/SearchActionItemInfo.java
@@ -94,6 +94,11 @@
                     "SearchActionItemInfo can only have either an Intent or a PendingIntent");
         }
         mIntent = intent;
+        // bandage fix for just one week
+        if (intent != null && "com.android.server.telecom".equals(intent.getPackage())) {
+            intent.setAction(Intent.ACTION_DIAL);
+            intent.setPackage(null);
+        }
     }
 
     public PendingIntent getPendingIntent() {
diff --git a/src/com/android/launcher3/popup/ArrowPopup.java b/src/com/android/launcher3/popup/ArrowPopup.java
index 48c5d0a..c63d69d 100644
--- a/src/com/android/launcher3/popup/ArrowPopup.java
+++ b/src/com/android/launcher3/popup/ArrowPopup.java
@@ -18,7 +18,6 @@
 
 import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
 import static com.android.launcher3.popup.PopupPopulator.MAX_SHORTCUTS;
-import static com.android.launcher3.util.ColorExtractionUtils.getColorExtractionRect;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -703,6 +702,7 @@
         mViewForRect.clear();
         if (mColorExtractor != null) {
             mColorExtractor.removeLocations();
+            mColorExtractor.setListener(null);
         }
     }
 
@@ -747,7 +747,7 @@
                     View view = getChildAt(i);
                     if (view.getVisibility() == VISIBLE) {
                         RectF rf = new RectF();
-                        getColorExtractionRect(Launcher.getLauncher(getContext()),
+                        mColorExtractor.getExtractedRectForView(Launcher.getLauncher(getContext()),
                                 workspace.getCurrentPage(), view, rf);
                         if (rf.isEmpty()) {
                             numVisibleChild++;
diff --git a/src/com/android/launcher3/qsb/QsbContainerView.java b/src/com/android/launcher3/qsb/QsbContainerView.java
index a191df4..22c3f58 100644
--- a/src/com/android/launcher3/qsb/QsbContainerView.java
+++ b/src/com/android/launcher3/qsb/QsbContainerView.java
@@ -20,7 +20,7 @@
 import static android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID;
 import static android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_PROVIDER;
 
-import static com.android.launcher3.Utilities.ATLEAST_S;
+import static com.android.launcher3.AppWidgetResizeFrame.getWidgetSizeOptions;
 
 import android.app.Activity;
 import android.app.Fragment;
@@ -32,11 +32,9 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.graphics.Rect;
 import android.os.Bundle;
 import android.provider.Settings;
 import android.util.AttributeSet;
-import android.util.SizeF;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -45,7 +43,6 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import com.android.launcher3.AppWidgetResizeFrame;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
@@ -53,8 +50,6 @@
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.graphics.FragmentWithPreview;
 
-import java.util.ArrayList;
-
 /**
  * A frame layout which contains a QSB. This internally uses fragment to bind the view, which
  * allows it to contain the logic for {@link Fragment#startActivityForResult(Intent, int)}.
@@ -163,7 +158,7 @@
 
         protected String mKeyWidgetId = "qsb_widget_id";
         private QsbWidgetHost mQsbWidgetHost;
-        private AppWidgetProviderInfo mWidgetInfo;
+        protected AppWidgetProviderInfo mWidgetInfo;
         private QsbWidgetHostView mQsb;
 
         // We need to store the orientation here, due to a bug (b/64916689) that results in widgets
@@ -297,19 +292,7 @@
 
         protected Bundle createBindOptions() {
             InvariantDeviceProfile idp = LauncherAppState.getIDP(getContext());
-
-            Bundle opts = new Bundle();
-            ArrayList<SizeF> sizes = AppWidgetResizeFrame
-                    .getWidgetSizes(getContext(), idp.numColumns, 1);
-            Rect size = AppWidgetResizeFrame.getMinMaxSizes(sizes, null /* outRect */);
-            opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, size.left);
-            opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, size.top);
-            opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, size.right);
-            opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, size.bottom);
-            if (ATLEAST_S) {
-                opts.putParcelableArrayList(AppWidgetManager.OPTION_APPWIDGET_SIZES, sizes);
-            }
-            return opts;
+            return getWidgetSizeOptions(getContext(), mWidgetInfo.provider, idp.numColumns, 1);
         }
 
         protected View getDefaultView(ViewGroup container, boolean showSetupIcon) {
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
index f5e74b7..5999091 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
@@ -87,8 +87,8 @@
         if (mDragLayer != null) {
             return;
         }
-        InvariantDeviceProfile currentDisplayIdp =
-                new InvariantDeviceProfile(this, getWindow().getDecorView().getDisplay());
+        InvariantDeviceProfile currentDisplayIdp = new InvariantDeviceProfile(
+                this, getWindow().getDecorView().getDisplay());
 
         // Disable transpose layout and use multi-window mode so that the icons are scaled properly
         mDeviceProfile = currentDisplayIdp.getDeviceProfile(this)
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
index 40630d3..f78f6dd 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDragLayer.java
@@ -29,7 +29,6 @@
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.R;
 import com.android.launcher3.allapps.AllAppsContainerView;
 import com.android.launcher3.model.data.ItemInfo;
@@ -109,8 +108,6 @@
         setMeasuredDimension(width, height);
 
         DeviceProfile grid = mActivity.getDeviceProfile();
-        InvariantDeviceProfile idp = grid.inv;
-
         int count = getChildCount();
         for (int i = 0; i < count; i++) {
             final View child = getChildAt(i);
@@ -118,10 +115,10 @@
                 int padding = 2 * (grid.desiredWorkspaceLeftRightMarginPx
                         + grid.cellLayoutPaddingLeftRightPx);
 
-                int maxWidth = grid.allAppsCellWidthPx * idp.numAllAppsColumns + padding;
+                int maxWidth = grid.allAppsCellWidthPx * grid.numShownAllAppsColumns + padding;
                 int appsWidth = Math.min(width, maxWidth);
 
-                int maxHeight = grid.allAppsCellHeightPx * idp.numAllAppsColumns + padding;
+                int maxHeight = grid.allAppsCellHeightPx * grid.numShownAllAppsColumns + padding;
                 int appsHeight = Math.min(height, maxHeight);
 
                 mAppsView.measure(
diff --git a/src/com/android/launcher3/states/StateAnimationConfig.java b/src/com/android/launcher3/states/StateAnimationConfig.java
index 2f26b4f..bd6f7d3 100644
--- a/src/com/android/launcher3/states/StateAnimationConfig.java
+++ b/src/com/android/launcher3/states/StateAnimationConfig.java
@@ -58,8 +58,7 @@
             ANIM_OVERVIEW_TRANSLATE_Y,
             ANIM_OVERVIEW_FADE,
             ANIM_ALL_APPS_FADE,
-            ANIM_WORKSPACE_SCRIM_FADE,
-            ANIM_ALL_APPS_HEADER_FADE,
+            ANIM_SCRIM_FADE,
             ANIM_OVERVIEW_MODAL,
             ANIM_DEPTH,
             ANIM_OVERVIEW_ACTIONS_FADE,
@@ -77,13 +76,12 @@
     public static final int ANIM_OVERVIEW_TRANSLATE_Y = 8;
     public static final int ANIM_OVERVIEW_FADE = 9;
     public static final int ANIM_ALL_APPS_FADE = 10;
-    public static final int ANIM_WORKSPACE_SCRIM_FADE = 11;
-    public static final int ANIM_ALL_APPS_HEADER_FADE = 12; // e.g. predictions
-    public static final int ANIM_OVERVIEW_MODAL = 13;
-    public static final int ANIM_DEPTH = 14;
-    public static final int ANIM_OVERVIEW_ACTIONS_FADE = 15;
+    public static final int ANIM_SCRIM_FADE = 11;
+    public static final int ANIM_OVERVIEW_MODAL = 12;
+    public static final int ANIM_DEPTH = 13;
+    public static final int ANIM_OVERVIEW_ACTIONS_FADE = 14;
 
-    private static final int ANIM_TYPES_COUNT = 16;
+    private static final int ANIM_TYPES_COUNT = 15;
 
     protected final Interpolator[] mInterpolators = new Interpolator[ANIM_TYPES_COUNT];
 
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index 2fd5efc..44e55e1 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -160,6 +160,19 @@
     }
 
     @Override
+    public int getSplitTaskViewDismissDirection(SplitPositionOption splitPosition,
+            DeviceProfile dp) {
+        // Don't use device profile here because we know we're in fake landscape, only split option
+        // available is top/left
+        if (splitPosition.mStagePosition == STAGE_POSITION_TOP_OR_LEFT) {
+            // Top (visually left) side
+            return SPLIT_TRANSLATE_PRIMARY_NEGATIVE;
+        }
+        throw new IllegalStateException("Invalid split stage position: " +
+                splitPosition.mStagePosition);
+    }
+
+    @Override
     public int getPrimaryScroll(View view) {
         return view.getScrollY();
     }
diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java
index 6c2f17e..d8e5a48 100644
--- a/src/com/android/launcher3/touch/PagedOrientationHandler.java
+++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java
@@ -42,6 +42,10 @@
  */
 public interface PagedOrientationHandler {
 
+    int SPLIT_TRANSLATE_PRIMARY_POSITIVE = 0;
+    int SPLIT_TRANSLATE_PRIMARY_NEGATIVE = 1;
+    int SPLIT_TRANSLATE_SECONDARY_NEGATIVE = 2;
+
     PagedOrientationHandler PORTRAIT = new PortraitPagedViewHandler();
     PagedOrientationHandler LANDSCAPE = new LandscapePagedViewHandler();
     PagedOrientationHandler SEASCAPE = new SeascapePagedViewHandler();
@@ -71,6 +75,13 @@
     int getSecondaryDimension(View view);
     FloatProperty<View> getPrimaryViewTranslate();
     FloatProperty<View> getSecondaryViewTranslate();
+
+    /**
+     * @param splitPosition The position where the view to be split will go
+     * @return {@link #SPLIT_TRANSLATE_*} constants to indicate which direction the
+     * dismissal should happen
+     */
+    int getSplitTaskViewDismissDirection(SplitPositionOption splitPosition, DeviceProfile dp);
     int getPrimaryScroll(View view);
     float getPrimaryScale(View view);
     int getChildStart(View view);
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index f6a6448..d3d77fd 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -156,6 +156,25 @@
     }
 
     @Override
+    public int getSplitTaskViewDismissDirection(SplitPositionOption splitPosition,
+            DeviceProfile dp) {
+        if (splitPosition.mStagePosition == STAGE_POSITION_TOP_OR_LEFT) {
+            if (dp.isLandscape) {
+                // Left side
+                return SPLIT_TRANSLATE_PRIMARY_NEGATIVE;
+            } else {
+                // Top side
+                return SPLIT_TRANSLATE_SECONDARY_NEGATIVE;
+            }
+        } else if (splitPosition.mStagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) {
+            // We don't have a bottom option, so should be right
+            return SPLIT_TRANSLATE_PRIMARY_POSITIVE;
+        }
+        throw new IllegalStateException("Invalid split stage position: " +
+                splitPosition.mStagePosition);
+    }
+
+    @Override
     public int getPrimaryScroll(View view) {
         return view.getScrollX();
     }
@@ -277,7 +296,7 @@
         } else {
             // Phone Portrait, LargeScreen Landscape/Portrait
             viewGroup.setOrientation(LinearLayout.HORIZONTAL);
-            lp.width = LinearLayout.LayoutParams.WRAP_CONTENT;
+            lp.width = LinearLayout.LayoutParams.MATCH_PARENT;
         }
 
         lp.height = LinearLayout.LayoutParams.WRAP_CONTENT;
diff --git a/src/com/android/launcher3/util/ColorExtractionUtils.java b/src/com/android/launcher3/util/ColorExtractionUtils.java
deleted file mode 100644
index b377ded..0000000
--- a/src/com/android/launcher3/util/ColorExtractionUtils.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.util;
-
-import android.content.res.Resources;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.view.View;
-
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.Utilities;
-
-/**
- * Utility class used to map launcher views to wallpaper rect.
- */
-public class ColorExtractionUtils {
-
-    public static final String TAG = "ColorExtractionUtils";
-
-    private static final Rect sTempRect = new Rect();
-    private static final RectF sTempRectF = new RectF();
-
-    /**
-     * Takes a view and returns its rect that can be used by the wallpaper local color extractor.
-     *
-     * @param launcher Launcher class class.
-     * @param pageId The page the workspace item is on.
-     * @param v The view.
-     * @param colorExtractionRectOut The location rect, but converted to a format expected by the
-     *                               wallpaper local color extractor.
-     */
-    public static void getColorExtractionRect(Launcher launcher, int pageId, View v,
-            RectF colorExtractionRectOut) {
-        Rect viewRect = sTempRect;
-        viewRect.set(0, 0, v.getWidth(), v.getHeight());
-        Utilities.getBoundsForViewInDragLayer(launcher.getDragLayer(), v, viewRect, false,
-                sTempRectF);
-        Utilities.setRect(sTempRectF, viewRect);
-        getColorExtractionRect(launcher, pageId, viewRect, colorExtractionRectOut);
-    }
-
-    /**
-     * Takes a rect in drag layer coordinates and returns the rect that can be used by the wallpaper
-     * local color extractor.
-     *
-     * @param launcher Launcher class.
-     * @param pageId The page the workspace item is on.
-     * @param rectInDragLayer The relevant bounds of the view in drag layer coordinates.
-     * @param colorExtractionRectOut The location rect, but converted to a format expected by the
-     *                               wallpaper local color extractor.
-     */
-    public static void getColorExtractionRect(Launcher launcher, int pageId, Rect rectInDragLayer,
-            RectF colorExtractionRectOut) {
-        // If the view hasn't been measured and laid out, we cannot do this.
-        if (rectInDragLayer.isEmpty()) {
-            colorExtractionRectOut.setEmpty();
-            return;
-        }
-
-        Resources res = launcher.getResources();
-        DeviceProfile dp = launcher.getDeviceProfile().inv.getDeviceProfile(launcher);
-        float screenWidth = dp.widthPx;
-        float screenHeight = dp.heightPx;
-        int numScreens = launcher.getWorkspace().getNumPagesForWallpaperParallax();
-        pageId = Utilities.isRtl(res) ? numScreens - pageId - 1 : pageId;
-        float relativeScreenWidth = 1f / numScreens;
-
-        int[] dragLayerBounds = new int[2];
-        launcher.getDragLayer().getLocationOnScreen(dragLayerBounds);
-        // Translate from drag layer coordinates to screen coordinates.
-        int screenLeft = rectInDragLayer.left + dragLayerBounds[0];
-        int screenTop = rectInDragLayer.top + dragLayerBounds[1];
-        int screenRight = rectInDragLayer.right + dragLayerBounds[0];
-        int screenBottom = rectInDragLayer.bottom + dragLayerBounds[1];
-
-        // This is the position of the view relative to the wallpaper, as expected by the
-        // local color extraction of the WallpaperManager.
-        // The coordinate system is such that, on the horizontal axis, each screen has a
-        // distinct range on the [0,1] segment. So if there are 3 screens, they will have the
-        // ranges [0, 1/3], [1/3, 2/3] and [2/3, 1]. The position on the subrange should be
-        // the position of the view relative to the screen. For the vertical axis, this is
-        // simply the location of the view relative to the screen.
-        // Translate from drag layer coordinates to screen coordinates
-        colorExtractionRectOut.left = (screenLeft / screenWidth + pageId) * relativeScreenWidth;
-        colorExtractionRectOut.right = (screenRight / screenWidth + pageId) * relativeScreenWidth;
-        colorExtractionRectOut.top = screenTop / screenHeight;
-        colorExtractionRectOut.bottom = screenBottom / screenHeight;
-
-        if (colorExtractionRectOut.left < 0
-                || colorExtractionRectOut.right > 1
-                || colorExtractionRectOut.top < 0
-                || colorExtractionRectOut.bottom > 1) {
-            colorExtractionRectOut.setEmpty();
-        }
-    }
-}
diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java
index 75c089e..b751207 100644
--- a/src/com/android/launcher3/util/DisplayController.java
+++ b/src/com/android/launcher3/util/DisplayController.java
@@ -18,8 +18,10 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 
+import static com.android.launcher3.Utilities.dpiFromPx;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import static com.android.launcher3.util.WindowManagerCompat.MIN_TABLET_WIDTH;
 
 import android.annotation.SuppressLint;
 import android.annotation.TargetApi;
@@ -32,16 +34,22 @@
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManager.DisplayListener;
 import android.os.Build;
+import android.util.ArraySet;
 import android.util.Log;
 import android.view.Display;
+import android.view.WindowMetrics;
 
 import androidx.annotation.AnyThread;
 import androidx.annotation.UiThread;
 import androidx.annotation.WorkerThread;
 
 import com.android.launcher3.Utilities;
+import com.android.launcher3.uioverrides.ApiWrapper;
 
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Objects;
+import java.util.Set;
 
 /**
  * Utility class to cache properties of default display to avoid a system RPC on every call.
@@ -54,13 +62,14 @@
     public static final MainThreadInitializedObject<DisplayController> INSTANCE =
             new MainThreadInitializedObject<>(DisplayController::new);
 
-    public static final int CHANGE_SIZE = 1 << 0;
+    public static final int CHANGE_ACTIVE_SCREEN = 1 << 0;
     public static final int CHANGE_ROTATION = 1 << 1;
     public static final int CHANGE_FRAME_DELAY = 1 << 2;
     public static final int CHANGE_DENSITY = 1 << 3;
+    public static final int CHANGE_SUPPORTED_BOUNDS = 1 << 4;
 
-    public static final int CHANGE_ALL = CHANGE_SIZE | CHANGE_ROTATION
-            | CHANGE_FRAME_DELAY | CHANGE_DENSITY;
+    public static final int CHANGE_ALL = CHANGE_ACTIVE_SCREEN | CHANGE_ROTATION
+            | CHANGE_FRAME_DELAY | CHANGE_DENSITY | CHANGE_SUPPORTED_BOUNDS;
 
     private final Context mContext;
     private final DisplayManager mDM;
@@ -87,7 +96,22 @@
                     new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED));
         }
 
-        mInfo = new Info(getContext(display), display);
+        // Create a single holder for all internal displays. External display holders created
+        // lazily.
+        Set<PortraitSize> extraInternalDisplays = new ArraySet<>();
+        for (Display d : mDM.getDisplays()) {
+            if (ApiWrapper.isInternalDisplay(display) && d.getDisplayId() != DEFAULT_DISPLAY) {
+                Point size = new Point();
+                d.getRealSize(size);
+                extraInternalDisplays.add(new PortraitSize(size.x, size.y));
+            }
+        }
+
+        if (extraInternalDisplays.isEmpty() || !Utilities.ATLEAST_S) {
+            mInfo = new Info(createDisplayInfoContext(display), display, Collections.emptySet());
+        } else {
+            mInfo = new Info(mWindowContext, display, extraInternalDisplays);
+        }
         mDM.registerDisplayListener(this, UI_HELPER_EXECUTOR.getHandler());
     }
 
@@ -139,7 +163,7 @@
      */
     private void onConfigChanged(Intent intent) {
         Configuration config = mContext.getResources().getConfiguration();
-        if (config.fontScale != config.fontScale || mInfo.densityDpi != config.densityDpi) {
+        if (mInfo.fontScale != config.fontScale || mInfo.densityDpi != config.densityDpi) {
             Log.d(TAG, "Configuration changed, notifying listeners");
             Display display = mDM.getDisplay(DEFAULT_DISPLAY);
             if (display != null) {
@@ -157,8 +181,7 @@
                 || config.fontScale != mInfo.fontScale
                 || display.getRotation() != mInfo.rotation
                 || !mInfo.mScreenSizeDp.equals(
-                        Math.min(config.screenHeightDp, config.screenWidthDp),
-                        Math.max(config.screenHeightDp, config.screenWidthDp))) {
+                        new PortraitSize(config.screenHeightDp, config.screenWidthDp))) {
             handleInfoChange(display);
         }
     }
@@ -178,18 +201,23 @@
         return mInfo;
     }
 
-    private Context getContext(Display display) {
-        return Utilities.ATLEAST_S ? mWindowContext : mContext.createDisplayContext(display);
+    private Context createDisplayInfoContext(Display display) {
+        return Utilities.ATLEAST_S
+                ? mContext.createWindowContext(display, TYPE_APPLICATION, null)
+                : mContext.createDisplayContext(display);
     }
 
     @AnyThread
     private void handleInfoChange(Display display) {
         Info oldInfo = mInfo;
-        Context context = getContext(display);
-        Info newInfo = new Info(context, display);
+        Set<PortraitSize> extraDisplaysSizes = oldInfo.mAllSizes.size() > 1
+                ? oldInfo.mAllSizes : Collections.emptySet();
+
+        Context displayContext = createDisplayInfoContext(display);
+        Info newInfo = new Info(displayContext, display, extraDisplaysSizes);
         int change = 0;
-        if (newInfo.hasDifferentSize(oldInfo)) {
-            change |= CHANGE_SIZE;
+        if (!newInfo.mScreenSizeDp.equals(oldInfo.mScreenSizeDp)) {
+            change |= CHANGE_ACTIVE_SCREEN;
         }
         if (newInfo.rotation != oldInfo.rotation) {
             change |= CHANGE_ROTATION;
@@ -200,11 +228,14 @@
         if (newInfo.densityDpi != oldInfo.densityDpi || newInfo.fontScale != oldInfo.fontScale) {
             change |= CHANGE_DENSITY;
         }
+        if (!newInfo.supportedBounds.equals(oldInfo.supportedBounds)) {
+            change |= CHANGE_SUPPORTED_BOUNDS;
+        }
 
         if (change != 0) {
             mInfo = newInfo;
             final int flags = change;
-            MAIN_EXECUTOR.execute(() -> notifyChange(context, flags));
+            MAIN_EXECUTOR.execute(() -> notifyChange(displayContext, flags));
         }
     }
 
@@ -224,13 +255,18 @@
         public final float fontScale;
         public final int densityDpi;
 
-        private final Point mScreenSizeDp;
+        private final PortraitSize mScreenSizeDp;
+        private final Set<PortraitSize> mAllSizes;
 
-        public final Point realSize;
-        public final Point smallestSize;
-        public final Point largestSize;
+        public final Point currentSize;
+
+        public final Set<WindowBounds> supportedBounds = new ArraySet<>();
 
         public Info(Context context, Display display) {
+            this(context, display, Collections.emptySet());
+        }
+
+        private Info(Context context, Display display, Set<PortraitSize> extraDisplaysSizes) {
             id = display.getDisplayId();
 
             rotation = display.getRotation();
@@ -238,35 +274,67 @@
             Configuration config = context.getResources().getConfiguration();
             fontScale = config.fontScale;
             densityDpi = config.densityDpi;
-            mScreenSizeDp = new Point(
-                    Math.min(config.screenHeightDp, config.screenWidthDp),
-                    Math.max(config.screenHeightDp, config.screenWidthDp));
+            mScreenSizeDp = new PortraitSize(config.screenHeightDp, config.screenWidthDp);
 
             singleFrameMs = getSingleFrameMs(display);
+            currentSize = new Point();
 
-            realSize = new Point();
-            smallestSize = new Point();
-            largestSize = new Point();
+            display.getRealSize(currentSize);
 
-            display.getRealSize(realSize);
-            display.getCurrentSizeRange(smallestSize, largestSize);
+            if (extraDisplaysSizes.isEmpty() || !Utilities.ATLEAST_S) {
+                Point smallestSize = new Point();
+                Point largestSize = new Point();
+                display.getCurrentSizeRange(smallestSize, largestSize);
+
+                int portraitWidth = Math.min(currentSize.x, currentSize.y);
+                int portraitHeight = Math.max(currentSize.x, currentSize.y);
+
+                supportedBounds.add(new WindowBounds(portraitWidth, portraitHeight,
+                        smallestSize.x, largestSize.y));
+                supportedBounds.add(new WindowBounds(portraitHeight, portraitWidth,
+                        largestSize.x, smallestSize.y));
+                mAllSizes = Collections.singleton(new PortraitSize(currentSize.x, currentSize.y));
+            } else {
+                mAllSizes = new ArraySet<>(extraDisplaysSizes);
+                mAllSizes.add(new PortraitSize(currentSize.x, currentSize.y));
+                Set<WindowMetrics> metrics = WindowManagerCompat.getDisplayProfiles(
+                        context, mAllSizes, densityDpi,
+                        ApiWrapper.TASKBAR_DRAWN_IN_PROCESS);
+                metrics.forEach(wm -> supportedBounds.add(WindowBounds.fromWindowMetrics(wm)));
+            }
         }
 
-        private boolean hasDifferentSize(Info info) {
-            if (!realSize.equals(info.realSize)
-                    && !realSize.equals(info.realSize.y, info.realSize.x)) {
-                Log.d(TAG, String.format("Display size changed from %s to %s",
-                        info.realSize, realSize));
-                return true;
-            }
+        /**
+         * Returns true if the bounds represent a tablet
+         */
+        public boolean isTablet(WindowBounds bounds) {
+            return dpiFromPx(Math.min(bounds.bounds.width(), bounds.bounds.height()),
+                    densityDpi) >= MIN_TABLET_WIDTH;
+        }
+    }
 
-            if (!smallestSize.equals(info.smallestSize) || !largestSize.equals(info.largestSize)) {
-                Log.d(TAG, String.format("Available size changed from [%s, %s] to [%s, %s]",
-                        smallestSize, largestSize, info.smallestSize, info.largestSize));
-                return true;
-            }
+    /**
+     * Utility class to hold a size information in an orientation independent way
+     */
+    public static class PortraitSize {
+        public final int width, height;
 
-            return false;
+        public PortraitSize(int w, int h) {
+            width = Math.min(w, h);
+            height = Math.max(w, h);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            PortraitSize that = (PortraitSize) o;
+            return width == that.width && height == that.height;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(width, height);
         }
     }
 
diff --git a/src/com/android/launcher3/util/Themes.java b/src/com/android/launcher3/util/Themes.java
index 898ac25..06cac08 100644
--- a/src/com/android/launcher3/util/Themes.java
+++ b/src/com/android/launcher3/util/Themes.java
@@ -19,6 +19,7 @@
 import static android.app.WallpaperColors.HINT_SUPPORTS_DARK_TEXT;
 import static android.app.WallpaperColors.HINT_SUPPORTS_DARK_THEME;
 
+import android.app.WallpaperColors;
 import android.app.WallpaperManager;
 import android.content.Context;
 import android.content.res.TypedArray;
@@ -40,9 +41,14 @@
 public class Themes {
 
     public static int getActivityThemeRes(Context context) {
-        int colorHints = Utilities.ATLEAST_P ? context.getSystemService(WallpaperManager.class)
-                .getWallpaperColors(WallpaperManager.FLAG_SYSTEM).getColorHints()
-                : 0;
+        final int colorHints;
+        if (Utilities.ATLEAST_P) {
+            WallpaperColors colors = context.getSystemService(WallpaperManager.class)
+                    .getWallpaperColors(WallpaperManager.FLAG_SYSTEM);
+            colorHints = colors == null ? 0 : colors.getColorHints();
+        } else {
+            colorHints = 0;
+        }
         return getActivityThemeRes(context, colorHints);
     }
 
diff --git a/src/com/android/launcher3/util/UiThreadHelper.java b/src/com/android/launcher3/util/UiThreadHelper.java
index be14e01..f5e1234 100644
--- a/src/com/android/launcher3/util/UiThreadHelper.java
+++ b/src/com/android/launcher3/util/UiThreadHelper.java
@@ -48,7 +48,13 @@
             WindowInsets rootInsets = launcher.getRootView().getRootWindowInsets();
             boolean isImeShown = rootInsets != null && rootInsets.isVisible(
                     WindowInsets.Type.ime());
-            if (!isImeShown) return;
+            if (isImeShown) {
+                // this call is already asynchronous
+                launcher.getAppsView().getWindowInsetsController().hide(
+                        WindowInsets.Type.ime()
+                );
+            }
+            return;
         }
 
         Message.obtain(HANDLER.get(launcher), MSG_HIDE_KEYBOARD, token).sendToTarget();
diff --git a/src/com/android/launcher3/util/WindowBounds.java b/src/com/android/launcher3/util/WindowBounds.java
index 3c2fb62..c92770e 100644
--- a/src/com/android/launcher3/util/WindowBounds.java
+++ b/src/com/android/launcher3/util/WindowBounds.java
@@ -15,11 +15,16 @@
  */
 package com.android.launcher3.util;
 
+import android.graphics.Insets;
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.view.WindowInsets.Type;
+import android.view.WindowMetrics;
 
 import androidx.annotation.Nullable;
 
+import java.util.Objects;
+
 /**
  * Utility class to hold information about window position and layout
  */
@@ -36,6 +41,18 @@
                 bounds.height() - insets.top - insets.bottom);
     }
 
+    public WindowBounds(int width, int height, int availableWidth, int availableHeight) {
+        this.bounds = new Rect(0, 0, width, height);
+        this.availableSize = new Point(availableWidth, availableHeight);
+        // We don't care about insets in this case
+        this.insets = new Rect(0, 0, width - availableWidth, height - availableHeight);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(bounds, insets);
+    }
+
     @Override
     public boolean equals(@Nullable Object obj) {
         if (!(obj instanceof WindowBounds)) {
@@ -44,4 +61,30 @@
         WindowBounds other = (WindowBounds) obj;
         return other.bounds.equals(bounds) && other.insets.equals(insets);
     }
+
+    @Override
+    public String toString() {
+        return "WindowBounds{"
+                + "bounds=" + bounds
+                + ", insets=" + insets
+                + ", availableSize=" + availableSize
+                + '}';
+    }
+
+    /**
+     * Returns true if the device is in landscape orientation
+     */
+    public final boolean isLandscape() {
+        return availableSize.x > availableSize.y;
+    }
+
+    /**
+     * Returns the bounds corresponding to the provided WindowMetrics
+     */
+    @SuppressWarnings("NewApi")
+    public static WindowBounds fromWindowMetrics(WindowMetrics wm) {
+        Insets insets = wm.getWindowInsets().getInsets(Type.systemBars());
+        return new WindowBounds(wm.getBounds(),
+                new Rect(insets.left, insets.top, insets.right, insets.bottom));
+    }
 }
diff --git a/src/com/android/launcher3/util/WindowManagerCompat.java b/src/com/android/launcher3/util/WindowManagerCompat.java
new file mode 100644
index 0000000..38a63de
--- /dev/null
+++ b/src/com/android/launcher3/util/WindowManagerCompat.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.util;
+
+import static com.android.launcher3.ResourceUtils.INVALID_RESOURCE_HANDLE;
+import static com.android.launcher3.Utilities.dpiFromPx;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.os.Build;
+import android.view.WindowInsets;
+import android.view.WindowInsets.Type;
+import android.view.WindowManager;
+import android.view.WindowMetrics;
+
+import com.android.launcher3.R;
+import com.android.launcher3.ResourceUtils;
+import com.android.launcher3.util.DisplayController.PortraitSize;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Utility class to simulate window manager APIs until proper APIs are available
+ */
+@TargetApi(Build.VERSION_CODES.S)
+public class WindowManagerCompat {
+
+    public static final int MIN_TABLET_WIDTH = 600;
+
+    /**
+     * Returns a set of supported render sizes for a set of internal displays.
+     * This is a temporary workaround which assumes only nav-bar insets change across displays
+     * @param consumeTaskBar if true, it assumes that task bar is part of the app window
+     *                       and ignores any insets because of task bar.
+     */
+    public static Set<WindowMetrics> getDisplayProfiles(
+            Context windowContext, Collection<PortraitSize> allDisplaySizes,
+            int densityDpi, boolean consumeTaskBar) {
+        WindowInsets metrics = windowContext.getSystemService(WindowManager.class)
+                .getMaximumWindowMetrics().getWindowInsets();
+        boolean hasNavbar = ResourceUtils.getIntegerByName(
+                "config_navBarInteractionMode",
+                windowContext.getResources(),
+                INVALID_RESOURCE_HANDLE) != 0;
+
+        WindowInsets.Builder insetsBuilder = new WindowInsets.Builder(metrics);
+
+        Set<WindowMetrics> result = new HashSet<>();
+        for (PortraitSize size : allDisplaySizes) {
+            int swDP = (int) dpiFromPx(size.width, densityDpi);
+            boolean isTablet = swDP >= MIN_TABLET_WIDTH;
+
+            final Insets portraitNav, landscapeNav;
+            if (isTablet && !consumeTaskBar) {
+                portraitNav = landscapeNav = Insets.of(0, 0, 0, windowContext.getResources()
+                        .getDimensionPixelSize(R.dimen.taskbar_size));
+            } else if (hasNavbar) {
+                portraitNav = Insets.of(0, 0, 0,
+                        getSystemResource(windowContext, "navigation_bar_height", swDP));
+                landscapeNav = isTablet
+                        ? Insets.of(0, 0, 0, getSystemResource(windowContext,
+                                "navigation_bar_height_landscape", swDP))
+                        : Insets.of(0, 0, getSystemResource(windowContext,
+                                "navigation_bar_width", swDP), 0);
+            } else {
+                portraitNav = landscapeNav = Insets.of(0, 0, 0, 0);
+            }
+
+            result.add(new WindowMetrics(
+                    new Rect(0, 0, size.width, size.height),
+                    insetsBuilder.setInsets(Type.navigationBars(), portraitNav).build()));
+            result.add(new WindowMetrics(
+                    new Rect(0, 0, size.height, size.width),
+                    insetsBuilder.setInsets(Type.navigationBars(), landscapeNav).build()));
+        }
+        return result;
+    }
+
+    private static int getSystemResource(Context context, String key, int swDp) {
+        int resourceId = context.getResources().getIdentifier(key, "dimen", "android");
+        if (resourceId > 0) {
+            Configuration conf = new Configuration();
+            conf.smallestScreenWidthDp = swDp;
+            return context.createConfigurationContext(conf)
+                    .getResources().getDimensionPixelSize(resourceId);
+        }
+        return 0;
+    }
+}
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index 1ddd1ba..81581fa 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -234,7 +234,7 @@
         }
 
         Utilities.getBoundsForViewInDragLayer(launcher.getDragLayer(), v, outViewBounds,
-                ignoreTransform, outRect);
+                ignoreTransform, null /** recycle */, outRect);
     }
 
     /**
diff --git a/src/com/android/launcher3/widget/DeferredAppWidgetHostView.java b/src/com/android/launcher3/widget/DeferredAppWidgetHostView.java
index 3a24c3d..149ac57 100644
--- a/src/com/android/launcher3/widget/DeferredAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/DeferredAppWidgetHostView.java
@@ -44,7 +44,7 @@
         mPaint = new TextPaint();
         mPaint.setColor(Color.WHITE);
         mPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX,
-                mLauncher.getDeviceProfile().getFullScreenProfile().iconTextSizePx,
+                mLauncher.getDeviceProfile().iconTextSizePx,
                 getResources().getDisplayMetrics()));
         setBackgroundResource(R.drawable.bg_deferred_app_widget);
     }
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
index 697c453..5deecd4 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
@@ -18,7 +18,6 @@
 
 import static com.android.launcher3.Utilities.getBoundsForViewInDragLayer;
 import static com.android.launcher3.Utilities.setRect;
-import static com.android.launcher3.util.ColorExtractionUtils.getColorExtractionRect;
 
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.Context;
@@ -102,6 +101,7 @@
     private final Rect mCurrentWidgetSize = new Rect();
     private final Rect mWidgetSizeAtDrag = new Rect();
 
+    private final float[] mTmpFloatArray = new float[4];
     private final RectF mTempRectF = new RectF();
     private final Rect mEnforcedRectangle = new Rect();
     private final float mEnforcedCornerRadius;
@@ -323,7 +323,7 @@
         if (!mIsInDragMode && getTag() instanceof LauncherAppWidgetInfo) {
             LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) getTag();
             getBoundsForViewInDragLayer(mLauncher.getDragLayer(), this, mCurrentWidgetSize, true,
-                    mTempRectF);
+                    mTmpFloatArray, mTempRectF);
             setRect(mTempRectF, mCurrentWidgetSize);
             updateColorExtraction(mCurrentWidgetSize,
                     mWorkspace.getPageIndexForScreenId(info.screenId));
@@ -357,7 +357,7 @@
      * @param pageId The workspace page the widget is on.
      */
     private void updateColorExtraction(Rect rectInDragLayer, int pageId) {
-        getColorExtractionRect(mLauncher, pageId, rectInDragLayer, mTempRectF);
+        mColorExtractor.getExtractedRectForViewRect(mLauncher, pageId, rectInDragLayer, mTempRectF);
 
         if (mTempRectF.isEmpty()) {
             return;
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java
index ad61495..de511cd 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java
@@ -13,6 +13,7 @@
 import android.os.Parcel;
 import android.os.UserHandle;
 
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.Utilities;
@@ -66,20 +67,25 @@
     }
 
     public void initSpans(Context context, InvariantDeviceProfile idp) {
-        Point landCellSize = idp.landscapeProfile.getCellSize();
-        Point portCellSize = idp.portraitProfile.getCellSize();
-
         // Always assume we're working with the smallest span to make sure we
         // reserve enough space in both orientations.
-        float smallestCellWidth = Math.min(landCellSize.x, portCellSize.x);
-        float smallestCellHeight = Math.min(landCellSize.y, portCellSize.y);
+        float smallestCellWidth = Float.MAX_VALUE;
+        float smallestCellHeight = Float.MAX_VALUE;
+
+        Point cellSize = new Point();
+        boolean isWidgetPadded = false;
+        for (DeviceProfile dp : idp.supportedProfiles) {
+            dp.getCellSize(cellSize);
+            smallestCellWidth = Math.min(smallestCellWidth, cellSize.x);
+            smallestCellHeight = Math.min(smallestCellHeight, cellSize.y);
+            isWidgetPadded = isWidgetPadded || !dp.shouldInsetWidgets();
+        }
 
         // We want to account for the extra amount of padding that we are adding to the widget
         // to ensure that it gets the full amount of space that it has requested.
         // If grids supports insetting widgets, we do not account for widget padding.
         Rect widgetPadding = new Rect();
-        if (!idp.landscapeProfile.shouldInsetWidgets()
-                || !idp.portraitProfile.shouldInsetWidgets()) {
+        if (isWidgetPadded) {
             AppWidgetHostView.getDefaultPaddingForWidget(context, provider, widgetPadding);
         }
 
diff --git a/src/com/android/launcher3/widget/LocalColorExtractor.java b/src/com/android/launcher3/widget/LocalColorExtractor.java
index 2121bc2..8ae6b2e 100644
--- a/src/com/android/launcher3/widget/LocalColorExtractor.java
+++ b/src/com/android/launcher3/widget/LocalColorExtractor.java
@@ -16,13 +16,17 @@
 
 package com.android.launcher3.widget;
 
+import android.app.WallpaperColors;
 import android.appwidget.AppWidgetHostView;
 import android.content.Context;
+import android.graphics.Rect;
 import android.graphics.RectF;
 import android.util.SparseIntArray;
+import android.view.View;
 
 import androidx.annotation.Nullable;
 
+import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.util.ResourceBasedOverride;
 
@@ -44,7 +48,7 @@
     }
 
     /**
-     * Returns a new instance of the LocalColorExtractor.
+     * Creates a new instance of LocalColorExtractor
      */
     public static LocalColorExtractor newInstance(Context context) {
         return Overrides.getObject(LocalColorExtractor.class, context.getApplicationContext(),
@@ -65,4 +69,38 @@
     public void removeLocations() {
         // no-op
     }
+
+    /**
+     * Updates the base context to contain the colors override
+     */
+    public void applyColorsOverride(Context base, WallpaperColors colors) { }
+
+    /**
+     * Takes a view and returns its rect that can be used by the wallpaper local color extractor.
+     *
+     * @param launcher Launcher class class.
+     * @param pageId The page the workspace item is on.
+     * @param v The view.
+     * @param colorExtractionRectOut The location rect, but converted to a format expected by the
+     *                               wallpaper local color extractor.
+     */
+    public void getExtractedRectForView(Launcher launcher, int pageId, View v,
+            RectF colorExtractionRectOut) {
+        // no-op
+    }
+
+    /**
+     * Takes a rect in drag layer coordinates and returns the rect that can be used by the wallpaper
+     * local color extractor.
+     *
+     * @param launcher Launcher class.
+     * @param pageId The page the workspace item is on.
+     * @param rectInDragLayer The relevant bounds of the view in drag layer coordinates.
+     * @param colorExtractionRectOut The location rect, but converted to a format expected by the
+     *                               wallpaper local color extractor.
+     */
+    public void getExtractedRectForViewRect(Launcher launcher, int pageId, Rect rectInDragLayer,
+            RectF colorExtractionRectOut) {
+        // no-op
+    }
 }
diff --git a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
index ee0b84e..3377abb 100644
--- a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
+++ b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
@@ -15,9 +15,11 @@
  */
 package com.android.launcher3.widget;
 
+import static com.android.launcher3.AppWidgetResizeFrame.getWidgetSizeOptions;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_TRAY;
 
 import android.appwidget.AppWidgetHostView;
+import android.content.Context;
 import android.os.Bundle;
 
 import com.android.launcher3.LauncherSettings;
@@ -57,4 +59,8 @@
     public WidgetAddFlowHandler getHandler() {
         return new WidgetAddFlowHandler(info);
     }
+
+    public Bundle getDefaultSizeOptions(Context context) {
+        return getWidgetSizeOptions(context, componentName, spanX, spanY);
+    }
 }
diff --git a/src/com/android/launcher3/widget/WidgetHostViewLoader.java b/src/com/android/launcher3/widget/WidgetHostViewLoader.java
index 12e0d43..46141e0 100644
--- a/src/com/android/launcher3/widget/WidgetHostViewLoader.java
+++ b/src/com/android/launcher3/widget/WidgetHostViewLoader.java
@@ -1,16 +1,12 @@
 package com.android.launcher3.widget;
 
 import android.appwidget.AppWidgetHostView;
-import android.appwidget.AppWidgetManager;
 import android.content.Context;
-import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.Handler;
 import android.util.Log;
-import android.util.SizeF;
 import android.view.View;
 
-import com.android.launcher3.AppWidgetResizeFrame;
 import com.android.launcher3.DropTarget;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.dragndrop.DragController;
@@ -18,9 +14,6 @@
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.util.Thunk;
 
-import java.util.ArrayList;
-import java.util.stream.Collectors;
-
 public class WidgetHostViewLoader implements DragController.DragListener {
     private static final String TAG = "WidgetHostViewLoader";
     private static final boolean LOGD = false;
@@ -90,7 +83,7 @@
         if (pInfo.isCustomWidget()) {
             return false;
         }
-        final Bundle options = getDefaultOptionsForWidget(mLauncher, mInfo);
+        final Bundle options = mInfo.getDefaultSizeOptions(mLauncher);
 
         // If there is a configuration activity, do not follow thru bound and inflate.
         if (mInfo.getHandler().needsConfigure()) {
@@ -154,29 +147,4 @@
         return true;
     }
 
-    public static Bundle getDefaultOptionsForWidget(Context context, PendingAddWidgetInfo info) {
-        ArrayList<SizeF> sizes = AppWidgetResizeFrame
-                .getWidgetSizes(context, info.spanX, info.spanY);
-
-        Rect padding = AppWidgetHostView.getDefaultPaddingForWidget(context,
-                info.componentName, null);
-        float density = context.getResources().getDisplayMetrics().density;
-        float xPaddingDips = (padding.left + padding.right) / density;
-        float yPaddingDips = (padding.top + padding.bottom) / density;
-
-        ArrayList<SizeF> paddedSizes = sizes.stream().map(
-                size -> new SizeF(Math.max(0.f, size.getWidth() - xPaddingDips),
-                        Math.max(0.f, size.getHeight() - yPaddingDips))).collect(
-                Collectors.toCollection(ArrayList::new));
-
-        Rect rect = AppWidgetResizeFrame.getMinMaxSizes(paddedSizes, null /* outRect */);
-
-        Bundle options = new Bundle();
-        options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, rect.left);
-        options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, rect.top);
-        options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, rect.right);
-        options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, rect.bottom);
-        options.putParcelableArrayList(AppWidgetManager.OPTION_APPWIDGET_SIZES, paddedSizes);
-        return options;
-    }
 }
diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index f18b63e..995ac47 100644
--- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -118,6 +118,7 @@
             ViewGroup.LayoutParams layoutParams = widgetsTableScrollView.getLayoutParams();
             layoutParams.height = mMaxTableHeight;
             widgetsTableScrollView.setLayoutParams(layoutParams);
+            findViewById(R.id.collapse_handle).setVisibility(VISIBLE);
         }
     }
 
diff --git a/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java b/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java
index 7f84077..d09cb8a 100644
--- a/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java
+++ b/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java
@@ -15,6 +15,8 @@
  */
 package com.android.launcher3.widget.picker;
 
+import android.graphics.Point;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup.MarginLayoutParams;
 import android.widget.RelativeLayout;
@@ -33,9 +35,11 @@
         RecyclerViewFastScroller.OnFastScrollChangeListener {
     private final boolean mHasWorkProfile;
     private final SearchAndRecommendationViewHolder mViewHolder;
+    private final View mSearchAndRecommendationViewParent;
     private final WidgetsRecyclerView mPrimaryRecyclerView;
     private final WidgetsRecyclerView mSearchRecyclerView;
     private final int mTabsHeight;
+    private final Point mTempOffset = new Point();
 
     // The following are only non null if mHasWorkProfile is true.
     @Nullable private final WidgetsRecyclerView mWorkRecyclerView;
@@ -62,6 +66,8 @@
      */
     private int mCollapsibleHeightForTabs = 0;
 
+    private boolean mShouldForwardToRecyclerView = false;
+
     SearchAndRecommendationsScrollController(
             boolean hasWorkProfile,
             int tabsHeight,
@@ -73,6 +79,8 @@
             @Nullable PersonalWorkPagedView primaryWorkViewPager) {
         mHasWorkProfile = hasWorkProfile;
         mViewHolder = viewHolder;
+        mViewHolder.mContainer.setSearchAndRecommendationScrollController(this);
+        mSearchAndRecommendationViewParent = (View) mViewHolder.mContainer.getParent();
         mPrimaryRecyclerView = primaryRecyclerView;
         mCurrentRecyclerView = mPrimaryRecyclerView;
         mWorkRecyclerView = workRecyclerView;
@@ -245,6 +253,43 @@
         }
     }
 
+    /**
+     * Returns {@code true} if a touch event should be intercepted by this controller.
+     */
+    public boolean onInterceptTouchEvent(MotionEvent event) {
+        calculateMotionEventOffset(mTempOffset);
+        event.offsetLocation(mTempOffset.x, mTempOffset.y);
+        try {
+            mShouldForwardToRecyclerView = mCurrentRecyclerView.onInterceptTouchEvent(event);
+            return mShouldForwardToRecyclerView;
+        } finally {
+            event.offsetLocation(-mTempOffset.x, -mTempOffset.y);
+        }
+    }
+
+    /**
+     * Returns {@code true} if this controller has intercepted and consumed a touch event.
+     */
+    public boolean onTouchEvent(MotionEvent event) {
+        if (mShouldForwardToRecyclerView) {
+            calculateMotionEventOffset(mTempOffset);
+            event.offsetLocation(mTempOffset.x, mTempOffset.y);
+            try {
+                return mCurrentRecyclerView.onTouchEvent(event);
+            } finally {
+                event.offsetLocation(-mTempOffset.x, -mTempOffset.y);
+            }
+        }
+        return false;
+    }
+
+    private void calculateMotionEventOffset(Point p) {
+        p.x = mViewHolder.mContainer.getLeft() - mCurrentRecyclerView.getLeft()
+                - mSearchAndRecommendationViewParent.getLeft();
+        p.y = mViewHolder.mContainer.getTop() - mCurrentRecyclerView.getTop()
+                - mSearchAndRecommendationViewParent.getTop();
+    }
+
     /** private the height, in pixel, + the vertical margins of a given view. */
     private static int measureHeightWithVerticalMargins(View view) {
         if (view.getVisibility() != View.VISIBLE) {
diff --git a/src/com/android/launcher3/widget/picker/SearchAndRecommendationsView.java b/src/com/android/launcher3/widget/picker/SearchAndRecommendationsView.java
new file mode 100644
index 0000000..0d7d2b5
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/SearchAndRecommendationsView.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.widget.picker;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.widget.LinearLayout;
+
+/**
+ * A {@link LinearLayout} container for holding search and widgets recommendation.
+ *
+ * <p>This class intercepts touch events and dispatch them to the right view.
+ */
+public class SearchAndRecommendationsView extends LinearLayout {
+    private SearchAndRecommendationsScrollController mController;
+
+    public SearchAndRecommendationsView(Context context) {
+        this(context, /* attrs= */ null);
+    }
+
+    public SearchAndRecommendationsView(Context context, AttributeSet attrs) {
+        this(context, attrs, /* defStyleAttr= */ 0);
+    }
+
+    public SearchAndRecommendationsView(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, /* defStyleRes= */ 0);
+    }
+
+    public SearchAndRecommendationsView(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    public void setSearchAndRecommendationScrollController(
+            SearchAndRecommendationsScrollController controller) {
+        mController = controller;
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent event) {
+        return mController.onInterceptTouchEvent(event) || super.onInterceptTouchEvent(event);
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        return mController.onTouchEvent(event) || super.onTouchEvent(event);
+    }
+}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index 7156953..4d8c1ca 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -709,13 +709,14 @@
     }
 
     final class SearchAndRecommendationViewHolder {
-        final ViewGroup mContainer;
+        final SearchAndRecommendationsView mContainer;
         final View mCollapseHandle;
         final WidgetsSearchBar mSearchBar;
         final TextView mHeaderTitle;
         final WidgetsRecommendationTableLayout mRecommendedWidgetsTable;
 
-        SearchAndRecommendationViewHolder(ViewGroup searchAndRecommendationContainer) {
+        SearchAndRecommendationViewHolder(
+                SearchAndRecommendationsView searchAndRecommendationContainer) {
             mContainer = searchAndRecommendationContainer;
             mCollapseHandle = mContainer.findViewById(R.id.collapse_handle);
             mSearchBar = mContainer.findViewById(R.id.widgets_search_bar);
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java b/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
index 6fd147a..4407fe1 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/ApiWrapper.java
@@ -18,12 +18,22 @@
 
 import android.app.Person;
 import android.content.pm.ShortcutInfo;
+import android.view.Display;
 
 import com.android.launcher3.Utilities;
 
 public class ApiWrapper {
 
+    public static final boolean TASKBAR_DRAWN_IN_PROCESS = false;
+
     public static Person[] getPersons(ShortcutInfo si) {
         return Utilities.EMPTY_PERSON_ARRAY;
     }
+
+    /**
+     * Returns true if the display is an internal displays
+     */
+    public static boolean isInternalDisplay(Display display) {
+        return display.getDisplayId() == Display.DEFAULT_DISPLAY;
+    }
 }
diff --git a/tests/src_common/com/android/launcher3/common/WidgetUtils.java b/tests/src_common/com/android/launcher3/common/WidgetUtils.java
index ffad93f..5e17e0a 100644
--- a/tests/src_common/com/android/launcher3/common/WidgetUtils.java
+++ b/tests/src_common/com/android/launcher3/common/WidgetUtils.java
@@ -16,7 +16,6 @@
 package com.android.launcher3.common;
 
 import static com.android.launcher3.WorkspaceLayoutManager.FIRST_SCREEN_ID;
-import static com.android.launcher3.widget.WidgetHostViewLoader.getDefaultOptionsForWidget;
 
 import android.appwidget.AppWidgetHost;
 import android.content.ContentResolver;
@@ -62,7 +61,7 @@
             pendingInfo.spanY = item.spanY;
             pendingInfo.minSpanX = item.minSpanX;
             pendingInfo.minSpanY = item.minSpanY;
-            Bundle options = getDefaultOptionsForWidget(targetContext, pendingInfo);
+            Bundle options = pendingInfo.getDefaultSizeOptions(targetContext);
 
             AppWidgetHost host = new LauncherAppWidgetHost(targetContext);
             int widgetId = host.allocateAppWidgetId();
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index ebad154..e5b93b1 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -695,6 +695,7 @@
                         waitUntilLauncherObjectGone(CONTEXT_MENU_RES_ID);
                         // Swiping up can temporarily bring Nexus Launcher if the current
                         // Launcher is a Launcher3 one. Wait for the current launcher to reappear.
+                        SystemClock.sleep(5000); // b/187080582
                         waitForLauncherObject(getAnyObjectSelector());
                     }
                 }