Merge "Adding support for keyboard based drag and drop" into sc-dev
diff --git a/Android.bp b/Android.bp
index b20b307..a720658 100644
--- a/Android.bp
+++ b/Android.bp
@@ -36,17 +36,37 @@
     name: "launcher_log_protos_lite",
     srcs: [
         "protos/*.proto",
+        "protos_overrides/*.proto",
     ],
     sdk_version: "current",
     proto: {
         type: "lite",
         local_include_dirs:[
             "protos",
+            "protos_overrides",
         ],
     },
     static_libs: ["libprotobuf-java-lite"],
 }
 
+java_library_static {
+    name: "launcher_quickstep_log_protos_lite",
+    srcs: [
+        "quickstep/protos_overrides/*.proto",
+    ],
+    sdk_version: "current",
+    proto: {
+        type: "lite",
+        local_include_dirs:[
+            "quickstep/protos_overrides",
+        ],
+    },
+    static_libs: [
+      "libprotobuf-java-lite",
+      "launcher_log_protos_lite"
+      ],
+}
+
 java_library {
     name: "LauncherPluginLib",
 
diff --git a/Android.mk b/Android.mk
index ed42039..19ad328 100644
--- a/Android.mk
+++ b/Android.mk
@@ -33,7 +33,7 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     LauncherPluginLib \
-    launcher_log_protos_lite \
+    launcher_quickstep_log_protos_lite \
     search_ui
 
 LOCAL_SRC_FILES := \
@@ -65,7 +65,7 @@
     $(call all-java-files-under, src_shortcuts_overrides) \
     $(call all-java-files-under, src_ui_overrides) \
     $(call all-java-files-under, ext_tests/src)
-    
+
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/ext_tests/res
 
 LOCAL_PROGUARD_FLAG_FILES := proguard.flags
@@ -129,8 +129,7 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     SystemUI-statsd \
-    SystemUISharedLib \
-    launcher_log_protos_lite
+    SystemUISharedLib
 ifneq (,$(wildcard frameworks/base))
   LOCAL_PRIVATE_PLATFORM_APIS := true
 else
@@ -196,8 +195,7 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     SystemUI-statsd \
-    SystemUISharedLib \
-    launcher_log_protos_lite
+    SystemUISharedLib
 ifneq (,$(wildcard frameworks/base))
   LOCAL_PRIVATE_PLATFORM_APIS := true
 else
diff --git a/build.gradle b/build.gradle
index 28a05d5..a7eef13 100644
--- a/build.gradle
+++ b/build.gradle
@@ -81,7 +81,7 @@
             java.srcDirs = ['src', 'src_plugins']
             manifest.srcFile 'AndroidManifest-common.xml'
             proto {
-                srcDir 'protos/'
+                srcDirs = ['protos/', 'protos_overrides/']
             }
         }
 
@@ -181,4 +181,4 @@
             }
         }
     }
-}
\ No newline at end of file
+}
diff --git a/protos/launcher_atom.proto b/protos/launcher_atom.proto
index cd229ae..b4c6138 100644
--- a/protos/launcher_atom.proto
+++ b/protos/launcher_atom.proto
@@ -18,6 +18,8 @@
 option java_package = "com.android.launcher3.logger";
 option java_outer_classname = "LauncherAtom";
 
+import "launcher_atom_extension.proto";
+
 //
 // ItemInfos
 message ItemInfo {
@@ -55,6 +57,7 @@
     SettingsContainer settings_container = 9;
     PredictedHotseatContainer predicted_hotseat_container = 10;
     TaskSwitcherContainer task_switcher_container = 11;
+    ExtendedContainers extended_containers = 20;
   }
 }
 
diff --git a/protos_overrides/launcher_atom_extension.proto b/protos_overrides/launcher_atom_extension.proto
new file mode 100644
index 0000000..a07daf8
--- /dev/null
+++ b/protos_overrides/launcher_atom_extension.proto
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+syntax = "proto2";
+
+option java_package = "com.android.launcher3.logger";
+option java_outer_classname = "LauncherAtomExtensions";
+
+
+// This proto file contains placeholder messages that can be overridden by
+// other Launcher variants.
+// Variants could have its own launcher_atom_extension.proto file(should have
+// same messages declared here but with different implementation); when building
+// variant's apk launcher_atom.proto will reference variant's extension file,
+// essentially overriding this file.
+
+
+// Wrapper message for additional containers used in variants.
+message ExtendedContainers {}
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index d95cc01..7431551 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -31,8 +31,17 @@
     <uses-permission android:name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS"/>
     <uses-permission android:name="android.permission.VIBRATE"/>
     <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
+    <uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS"/>
+    <uses-permission android:name="android.permission.REMOVE_TASKS"/>
+    <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS"/>
+    <uses-permission android:name="android.permission.STATUS_BAR"/>
+    <uses-permission android:name="android.permission.STOP_APP_SWITCHES"/>
+    <uses-permission android:name="android.permission.SET_ORIENTATION"/>
+    <uses-permission android:name="android.permission.READ_FRAME_BUFFER"/>
+    <uses-permission android:name="android.permission.MANAGE_ACCESSIBILITY"/>
+
     <uses-permission android:name="${packageName}.permission.HOTSEAT_EDU" />
-    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+    <uses-permission android:name="android.permission.SYSTEM_APPLICATION_OVERLAY" />
 
     <application android:backupAgent="com.android.launcher3.LauncherBackupAgent"
          android:fullBackupOnly="true"
diff --git a/quickstep/protos_overrides/launcher_atom_extension.proto b/quickstep/protos_overrides/launcher_atom_extension.proto
new file mode 100644
index 0000000..2766acf
--- /dev/null
+++ b/quickstep/protos_overrides/launcher_atom_extension.proto
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+syntax = "proto2";
+
+option java_package = "com.android.launcher3.logger";
+option java_outer_classname = "LauncherAtomExtensions";
+
+
+// Wrapper message for containers used at the quickstep level.
+// Message name should match with launcher_atom_extension.proto message at
+// the AOSP level.
+message ExtendedContainers {
+
+  oneof Container{
+    DeviceSearchResultContainer device_search_result_container = 1;
+  }
+}
+
+// Represents on-device search result container.
+message DeviceSearchResultContainer{
+}
diff --git a/quickstep/res/layout/search_result_icon_row.xml b/quickstep/res/layout/search_result_icon_row.xml
index 2119fc3..084920a 100644
--- a/quickstep/res/layout/search_result_icon_row.xml
+++ b/quickstep/res/layout/search_result_icon_row.xml
@@ -20,10 +20,10 @@
     android:padding="@dimen/dynamic_grid_edge_margin">
 
     <com.android.launcher3.search.SearchResultIcon
-        android:layout_width="wrap_content"
         android:id="@+id/icon"
-        launcher:iconDisplay="hero_app"
-        android:layout_height="wrap_content" />
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        launcher:iconDisplay="hero_app" />
 
     <LinearLayout
         android:layout_width="0dp"
@@ -34,8 +34,8 @@
         android:layout_gravity="center_vertical">
 
         <TextView
-            android:layout_width="wrap_content"
             android:id="@id/title"
+            android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:gravity="start|center_vertical"
             android:maxLines="1"
@@ -57,9 +57,10 @@
         style="@style/BaseIcon"
         android:layout_width="@dimen/deep_shortcut_icon_size"
         android:layout_height="match_parent"
-        android:gravity="start|center_vertical"
+        android:layout_marginStart="4dp"
+        android:gravity="center"
         launcher:iconDisplay="shortcut_popup"
-        android:textSize="@dimen/search_hero_subtitle_size"
+        android:textSize="@dimen/search_hero_inline_button_size"
         launcher:iconSizeOverride="@dimen/deep_shortcut_icon_size"
         launcher:layoutHorizontal="false" />
 
@@ -68,6 +69,20 @@
         style="@style/BaseIcon"
         android:layout_width="@dimen/deep_shortcut_icon_size"
         android:layout_height="match_parent"
+        android:layout_marginStart="4dp"
+        android:gravity="center"
+        launcher:iconDisplay="shortcut_popup"
+        android:textSize="@dimen/search_hero_inline_button_size"
+        launcher:iconSizeOverride="@dimen/deep_shortcut_icon_size"
+        launcher:layoutHorizontal="false" />
+
+    <com.android.launcher3.search.SearchResultIcon
+        android:id="@+id/shortcut_2"
+        style="@style/BaseIcon"
+        android:layout_width="@dimen/deep_shortcut_icon_size"
+        android:layout_height="match_parent"
+        android:layout_marginStart="4dp"
+        android:gravity="center"
         launcher:iconDisplay="shortcut_popup"
         android:textSize="@dimen/search_hero_inline_button_size"
         launcher:iconSizeOverride="@dimen/deep_shortcut_icon_size"
diff --git a/quickstep/res/layout/search_result_people_item.xml b/quickstep/res/layout/search_result_people_item.xml
deleted file mode 100644
index 964300d..0000000
--- a/quickstep/res/layout/search_result_people_item.xml
+++ /dev/null
@@ -1,63 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!-- 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.
--->
-<com.android.launcher3.search.SearchResultPeopleView xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:gravity="center_vertical"
-    android:layout_height="wrap_content"
-    android:padding="8dp"
-    android:orientation="horizontal">
-
-    <View
-        android:id="@+id/icon"
-        android:layout_marginRight="8dp"
-        android:layout_width="@dimen/deep_shortcut_icon_size"
-        android:layout_height="@dimen/deep_shortcut_icon_size" />
-
-    <TextView
-        android:layout_width="0dp"
-        android:textColor="?android:attr/textColorPrimary"
-        android:id="@+id/title"
-        android:textSize="@dimen/search_hero_title_size"
-        android:layout_height="wrap_content"
-        android:layout_weight="1" />
-
-    <ImageButton
-        android:id="@+id/provider_0"
-        android:scaleType="fitCenter"
-        android:adjustViewBounds="true"
-        android:layout_margin="5dp"
-        android:background="?android:attr/selectableItemBackground"
-        android:layout_width="@dimen/deep_shortcut_icon_size"
-        android:layout_height="@dimen/deep_shortcut_icon_size" />
-
-    <ImageButton
-        android:id="@+id/provider_1"
-        android:layout_margin="5dp"
-        android:scaleType="fitCenter"
-        android:adjustViewBounds="true"
-        android:background="?android:attr/selectableItemBackground"
-        android:layout_width="@dimen/deep_shortcut_icon_size"
-        android:layout_height="@dimen/deep_shortcut_icon_size" />
-
-    <ImageButton
-        android:id="@+id/provider_2"
-        android:layout_margin="5dp"
-        android:scaleType="fitCenter"
-        android:adjustViewBounds="true"
-        android:background="?android:attr/selectableItemBackground"
-        android:layout_width="@dimen/deep_shortcut_icon_size"
-        android:layout_height="@dimen/deep_shortcut_icon_size" />
-
-</com.android.launcher3.search.SearchResultPeopleView>
\ No newline at end of file
diff --git a/quickstep/res/layout/search_result_small_icon_row.xml b/quickstep/res/layout/search_result_small_icon_row.xml
new file mode 100644
index 0000000..41856fe
--- /dev/null
+++ b/quickstep/res/layout/search_result_small_icon_row.xml
@@ -0,0 +1,73 @@
+<?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.
+-->
+<com.android.launcher3.search.SearchResultSmallIconRow
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:launcher="http://schemas.android.com/apk/res-auto"
+    android:orientation="horizontal"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:padding="@dimen/dynamic_grid_edge_margin">
+
+    <com.android.launcher3.search.SearchResultIcon
+        android:id="@+id/icon"
+        style="@style/BaseIcon"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:drawablePadding="@dimen/dynamic_grid_icon_drawable_padding"
+        android:drawableTint="?android:attr/textColorPrimary"
+        android:padding="@dimen/dynamic_grid_edge_margin"
+        launcher:iconDisplay="hero_app"
+        launcher:iconSizeOverride="48dp"
+        launcher:matchTextInsetWithQuery="true"
+        launcher:layoutHorizontal="true" />
+
+    <LinearLayout
+        android:layout_width="0dp"
+        android:layout_weight="1"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:orientation="horizontal"
+        android:padding="@dimen/dynamic_grid_edge_margin" >
+
+        <TextView
+            android:id="@id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:gravity="start|center_vertical"
+            android:maxLines="1"
+            android:paddingEnd="4dp"
+            android:textAlignment="viewStart"
+            android:textColor="?android:attr/textColorPrimary"
+            android:textSize="@dimen/search_hero_title_size" />
+        <TextView
+            android:id="@+id/delimeter"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:gravity="start|center_vertical"
+            android:maxLines="1"
+            android:text="\u2022"
+            android:textColor="?android:attr/textColorPrimary"
+            android:textSize="@dimen/search_hero_subtitle_size" />
+        <TextView
+            android:id="@+id/subtitle"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:maxLines="1"
+            android:paddingStart="4dp"
+            android:textColor="?android:attr/textColorTertiary"
+            android:textSize="@dimen/search_hero_subtitle_size" />
+    </LinearLayout>
+</com.android.launcher3.search.SearchResultSmallIconRow>
\ No newline at end of file
diff --git a/quickstep/res/layout/taskbar.xml b/quickstep/res/layout/taskbar.xml
index 5f1046d..b124b33 100644
--- a/quickstep/res/layout/taskbar.xml
+++ b/quickstep/res/layout/taskbar.xml
@@ -24,6 +24,8 @@
         android:id="@+id/taskbar_view"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:background="@color/taskbar_background"/>
+        android:background="@color/taskbar_background"
+        android:gravity="center"
+        android:animateLayoutChanges="true"/>
 
 </com.android.launcher3.taskbar.TaskbarContainerView>
\ No newline at end of file
diff --git a/quickstep/res/layout/taskbar_app_icon.xml b/quickstep/res/layout/taskbar_app_icon.xml
new file mode 100644
index 0000000..6fefdb6
--- /dev/null
+++ b/quickstep/res/layout/taskbar_app_icon.xml
@@ -0,0 +1,17 @@
+<?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.
+-->
+
+<com.android.launcher3.views.DoubleShadowBubbleTextView style="@style/BaseIcon.Workspace.Taskbar" />
diff --git a/quickstep/res/layout/taskbar_divider.xml b/quickstep/res/layout/taskbar_divider.xml
new file mode 100644
index 0000000..6e1aa1e
--- /dev/null
+++ b/quickstep/res/layout/taskbar_divider.xml
@@ -0,0 +1,23 @@
+<?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.
+-->
+
+<View
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="@dimen/taskbar_divider_thickness"
+    android:layout_height="@dimen/taskbar_divider_height"
+    android:layout_marginStart="@dimen/taskbar_icon_spacing"
+    android:layout_marginEnd="@dimen/taskbar_icon_spacing"
+    android:background="@color/taskbar_divider" />
\ No newline at end of file
diff --git a/quickstep/res/layout/taskbar_predicted_app_icon.xml b/quickstep/res/layout/taskbar_predicted_app_icon.xml
new file mode 100644
index 0000000..211ebc8
--- /dev/null
+++ b/quickstep/res/layout/taskbar_predicted_app_icon.xml
@@ -0,0 +1,17 @@
+<?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.
+-->
+
+<com.android.launcher3.uioverrides.PredictedAppIcon style="@style/BaseIcon.Workspace.Taskbar" />
diff --git a/quickstep/res/values/colors.xml b/quickstep/res/values/colors.xml
index 3bc8ddc..54730f1 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_divider">#C0C0C0</color>
 </resources>
\ No newline at end of file
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 4272f50..0f40775 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -122,4 +122,11 @@
 
     <!-- Taskbar -->
     <dimen name="taskbar_size">48dp</dimen>
+    <dimen name="taskbar_icon_size">32dp</dimen>
+    <dimen name="taskbar_icon_touch_size">48dp</dimen>
+    <dimen name="taskbar_icon_drag_icon_size">54dp</dimen>
+    <!-- Note that this applies to both sides of all icons, so visible space is double this. -->
+    <dimen name="taskbar_icon_spacing">14dp</dimen>
+    <dimen name="taskbar_divider_thickness">1dp</dimen>
+    <dimen name="taskbar_divider_height">24dp</dimen>
 </resources>
diff --git a/quickstep/res/values/styles.xml b/quickstep/res/values/styles.xml
index 8d054b4..5a353f0 100644
--- a/quickstep/res/values/styles.xml
+++ b/quickstep/res/values/styles.xml
@@ -85,4 +85,10 @@
         <item name="android:drawablePadding">8dp</item>
         <item name="android:textAllCaps">false</item>
     </style>
+
+    <!-- Icon displayed on the taskbar -->
+    <style name="BaseIcon.Workspace.Taskbar" >
+        <item name="iconDisplay">taskbar</item>
+        <item name="iconSizeOverride">@dimen/taskbar_icon_size</item>
+    </style>
 </resources>
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index 2518f42..edcd0a2 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -45,6 +45,7 @@
 import com.android.launcher3.statemanager.StateManager.StateHandler;
 import com.android.launcher3.taskbar.TaskbarContainerView;
 import com.android.launcher3.taskbar.TaskbarController;
+import com.android.launcher3.taskbar.TaskbarStateHandler;
 import com.android.launcher3.uioverrides.RecentsViewStateController;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.UiThreadHelper;
@@ -82,6 +83,7 @@
     private OverviewActionsView mActionsView;
 
     private @Nullable TaskbarController mTaskbarController;
+    private final TaskbarStateHandler mTaskbarStateHandler = new TaskbarStateHandler(this);
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -245,13 +247,28 @@
                 getWorkspace(),
                 getDepthController(),
                 new RecentsViewStateController(this),
-                new BackButtonAlphaHandler(this)};
+                new BackButtonAlphaHandler(this),
+                getTaskbarStateHandler(),
+        };
     }
 
     public DepthController getDepthController() {
         return mDepthController;
     }
 
+    public @Nullable TaskbarController getTaskbarController() {
+        return mTaskbarController;
+    }
+
+    public TaskbarStateHandler getTaskbarStateHandler() {
+        return mTaskbarStateHandler;
+    }
+
+    @Override
+    public boolean isViewInTaskbar(View v) {
+        return mTaskbarController != null && mTaskbarController.isViewInTaskbar(v);
+    }
+
     @Override
     public void useFadeOutAnimationForLauncherStart(CancellationSignal signal) {
         QuickstepAppTransitionManagerImpl appTransitionManager =
@@ -296,6 +313,12 @@
             mDepthController.setActivityStarted(isStarted());
         }
 
+        if ((changeBits & ACTIVITY_STATE_RESUMED) != 0) {
+            if (mTaskbarController != null) {
+                mTaskbarController.onLauncherResumedOrPaused(hasBeenResumed());
+            }
+        }
+
         super.onActivityFlagsChanged(changeBits);
     }
 
diff --git a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
index 034d51f..588d676 100644
--- a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
+++ b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
@@ -56,17 +56,22 @@
         return mHandler;
     }
 
-    // Called only in R+ platform
+    // Called only in S+ platform
     @BinderThread
-    public void onAnimationStart(RemoteAnimationTargetCompat[] appTargets,
-            RemoteAnimationTargetCompat[] wallpaperTargets, Runnable runnable) {
+    public void onAnimationStart(
+            int transit,
+            RemoteAnimationTargetCompat[] appTargets,
+            RemoteAnimationTargetCompat[] wallpaperTargets,
+            RemoteAnimationTargetCompat[] nonAppTargets,
+            Runnable runnable) {
         Runnable r = () -> {
             finishExistingAnimation();
             mAnimationResult = new AnimationResult(() -> {
                 UI_HELPER_EXECUTOR.execute(runnable);
                 mAnimationResult = null;
             });
-            onCreateAnimation(appTargets, wallpaperTargets, mAnimationResult);
+            onCreateAnimation(transit, appTargets, wallpaperTargets, nonAppTargets,
+                    mAnimationResult);
         };
         if (mStartAtFrontOfQueue) {
             postAtFrontOfQueueAsynchronously(mHandler, r);
@@ -75,6 +80,14 @@
         }
     }
 
+    // Called only in R platform
+    @BinderThread
+    public void onAnimationStart(RemoteAnimationTargetCompat[] appTargets,
+            RemoteAnimationTargetCompat[] wallpaperTargets, Runnable runnable) {
+        onAnimationStart(0 /* transit */, appTargets, wallpaperTargets,
+                new RemoteAnimationTargetCompat[0], runnable);
+    }
+
     // Called only in Q platform
     @BinderThread
     @Deprecated
@@ -88,8 +101,11 @@
      */
     @UiThread
     public abstract void onCreateAnimation(
+            int transit,
             RemoteAnimationTargetCompat[] appTargets,
-            RemoteAnimationTargetCompat[] wallpaperTargets, AnimationResult result);
+            RemoteAnimationTargetCompat[] wallpaperTargets,
+            RemoteAnimationTargetCompat[] nonAppTargets,
+            AnimationResult result);
 
     @UiThread
     private void finishExistingAnimation() {
diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
index 470a442..c4b6961 100644
--- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
@@ -139,7 +139,7 @@
     private static final int LAUNCHER_RESUME_START_DELAY = 100;
     private static final int CLOSING_TRANSITION_DURATION_MS = 250;
 
-    protected static final int CONTENT_ALPHA_DURATION = 217;
+    public static final int CONTENT_ALPHA_DURATION = 217;
     protected static final int CONTENT_TRANSLATION_DURATION = 350;
 
     // Progress = 0: All apps is fully pulled up, Progress = 1: All apps is fully pulled down.
@@ -202,9 +202,10 @@
     }
 
     @Override
-    public boolean supportsAdaptiveIconAnimation() {
+    public boolean supportsAdaptiveIconAnimation(View clickedView) {
         return hasControlRemoteAppTransitionPermission()
-                && FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM.get();
+                && FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM.get()
+                && !mLauncher.isViewInTaskbar(clickedView);
     }
 
     /**
@@ -865,8 +866,10 @@
         }
 
         @Override
-        public void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
+        public void onCreateAnimation(int transit,
+                RemoteAnimationTargetCompat[] appTargets,
                 RemoteAnimationTargetCompat[] wallpaperTargets,
+                RemoteAnimationTargetCompat[] nonAppTargets,
                 LauncherAnimationRunner.AnimationResult result) {
             if (mLauncher.isDestroyed()) {
                 AnimatorSet anim = new AnimatorSet();
@@ -879,7 +882,8 @@
                 // If launcher is not resumed, wait until new async-frame after resume
                 mLauncher.addOnResumeCallback(() ->
                         postAsyncCallback(mHandler, () ->
-                                onCreateAnimation(appTargets, wallpaperTargets, result)));
+                                onCreateAnimation(transit, appTargets, wallpaperTargets,
+                                        nonAppTargets, result)));
                 return;
             }
 
@@ -963,8 +967,10 @@
         }
 
         @Override
-        public void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
+        public void onCreateAnimation(int transit,
+                RemoteAnimationTargetCompat[] appTargets,
                 RemoteAnimationTargetCompat[] wallpaperTargets,
+                RemoteAnimationTargetCompat[] nonAppTargets,
                 LauncherAnimationRunner.AnimationResult result) {
             AnimatorSet anim = new AnimatorSet();
 
@@ -972,9 +978,12 @@
                     launcherIsATargetWithMode(appTargets, MODE_CLOSING);
 
             final boolean launchingFromRecents = isLaunchingFromRecents(mV, appTargets);
+            final boolean launchingFromTaskbar = mLauncher.isViewInTaskbar(mV);
             if (launchingFromRecents) {
                 composeRecentsLaunchAnimator(anim, mV, appTargets, wallpaperTargets,
                         launcherClosing);
+            } else if (launchingFromTaskbar) {
+                // TODO
             } else {
                 composeIconLaunchAnimator(anim, mV, appTargets, wallpaperTargets,
                         launcherClosing);
diff --git a/quickstep/src/com/android/launcher3/WrappedAnimationRunnerImpl.java b/quickstep/src/com/android/launcher3/WrappedAnimationRunnerImpl.java
index da2aee4..03cc28e 100644
--- a/quickstep/src/com/android/launcher3/WrappedAnimationRunnerImpl.java
+++ b/quickstep/src/com/android/launcher3/WrappedAnimationRunnerImpl.java
@@ -26,7 +26,9 @@
  */
 public interface WrappedAnimationRunnerImpl {
     Handler getHandler();
-    void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
+    void onCreateAnimation(int transit,
+            RemoteAnimationTargetCompat[] appTargets,
             RemoteAnimationTargetCompat[] wallpaperTargets,
+            RemoteAnimationTargetCompat[] nonAppTargets,
             LauncherAnimationRunner.AnimationResult result);
 }
diff --git a/quickstep/src/com/android/launcher3/WrappedLauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/WrappedLauncherAnimationRunner.java
index 1753b62..1e1631b 100644
--- a/quickstep/src/com/android/launcher3/WrappedLauncherAnimationRunner.java
+++ b/quickstep/src/com/android/launcher3/WrappedLauncherAnimationRunner.java
@@ -46,11 +46,15 @@
     }
 
     @Override
-    public void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
-            RemoteAnimationTargetCompat[] wallpaperTargets, AnimationResult result) {
+    public void onCreateAnimation(int transit,
+            RemoteAnimationTargetCompat[] appTargets,
+            RemoteAnimationTargetCompat[] wallpaperTargets,
+            RemoteAnimationTargetCompat[] nonAppTargets,
+            AnimationResult result) {
         R animationRunnerImpl = mImpl.get();
         if (animationRunnerImpl != null) {
-            animationRunnerImpl.onCreateAnimation(appTargets, wallpaperTargets, result);
+            animationRunnerImpl.onCreateAnimation(transit, appTargets, wallpaperTargets,
+                    nonAppTargets, result);
         }
     }
 }
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
index b2de4c9..88cfacb 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatPredictionController.java
@@ -20,6 +20,7 @@
 import static com.android.launcher3.hybridhotseat.HotseatEduController.getSettingsIntent;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_PREDICTION_PINNED;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_RANKED;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 
 import android.animation.Animator;
 import android.animation.AnimatorSet;
@@ -37,7 +38,6 @@
 import com.android.launcher3.DropTarget;
 import com.android.launcher3.Hotseat;
 import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
@@ -71,18 +71,21 @@
  */
 public class HotseatPredictionController implements DragController.DragListener,
         SystemShortcut.Factory<QuickstepLauncher>, InvariantDeviceProfile.OnIDPChangeListener,
-        DragSource {
+        DragSource, ViewGroup.OnHierarchyChangeListener {
+
+    private static final int FLAG_UPDATE_PAUSED = 1 << 0;
+    private static final int FLAG_DRAG_IN_PROGRESS = 1 << 1;
+    private static final int FLAG_FILL_IN_PROGRESS = 1 << 2;
 
     private int mHotSeatItemsCount;
 
-    private Launcher mLauncher;
+    private QuickstepLauncher mLauncher;
     private final Hotseat mHotseat;
 
     private List<ItemInfo> mPredictedItems = Collections.emptyList();
 
     private AnimatorSet mIconRemoveAnimators;
-    private boolean mUIUpdatePaused = false;
-    private boolean mDragInProgress = false;
+    private int mPauseFlags = 0;
 
     private List<PredictedAppIcon.PredictedIconOutlineDrawing> mOutlineDrawings = new ArrayList<>();
 
@@ -108,13 +111,37 @@
         return true;
     };
 
-    public HotseatPredictionController(Launcher launcher) {
+    public HotseatPredictionController(QuickstepLauncher launcher) {
         mLauncher = launcher;
         mHotseat = launcher.getHotseat();
         mHotSeatItemsCount = mLauncher.getDeviceProfile().inv.numHotseatIcons;
         mLauncher.getDragController().addDragListener(this);
 
         launcher.getDeviceProfile().inv.addOnChangeListener(this);
+        mHotseat.getShortcutsAndWidgets().setOnHierarchyChangeListener(this);
+    }
+
+    @Override
+    public void onChildViewAdded(View parent, View child) {
+        onHotseatHierarchyChanged();
+    }
+
+    @Override
+    public void onChildViewRemoved(View parent, View child) {
+        onHotseatHierarchyChanged();
+    }
+
+    private void onHotseatHierarchyChanged() {
+        if (mPauseFlags == 0 && !mLauncher.isWorkspaceLoading()) {
+            // Post update after a single frame to avoid layout within layout
+            MAIN_EXECUTOR.getHandler().post(this::updateFillIfNotLoading);
+        }
+    }
+
+    private void updateFillIfNotLoading() {
+        if (mPauseFlags == 0 && !mLauncher.isWorkspaceLoading()) {
+            fillGapsWithPrediction(true);
+        }
     }
 
     /**
@@ -161,11 +188,11 @@
     }
 
     private void fillGapsWithPrediction() {
-        fillGapsWithPrediction(false, null);
+        fillGapsWithPrediction(false);
     }
 
-    private void fillGapsWithPrediction(boolean animate, Runnable callback) {
-        if (mUIUpdatePaused || mDragInProgress) {
+    private void fillGapsWithPrediction(boolean animate) {
+        if (mPauseFlags != 0) {
             return;
         }
 
@@ -176,12 +203,14 @@
             mIconRemoveAnimators.addListener(new AnimationSuccessListener() {
                 @Override
                 public void onAnimationSuccess(Animator animator) {
-                    fillGapsWithPrediction(animate, callback);
+                    fillGapsWithPrediction(animate);
                     mIconRemoveAnimators.removeListener(this);
                 }
             });
             return;
         }
+
+        mPauseFlags |= FLAG_FILL_IN_PROGRESS;
         for (int rank = 0; rank < mHotSeatItemsCount; rank++) {
             View child = mHotseat.getChildAt(
                     mHotseat.getCellXFromOrder(rank),
@@ -208,10 +237,12 @@
             }
             preparePredictionInfo(predictedItem, rank);
         }
-        bindItems(newItems, animate, callback);
+        bindItems(newItems, animate);
+
+        mPauseFlags &= ~FLAG_FILL_IN_PROGRESS;
     }
 
-    private void bindItems(List<WorkspaceItemInfo> itemsToAdd, boolean animate, Runnable callback) {
+    private void bindItems(List<WorkspaceItemInfo> itemsToAdd, boolean animate) {
         AnimatorSet animationSet = new AnimatorSet();
         for (WorkspaceItemInfo item : itemsToAdd) {
             PredictedAppIcon icon = PredictedAppIcon.createIcon(mHotseat, item);
@@ -222,15 +253,28 @@
             }
         }
         if (animate) {
-            if (callback != null) {
-                animationSet.addListener(AnimationSuccessListener.forRunnable(callback));
-            }
+            animationSet.addListener(AnimationSuccessListener
+                    .forRunnable(this::removeOutlineDrawings));
             animationSet.start();
         } else {
-            if (callback != null) callback.run();
+            removeOutlineDrawings();
+        }
+
+        if (mLauncher.getTaskbarController() != null) {
+            mLauncher.getTaskbarController().onHotseatUpdated();
         }
     }
 
+    private void removeOutlineDrawings() {
+        if (mOutlineDrawings.isEmpty()) return;
+        for (PredictedAppIcon.PredictedIconOutlineDrawing outlineDrawing : mOutlineDrawings) {
+            mHotseat.removeDelegatedCellDrawing(outlineDrawing);
+        }
+        mHotseat.invalidate();
+        mOutlineDrawings.clear();
+    }
+
+
     /**
      * Unregisters callbacks and frees resources
      */
@@ -242,7 +286,9 @@
      * start and pauses predicted apps update on the hotseat
      */
     public void setPauseUIUpdate(boolean paused) {
-        mUIUpdatePaused = paused;
+        mPauseFlags = paused
+                ? (mPauseFlags | FLAG_UPDATE_PAUSED)
+                : (mPauseFlags & ~FLAG_UPDATE_PAUSED);
         if (!paused) {
             fillGapsWithPrediction();
         }
@@ -358,14 +404,14 @@
         for (PredictedAppIcon.PredictedIconOutlineDrawing outlineDrawing : mOutlineDrawings) {
             mHotseat.addDelegatedCellDrawing(outlineDrawing);
         }
-        mDragInProgress = true;
+        mPauseFlags |= FLAG_DRAG_IN_PROGRESS;
         mHotseat.invalidate();
     }
 
     @Override
     public void onDragEnd() {
-        mDragInProgress = false;
-        fillGapsWithPrediction(true, this::removeOutlineDrawings);
+        mPauseFlags &= ~FLAG_DRAG_IN_PROGRESS;
+        fillGapsWithPrediction(true);
     }
 
     @Nullable
@@ -386,15 +432,6 @@
         itemInfo.screenId = rank;
     }
 
-    private void removeOutlineDrawings() {
-        if (mOutlineDrawings.isEmpty()) return;
-        for (PredictedAppIcon.PredictedIconOutlineDrawing outlineDrawing : mOutlineDrawings) {
-            mHotseat.removeDelegatedCellDrawing(outlineDrawing);
-        }
-        mHotseat.invalidate();
-        mOutlineDrawings.clear();
-    }
-
     @Override
     public void onIdpChanged(int changeFlags, InvariantDeviceProfile profile) {
         this.mHotSeatItemsCount = profile.numHotseatIcons;
diff --git a/quickstep/src/com/android/launcher3/proxy/ProxyActivityStarter.java b/quickstep/src/com/android/launcher3/proxy/ProxyActivityStarter.java
index e302b4f..4d7cc85 100644
--- a/quickstep/src/com/android/launcher3/proxy/ProxyActivityStarter.java
+++ b/quickstep/src/com/android/launcher3/proxy/ProxyActivityStarter.java
@@ -17,6 +17,7 @@
 package com.android.launcher3.proxy;
 
 import android.app.Activity;
+import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentSender.SendIntentException;
@@ -48,19 +49,20 @@
             return;
         }
 
-        if (mParams.intent != null) {
-            startActivityForResult(mParams.intent, mParams.requestCode, mParams.options);
-            return;
-        } else if (mParams.intentSender != null) {
-            try {
+        try {
+            if (mParams.intent != null) {
+                startActivityForResult(mParams.intent, mParams.requestCode, mParams.options);
+                return;
+            } else if (mParams.intentSender != null) {
                 startIntentSenderForResult(mParams.intentSender, mParams.requestCode,
                         mParams.fillInIntent, mParams.flagsMask, mParams.flagsValues,
                         mParams.extraFlags,
                         mParams.options);
                 return;
-            } catch (SendIntentException e) {
-                mParams.deliverResult(this, RESULT_CANCELED, null);
             }
+        } catch (NullPointerException | ActivityNotFoundException | SecurityException
+                | SendIntentException e) {
+            mParams.deliverResult(this, RESULT_CANCELED, null);
         }
         finishAndRemoveTask();
     }
diff --git a/quickstep/src/com/android/launcher3/search/DeviceSearchAdapterProvider.java b/quickstep/src/com/android/launcher3/search/DeviceSearchAdapterProvider.java
index 02be0b9..4a656c1 100644
--- a/quickstep/src/com/android/launcher3/search/DeviceSearchAdapterProvider.java
+++ b/quickstep/src/com/android/launcher3/search/DeviceSearchAdapterProvider.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3.search;
 
+import static com.android.launcher3.allapps.AllAppsGridAdapter.VIEW_TYPE_ALL_APPS_DIVIDER;
 import static com.android.launcher3.allapps.AllAppsGridAdapter.VIEW_TYPE_ICON;
 
 import android.app.search.SearchTarget;
@@ -31,6 +32,7 @@
 import com.android.launcher3.allapps.AllAppsContainerView;
 import com.android.launcher3.allapps.AllAppsGridAdapter;
 import com.android.launcher3.allapps.search.SearchAdapterProvider;
+import com.android.launcher3.config.FeatureFlags;
 
 /**
  * Provides views for on-device search results
@@ -41,12 +43,12 @@
     public static final int VIEW_TYPE_SEARCH_SLICE = 1 << 7;
     public static final int VIEW_TYPE_SEARCH_ICON = (1 << 8) | VIEW_TYPE_ICON;
     public static final int VIEW_TYPE_SEARCH_ICON_ROW = (1 << 9);
-    public static final int VIEW_TYPE_SEARCH_PEOPLE = 1 << 11;
+    public static final int VIEW_TYPE_SEARCH_SMALL_ICON_ROW = (1 << 10);
     public static final int VIEW_TYPE_SEARCH_THUMBNAIL = 1 << 12;
     public static final int VIEW_TYPE_SEARCH_WIDGET_LIVE = 1 << 15;
     public static final int VIEW_TYPE_SEARCH_WIDGET_PREVIEW = 1 << 16;
 
-    private static final String TAG = "SearchServiceAdapterProvider";
+    private static final String TAG = "SearchServiceAdapter";
 
     private final AllAppsContainerView mAppsView;
     private final SparseIntArray mViewTypeToLayoutMap = new SparseIntArray();
@@ -58,12 +60,13 @@
         mViewTypeToLayoutMap.put(VIEW_TYPE_SEARCH_CORPUS_TITLE, R.layout.search_section_title);
         mViewTypeToLayoutMap.put(VIEW_TYPE_SEARCH_ICON, R.layout.search_result_icon);
         mViewTypeToLayoutMap.put(VIEW_TYPE_SEARCH_ICON_ROW, R.layout.search_result_icon_row);
+        mViewTypeToLayoutMap.put(VIEW_TYPE_SEARCH_SMALL_ICON_ROW, R.layout.search_result_small_icon_row);
         mViewTypeToLayoutMap.put(VIEW_TYPE_SEARCH_SLICE, R.layout.search_result_slice);
-        mViewTypeToLayoutMap.put(VIEW_TYPE_SEARCH_PEOPLE, R.layout.search_result_people_item);
         mViewTypeToLayoutMap.put(VIEW_TYPE_SEARCH_THUMBNAIL, R.layout.search_result_thumbnail);
         mViewTypeToLayoutMap.put(VIEW_TYPE_SEARCH_WIDGET_LIVE, R.layout.search_result_widget_live);
         mViewTypeToLayoutMap.put(VIEW_TYPE_SEARCH_WIDGET_PREVIEW,
                 R.layout.search_result_widget_preview);
+        mViewTypeToLayoutMap.put(VIEW_TYPE_ALL_APPS_DIVIDER, R.layout.all_apps_divider);
     }
 
     @Override
@@ -118,24 +121,33 @@
             case LayoutType.ICON_SINGLE_VERTICAL_TEXT:
                 return VIEW_TYPE_SEARCH_ICON;
             case LayoutType.ICON_SLICE:
+                if (FeatureFlags.DISABLE_SLICE_IN_ALLAPPS.get()) {
+                    return -1;
+                }
                 if (t.getSliceUri() != null) {
                     return VIEW_TYPE_SEARCH_SLICE;
                 }
-                Log.w(TAG, "Dropping as LayoutType.ICON_SLICE target doesn't contain sliceUri.");
+                Log.w(TAG, "LayoutType.ICON_SLICE target doesn't contain sliceUri.");
                 break;
             case LayoutType.ICON_DOUBLE_HORIZONTAL_TEXT:
             case LayoutType.ICON_SINGLE_HORIZONTAL_TEXT:
+            case LayoutType.ICON_DOUBLE_HORIZONTAL_TEXT_BUTTON:
+            case LayoutType.ICON_HORIZONTAL_TEXT:
                 return VIEW_TYPE_SEARCH_ICON_ROW;
+            case LayoutType.SMALL_ICON_HORIZONTAL_TEXT:
+                return VIEW_TYPE_SEARCH_SMALL_ICON_ROW;
             case LayoutType.THUMBNAIL:
                 if (t.getSearchAction() != null) {
                     return VIEW_TYPE_SEARCH_THUMBNAIL;
                 }
-                Log.w(TAG, "Dropping as LayoutType.THUMBNAIL target doesn't contain searchAction.");
+                Log.w(TAG, "LayoutType.THUMBNAIL target doesn't contain searchAction.");
                 break;
             case LayoutType.WIDGET_PREVIEW:
                 return VIEW_TYPE_SEARCH_WIDGET_PREVIEW;
             case LayoutType.WIDGET_LIVE:
                 return VIEW_TYPE_SEARCH_WIDGET_LIVE;
+            case LayoutType.DIVIDER:
+                return VIEW_TYPE_ALL_APPS_DIVIDER;
         }
 
         return -1;
diff --git a/quickstep/src/com/android/launcher3/search/DeviceSearchEdu.java b/quickstep/src/com/android/launcher3/search/DeviceSearchEdu.java
index 425e557..71270cc 100644
--- a/quickstep/src/com/android/launcher3/search/DeviceSearchEdu.java
+++ b/quickstep/src/com/android/launcher3/search/DeviceSearchEdu.java
@@ -73,11 +73,9 @@
     }
 
 
-    private void close(boolean animate, boolean markAsSeen) {
-        handleClose(animate);
-        if (markAsSeen) {
-            mLauncher.getOnboardingPrefs().markChecked(SEARCH_EDU_SEEN);
-        }
+    private void dismiss() {
+        handleClose(true);
+        mLauncher.getOnboardingPrefs().markChecked(SEARCH_EDU_SEEN);
     }
 
     @Override
@@ -110,7 +108,7 @@
 
         findViewById(R.id.dismiss_edu).setOnClickListener((view) -> {
             mSwitchFocusOnDismiss = true;
-            close(true, true);
+            dismiss();
         });
     }
 
@@ -176,7 +174,7 @@
 
     @Override
     public void onStateTransitionStart(LauncherState toState) {
-        close(true, false);
+        dismiss();
     }
 
     @Override
@@ -203,7 +201,7 @@
         if (mSearchInput != null) {
             mSearchInput.setText(charSequence.toString());
             mSwitchFocusOnDismiss = true;
-            close(true, true);
+            dismiss();
         }
     }
 
@@ -215,7 +213,7 @@
     @Override
     public boolean onEditorAction(TextView textView, int i, KeyEvent keyEvent) {
         mSearchInput.onEditorAction(i);
-        close(true, true);
+        dismiss();
         return true;
     }
 }
diff --git a/quickstep/src/com/android/launcher3/search/SearchAdapterItem.java b/quickstep/src/com/android/launcher3/search/SearchAdapterItem.java
index 0eb3edb..8983c4f 100644
--- a/quickstep/src/com/android/launcher3/search/SearchAdapterItem.java
+++ b/quickstep/src/com/android/launcher3/search/SearchAdapterItem.java
@@ -18,7 +18,7 @@
 
 import static com.android.launcher3.search.DeviceSearchAdapterProvider.VIEW_TYPE_SEARCH_ICON;
 import static com.android.launcher3.search.DeviceSearchAdapterProvider.VIEW_TYPE_SEARCH_ICON_ROW;
-import static com.android.launcher3.search.DeviceSearchAdapterProvider.VIEW_TYPE_SEARCH_PEOPLE;
+import static com.android.launcher3.search.DeviceSearchAdapterProvider.VIEW_TYPE_SEARCH_SMALL_ICON_ROW;
 import static com.android.launcher3.search.DeviceSearchAdapterProvider.VIEW_TYPE_SEARCH_SLICE;
 import static com.android.launcher3.search.DeviceSearchAdapterProvider.VIEW_TYPE_SEARCH_THUMBNAIL;
 import static com.android.launcher3.search.DeviceSearchAdapterProvider.VIEW_TYPE_SEARCH_WIDGET_LIVE;
@@ -27,7 +27,6 @@
 import android.app.search.SearchTarget;
 
 import com.android.launcher3.allapps.AllAppsGridAdapter;
-import com.android.systemui.plugins.shared.SearchTargetLegacy;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -36,30 +35,23 @@
  * Extension of AdapterItem that contains an extra payload specific to item
  */
 public class SearchAdapterItem extends AllAppsGridAdapter.AdapterItem {
-    private SearchTargetLegacy mSearchTargetLegacy;
     private SearchTarget mSearchTarget;
     private List<SearchTarget> mInlineItems = new ArrayList<>();
 
 
-    private static final int AVAILABLE_FOR_ACCESSIBILITY =
-            VIEW_TYPE_SEARCH_SLICE | VIEW_TYPE_SEARCH_PEOPLE | VIEW_TYPE_SEARCH_THUMBNAIL
-                    | VIEW_TYPE_SEARCH_ICON_ROW | VIEW_TYPE_SEARCH_ICON
-                    | VIEW_TYPE_SEARCH_WIDGET_PREVIEW | VIEW_TYPE_SEARCH_WIDGET_LIVE;
-
-    public SearchAdapterItem(SearchTargetLegacy searchTargetLegacy, int type) {
-        mSearchTargetLegacy = searchTargetLegacy;
-        viewType = type;
-    }
+    private static final int AVAILABLE_FOR_ACCESSIBILITY = VIEW_TYPE_SEARCH_SLICE
+            | VIEW_TYPE_SEARCH_THUMBNAIL
+            | VIEW_TYPE_SEARCH_ICON_ROW
+            | VIEW_TYPE_SEARCH_ICON
+            | VIEW_TYPE_SEARCH_SMALL_ICON_ROW
+            | VIEW_TYPE_SEARCH_WIDGET_PREVIEW
+            | VIEW_TYPE_SEARCH_WIDGET_LIVE;
 
     public SearchAdapterItem(SearchTarget searchTarget, int type) {
         mSearchTarget = searchTarget;
         viewType = type;
     }
 
-    public SearchTargetLegacy getSearchTargetLegacy() {
-        return mSearchTargetLegacy;
-    }
-
     public SearchTarget getSearchTarget() {
         return mSearchTarget;
     }
diff --git a/quickstep/src/com/android/launcher3/search/SearchResultIcon.java b/quickstep/src/com/android/launcher3/search/SearchResultIcon.java
index 3d89ac5..f7d5f45 100644
--- a/quickstep/src/com/android/launcher3/search/SearchResultIcon.java
+++ b/quickstep/src/com/android/launcher3/search/SearchResultIcon.java
@@ -15,12 +15,9 @@
  */
 package com.android.launcher3.search;
 
-import static com.android.launcher3.model.data.SearchActionItemInfo.FLAG_BADGE_FROM_ICON;
+import static com.android.launcher3.model.data.SearchActionItemInfo.FLAG_BADGE_WITH_PACKAGE;
 import static com.android.launcher3.model.data.SearchActionItemInfo.FLAG_PRIMARY_ICON_FROM_TITLE;
-import static com.android.launcher3.search.SearchTargetUtil.BUNDLE_EXTRA_BADGE_FROM_ICON;
 import static com.android.launcher3.search.SearchTargetUtil.BUNDLE_EXTRA_PRIMARY_ICON_FROM_TITLE;
-import static com.android.launcher3.search.SearchTargetUtil.BUNDLE_EXTRA_SHOULD_START;
-import static com.android.launcher3.search.SearchTargetUtil.BUNDLE_EXTRA_SHOULD_START_FOR_RESULT;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
 
@@ -30,9 +27,14 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ShortcutInfo;
-import android.graphics.drawable.Drawable;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.RectF;
 import android.graphics.drawable.Icon;
-import android.os.Build;
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.util.AttributeSet;
@@ -45,7 +47,11 @@
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.allapps.AllAppsStore;
 import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.icons.BitmapRenderer;
 import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.logger.LauncherAtom.ContainerInfo;
+import com.android.launcher3.logger.LauncherAtomExtensions.DeviceSearchResultContainer;
+import com.android.launcher3.logger.LauncherAtomExtensions.ExtendedContainers;
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.model.data.PackageItemInfo;
@@ -54,14 +60,25 @@
 import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.touch.ItemLongClickListener;
 import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.Themes;
 
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLConnection;
 import java.util.List;
 import java.util.function.Consumer;
 
 /**
  * A {@link BubbleTextView} representing a single cell result in AllApps
  */
-public class SearchResultIcon extends BubbleTextView implements SearchTargetHandler {
+public class SearchResultIcon extends BubbleTextView implements
+        SearchTargetHandler, View.OnClickListener,
+        View.OnLongClickListener {
+
+    //Play store thumbnail process workaround
+    private final Rect mTempRect = new Rect();
+    private final Paint mIconPaint = new Paint();
+    private static final int BITMAP_CROP_MASK_COLOR = 0xff424242;
 
     private final Launcher mLauncher;
 
@@ -99,7 +116,7 @@
      * Applies {@link SearchTarget} to view. registers a consumer after a corresponding
      * {@link ItemInfoWithIcon} is created
      */
-    public void applySearchTarget(SearchTarget searchTarget, List<SearchTarget> inlineItems,
+    public void apply(SearchTarget searchTarget, List<SearchTarget> inlineItems,
             Consumer<ItemInfoWithIcon> cb) {
         mOnItemInfoChanged = cb;
         apply(searchTarget, inlineItems);
@@ -115,8 +132,8 @@
             prepareUsingSearchAction(parentTarget);
             mLongPressSupported = false;
         } else {
-            prepareUsingApp(new ComponentName(parentTarget.getPackageName(),
-                    parentTarget.getExtras().getString(SearchTargetUtil.EXTRA_CLASS)),
+            String className = parentTarget.getExtras().getString(SearchTargetUtil.EXTRA_CLASS);
+            prepareUsingApp(new ComponentName(parentTarget.getPackageName(), className),
                     parentTarget.getUserHandle());
             mLongPressSupported = true;
         }
@@ -128,22 +145,31 @@
 
         SearchActionItemInfo itemInfo = new SearchActionItemInfo(searchAction.getIcon(),
                 searchTarget.getPackageName(), searchTarget.getUserHandle(),
-                searchAction.getTitle()
-        );
+                searchAction.getTitle()) {
+            // Workaround to log ItemInfo with DeviceSearchResultContainer without
+            // updating ItemInfo.container field.
+            @Override
+            public ContainerInfo getContainerInfo() {
+                return buildDeviceSearchResultContainer();
+            }
+        };
         itemInfo.setIntent(searchAction.getIntent());
         itemInfo.setPendingIntent(searchAction.getPendingIntent());
 
         //TODO: remove this after flags are introduced in SearchAction. Settings results require
         // startActivityForResult
         boolean isSettingsResult = searchTarget.getResultType() == ResultType.SETTING;
-        if ((extras != null && extras.getBoolean(BUNDLE_EXTRA_SHOULD_START_FOR_RESULT))
+        if ((extras != null && extras.getBoolean(
+                SearchTargetUtil.BUNDLE_EXTRA_SHOULD_START_FOR_RESULT))
                 || isSettingsResult) {
             itemInfo.setFlags(SearchActionItemInfo.FLAG_SHOULD_START_FOR_RESULT);
-        } else if (extras != null && extras.getBoolean(BUNDLE_EXTRA_SHOULD_START)) {
+        } else if (extras != null && extras.getBoolean(
+                SearchTargetUtil.BUNDLE_EXTRA_SHOULD_START)) {
             itemInfo.setFlags(SearchActionItemInfo.FLAG_SHOULD_START);
         }
-        if (extras != null && extras.getBoolean(BUNDLE_EXTRA_BADGE_FROM_ICON)) {
-            itemInfo.setFlags(FLAG_BADGE_FROM_ICON);
+        if (extras != null && extras.getBoolean(
+                SearchTargetUtil.BUNDLE_EXTRA_BADGE_WITH_PACKAGE)) {
+            itemInfo.setFlags(FLAG_BADGE_WITH_PACKAGE);
         }
         if (extras != null && extras.getBoolean(BUNDLE_EXTRA_PRIMARY_ICON_FROM_TITLE)) {
             itemInfo.setFlags(FLAG_PRIMARY_ICON_FROM_TITLE);
@@ -154,42 +180,87 @@
         MODEL_EXECUTOR.post(() -> {
             try (LauncherIcons li = LauncherIcons.obtain(getContext())) {
                 Icon icon = searchTarget.getSearchAction().getIcon();
-                Drawable d;
-                // This bitmapInfo can be used as main icon or as a badge
-                BitmapInfo bitmapInfo;
-                if (icon == null) {
-                    PackageItemInfo pkgInfo = new PackageItemInfo(searchTarget.getPackageName());
-                    pkgInfo.user = searchTarget.getUserHandle();
-                    appState.getIconCache().getTitleAndIconForApp(pkgInfo, false);
-                    bitmapInfo = pkgInfo.bitmap;
-                } else {
-                    d = itemInfo.getIcon().loadDrawable(getContext());
-                    bitmapInfo = li.createBadgedIconBitmap(d, itemInfo.user,
-                            Build.VERSION.SDK_INT);
-                }
-
-                BitmapInfo bitmapMainIcon;
+                BitmapInfo pkgBitmap = getPackageBitmap(appState, searchTarget);
                 if (itemInfo.hasFlags(FLAG_PRIMARY_ICON_FROM_TITLE)) {
-                    bitmapMainIcon = li.createIconBitmap(
-                            String.valueOf(itemInfo.title.charAt(0)),
-                            bitmapInfo.color);
+                    // create a bitmap with first char if FLAG_PRIMARY_ICON_FROM_TITLE is set
+                    itemInfo.bitmap = li.createIconBitmap(String.valueOf(itemInfo.title.charAt(0)),
+                            pkgBitmap.color);
+                } else if (icon == null) {
+                    // Use default icon from package name
+                    itemInfo.bitmap = pkgBitmap;
                 } else {
-                    bitmapMainIcon = bitmapInfo;
-                }
-                if (itemInfo.hasFlags(FLAG_BADGE_FROM_ICON)) {
-                    itemInfo.bitmap = li.badgeBitmap(bitmapMainIcon.icon, bitmapInfo);
-                } else {
-                    itemInfo.bitmap = bitmapInfo;
+                    boolean isPlayResult = searchTarget.getResultType() == ResultType.PLAY;
+                    if (isPlayResult) {
+                        Bitmap b = getPlayResultBitmap(searchAction.getIcon());
+                        itemInfo.bitmap = b == null
+                                ? BitmapInfo.LOW_RES_INFO : BitmapInfo.fromBitmap(b);
+                    } else {
+                        itemInfo.bitmap = li.createBadgedIconBitmap(icon.loadDrawable(getContext()),
+                                itemInfo.user, false);
+                    }
                 }
 
+                // badge with package name
+                if (itemInfo.hasFlags(FLAG_BADGE_WITH_PACKAGE) && itemInfo.bitmap != pkgBitmap) {
+                    itemInfo.bitmap = li.badgeBitmap(itemInfo.bitmap.icon, pkgBitmap);
+                }
             }
             MAIN_EXECUTOR.post(() -> applyFromSearchActionItemInfo(itemInfo));
         });
     }
 
+    private static BitmapInfo getPackageBitmap(LauncherAppState appState, SearchTarget target) {
+        PackageItemInfo pkgInfo = new PackageItemInfo(target.getPackageName());
+        pkgInfo.user = target.getUserHandle();
+        appState.getIconCache().getTitleAndIconForApp(pkgInfo, false);
+        return pkgInfo.bitmap;
+    }
+
+    private Bitmap getPlayResultBitmap(Icon icon) {
+        try {
+            int iconSize = getIconSize();
+            URL url = new URL(icon.getUri().toString());
+            URLConnection con = url.openConnection();
+            con.addRequestProperty("Cache-Control", "max-age: 0");
+            con.setUseCaches(true);
+            Bitmap bitmap = BitmapFactory.decodeStream(con.getInputStream());
+            return getRoundedBitmap(Bitmap.createScaledBitmap(bitmap, iconSize, iconSize, false));
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    private Bitmap getRoundedBitmap(Bitmap bitmap) {
+        final int iconSize = bitmap.getWidth();
+        final float radius = Themes.getDialogCornerRadius(getContext());
+
+        return BitmapRenderer.createHardwareBitmap(iconSize, iconSize, (canvas) -> {
+            mTempRect.set(0, 0, iconSize, iconSize);
+            final RectF rectF = new RectF(mTempRect);
+
+            mIconPaint.setAntiAlias(true);
+            mIconPaint.reset();
+            canvas.drawARGB(0, 0, 0, 0);
+            mIconPaint.setColor(BITMAP_CROP_MASK_COLOR);
+            canvas.drawRoundRect(rectF, radius, radius, mIconPaint);
+
+            mIconPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
+            canvas.drawBitmap(bitmap, mTempRect, mTempRect, mIconPaint);
+        });
+    }
+
     private void prepareUsingApp(ComponentName componentName, UserHandle userHandle) {
         AllAppsStore appsStore = mLauncher.getAppsView().getAppsStore();
-        AppInfo appInfo = appsStore.getApp(new ComponentKey(componentName, userHandle));
+        AppInfo appInfo = new AppInfo(
+                appsStore.getApp(new ComponentKey(componentName, userHandle))) {
+            // Workaround to log ItemInfo with DeviceSearchResultContainer without
+            // updating ItemInfo.container field.
+            @Override
+            public ContainerInfo getContainerInfo() {
+                return buildDeviceSearchResultContainer();
+            }
+        };
 
         if (appInfo == null) {
             setVisibility(GONE);
@@ -199,9 +270,15 @@
         notifyItemInfoChanged(appInfo);
     }
 
-
     private void prepareUsingShortcutInfo(ShortcutInfo shortcutInfo) {
-        WorkspaceItemInfo workspaceItemInfo = new WorkspaceItemInfo(shortcutInfo, getContext());
+        WorkspaceItemInfo workspaceItemInfo = new WorkspaceItemInfo(shortcutInfo, getContext()) {
+            // Workaround to log ItemInfo with DeviceSearchResultContainer without
+            // updating ItemInfo.container field.
+            @Override
+            public ContainerInfo getContainerInfo() {
+                return buildDeviceSearchResultContainer();
+            }
+        };
         notifyItemInfoChanged(workspaceItemInfo);
         LauncherAppState launcherAppState = LauncherAppState.getInstance(getContext());
         MODEL_EXECUTOR.execute(() -> {
@@ -233,11 +310,20 @@
     }
 
 
-
     private void notifyItemInfoChanged(ItemInfoWithIcon itemInfoWithIcon) {
         if (mOnItemInfoChanged != null) {
             mOnItemInfoChanged.accept(itemInfoWithIcon);
             mOnItemInfoChanged = null;
         }
     }
+
+    private static ContainerInfo buildDeviceSearchResultContainer() {
+        return ContainerInfo.newBuilder().setExtendedContainers(
+                ExtendedContainers
+                        .newBuilder()
+                        .setDeviceSearchResultContainer(
+                                DeviceSearchResultContainer
+                                        .newBuilder()))
+                .build();
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/search/SearchResultIconRow.java b/quickstep/src/com/android/launcher3/search/SearchResultIconRow.java
index eafbc73..12a1a1c 100644
--- a/quickstep/src/com/android/launcher3/search/SearchResultIconRow.java
+++ b/quickstep/src/com/android/launcher3/search/SearchResultIconRow.java
@@ -23,7 +23,6 @@
 import android.content.pm.ShortcutInfo;
 import android.text.TextUtils;
 import android.util.AttributeSet;
-import android.view.View;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
@@ -44,15 +43,15 @@
  */
 public class SearchResultIconRow extends LinearLayout implements SearchTargetHandler {
 
-    public static final int MAX_INLINE_ITEMS = 2;
+    public static final int MAX_INLINE_ITEMS = 3;
 
     protected final Launcher mLauncher;
-    private final LauncherAppState mLauncherAppState;
+    protected final SearchResultIcon[] mInlineIcons = new SearchResultIcon[MAX_INLINE_ITEMS];
     private SearchResultIcon mResultIcon;
 
+    private final LauncherAppState mLauncherAppState;
     private TextView mTitleView;
     private TextView mSubTitleView;
-    private final SearchResultIcon[] mInlineIcons = new SearchResultIcon[MAX_INLINE_ITEMS];
 
     private PackageItemInfo mProviderInfo;
 
@@ -78,32 +77,34 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-
         int iconSize = getIconSize();
 
         mResultIcon = findViewById(R.id.icon);
+
         mTitleView = findViewById(R.id.title);
         mSubTitleView = findViewById(R.id.subtitle);
         mSubTitleView.setVisibility(GONE);
+
         mResultIcon.getLayoutParams().height = iconSize;
         mResultIcon.getLayoutParams().width = iconSize;
         mResultIcon.setTextVisibility(false);
 
         mInlineIcons[0] = findViewById(R.id.shortcut_0);
         mInlineIcons[1] = findViewById(R.id.shortcut_1);
+        mInlineIcons[2] = findViewById(R.id.shortcut_2);
         for (SearchResultIcon inlineIcon : mInlineIcons) {
             inlineIcon.getLayoutParams().width = getIconSize();
-            // TODO: inlineIcon.setOnClickListener();
         }
-
         setOnClickListener(mResultIcon);
         setOnLongClickListener(mResultIcon);
     }
 
     @Override
     public void apply(SearchTarget parentTarget, List<SearchTarget> children) {
+        mResultIcon.apply(parentTarget, children, this::onItemInfoCreated);
+
         showSubtitleIfNeeded(null);
-        mResultIcon.applySearchTarget(parentTarget, children, this::onItemInfoCreated);
+
         if (parentTarget.getShortcutInfo() != null) {
             updateWithShortcutInfo(parentTarget.getShortcutInfo());
         } else if (parentTarget.getSearchAction() != null) {
@@ -142,7 +143,7 @@
     protected void showInlineItems(List<SearchTarget> children) {
         for (int i = 0; i < MAX_INLINE_ITEMS; i++) {
             if (i < children.size()) {
-                mInlineIcons[i].apply(children.get(0), new ArrayList<>());
+                mInlineIcons[i].apply(children.get(i), new ArrayList<>());
                 mInlineIcons[i].setVisibility(VISIBLE);
             } else {
                 mInlineIcons[i].setVisibility(GONE);
@@ -154,15 +155,4 @@
         setTag(info);
         mTitleView.setText(info.title);
     }
-
-    @Override
-    public void onClick(View view) {
-        // do nothing.
-    }
-
-    @Override
-    public boolean onLongClick(View view) {
-        // do nothing.
-        return false;
-    }
 }
diff --git a/quickstep/src/com/android/launcher3/search/SearchResultIconSlice.java b/quickstep/src/com/android/launcher3/search/SearchResultIconSlice.java
index f6e5eba..4bf3432 100644
--- a/quickstep/src/com/android/launcher3/search/SearchResultIconSlice.java
+++ b/quickstep/src/com/android/launcher3/search/SearchResultIconSlice.java
@@ -15,28 +15,22 @@
  */
 package com.android.launcher3.search;
 
-import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
-
 import android.app.search.SearchTarget;
 import android.app.search.SearchTargetEvent;
 import android.content.Context;
 import android.util.AttributeSet;
-import android.util.Log;
-import android.view.View;
 import android.widget.LinearLayout;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import androidx.lifecycle.LiveData;
-import androidx.slice.Slice;
 import androidx.slice.SliceItem;
 import androidx.slice.widget.EventInfo;
 import androidx.slice.widget.SliceView;
 
 import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
 import com.android.launcher3.model.data.PackageItemInfo;
+import com.android.launcher3.util.SafeCloseable;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -47,13 +41,11 @@
 public class SearchResultIconSlice extends LinearLayout implements SearchTargetHandler,
         SliceView.OnSliceActionListener {
 
-    private static final String TAG = "SearchSliceController";
-
     private final Launcher mLauncher;
 
     private SliceView mSliceView;
     private SearchResultIcon mIcon;
-    private LiveData<Slice> mSliceLiveData;
+    private SafeCloseable mSliceSession;
     private String mTargetId;
 
     public SearchResultIconSlice(Context context) {
@@ -87,26 +79,20 @@
         mTargetId = parentTarget.getId();
         reset();
         updateIcon(parentTarget, children);
-        try {
-            mSliceLiveData = mLauncher.getLiveSearchManager().getSliceForUri(
-                    parentTarget.getSliceUri());
-            mSliceLiveData.observe(mLauncher, mSliceView);
-        } catch (Exception ex) {
-            Log.e(TAG, "unable to bind slice", ex);
-        }
+        mSliceSession = mLauncher.getLiveSearchManager()
+                .addObserver(parentTarget.getSliceUri(), mSliceView);
     }
 
     private void updateIcon(SearchTarget parentTarget, List<SearchTarget> children) {
         if (children.size() == 1) {
             mIcon.apply(children.get(0), new ArrayList<>());
         } else {
-            LauncherAppState appState = LauncherAppState.getInstance(getContext());
-            MODEL_EXECUTOR.post(() -> {
-                PackageItemInfo pkgItem = new PackageItemInfo(parentTarget.getPackageName());
-                pkgItem.user = parentTarget.getUserHandle();
-                appState.getIconCache().getTitleAndIconForApp(pkgItem, false);
+            PackageItemInfo pkgItem = new PackageItemInfo(parentTarget.getPackageName());
+            pkgItem.user = parentTarget.getUserHandle();
+            if (!pkgItem.equals(mIcon.getTag())) {
+                // The icon will load and apply high res icon automatically
                 mIcon.applyFromItemInfoWithIcon(pkgItem);
-            });
+            }
         }
     }
 
@@ -124,8 +110,8 @@
 
     private void reset() {
         mSliceView.setOnSliceActionListener(null);
-        if (mSliceLiveData != null) {
-            mSliceLiveData.removeObservers(mLauncher);
+        if (mSliceSession != null) {
+            mSliceSession.close();
         }
     }
 
@@ -133,15 +119,4 @@
     public void onSliceAction(@NonNull EventInfo eventInfo, @NonNull SliceItem sliceItem) {
         notifyEvent(mLauncher, mTargetId, SearchTargetEvent.ACTION_TAP);
     }
-
-    @Override
-    public void onClick(View view) {
-        notifyEvent(mLauncher, mTargetId, SearchTargetEvent.ACTION_LONGPRESS);
-    }
-
-    @Override
-    public boolean onLongClick(View view) {
-        // do nothing
-        return false;
-    }
 }
diff --git a/quickstep/src/com/android/launcher3/search/SearchResultPeopleView.java b/quickstep/src/com/android/launcher3/search/SearchResultPeopleView.java
deleted file mode 100644
index 99e6aa5..0000000
--- a/quickstep/src/com/android/launcher3/search/SearchResultPeopleView.java
+++ /dev/null
@@ -1,199 +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.launcher3.search;
-
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-
-import android.app.search.SearchTargetEvent;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Process;
-import android.util.AttributeSet;
-import android.view.View;
-import android.widget.ImageButton;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import androidx.annotation.Nullable;
-import androidx.core.graphics.drawable.RoundedBitmapDrawable;
-import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
-
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.R;
-import com.android.launcher3.icons.BitmapInfo;
-import com.android.launcher3.icons.LauncherIcons;
-import com.android.systemui.plugins.shared.SearchTargetLegacy;
-
-import java.util.ArrayList;
-
-/**
- * A view representing a single people search result in all apps
- */
-public class SearchResultPeopleView extends LinearLayout implements SearchTargetHandler {
-
-    public static final String TARGET_TYPE_PEOPLE = "people";
-
-    private final int mIconSize;
-    private final int mButtonSize;
-    private final PackageManager mPackageManager;
-    private View mIconView;
-    private TextView mTitleView;
-    private ImageButton[] mProviderButtons = new ImageButton[3];
-    private Intent mIntent;
-
-    private SearchTargetLegacy mSearchTarget;
-
-    public SearchResultPeopleView(Context context) {
-        this(context, null, 0);
-    }
-
-    public SearchResultPeopleView(Context context,
-            @Nullable AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public SearchResultPeopleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-        DeviceProfile deviceProfile = Launcher.getLauncher(getContext()).getDeviceProfile();
-        mPackageManager = getContext().getPackageManager();
-        mIconSize = deviceProfile.iconSizePx;
-        mButtonSize = (int) (deviceProfile.iconSizePx / 1.5f);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mIconView = findViewById(R.id.icon);
-        mIconView.getLayoutParams().height = mIconSize;
-        mIconView.getLayoutParams().width = mIconSize;
-        mTitleView = findViewById(R.id.title);
-        mProviderButtons[0] = findViewById(R.id.provider_0);
-        mProviderButtons[1] = findViewById(R.id.provider_1);
-        mProviderButtons[2] = findViewById(R.id.provider_2);
-        for (ImageButton button : mProviderButtons) {
-            button.getLayoutParams().width = mButtonSize;
-            button.getLayoutParams().height = mButtonSize;
-        }
-    }
-
-    /*
-    @Override
-    public void applySearchTarget(SearchTargetLegacy searchTarget) {
-        mSearchTarget = searchTarget;
-        Bundle payload = searchTarget.getExtras();
-        mTitleView.setText(payload.getString("title"));
-        mIntent = payload.getParcelable("intent");
-        Bitmap contactIcon = payload.getParcelable("icon");
-        try (LauncherIcons li = LauncherIcons.obtain(getContext())) {
-            BitmapInfo badgeInfo = li.createBadgedIconBitmap(
-                    getAppIcon(mIntent.getPackage()), Process.myUserHandle(),
-                    Build.VERSION.SDK_INT);
-            setIcon(li.badgeBitmap(roundBitmap(contactIcon), badgeInfo).icon, false);
-        } catch (Exception e) {
-            setIcon(contactIcon, true);
-        }
-
-        ArrayList<Bundle> providers = payload.getParcelableArrayList("providers");
-        for (int i = 0; i < mProviderButtons.length; i++) {
-            ImageButton button = mProviderButtons[i];
-            if (providers != null && i < providers.size()) {
-                Bundle provider = providers.get(i);
-                Intent intent = provider.getParcelable("intent");
-                setupProviderButton(button, provider, intent);
-                UI_HELPER_EXECUTOR.post(() -> {
-                    String pkg = provider.getString("package_name");
-                    Drawable appIcon = getAppIcon(pkg);
-                    if (appIcon != null) {
-                        MAIN_EXECUTOR.post(() -> button.setImageDrawable(appIcon));
-                    }
-                });
-                button.setVisibility(VISIBLE);
-            } else {
-                button.setVisibility(GONE);
-            }
-        }
-    }
-    */
-
-    /**
-     * Normalizes the bitmap to look like rounded App Icon
-     * TODO(b/170234747) to support styling, generate adaptive icon drawable and generate
-     * bitmap from it.
-     */
-    private Bitmap roundBitmap(Bitmap icon) {
-        final RoundedBitmapDrawable d = RoundedBitmapDrawableFactory.create(getResources(), icon);
-        d.setCornerRadius(R.attr.folderIconRadius);
-        d.setBounds(0, 0, mIconSize, mIconSize);
-        final Bitmap bitmap = Bitmap.createBitmap(d.getBounds().width(), d.getBounds().height(),
-                Bitmap.Config.ARGB_8888);
-        Canvas canvas = new Canvas(bitmap);
-        d.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
-        d.draw(canvas);
-        return bitmap;
-    }
-
-    private void setIcon(Bitmap icon, Boolean round) {
-        if (round) {
-            RoundedBitmapDrawable d = RoundedBitmapDrawableFactory.create(getResources(), icon);
-            d.setCornerRadius(R.attr.folderIconRadius);
-            d.setBounds(0, 0, mIconSize, mIconSize);
-            mIconView.setBackground(d);
-        } else {
-            mIconView.setBackground(new BitmapDrawable(getResources(), icon));
-        }
-    }
-
-
-    private Drawable getAppIcon(String pkg) {
-        try {
-            ApplicationInfo applicationInfo = mPackageManager.getApplicationInfo(
-                    pkg, 0);
-            return applicationInfo.loadIcon(mPackageManager);
-        } catch (PackageManager.NameNotFoundException ignored) {
-            return null;
-        }
-    }
-
-    private void setupProviderButton(ImageButton button, Bundle provider, Intent intent) {
-        Launcher launcher = Launcher.getLauncher(getContext());
-        button.setOnClickListener(b -> {
-            launcher.startActivitySafely(b, intent, null);
-            Bundle bundle = new Bundle();
-            bundle.putBundle("provider", provider);
-        });
-    }
-
-    @Override
-    public void onClick(View view) {
-        // do nothing.
-    }
-
-    @Override
-    public boolean onLongClick(View view) {
-        // do nothing.
-        return false;
-    }
-}
diff --git a/quickstep/src/com/android/launcher3/search/SearchResultSmallIconRow.java b/quickstep/src/com/android/launcher3/search/SearchResultSmallIconRow.java
new file mode 100644
index 0000000..ca8aa81
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/search/SearchResultSmallIconRow.java
@@ -0,0 +1,142 @@
+/*
+ * 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.launcher3.search;
+
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+
+import android.app.search.SearchTarget;
+import android.content.Context;
+import android.content.pm.ShortcutInfo;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.R;
+import com.android.launcher3.model.data.ItemInfoWithIcon;
+import com.android.launcher3.model.data.PackageItemInfo;
+
+import java.util.List;
+
+/**
+ * A full width representation of {@link SearchResultIcon} with a secondary label and inline
+ * SearchTargets
+ */
+public class SearchResultSmallIconRow extends LinearLayout implements SearchTargetHandler {
+
+    protected final Launcher mLauncher;
+    private final LauncherAppState mLauncherAppState;
+    protected SearchResultIcon mResultIcon;
+
+    private TextView mTitleView;
+    private TextView mDelimeterView;
+    private TextView mSubTitleView;
+
+    private PackageItemInfo mProviderInfo;
+
+    public SearchResultSmallIconRow(Context context) {
+        this(context, null, 0);
+    }
+
+    public SearchResultSmallIconRow(Context context,
+            @Nullable AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public SearchResultSmallIconRow(Context context,
+            @Nullable AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+        mLauncher = Launcher.getLauncher(getContext());
+        mLauncherAppState = LauncherAppState.getInstance(getContext());
+    }
+
+    protected int getIconSize() {
+        return mLauncher.getDeviceProfile().allAppsIconSizePx;
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        int iconSize = getIconSize();
+
+        mResultIcon = findViewById(R.id.icon);
+
+        mTitleView = findViewById(R.id.title);
+        mDelimeterView = findViewById(R.id.delimeter);
+        mDelimeterView.setVisibility(GONE);
+        mSubTitleView = findViewById(R.id.subtitle);
+        mSubTitleView.setVisibility(GONE);
+
+        mResultIcon.getLayoutParams().height = iconSize;
+        mResultIcon.getLayoutParams().width = iconSize;
+        mResultIcon.setTextVisibility(false);
+
+        setOnClickListener(mResultIcon);
+        setOnLongClickListener(mResultIcon);
+    }
+
+    @Override
+    public void apply(SearchTarget parentTarget, List<SearchTarget> children) {
+        mResultIcon.apply(parentTarget, children, this::onItemInfoCreated);
+
+        showSubtitleIfNeeded(null);
+
+        if (parentTarget.getShortcutInfo() != null) {
+            updateWithShortcutInfo(parentTarget.getShortcutInfo());
+        } else if (parentTarget.getSearchAction() != null) {
+            showSubtitleIfNeeded(parentTarget.getSearchAction().getSubtitle());
+        }
+    }
+
+    @Override
+    public boolean quickSelect() {
+        this.performClick();
+        return true;
+    }
+
+    private void updateWithShortcutInfo(ShortcutInfo shortcutInfo) {
+        PackageItemInfo packageItemInfo = new PackageItemInfo(shortcutInfo.getPackage());
+        if (packageItemInfo.equals(mProviderInfo)) return;
+        MODEL_EXECUTOR.post(() -> {
+            mLauncherAppState.getIconCache().getTitleAndIconForApp(packageItemInfo, true);
+            MAIN_EXECUTOR.post(() -> {
+                showSubtitleIfNeeded(packageItemInfo.title);
+                mProviderInfo = packageItemInfo;
+            });
+        });
+    }
+
+    protected void showSubtitleIfNeeded(CharSequence subTitle) {
+        if (!TextUtils.isEmpty(subTitle)) {
+            mSubTitleView.setText(subTitle);
+            mSubTitleView.setVisibility(VISIBLE);
+            mDelimeterView.setVisibility(VISIBLE);
+
+        } else {
+            mSubTitleView.setVisibility(GONE);
+        }
+    }
+
+    protected void onItemInfoCreated(ItemInfoWithIcon info) {
+        setTag(info);
+        mTitleView.setText(info.title);
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/search/SearchResultThumbnailView.java b/quickstep/src/com/android/launcher3/search/SearchResultThumbnailView.java
index dd95461..8803c98 100644
--- a/quickstep/src/com/android/launcher3/search/SearchResultThumbnailView.java
+++ b/quickstep/src/com/android/launcher3/search/SearchResultThumbnailView.java
@@ -23,7 +23,6 @@
 import android.graphics.drawable.BitmapDrawable;
 import android.util.AttributeSet;
 import android.view.View;
-import android.view.ViewGroup;
 
 import androidx.core.graphics.drawable.RoundedBitmapDrawable;
 import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
@@ -39,10 +38,9 @@
  * A view representing a high confidence app search result that includes shortcuts
  */
 public class SearchResultThumbnailView extends androidx.appcompat.widget.AppCompatImageView
-        implements SearchTargetHandler {
+        implements SearchTargetHandler, View.OnClickListener {
 
     private SearchTarget mSearchTarget;
-    private SearchResultIcon mResultIcon;
 
     public SearchResultThumbnailView(Context context) {
         super(context);
@@ -61,7 +59,6 @@
         super.onFinishInflate();
         setOnFocusChangeListener(Launcher.getLauncher(getContext()).getFocusHandler());
         setOnClickListener(this);
-        setOnLongClickListener(this);
     }
 
     @Override
@@ -102,10 +99,4 @@
                 (SearchActionItemInfo) view.getTag());
         notifyEvent(getContext(), mSearchTarget.getId(), SearchTargetEvent.ACTION_LAUNCH_TOUCH);
     }
-
-    @Override
-    public boolean onLongClick(View view) {
-        // do nothing.
-        return false;
-    }
 }
diff --git a/quickstep/src/com/android/launcher3/search/SearchResultWidget.java b/quickstep/src/com/android/launcher3/search/SearchResultWidget.java
index da14959..e22f6ab 100644
--- a/quickstep/src/com/android/launcher3/search/SearchResultWidget.java
+++ b/quickstep/src/com/android/launcher3/search/SearchResultWidget.java
@@ -15,6 +15,9 @@
  */
 package com.android.launcher3.search;
 
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+
 import android.app.search.SearchTarget;
 import android.app.search.SearchTargetEvent;
 import android.appwidget.AppWidgetHostView;
@@ -36,16 +39,19 @@
 import com.android.launcher3.CheckLongPressHelper;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.allapps.search.SearchWidgetInfoContainer;
 import com.android.launcher3.dragndrop.DraggableView;
+import com.android.launcher3.icons.cache.HandlerRunnable;
 import com.android.launcher3.model.data.PackageItemInfo;
 import com.android.launcher3.touch.ItemLongClickListener;
 import com.android.launcher3.widget.PendingAddWidgetInfo;
 
 import java.util.List;
 
-
 /**
  * displays live version of a widget upon receiving {@link AppWidgetProviderInfo} from Search
  * provider
@@ -63,6 +69,7 @@
     private final float mScaleToFit;
 
     private SearchWidgetInfoContainer mInfoContainer;
+    private HandlerRunnable mLabelRequest;
     private BubbleTextView mWidgetProvider;
     private TextView mWidgetLabel;
 
@@ -124,11 +131,18 @@
     }
 
     private void showWidgetInfo(AppWidgetProviderInfo providerInfo) {
-        String title = providerInfo.loadLabel(mLauncher.getPackageManager());
         PackageItemInfo pinfo = new PackageItemInfo(providerInfo.provider.getPackageName());
         pinfo.user = providerInfo.getProfile();
         mWidgetProvider.applyFromItemInfoWithIcon(pinfo);
-        mWidgetLabel.setText(title);
+
+        mLabelRequest = new HandlerRunnable<>(
+                MODEL_EXECUTOR.getHandler(),
+                () -> LauncherAppState.getInstance(mLauncher).getIconCache()
+                        .getTitleNoCache(LauncherAppWidgetProviderInfo
+                                .fromProviderInfo(mLauncher, providerInfo)),
+                MAIN_EXECUTOR,
+                mWidgetLabel::setText);
+        Utilities.postAsyncCallback(MODEL_EXECUTOR.getHandler(), mLabelRequest);
     }
 
     /**
@@ -137,7 +151,18 @@
     public void removeListener() {
         if (mInfoContainer != null) {
             mInfoContainer.detachWidget(mHostView);
+            mInfoContainer = null;
         }
+        if (mLabelRequest != null) {
+            mLabelRequest.cancel();
+            mLabelRequest = null;
+        }
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        removeListener();
     }
 
     private void reportEvent(int eventType) {
@@ -186,10 +211,6 @@
         return false;
     }
 
-    @Override
-    public void onClick(View view) {
-        // do nothing.
-    }
 
     static class ClickListener extends GestureDetector.SimpleOnGestureListener {
         private final Runnable mCb;
diff --git a/quickstep/src/com/android/launcher3/search/SearchResultWidgetPreview.java b/quickstep/src/com/android/launcher3/search/SearchResultWidgetPreview.java
index 1786baf..5bf1908 100644
--- a/quickstep/src/com/android/launcher3/search/SearchResultWidgetPreview.java
+++ b/quickstep/src/com/android/launcher3/search/SearchResultWidgetPreview.java
@@ -47,7 +47,8 @@
 /**
  * displays preview of a widget upon receiving {@link AppWidgetProviderInfo} from Search provider
  */
-public class SearchResultWidgetPreview extends LinearLayout implements SearchTargetHandler {
+public class SearchResultWidgetPreview extends LinearLayout implements SearchTargetHandler,
+        View.OnClickListener, View.OnLongClickListener {
 
     private final Launcher mLauncher;
     private final LauncherAppState mAppState;
diff --git a/quickstep/src/com/android/launcher3/search/SearchSectionHeaderView.java b/quickstep/src/com/android/launcher3/search/SearchSectionHeaderView.java
index d7c5c5d..9276841 100644
--- a/quickstep/src/com/android/launcher3/search/SearchSectionHeaderView.java
+++ b/quickstep/src/com/android/launcher3/search/SearchSectionHeaderView.java
@@ -18,7 +18,6 @@
 import android.app.search.SearchTarget;
 import android.content.Context;
 import android.util.AttributeSet;
-import android.view.View;
 import android.widget.TextView;
 
 import androidx.annotation.Nullable;
@@ -48,15 +47,4 @@
         setText(parentTarget.getSearchAction().getTitle());
         setVisibility(VISIBLE);
     }
-
-    @Override
-    public void onClick(View view) {
-        // do nothing.
-    }
-
-    @Override
-    public boolean onLongClick(View view) {
-        // do nothing.
-        return false;
-    }
 }
diff --git a/quickstep/src/com/android/launcher3/search/SearchServicePipeline.java b/quickstep/src/com/android/launcher3/search/SearchServicePipeline.java
index 197aecb..fac6ba7 100644
--- a/quickstep/src/com/android/launcher3/search/SearchServicePipeline.java
+++ b/quickstep/src/com/android/launcher3/search/SearchServicePipeline.java
@@ -15,6 +15,8 @@
  */
 package com.android.launcher3.search;
 
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+
 import android.app.search.Query;
 import android.app.search.SearchContext;
 import android.app.search.SearchSession;
@@ -39,8 +41,6 @@
 import java.util.List;
 import java.util.function.Consumer;
 
-import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-
 /**
  * Search pipeline utilizing {@link android.app.search.SearchUiManager}
  */
diff --git a/quickstep/src/com/android/launcher3/search/SearchTargetHandler.java b/quickstep/src/com/android/launcher3/search/SearchTargetHandler.java
index 0e7b8f2..acf6f8a 100644
--- a/quickstep/src/com/android/launcher3/search/SearchTargetHandler.java
+++ b/quickstep/src/com/android/launcher3/search/SearchTargetHandler.java
@@ -19,15 +19,13 @@
 import android.app.search.SearchTarget;
 import android.app.search.SearchTargetEvent;
 import android.content.Context;
-import android.view.View;
 
 import java.util.List;
-import java.util.function.Consumer;
 
 /**
  * An interface for supporting dynamic search results
  */
-public interface SearchTargetHandler extends View.OnClickListener, View.OnLongClickListener {
+public interface SearchTargetHandler {
 
     /**
      * Update view using values from {@link SearchTarget}
diff --git a/quickstep/src/com/android/launcher3/search/SearchTargetUtil.java b/quickstep/src/com/android/launcher3/search/SearchTargetUtil.java
index 95f4f58..ede3b9d 100644
--- a/quickstep/src/com/android/launcher3/search/SearchTargetUtil.java
+++ b/quickstep/src/com/android/launcher3/search/SearchTargetUtil.java
@@ -13,8 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.android.launcher3.search;
 
+import static com.android.app.search.LayoutType.DIVIDER;
+import static com.android.app.search.LayoutType.ICON_HORIZONTAL_TEXT;
+import static com.android.app.search.LayoutType.SMALL_ICON_HORIZONTAL_TEXT;
+import static com.android.app.search.LayoutType.THUMBNAIL;
+import static com.android.app.search.ResultType.ACTION;
+import static com.android.app.search.ResultType.SCREENSHOT;
+import static com.android.app.search.ResultType.SUGGEST;
+
 import android.app.PendingIntent;
 import android.app.search.SearchAction;
 import android.app.search.SearchTarget;
@@ -31,18 +40,11 @@
 
 import com.android.app.search.ResultType;
 
-import static com.android.app.search.LayoutType.ICON_DOUBLE_HORIZONTAL_TEXT;
-import static com.android.app.search.LayoutType.ICON_SINGLE_HORIZONTAL_TEXT;
-import static com.android.app.search.LayoutType.THUMBNAIL;
-import static com.android.app.search.ResultType.ACTION;
-import static com.android.app.search.ResultType.SCREENSHOT;
-import static com.android.app.search.ResultType.SUGGEST;
-
 public class SearchTargetUtil {
 
     public static final String BUNDLE_EXTRA_SHOULD_START = "should_start";
     public static final String BUNDLE_EXTRA_SHOULD_START_FOR_RESULT = "should_start_for_result";
-    public static final String BUNDLE_EXTRA_BADGE_FROM_ICON = "badge_from_icon";
+    public static final String BUNDLE_EXTRA_BADGE_WITH_PACKAGE = "badge_with_package";
     public static final String BUNDLE_EXTRA_PRIMARY_ICON_FROM_TITLE = "primary_icon_from_title";
 
     public static final String EXTRA_CLASS = "class";
@@ -54,17 +56,17 @@
 
 
     /**
-     * Generate SearchTargetUtil for ICON_DOUBLE_HORIZONTAL_TEXT layout type.
+     * Generate SearchTargetUtil for ICON_HORIZONTAL_TEXT layout type.
      *
-       targets.add(SearchTargetUtil.generateIconDoubleHorizontalText_SearchAction(
-           mContext, "red", Color.RED));
-       targets.add(SearchTargetUtil.generateIconDoubleHorizontalText_SearchAction(
-           mContext, "yellow", Color.YELLOW));
+     * targets.add(SearchTargetUtil.generateIconDoubleHorizontalText_SearchAction(
+     * mContext, "red", Color.RED));
+     * targets.add(SearchTargetUtil.generateIconDoubleHorizontalText_SearchAction(
+     * mContext, "yellow", Color.YELLOW));
      */
-    public static SearchTarget generateIconDoubleHorizontalText_SearchAction(
+    public static SearchTarget generateIcoHorizontalText_usingSearchAction(
             Context context, String id, int color) {
         SearchTarget.Builder builder =
-                new SearchTarget.Builder(ACTION, ICON_DOUBLE_HORIZONTAL_TEXT, id)
+                new SearchTarget.Builder(ACTION, ICON_HORIZONTAL_TEXT, id)
                         .setPackageName(PACKAGE2) /* required */
                         .setUserHandle(USERHANDLE); /* required */
 
@@ -83,7 +85,7 @@
 
         Bundle b = new Bundle();
         b.putBoolean(BUNDLE_EXTRA_SHOULD_START_FOR_RESULT, true);
-        b.putBoolean(BUNDLE_EXTRA_BADGE_FROM_ICON, true);
+        b.putBoolean(BUNDLE_EXTRA_BADGE_WITH_PACKAGE, true);
         b.putBoolean(BUNDLE_EXTRA_PRIMARY_ICON_FROM_TITLE, true);
 
         builder.setSearchAction(new SearchAction.Builder(id, id + TITLE)
@@ -96,13 +98,13 @@
     }
 
     /**
-     *  Inside SearchServicePipeline, add following samples to test the search target.
+     * Inside SearchServicePipeline, add following samples to test the search target.
      *
      * targets.add(SearchTargetUtil.generateThumbnail_SearchAction("blue", Color.BLUE));
      * targets.add(SearchTargetUtil.generateThumbnail_SearchAction("red", Color.RED));
      * targets.add(SearchTargetUtil.generateThumbnail_SearchAction("green", Color.GREEN));
      */
-    public static SearchTarget generateThumbnail_SearchAction(String id, int color) {
+    public static SearchTarget generateThumbnail_usingSearchAction(String id, int color) {
         SearchTarget.Builder builder =
                 new SearchTarget.Builder(SCREENSHOT, THUMBNAIL, id)
                         .setPackageName(PACKAGE2) /* required */
@@ -130,17 +132,19 @@
     }
 
     /**
+     * Generate SearchTargetUtil for SMALL_ICON_HORIZONTAL_TEXT layout type.
      *
      * targets.add(SearchTargetUtil.generateIconHorizontalText_SearchAction(
-     *     mContext, "red", Color.RED));
+     * mContext, "red", Color.RED));
      * targets.add(SearchTargetUtil.generateIconHorizontalText_SearchAction(
-     *     mContext, "yellow", Color.YELLOW));
+     * mContext, "yellow", Color.YELLOW));
      */
-    public static SearchTarget generateIconHorizontalText_SearchAction(
+    public static SearchTarget generateSmallIconHorizontalText_usingSearchAction(
             Context context, String id, int color) {
-        String fallbackQuery = "How to make cookie";
+        String title = "Ask the assistant";
+        String fallbackQuery = "sourdough bread";
         SearchTarget.Builder builder =
-                new SearchTarget.Builder(SUGGEST, ICON_SINGLE_HORIZONTAL_TEXT, id)
+                new SearchTarget.Builder(SUGGEST, SMALL_ICON_HORIZONTAL_TEXT, id)
                         .setPackageName(PACKAGE2) /* required */
                         .setUserHandle(USERHANDLE); /* required */
 
@@ -160,7 +164,8 @@
         Bundle extra = new Bundle();
         extra.putBoolean(BUNDLE_EXTRA_SHOULD_START_FOR_RESULT, true);
 
-        SearchAction searchAction = new SearchAction.Builder(id, fallbackQuery)
+        SearchAction searchAction = new SearchAction.Builder(id, title)
+                .setSubtitle(fallbackQuery)
                 .setPendingIntent(pendingIntent3)
                 .setIcon(icon)
                 .setExtras(extra)
@@ -168,6 +173,14 @@
         return builder.setSearchAction(searchAction).build();
     }
 
+    public static SearchTarget generateDivider() {
+        SearchTarget.Builder builder =
+                new SearchTarget.Builder(SUGGEST, DIVIDER, "divider")
+                        .setPackageName("") /* required but not used*/
+                        .setUserHandle(USERHANDLE); /* required */
+        return builder.build();
+    }
+
 
     /**
      * Generate SearchTargetUtil for ICON_DOUBLE_HORIZONTAL_TEXT layout type.
@@ -175,7 +188,7 @@
     public static SearchTarget generateIconDoubleHorizontalText_ShortcutInfo(Context context) {
         String id = "23456";
         SearchTarget.Builder builder =
-                new SearchTarget.Builder(ResultType.SHORTCUT, ICON_DOUBLE_HORIZONTAL_TEXT, id)
+                new SearchTarget.Builder(ResultType.SHORTCUT, SMALL_ICON_HORIZONTAL_TEXT, id)
                         .setPackageName("com.google.android.gm") /* required */
                         .setUserHandle(UserHandle.CURRENT); /* required */
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarContainerView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarContainerView.java
index 0093e66..3b361c4 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarContainerView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarContainerView.java
@@ -15,6 +15,9 @@
  */
 package com.android.launcher3.taskbar;
 
+import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_FRAME;
+import static com.android.systemui.shared.system.ViewTreeObserverWrapper.InsetsInfo.TOUCHABLE_INSETS_REGION;
+
 import android.content.Context;
 import android.util.AttributeSet;
 import android.widget.FrameLayout;
@@ -22,10 +25,18 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import com.android.launcher3.anim.AlphaUpdateListener;
+import com.android.systemui.shared.system.ViewTreeObserverWrapper;
+
 /**
  * Top-level ViewGroup that hosts the TaskbarView as well as Views created by it such as Folder.
  */
 public class TaskbarContainerView extends FrameLayout {
+
+    // Initialized in init.
+    private TaskbarView mTaskbarView;
+    private ViewTreeObserverWrapper.OnComputeInsetsListener mTaskbarInsetsComputer;
+
     public TaskbarContainerView(@NonNull Context context) {
         this(context, null);
     }
@@ -43,4 +54,41 @@
             int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
     }
+
+    protected void init(TaskbarView taskbarView) {
+        mTaskbarView = taskbarView;
+        mTaskbarInsetsComputer = createTaskbarInsetsComputer();
+    }
+
+    private ViewTreeObserverWrapper.OnComputeInsetsListener createTaskbarInsetsComputer() {
+        return insetsInfo -> {
+            if (getAlpha() < AlphaUpdateListener.ALPHA_CUTOFF_THRESHOLD) {
+                // We're invisible, let touches pass through us.
+                insetsInfo.touchableRegion.setEmpty();
+                insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
+            } else {
+                 // We're visible again, accept touches anywhere in our bounds.
+                insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_FRAME);
+            }
+        };
+    }
+
+    protected void cleanup() {
+        ViewTreeObserverWrapper.removeOnComputeInsetsListener(mTaskbarInsetsComputer);
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+
+        ViewTreeObserverWrapper.addOnComputeInsetsListener(getViewTreeObserver(),
+                mTaskbarInsetsComputer);
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+
+        cleanup();
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
index 7be1b92..260428d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
@@ -22,15 +22,34 @@
 import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_BOTTOM_TAPPABLE_ELEMENT;
 import static com.android.systemui.shared.system.WindowManagerWrapper.ITYPE_EXTRA_NAVIGATION_BAR;
 
+import android.animation.Animator;
+import android.app.ActivityOptions;
+import android.content.ComponentName;
 import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
 import android.view.WindowManager;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.QuickstepAppTransitionManagerImpl;
 import com.android.launcher3.R;
+import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.states.StateAnimationConfig;
+import com.android.launcher3.touch.ItemClickHandler;
+import com.android.quickstep.AnimatedFloat;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.WindowManagerWrapper;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * Interfaces with Launcher/WindowManager/SystemUI to determine what to show in TaskbarView.
  */
@@ -44,31 +63,136 @@
     private final WindowManager mWindowManager;
     // Layout width and height of the Taskbar in the default state.
     private final Point mTaskbarSize;
+    private final TaskbarStateHandler mTaskbarStateHandler;
+    private final TaskbarVisibilityController mTaskbarVisibilityController;
+    private final TaskbarHotseatController mHotseatController;
+    private final TaskbarRecentsController mRecentsController;
+    private final TaskbarDragController mDragController;
 
+    // Initialized in init().
     private WindowManager.LayoutParams mWindowLayoutParams;
 
+    // Contains all loaded Tasks, not yet deduped from Hotseat items.
+    private List<Task> mLatestLoadedRecentTasks;
+    // Contains all loaded Hotseat items.
+    private ItemInfo[] mLatestLoadedHotseatItems;
+
     public TaskbarController(BaseQuickstepLauncher launcher,
             TaskbarContainerView taskbarContainerView) {
         mLauncher = launcher;
         mTaskbarContainerView = taskbarContainerView;
         mTaskbarView = mTaskbarContainerView.findViewById(R.id.taskbar_view);
+        mTaskbarView.setCallbacks(createTaskbarViewCallbacks());
         mWindowManager = mLauncher.getWindowManager();
         mTaskbarSize = new Point(MATCH_PARENT,
                 mLauncher.getResources().getDimensionPixelSize(R.dimen.taskbar_size));
+        mTaskbarStateHandler = mLauncher.getTaskbarStateHandler();
+        mTaskbarVisibilityController = new TaskbarVisibilityController(mLauncher,
+                createTaskbarVisibilityControllerCallbacks());
+        mHotseatController = new TaskbarHotseatController(mLauncher,
+                createTaskbarHotseatControllerCallbacks());
+        mRecentsController = new TaskbarRecentsController(mLauncher,
+                createTaskbarRecentsControllerCallbacks());
+        mDragController = new TaskbarDragController(mLauncher);
+    }
+
+    private TaskbarVisibilityControllerCallbacks createTaskbarVisibilityControllerCallbacks() {
+        return new TaskbarVisibilityControllerCallbacks() {
+            @Override
+            public void updateTaskbarBackgroundAlpha(float alpha) {
+                mTaskbarView.setBackgroundAlpha(alpha);
+            }
+
+            @Override
+            public void updateTaskbarVisibilityAlpha(float alpha) {
+                mTaskbarContainerView.setAlpha(alpha);
+            }
+        };
+    }
+
+    private TaskbarViewCallbacks createTaskbarViewCallbacks() {
+        return new TaskbarViewCallbacks() {
+            @Override
+            public View.OnClickListener getItemOnClickListener() {
+                return view -> {
+                    Object tag = view.getTag();
+                    if (tag instanceof Task) {
+                        Task task = (Task) tag;
+                        ActivityManagerWrapper.getInstance().startActivityFromRecents(task.key,
+                                ActivityOptions.makeBasic());
+                    } else {
+                        ItemClickHandler.INSTANCE.onClick(view);
+                    }
+                };
+            }
+
+            @Override
+            public View.OnLongClickListener getItemOnLongClickListener() {
+                return mDragController::startDragOnLongClick;
+            }
+        };
+    }
+
+    private TaskbarHotseatControllerCallbacks createTaskbarHotseatControllerCallbacks() {
+        return new TaskbarHotseatControllerCallbacks() {
+            @Override
+            public void updateHotseatItems(ItemInfo[] hotseatItemInfos) {
+                mTaskbarView.updateHotseatItems(hotseatItemInfos);
+                mLatestLoadedHotseatItems = hotseatItemInfos;
+                dedupeAndUpdateRecentItems();
+            }
+        };
+    }
+
+    private TaskbarRecentsControllerCallbacks createTaskbarRecentsControllerCallbacks() {
+        return new TaskbarRecentsControllerCallbacks() {
+            @Override
+            public void updateRecentItems(ArrayList<Task> recentTasks) {
+                mLatestLoadedRecentTasks = recentTasks;
+                dedupeAndUpdateRecentItems();
+            }
+
+            @Override
+            public void updateRecentTaskAtIndex(int taskIndex, Task task) {
+                mTaskbarView.updateRecentTaskAtIndex(taskIndex, task);
+            }
+        };
     }
 
     /**
      * Initializes the Taskbar, including adding it to the screen.
      */
     public void init() {
+        mTaskbarView.init(mHotseatController.getNumHotseatIcons(),
+                mRecentsController.getNumRecentIcons());
+        mTaskbarContainerView.init(mTaskbarView);
         addToWindowManager();
+        mTaskbarStateHandler.setTaskbarCallbacks(createTaskbarStateHandlerCallbacks());
+        mTaskbarVisibilityController.init();
+        mHotseatController.init();
+        mRecentsController.init();
+    }
+
+    private TaskbarStateHandlerCallbacks createTaskbarStateHandlerCallbacks() {
+        return new TaskbarStateHandlerCallbacks() {
+            @Override
+            public AnimatedFloat getAlphaTarget() {
+                return mTaskbarVisibilityController.getTaskbarVisibilityForLauncherState();
+            }
+        };
     }
 
     /**
      * Removes the Taskbar from the screen, and removes any obsolete listeners etc.
      */
     public void cleanup() {
+        mTaskbarView.cleanup();
+        mTaskbarContainerView.cleanup();
         removeFromWindowManager();
+        mTaskbarStateHandler.setTaskbarCallbacks(null);
+        mTaskbarVisibilityController.cleanup();
+        mHotseatController.cleanup();
+        mRecentsController.cleanup();
     }
 
     private void removeFromWindowManager() {
@@ -94,6 +218,7 @@
         mWindowLayoutParams.setFitInsetsTypes(0);
         mWindowLayoutParams.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
         mWindowLayoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+        mWindowLayoutParams.setSystemApplicationOverlay(true);
 
         WindowManagerWrapper wmWrapper = WindowManagerWrapper.getInstance();
         wmWrapper.setProvidesInsetsTypes(
@@ -108,4 +233,153 @@
 
         mWindowManager.addView(mTaskbarContainerView, mWindowLayoutParams);
     }
+
+    /**
+     * Should be called from onResume() and onPause(), and animates the Taskbar accordingly.
+     */
+    public void onLauncherResumedOrPaused(boolean isResumed) {
+        long duration = QuickstepAppTransitionManagerImpl.CONTENT_ALPHA_DURATION;
+        final Animator anim;
+        if (isResumed) {
+            anim = createAnimToLauncher(null, duration);
+        } else {
+            anim = createAnimToApp(duration);
+        }
+        anim.start();
+    }
+
+    /**
+     * Create Taskbar animation when going from an app to Launcher.
+     * @param toState If known, the state we will end up in when reaching Launcher.
+     */
+    public Animator createAnimToLauncher(@Nullable LauncherState toState, long duration) {
+        PendingAnimation anim = new PendingAnimation(duration);
+        anim.add(mTaskbarVisibilityController.createAnimToBackgroundAlpha(0, duration));
+        if (toState != null) {
+            mTaskbarStateHandler.setStateWithAnimation(toState, new StateAnimationConfig(), anim);
+        }
+        return anim.buildAnim();
+    }
+
+    private Animator createAnimToApp(long duration) {
+        return mTaskbarVisibilityController.createAnimToBackgroundAlpha(1, duration);
+    }
+
+    /**
+     * Should be called when the IME visibility changes, so we can hide/show Taskbar accordingly.
+     */
+    public void setIsImeVisible(boolean isImeVisible) {
+        mTaskbarVisibilityController.animateToVisibilityForIme(isImeVisible ? 0 : 1);
+    }
+
+    /**
+     * Should be called when one or more items in the Hotseat have changed.
+     */
+    public void onHotseatUpdated() {
+        mHotseatController.onHotseatUpdated();
+    }
+
+    /**
+     * @param ev MotionEvent in screen coordinates.
+     * @return Whether any Taskbar item could handle the given MotionEvent if given the chance.
+     */
+    public boolean isEventOverAnyTaskbarItem(MotionEvent ev) {
+        return mTaskbarView.isEventOverAnyItem(ev);
+    }
+
+    public boolean isDraggingItem() {
+        return mTaskbarView.isDraggingItem();
+    }
+
+    private void dedupeAndUpdateRecentItems() {
+        if (mLatestLoadedRecentTasks == null || mLatestLoadedHotseatItems == null) {
+            return;
+        }
+
+        final int numRecentIcons = mRecentsController.getNumRecentIcons();
+
+        // From most recent to least recently opened.
+        List<Task> dedupedTasksInDescendingOrder = new ArrayList<>();
+        for (int i = mLatestLoadedRecentTasks.size() - 1; i >= 0; i--) {
+            Task task = mLatestLoadedRecentTasks.get(i);
+            boolean isTaskInHotseat = false;
+            for (ItemInfo hotseatItem : mLatestLoadedHotseatItems) {
+                if (hotseatItem == null) {
+                    continue;
+                }
+                ComponentName hotseatActivity = hotseatItem.getTargetComponent();
+                if (hotseatActivity != null && task.key.sourceComponent.getPackageName()
+                        .equals(hotseatActivity.getPackageName())) {
+                    isTaskInHotseat = true;
+                    break;
+                }
+            }
+            if (!isTaskInHotseat) {
+                dedupedTasksInDescendingOrder.add(task);
+                if (dedupedTasksInDescendingOrder.size() == numRecentIcons) {
+                    break;
+                }
+            }
+        }
+
+        // TaskbarView expects an array of all the recent tasks to show, in the order to show them.
+        // So we create an array of the proper size, then fill it in such that the most recent items
+        // are at the end. If there aren't enough elements to fill the array, leave them null.
+        Task[] tasksArray = new Task[numRecentIcons];
+        for (int i = 0; i < tasksArray.length; i++) {
+            Task task = i >= dedupedTasksInDescendingOrder.size()
+                    ? null
+                    : dedupedTasksInDescendingOrder.get(i);
+            tasksArray[tasksArray.length - 1 - i] = task;
+        }
+
+        mTaskbarView.updateRecentTasks(tasksArray);
+        mRecentsController.loadIconsForTasks(tasksArray);
+    }
+
+    /**
+     * @return Whether the given View is in the same window as Taskbar.
+     */
+    public boolean isViewInTaskbar(View v) {
+        return mTaskbarContainerView.getWindowId().equals(v.getWindowId());
+    }
+
+    /**
+     * Contains methods that TaskbarStateHandler can call to interface with TaskbarController.
+     */
+    protected interface TaskbarStateHandlerCallbacks {
+        AnimatedFloat getAlphaTarget();
+    }
+
+    /**
+     * Contains methods that TaskbarVisibilityController can call to interface with
+     * TaskbarController.
+     */
+    protected interface TaskbarVisibilityControllerCallbacks {
+        void updateTaskbarBackgroundAlpha(float alpha);
+        void updateTaskbarVisibilityAlpha(float alpha);
+    }
+
+    /**
+     * Contains methods that TaskbarView can call to interface with TaskbarController.
+     */
+    protected interface TaskbarViewCallbacks {
+        View.OnClickListener getItemOnClickListener();
+        View.OnLongClickListener getItemOnLongClickListener();
+    }
+
+    /**
+     * Contains methods that TaskbarHotseatController can call to interface with TaskbarController.
+     */
+    protected interface TaskbarHotseatControllerCallbacks {
+        void updateHotseatItems(ItemInfo[] hotseatItemInfos);
+    }
+
+    /**
+     * Contains methods that TaskbarRecentsController can call to interface with TaskbarController.
+     */
+    protected interface TaskbarRecentsControllerCallbacks {
+        void updateRecentItems(ArrayList<Task> recentTasks);
+        void updateRecentTaskAtIndex(int taskIndex, Task task);
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
new file mode 100644
index 0000000..baec899
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
@@ -0,0 +1,144 @@
+/*
+ * 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.taskbar;
+
+import static android.view.View.INVISIBLE;
+import static android.view.View.VISIBLE;
+
+import android.content.ClipData;
+import android.content.ClipDescription;
+import android.content.Intent;
+import android.content.pm.LauncherApps;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Point;
+import android.os.UserHandle;
+import android.view.DragEvent;
+import android.view.View;
+
+import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.R;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.system.ClipDescriptionCompat;
+import com.android.systemui.shared.system.LauncherAppsCompat;
+
+/**
+ * Handles long click on Taskbar items to start a system drag and drop operation.
+ */
+public class TaskbarDragController {
+
+    private final BaseQuickstepLauncher mLauncher;
+    private final int mDragIconSize;
+
+    public TaskbarDragController(BaseQuickstepLauncher launcher) {
+        mLauncher = launcher;
+        Resources resources = mLauncher.getResources();
+        mDragIconSize = resources.getDimensionPixelSize(R.dimen.taskbar_icon_drag_icon_size);
+    }
+
+    /**
+     * Attempts to start a system drag and drop operation for the given View, using its tag to
+     * generate the ClipDescription and Intent.
+     * @return Whether {@link View#startDragAndDrop} started successfully.
+     */
+    protected boolean startDragOnLongClick(View view) {
+        if (!(view instanceof BubbleTextView)) {
+            return false;
+        }
+
+        BubbleTextView btv = (BubbleTextView) view;
+
+        View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view) {
+            @Override
+            public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint) {
+                shadowSize.set(mDragIconSize, mDragIconSize);
+                // TODO: should be based on last touch point on the icon.
+                shadowTouchPoint.set(shadowSize.x / 2, shadowSize.y / 2);
+            }
+
+            @Override
+            public void onDrawShadow(Canvas canvas) {
+                canvas.save();
+                float scale = (float) mDragIconSize / btv.getIconSize();
+                canvas.scale(scale, scale);
+                btv.getIcon().draw(canvas);
+                canvas.restore();
+            }
+        };
+
+        Object tag = view.getTag();
+        ClipDescription clipDescription = null;
+        Intent intent = null;
+        if (tag instanceof WorkspaceItemInfo) {
+            WorkspaceItemInfo item = (WorkspaceItemInfo) tag;
+            LauncherApps launcherApps = mLauncher.getSystemService(LauncherApps.class);
+            clipDescription = new ClipDescription(item.title,
+                    new String[] {
+                            item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
+                                    ? ClipDescriptionCompat.MIMETYPE_APPLICATION_SHORTCUT
+                                    : ClipDescriptionCompat.MIMETYPE_APPLICATION_ACTIVITY
+                    });
+            intent = new Intent();
+            if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+                intent.putExtra(Intent.EXTRA_PACKAGE_NAME, item.getIntent().getPackage());
+                intent.putExtra(Intent.EXTRA_SHORTCUT_ID, item.getDeepShortcutId());
+            } else {
+                intent.putExtra(ClipDescriptionCompat.EXTRA_PENDING_INTENT,
+                        LauncherAppsCompat.getMainActivityLaunchIntent(launcherApps,
+                                item.getIntent().getComponent(), null, item.user));
+            }
+            intent.putExtra(Intent.EXTRA_USER, item.user);
+        } else if (tag instanceof Task) {
+            Task task = (Task) tag;
+            clipDescription = new ClipDescription(task.titleDescription,
+                    new String[] {
+                            ClipDescriptionCompat.MIMETYPE_APPLICATION_TASK
+                    });
+            intent = new Intent();
+            intent.putExtra(ClipDescriptionCompat.EXTRA_TASK_ID, task.key.id);
+            intent.putExtra(Intent.EXTRA_USER, UserHandle.of(task.key.userId));
+        }
+
+        if (clipDescription != null && intent != null) {
+            ClipData clipData = new ClipData(clipDescription, new ClipData.Item(intent));
+            view.setOnDragListener(getDraggedViewDragListener());
+            return view.startDragAndDrop(clipData, shadowBuilder, null /* localState */,
+                    View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_OPAQUE);
+        }
+        return false;
+    }
+
+    /**
+     * Hide the original Taskbar item while it is being dragged.
+     */
+    private View.OnDragListener getDraggedViewDragListener() {
+        return (view, dragEvent) -> {
+            switch (dragEvent.getAction()) {
+                case DragEvent.ACTION_DRAG_STARTED:
+                    view.setVisibility(INVISIBLE);
+                    return true;
+                case DragEvent.ACTION_DRAG_ENDED:
+                    view.setVisibility(VISIBLE);
+                    view.setOnDragListener(null);
+                    return true;
+            }
+            return false;
+        };
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarHotseatController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarHotseatController.java
new file mode 100644
index 0000000..4dc051a
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarHotseatController.java
@@ -0,0 +1,90 @@
+/*
+ * 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.taskbar;
+
+import android.view.View;
+
+import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.CellLayout;
+import com.android.launcher3.DropTarget;
+import com.android.launcher3.Hotseat;
+import com.android.launcher3.ShortcutAndWidgetContainer;
+import com.android.launcher3.dragndrop.DragController;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.model.data.ItemInfo;
+
+/**
+ * Works with TaskbarController to update the TaskbarView's Hotseat items.
+ */
+public class TaskbarHotseatController {
+
+    private final BaseQuickstepLauncher mLauncher;
+    private final Hotseat mHotseat;
+    private final TaskbarController.TaskbarHotseatControllerCallbacks mTaskbarCallbacks;
+    private final int mNumHotseatIcons;
+
+    private final DragController.DragListener mDragListener = new DragController.DragListener() {
+        @Override
+        public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
+        }
+
+        @Override
+        public void onDragEnd() {
+            onHotseatUpdated();
+        }
+    };
+
+    public TaskbarHotseatController(BaseQuickstepLauncher launcher,
+            TaskbarController.TaskbarHotseatControllerCallbacks taskbarCallbacks) {
+        mLauncher = launcher;
+        mHotseat = mLauncher.getHotseat();
+        mTaskbarCallbacks = taskbarCallbacks;
+        mNumHotseatIcons = mLauncher.getDeviceProfile().inv.numHotseatIcons;
+    }
+
+    protected void init() {
+        mLauncher.getDragController().addDragListener(mDragListener);
+    }
+
+    protected void cleanup() {
+        mLauncher.getDragController().removeDragListener(mDragListener);
+    }
+
+    /**
+     * Called when any Hotseat item changes, and reports the new list of items to TaskbarController.
+     */
+    protected void onHotseatUpdated() {
+        ShortcutAndWidgetContainer shortcutsAndWidgets = mHotseat.getShortcutsAndWidgets();
+        ItemInfo[] hotseatItemInfos = new ItemInfo[mNumHotseatIcons];
+        for (int i = 0; i < shortcutsAndWidgets.getChildCount(); i++) {
+            View child = shortcutsAndWidgets.getChildAt(i);
+            Object tag = shortcutsAndWidgets.getChildAt(i).getTag();
+            if (tag instanceof ItemInfo) {
+                ItemInfo itemInfo = (ItemInfo) tag;
+                CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
+                // Since the hotseat might be laid out vertically or horizontally, use whichever
+                // index is higher.
+                hotseatItemInfos[Math.max(lp.cellX, lp.cellY)] = itemInfo;
+            }
+        }
+
+        mTaskbarCallbacks.updateHotseatItems(hotseatItemInfos);
+    }
+
+    protected int getNumHotseatIcons() {
+        return mNumHotseatIcons;
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentsController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentsController.java
new file mode 100644
index 0000000..9d4e000
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarRecentsController.java
@@ -0,0 +1,114 @@
+/*
+ * 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.taskbar;
+
+import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.quickstep.RecentsModel;
+import com.android.quickstep.util.CancellableTask;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
+
+import java.util.ArrayList;
+
+/**
+ * Works with TaskbarController to update the TaskbarView's Recent items.
+ */
+public class TaskbarRecentsController {
+
+    private final int mNumRecentIcons = 2;
+    private final BaseQuickstepLauncher mLauncher;
+    private final TaskbarController.TaskbarRecentsControllerCallbacks mTaskbarCallbacks;
+    private final RecentsModel mRecentsModel;
+
+    private final TaskStackChangeListener mTaskStackChangeListener = new TaskStackChangeListener() {
+        @Override
+        public void onTaskStackChanged() {
+            reloadRecentTasksIfNeeded();
+        }
+    };
+
+    // TODO: add TaskbarVisualsChangedListener as well (for calendar/clock?)
+
+    // Used to keep track of the last requested task list id, so that we do not request to load the
+    // tasks again if we have already requested it and the task list has not changed
+    private int mTaskListChangeId = -1;
+
+    // The current background requests to load the task icons
+    private CancellableTask[] mIconLoadRequests = new CancellableTask[mNumRecentIcons];
+
+    public TaskbarRecentsController(BaseQuickstepLauncher launcher,
+            TaskbarController.TaskbarRecentsControllerCallbacks taskbarCallbacks) {
+        mLauncher = launcher;
+        mTaskbarCallbacks = taskbarCallbacks;
+        mRecentsModel = RecentsModel.INSTANCE.get(mLauncher);
+    }
+
+    protected void init() {
+        TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackChangeListener);
+        reloadRecentTasksIfNeeded();
+    }
+
+    protected void cleanup() {
+        TaskStackChangeListeners.getInstance().unregisterTaskStackListener(
+                mTaskStackChangeListener);
+        cancelAllPendingIconLoadTasks();
+    }
+
+    private void reloadRecentTasksIfNeeded() {
+        if (!mRecentsModel.isTaskListValid(mTaskListChangeId)) {
+            mTaskListChangeId = mRecentsModel.getTasks(this::onRecentTasksChanged);
+        }
+    }
+
+    private void cancelAllPendingIconLoadTasks() {
+        for (int i = 0; i < mIconLoadRequests.length; i++) {
+            if (mIconLoadRequests[i] != null) {
+                mIconLoadRequests[i].cancel();
+            }
+            mIconLoadRequests[i] = null;
+        }
+    }
+
+    private void onRecentTasksChanged(ArrayList<Task> tasks) {
+        mTaskbarCallbacks.updateRecentItems(tasks);
+    }
+
+    /**
+     * For each Task, loads its icon from the cache in the background, then calls
+     * {@link TaskbarController.TaskbarRecentsControllerCallbacks#updateRecentTaskAtIndex}.
+     */
+    protected void loadIconsForTasks(Task[] tasks) {
+        cancelAllPendingIconLoadTasks();
+        for (int i = 0; i < tasks.length; i++) {
+            Task task = tasks[i];
+            if (task == null) {
+                continue;
+            }
+            final int taskIndex = i;
+            mIconLoadRequests[i] = mRecentsModel.getIconCache().updateIconInBackground(
+                    task, updatedTask -> onTaskIconLoaded(task, taskIndex));
+        }
+    }
+
+    private void onTaskIconLoaded(Task task, int taskIndex) {
+        mTaskbarCallbacks.updateRecentTaskAtIndex(taskIndex, task);
+    }
+
+    protected int getNumRecentIcons() {
+        return mNumRecentIcons;
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java
new file mode 100644
index 0000000..b4b5d8b
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStateHandler.java
@@ -0,0 +1,77 @@
+/*
+ * 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.taskbar;
+
+import static com.android.launcher3.LauncherState.TASKBAR;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_TASKBAR_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.SKIP_TASKBAR;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.statemanager.StateManager;
+import com.android.launcher3.states.StateAnimationConfig;
+import com.android.quickstep.AnimatedFloat;
+
+/**
+ * StateHandler to animate Taskbar according to Launcher's state machine. Does nothing if Taskbar
+ * isn't present (i.e. {@link #setTaskbarCallbacks} is never called).
+ */
+public class TaskbarStateHandler implements StateManager.StateHandler<LauncherState> {
+
+    private final BaseQuickstepLauncher mLauncher;
+
+    // Contains Taskbar-related methods and fields we should aniamte. If null, don't do anything.
+    private @Nullable TaskbarController.TaskbarStateHandlerCallbacks mTaskbarCallbacks = null;
+
+    public TaskbarStateHandler(BaseQuickstepLauncher launcher) {
+        mLauncher = launcher;
+    }
+
+    public void setTaskbarCallbacks(TaskbarController.TaskbarStateHandlerCallbacks callbacks) {
+        mTaskbarCallbacks = callbacks;
+    }
+
+    @Override
+    public void setState(LauncherState state) {
+        if (mTaskbarCallbacks == null) {
+            return;
+        }
+
+        AnimatedFloat alphaTarget = mTaskbarCallbacks.getAlphaTarget();
+        boolean isTaskbarVisible = (state.getVisibleElements(mLauncher) & TASKBAR) != 0;
+        alphaTarget.updateValue(isTaskbarVisible ? 1f : 0f);
+    }
+
+    @Override
+    public void setStateWithAnimation(LauncherState toState, StateAnimationConfig config,
+            PendingAnimation animation) {
+        if (mTaskbarCallbacks == null) {
+            return;
+        }
+        if (config.hasAnimationFlag(SKIP_TASKBAR)) {
+            return;
+        }
+
+        AnimatedFloat alphaTarget = mTaskbarCallbacks.getAlphaTarget();
+        boolean isTaskbarVisible = (toState.getVisibleElements(mLauncher) & TASKBAR) != 0;
+        animation.setFloat(alphaTarget, AnimatedFloat.VALUE, isTaskbarVisible ? 1f : 0f,
+                config.getInterpolator(ANIM_TASKBAR_FADE, Interpolators.LINEAR));
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index 5df8d5f..d8f3bb5 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -16,16 +16,56 @@
 package com.android.launcher3.taskbar;
 
 import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.RectF;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
+import android.view.DragEvent;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
 import android.widget.LinearLayout;
 
+import androidx.annotation.LayoutRes;
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.R;
+import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.systemui.shared.recents.model.Task;
+
 /**
  * Hosts the Taskbar content such as Hotseat and Recent Apps. Drawn on top of other apps.
  */
 public class TaskbarView extends LinearLayout {
+
+    private final ColorDrawable mBackgroundDrawable;
+    private final int mItemMarginLeftRight;
+    private final int mIconTouchSize;
+    private final int mTouchSlop;
+    private final RectF mTempDelegateBounds = new RectF();
+    private final RectF mDelegateSlopBounds = new RectF();
+    private final int[] mTempOutLocation = new int[2];
+
+    // Initialized in init().
+    private int mHotseatStartIndex;
+    private int mHotseatEndIndex;
+    private View mHotseatRecentsDivider;
+    private int mRecentsStartIndex;
+    private int mRecentsEndIndex;
+
+    private TaskbarController.TaskbarViewCallbacks mControllerCallbacks;
+
+    // Delegate touches to the closest view if within mIconTouchSize.
+    private boolean mDelegateTargeted;
+    private View mDelegateView;
+
+    private boolean mIsDraggingItem;
+
     public TaskbarView(@NonNull Context context) {
         this(context, null);
     }
@@ -42,5 +82,282 @@
     public TaskbarView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr,
             int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
+
+        Resources resources = getResources();
+        mBackgroundDrawable = (ColorDrawable) getBackground();
+        mItemMarginLeftRight = resources.getDimensionPixelSize(R.dimen.taskbar_icon_spacing);
+        mIconTouchSize = resources.getDimensionPixelSize(R.dimen.taskbar_icon_touch_size);
+        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+    }
+
+    protected void setCallbacks(TaskbarController.TaskbarViewCallbacks taskbarViewCallbacks) {
+        mControllerCallbacks = taskbarViewCallbacks;
+    }
+
+    protected void init(int numHotseatIcons, int numRecentIcons) {
+        mHotseatStartIndex = 0;
+        mHotseatEndIndex = mHotseatStartIndex + numHotseatIcons - 1;
+        updateHotseatItems(new ItemInfo[numHotseatIcons]);
+
+        int dividerIndex = mHotseatEndIndex + 1;
+        mHotseatRecentsDivider = addDivider(dividerIndex);
+
+        mRecentsStartIndex = dividerIndex + 1;
+        mRecentsEndIndex = mRecentsStartIndex + numRecentIcons - 1;
+        updateRecentTasks(new Task[numRecentIcons]);
+    }
+
+    protected void cleanup() {
+        removeAllViews();
+    }
+
+    /**
+     * Sets the alpha of the background color behind all the Taskbar contents.
+     * @param alpha 0 is fully transparent, 1 is fully opaque.
+     */
+    public void setBackgroundAlpha(float alpha) {
+        mBackgroundDrawable.setAlpha((int) (alpha * 255));
+    }
+
+    /**
+     * Inflates/binds the Hotseat views to show in the Taskbar given their ItemInfos.
+     */
+    protected void updateHotseatItems(ItemInfo[] hotseatItemInfos) {
+        for (int i = 0; i < hotseatItemInfos.length; i++) {
+            ItemInfo hotseatItemInfo = hotseatItemInfos[i];
+            int hotseatIndex = mHotseatStartIndex + i;
+            View hotseatView = getChildAt(hotseatIndex);
+
+            // Replace any Hotseat views with the appropriate type if it's not already that type.
+            final int expectedLayoutResId;
+            if (hotseatItemInfo != null && hotseatItemInfo.isPredictedItem()) {
+                expectedLayoutResId = R.layout.taskbar_predicted_app_icon;
+            } else {
+                expectedLayoutResId = R.layout.taskbar_app_icon;
+            }
+            if (hotseatView == null || hotseatView.getSourceLayoutResId() != expectedLayoutResId) {
+                removeView(hotseatView);
+                BubbleTextView btv = (BubbleTextView) inflate(expectedLayoutResId);
+                LayoutParams lp = new LayoutParams(btv.getIconSize(), btv.getIconSize());
+                lp.setMargins(mItemMarginLeftRight, 0, mItemMarginLeftRight, 0);
+                hotseatView = btv;
+                addView(hotseatView, hotseatIndex, lp);
+            }
+
+            // Apply the Hotseat ItemInfos, or hide the view if there is none for a given index.
+            if (hotseatView instanceof BubbleTextView
+                    && hotseatItemInfo instanceof WorkspaceItemInfo) {
+                ((BubbleTextView) hotseatView).applyFromWorkspaceItem(
+                        (WorkspaceItemInfo) hotseatItemInfo);
+                hotseatView.setVisibility(VISIBLE);
+                hotseatView.setOnClickListener(mControllerCallbacks.getItemOnClickListener());
+                hotseatView.setOnLongClickListener(
+                        mControllerCallbacks.getItemOnLongClickListener());
+            } else {
+                hotseatView.setVisibility(GONE);
+                hotseatView.setOnClickListener(null);
+                hotseatView.setOnLongClickListener(null);
+            }
+        }
+
+        updateHotseatRecentsDividerVisibility();
+    }
+
+    private View addDivider(int dividerIndex) {
+        View divider = inflate(R.layout.taskbar_divider);
+        addView(divider, dividerIndex);
+        return divider;
+    }
+
+    /**
+     * Inflates/binds the Recents items to show in the Taskbar given their Tasks.
+     */
+    protected void updateRecentTasks(Task[] tasks) {
+        for (int i = 0; i < tasks.length; i++) {
+            Task task = tasks[i];
+            int recentsIndex = mRecentsStartIndex + i;
+            View recentsView = getChildAt(recentsIndex);
+
+            // Inflate empty icon Views.
+            if (recentsView == null) {
+                BubbleTextView btv = (BubbleTextView) inflate(R.layout.taskbar_app_icon);
+                LayoutParams lp = new LayoutParams(btv.getIconSize(), btv.getIconSize());
+                lp.setMargins(mItemMarginLeftRight, 0, mItemMarginLeftRight, 0);
+                recentsView = btv;
+                addView(recentsView, recentsIndex, lp);
+            }
+
+            // Apply the Task, or hide the view if there is none for a given index.
+            if (recentsView instanceof BubbleTextView && task != null) {
+                applyTaskToBubbleTextView((BubbleTextView) recentsView, task);
+                recentsView.setVisibility(VISIBLE);
+                recentsView.setOnClickListener(mControllerCallbacks.getItemOnClickListener());
+                recentsView.setOnLongClickListener(
+                        mControllerCallbacks.getItemOnLongClickListener());
+            } else {
+                recentsView.setVisibility(GONE);
+                recentsView.setOnClickListener(null);
+                recentsView.setOnLongClickListener(null);
+            }
+        }
+
+        updateHotseatRecentsDividerVisibility();
+    }
+
+    private void applyTaskToBubbleTextView(BubbleTextView btv, Task task) {
+        if (task.icon != null) {
+            Drawable icon = task.icon.getConstantState().newDrawable().mutate();
+            btv.applyIconAndLabel(icon, task.titleDescription);
+        }
+        btv.setTag(task);
+    }
+
+    protected void updateRecentTaskAtIndex(int taskIndex, Task task) {
+        View taskView = getChildAt(mRecentsStartIndex + taskIndex);
+        if (taskView instanceof BubbleTextView) {
+            applyTaskToBubbleTextView((BubbleTextView) taskView, task);
+        }
+    }
+
+    /**
+     * Make the divider VISIBLE between the Hotseat and Recents if there is at least one icon in
+     * each, otherwise make it GONE.
+     */
+    private void updateHotseatRecentsDividerVisibility() {
+        if (mHotseatRecentsDivider == null) {
+            return;
+        }
+
+        boolean hasAtLeastOneHotseatItem = false;
+        for (int i = mHotseatStartIndex; i <= mHotseatEndIndex; i++) {
+            if (getChildAt(i).getVisibility() != GONE) {
+                hasAtLeastOneHotseatItem = true;
+                break;
+            }
+        }
+
+        boolean hasAtLeastOneRecentItem = false;
+        for (int i = mRecentsStartIndex; i <= mRecentsEndIndex; i++) {
+            if (getChildAt(i).getVisibility() != GONE) {
+                hasAtLeastOneRecentItem = true;
+                break;
+            }
+        }
+
+        mHotseatRecentsDivider.setVisibility(hasAtLeastOneHotseatItem && hasAtLeastOneRecentItem
+                ? VISIBLE : GONE);
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        boolean handled = delegateTouchIfNecessary(event);
+        return super.onTouchEvent(event) || handled;
+    }
+
+    /**
+     * User touched the Taskbar background. Determine whether the touch is close enough to a view
+     * that we should forward the touches to it.
+     * @return Whether a delegate view was chosen and it handled the touch event.
+     */
+    private boolean delegateTouchIfNecessary(MotionEvent event) {
+        final float x = event.getX();
+        final float y = event.getY();
+        if (mDelegateView == null && event.getAction() == MotionEvent.ACTION_DOWN) {
+            View delegateView = findDelegateView(x, y);
+            if (delegateView != null) {
+                mDelegateTargeted = true;
+                mDelegateView = delegateView;
+                mDelegateSlopBounds.set(mTempDelegateBounds);
+                mDelegateSlopBounds.inset(-mTouchSlop, -mTouchSlop);
+            }
+        }
+
+        boolean sendToDelegate = mDelegateTargeted;
+        boolean inBounds = true;
+        switch (event.getAction()) {
+            case MotionEvent.ACTION_MOVE:
+                inBounds = mDelegateSlopBounds.contains(x, y);
+                break;
+            case MotionEvent.ACTION_UP:
+            case MotionEvent.ACTION_CANCEL:
+                mDelegateTargeted = false;
+                break;
+        }
+
+        boolean handled = false;
+        if (sendToDelegate) {
+            if (inBounds) {
+                // Offset event coordinates to be inside the target view
+                event.setLocation(mDelegateView.getWidth() / 2f, mDelegateView.getHeight() / 2f);
+            } else {
+                // Offset event coordinates to be outside the target view (in case it does
+                // something like tracking pressed state)
+                event.setLocation(-mTouchSlop * 2, -mTouchSlop * 2);
+            }
+            handled = mDelegateView.dispatchTouchEvent(event);
+            // Cleanup if this was the last event to send to the delegate.
+            if (!mDelegateTargeted) {
+                mDelegateView = null;
+            }
+        }
+        return handled;
+    }
+
+    /**
+     * Return an item whose touch bounds contain the given coordinates,
+     * or null if no such item exists.
+     *
+     * Also sets {@link #mTempDelegateBounds} to be the touch bounds of the chosen delegate view.
+     */
+    private @Nullable View findDelegateView(float x, float y) {
+        for (int i = 0; i < getChildCount(); i++) {
+            View child = getChildAt(i);
+            if (!child.isShown() || !child.isClickable()) {
+                continue;
+            }
+            int childCenterX = child.getLeft() + child.getWidth() / 2;
+            int childCenterY = child.getTop() + child.getHeight() / 2;
+            mTempDelegateBounds.set(
+                    childCenterX - mIconTouchSize / 2f,
+                    childCenterY - mIconTouchSize / 2f,
+                    childCenterX + mIconTouchSize / 2f,
+                    childCenterY + mIconTouchSize / 2f);
+            if (mTempDelegateBounds.contains(x, y)) {
+                return child;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns whether the given MotionEvent, *in screen coorindates*, is within any Taskbar item's
+     * touch bounds.
+     */
+    public boolean isEventOverAnyItem(MotionEvent ev) {
+        getLocationOnScreen(mTempOutLocation);
+        float xInOurCoordinates = ev.getX() - mTempOutLocation[0];
+        float yInOurCoorindates = ev.getY() - mTempOutLocation[1];
+        return findDelegateView(xInOurCoordinates, yInOurCoorindates) != null;
+    }
+
+    @Override
+    public boolean onDragEvent(DragEvent event) {
+        switch (event.getAction()) {
+            case DragEvent.ACTION_DRAG_STARTED:
+                mIsDraggingItem = true;
+                return true;
+            case DragEvent.ACTION_DRAG_ENDED:
+                mIsDraggingItem = false;
+                break;
+        }
+        return super.onDragEvent(event);
+    }
+
+    public boolean isDraggingItem() {
+        return mIsDraggingItem;
+    }
+
+    private View inflate(@LayoutRes int layoutResId) {
+        return LayoutInflater.from(getContext()).inflate(layoutResId, this, false);
     }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarVisibilityController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarVisibilityController.java
new file mode 100644
index 0000000..4cf55d8
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarVisibilityController.java
@@ -0,0 +1,95 @@
+/*
+ * 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.taskbar;
+
+import static com.android.launcher3.LauncherState.TASKBAR;
+
+import android.animation.Animator;
+
+import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.quickstep.AnimatedFloat;
+import com.android.quickstep.SystemUiProxy;
+import com.android.systemui.shared.system.QuickStepContract;
+
+/**
+ * Works with TaskbarController to update the TaskbarView's alpha based on LauncherState, whether
+ * Launcher is in the foreground, etc.
+ */
+public class TaskbarVisibilityController {
+
+    private static final long IME_VISIBILITY_ALPHA_DURATION = 120;
+
+    private final BaseQuickstepLauncher mLauncher;
+    private final TaskbarController.TaskbarVisibilityControllerCallbacks mTaskbarCallbacks;
+
+    // Background alpha.
+    private AnimatedFloat mTaskbarBackgroundAlpha = new AnimatedFloat(
+            this::onTaskbarBackgroundAlphaChanged);
+
+    // Overall visibility.
+    private AnimatedFloat mTaskbarVisibilityAlphaForLauncherState = new AnimatedFloat(
+            this::updateVisibilityAlpha);
+    private AnimatedFloat mTaskbarVisibilityAlphaForIme = new AnimatedFloat(
+            this::updateVisibilityAlpha);
+
+    public TaskbarVisibilityController(BaseQuickstepLauncher launcher,
+            TaskbarController.TaskbarVisibilityControllerCallbacks taskbarCallbacks) {
+        mLauncher = launcher;
+        mTaskbarCallbacks = taskbarCallbacks;
+    }
+
+    protected void init() {
+        mTaskbarBackgroundAlpha.updateValue(mLauncher.hasBeenResumed() ? 0f : 1f);
+        boolean isVisibleForLauncherState = (mLauncher.getStateManager().getState()
+                .getVisibleElements(mLauncher) & TASKBAR) != 0;
+        mTaskbarVisibilityAlphaForLauncherState.updateValue(isVisibleForLauncherState ? 1f : 0f);
+        boolean isImeVisible = (SystemUiProxy.INSTANCE.get(mLauncher).getLastSystemUiStateFlags()
+                & QuickStepContract.SYSUI_STATE_IME_SHOWING) != 0;
+        mTaskbarVisibilityAlphaForIme.updateValue(isImeVisible ? 0f : 1f);
+    }
+
+    protected void cleanup() {
+    }
+
+    protected AnimatedFloat getTaskbarVisibilityForLauncherState() {
+        return mTaskbarVisibilityAlphaForLauncherState;
+    }
+
+    protected Animator createAnimToBackgroundAlpha(float toAlpha, long duration) {
+        return mTaskbarBackgroundAlpha.animateToValue(mTaskbarBackgroundAlpha.value, toAlpha)
+                .setDuration(duration);
+    }
+
+    protected void animateToVisibilityForIme(float toAlpha) {
+        mTaskbarVisibilityAlphaForIme.animateToValue(mTaskbarVisibilityAlphaForIme.value, toAlpha)
+                .setDuration(IME_VISIBILITY_ALPHA_DURATION).start();
+    }
+
+    private void onTaskbarBackgroundAlphaChanged() {
+        mTaskbarCallbacks.updateTaskbarBackgroundAlpha(mTaskbarBackgroundAlpha.value);
+        updateVisibilityAlpha();
+    }
+
+    private void updateVisibilityAlpha() {
+        // 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 alphaDueToLauncher = Math.max(mTaskbarBackgroundAlpha.value,
+                mTaskbarVisibilityAlphaForLauncherState.value);
+        float alphaDueToOther = mTaskbarVisibilityAlphaForIme.value;
+        mTaskbarCallbacks.updateTaskbarVisibilityAlpha(alphaDueToLauncher * alphaDueToOther);
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
index 597c17b..22c4a7e 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
@@ -184,7 +184,10 @@
     }
 
     private int getOutlineOffsetY() {
-        return getPaddingTop() + mDeviceProfile.folderIconOffsetYPx;
+        if (mDisplay != DISPLAY_TASKBAR) {
+            return getPaddingTop() + mDeviceProfile.folderIconOffsetYPx;
+        }
+        return (getMeasuredHeight() / 2) - mNormalizedIconRadius;
     }
 
     private void drawEffect(Canvas canvas, boolean isBadged) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index a00ce56..9a1e707 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -17,6 +17,7 @@
 
 import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED;
 
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
@@ -24,6 +25,8 @@
 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.logger.LauncherAtom.ContainerInfo.ContainerCase.EXTENDED_CONTAINERS;
+import static com.android.launcher3.logger.LauncherAtomExtensions.ExtendedContainers.ContainerCase.DEVICE_SEARCH_RESULT_CONTAINER;
 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.OVERVIEW_STATE_ORDINAL;
@@ -52,6 +55,7 @@
 import com.android.launcher3.logging.StatsLogManager.StatsLogger;
 import com.android.launcher3.model.BgDataModel.FixedContainerItems;
 import com.android.launcher3.model.data.ItemInfo;
+import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.popup.SystemShortcut;
 import com.android.launcher3.search.DeviceSearchAdapterProvider;
 import com.android.launcher3.statemanager.StateManager.AtomicAnimationFactory;
@@ -80,7 +84,9 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.List;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.stream.Stream;
 
 public class QuickstepLauncher extends BaseQuickstepLauncher {
@@ -105,6 +111,15 @@
 
     @Override
     protected void logAppLaunch(ItemInfo info, InstanceId instanceId) {
+        // If the app launch is from DeviceSearchResultContainer then add the InstanceId from
+        // LiveSearchManager to recreate the AllApps search session on the server side.
+        Optional<InstanceId> logInstanceId = this.getLiveSearchManager().getLogInstanceId();
+        if (info.getContainerInfo().getContainerCase() == EXTENDED_CONTAINERS
+                && info.getContainerInfo().getExtendedContainers().getContainerCase()
+                == DEVICE_SEARCH_RESULT_CONTAINER && logInstanceId.isPresent()) {
+            instanceId = logInstanceId.get();
+        }
+
         StatsLogger logger = getStatsLogManager()
                 .logger().withItemInfo(info).withInstanceId(instanceId);
 
@@ -152,7 +167,8 @@
     @Override
     public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
         if (mHotseatPredictionController != null) {
-            mHotseatPredictionController.setPauseUIUpdate(true);
+            // Only pause is taskbar controller is not present
+            mHotseatPredictionController.setPauseUIUpdate(getTaskbarController() == null);
         }
         return super.startActivitySafely(v, intent, item);
     }
@@ -218,6 +234,15 @@
     }
 
     @Override
+    public void bindWorkspaceItemsChanged(List<WorkspaceItemInfo> updated) {
+        super.bindWorkspaceItemsChanged(updated);
+        if (getTaskbarController() != null && updated.stream()
+                .filter(w -> w.container == CONTAINER_HOTSEAT).findFirst().isPresent()) {
+            getTaskbarController().onHotseatUpdated();
+        }
+    }
+
+    @Override
     public void onDestroy() {
         super.onDestroy();
         getAppsView().getSearchUiManager().destroy();
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index 4b4f955..2cf65af 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -69,7 +69,9 @@
     @Override
     public int getVisibleElements(Launcher launcher) {
         return super.getVisibleElements(launcher)
-                & ~OVERVIEW_BUTTONS & ~VERTICAL_SWIPE_INDICATOR;
+                & ~OVERVIEW_BUTTONS
+                & ~VERTICAL_SWIPE_INDICATOR
+                | TASKBAR;
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
index 51e72da..965f474 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
@@ -40,6 +40,6 @@
 
     @Override
     public int getVisibleElements(Launcher launcher) {
-        return NONE;
+        return TASKBAR;
     }
 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
index efb91c6..69b8aca 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
@@ -36,6 +36,7 @@
 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_TASKBAR_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;
@@ -80,6 +81,7 @@
         if (toState == NORMAL && fromState == OVERVIEW) {
             config.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL);
             config.setInterpolator(ANIM_WORKSPACE_FADE, ACCEL);
+            config.setInterpolator(ANIM_TASKBAR_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);
@@ -138,6 +140,7 @@
             config.setInterpolator(ANIM_DEPTH, OVERSHOOT_1_2);
             config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, OVERSHOOT_1_2);
             config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, OVERSHOOT_1_2);
+            config.setInterpolator(ANIM_TASKBAR_FADE, OVERSHOOT_1_2);
         } else if (fromState == HINT_STATE && toState == NORMAL) {
             config.setInterpolator(ANIM_DEPTH, DEACCEL_3);
             if (mHintToNormalDuration == -1) {
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 39a3a7c..36b51cd 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -731,7 +731,6 @@
         setIsLikelyToStartNewTask(isLikelyToStartNewTask, false /* animate */);
         mStateCallback.setStateOnUiThread(STATE_GESTURE_STARTED);
         mGestureStarted = true;
-        mTaskViewSimulator.setDrawsBelowRecents(true);
     }
 
     /**
@@ -958,7 +957,6 @@
         if (endTarget == HOME) {
             duration = Math.max(MIN_OVERSHOOT_DURATION, duration);
         } else if (endTarget == RECENTS) {
-            LiveTileOverlay.INSTANCE.startIconAnimation();
             if (mRecentsView != null) {
                 int nearestPage = mRecentsView.getPageNearestToCenterOfScreen();
                 if (mRecentsView.getNextPage() != nearestPage) {
@@ -971,9 +969,6 @@
                 }
                 duration = Math.max(duration, mRecentsView.getScroller().getDuration());
             }
-            if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
-                mRecentsView.getRunningTaskView().setIsClickableAsLiveTile(false);
-            }
         }
 
         // Let RecentsView handle the scrolling to the task, which we launch in startNewTask()
@@ -1062,10 +1057,11 @@
         if (mGestureState.getEndTarget().isLauncher) {
             ActivityManagerWrapper.getInstance().registerTaskStackListener(
                     mActivityRestartListener);
+
+            mActivityInterface.onAnimateToLauncher(mGestureState.getEndTarget(), duration);
         }
 
         if (mGestureState.getEndTarget() == HOME) {
-            mTaskViewSimulator.setDrawsBelowRecents(false);
             getOrientationHandler().adjustFloatingIconStartVelocity(velocityPxPerMs);
             final RemoteAnimationTargetCompat runningTaskTarget = mRecentsAnimationTargets != null
                     ? mRecentsAnimationTargets.findTask(mGestureState.getRunningTaskId())
@@ -1451,10 +1447,6 @@
     private void finishCurrentTransitionToRecents() {
         if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
             mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
-            final TaskView runningTaskView = mRecentsView.getRunningTaskView();
-            if (runningTaskView != null) {
-                runningTaskView.setIsClickableAsLiveTile(true);
-            }
         } else if (!hasTargets() || mRecentsAnimationController == null) {
             // If there are no targets or the animation not started, then there is nothing to finish
             mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
diff --git a/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
index efd4530..d159fa0 100644
--- a/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
+++ b/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
@@ -33,6 +33,7 @@
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.statehandlers.DepthController;
 import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.taskbar.TaskbarController;
 import com.android.quickstep.util.RemoteAnimationProvider;
 import com.android.quickstep.util.SurfaceTransactionApplier;
 import com.android.quickstep.util.TaskViewSimulator;
@@ -119,6 +120,11 @@
                     OVERVIEW.getDepth(mActivity), TOUCH_RESPONSE_INTERPOLATOR);
         }
 
+        TaskbarController taskbarController = mActivityInterface.getTaskbarController();
+        if (taskbarController != null) {
+            pa.add(taskbarController.createAnimToLauncher(OVERVIEW, getRecentsLaunchDuration()));
+        }
+
         RemoteAnimationTargets targets = new RemoteAnimationTargets(appTargets,
                 wallpaperTargets, MODE_CLOSING);
 
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 5bed929..ce14197 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -45,6 +45,7 @@
 import com.android.launcher3.statehandlers.DepthController;
 import com.android.launcher3.statemanager.BaseState;
 import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.taskbar.TaskbarController;
 import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.util.WindowBounds;
 import com.android.quickstep.SysUINavigationMode.Mode;
@@ -121,6 +122,11 @@
         return null;
     }
 
+    @Nullable
+    public TaskbarController getTaskbarController() {
+        return null;
+    }
+
     public final boolean isResumed() {
         ACTIVITY_TYPE activity = getCreatedActivity();
         return activity != null && activity.hasBeenResumed();
@@ -147,6 +153,13 @@
         return deviceState.isInDeferredGestureRegion(ev);
     }
 
+    /**
+     * @return Whether the gesture in progress should be cancelled.
+     */
+    public boolean shouldCancelCurrentGesture() {
+        return false;
+    }
+
     public abstract void onExitOverview(RotationTouchHelper deviceState,
             Runnable exitRunnable);
 
@@ -276,6 +289,20 @@
         return overviewActionsHeight;
     }
 
+    /**
+     * Called when the gesture ends and the animation starts towards the given target. No-op by
+     * default, but subclasses can override to add an additional animation with the same duration.
+     */
+    public void onAnimateToLauncher(GestureState.GestureEndTarget endTarget, long duration) {
+    }
+
+    /**
+     * See {@link com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags}
+     * @param systemUiStateFlags The latest SystemUiStateFlags
+     */
+    public void onSystemUiFlagsChanged(int systemUiStateFlags) {
+    }
+
     public interface AnimationFactory {
 
         void createActivityInterface(long transitionLength);
diff --git a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index 7630bc4..8b0d782 100644
--- a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -16,14 +16,17 @@
 package com.android.quickstep;
 
 import static com.android.launcher3.LauncherState.BACKGROUND_APP;
+import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.quickstep.SysUINavigationMode.getMode;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
 
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Rect;
 import android.util.Log;
+import android.view.MotionEvent;
 
 import androidx.annotation.Nullable;
 import androidx.annotation.UiThread;
@@ -38,8 +41,10 @@
 import com.android.launcher3.statehandlers.DepthController;
 import com.android.launcher3.statehandlers.DepthController.ClampedDepthProperty;
 import com.android.launcher3.statemanager.StateManager;
+import com.android.launcher3.taskbar.TaskbarController;
 import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.touch.PagedOrientationHandler;
+import com.android.quickstep.GestureState.GestureEndTarget;
 import com.android.quickstep.SysUINavigationMode.Mode;
 import com.android.quickstep.util.ActivityInitListener;
 import com.android.quickstep.util.AnimatorControllerWithResistance;
@@ -156,6 +161,16 @@
 
     @Nullable
     @Override
+    public TaskbarController getTaskbarController() {
+        BaseQuickstepLauncher launcher = getCreatedActivity();
+        if (launcher == null) {
+            return null;
+        }
+        return launcher.getTaskbarController();
+    }
+
+    @Nullable
+    @Override
     public RecentsView getVisibleRecentsView() {
         Launcher launcher = getVisibleLauncher();
         return launcher != null && launcher.getStateManager().getState().overviewUi
@@ -277,4 +292,42 @@
         if (activity == null) return;
         activity.getAppTransitionManager().registerRemoteTransitions();
     }
+
+    @Override
+    public void onAnimateToLauncher(GestureEndTarget endTarget, long duration) {
+        TaskbarController taskbarController = getTaskbarController();
+        if (taskbarController == null) {
+            return;
+        }
+        LauncherState toState = endTarget == GestureEndTarget.RECENTS ? OVERVIEW : NORMAL;
+        taskbarController.createAnimToLauncher(toState, duration).start();
+    }
+
+    @Override
+    public void onSystemUiFlagsChanged(int systemUiStateFlags) {
+        TaskbarController taskbarController = getTaskbarController();
+        if (taskbarController == null) {
+            return;
+        }
+        boolean isImeVisible = (systemUiStateFlags & SYSUI_STATE_IME_SHOWING) != 0;
+        taskbarController.setIsImeVisible(isImeVisible);
+    }
+
+    @Override
+    public boolean deferStartingActivity(RecentsAnimationDeviceState deviceState, MotionEvent ev) {
+        TaskbarController taskbarController = getTaskbarController();
+        if (taskbarController == null) {
+            return super.deferStartingActivity(deviceState, ev);
+        }
+        return taskbarController.isEventOverAnyTaskbarItem(ev);
+    }
+
+    @Override
+    public boolean shouldCancelCurrentGesture() {
+        TaskbarController taskbarController = getTaskbarController();
+        if (taskbarController == null) {
+            return super.shouldCancelCurrentGesture();
+        }
+        return taskbarController.isDraggingItem();
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 7beeae2..8aa0842 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -184,8 +184,11 @@
             }
 
             @Override
-            public void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
-                    RemoteAnimationTargetCompat[] wallpaperTargets, AnimationResult result) {
+            public void onCreateAnimation(int transit,
+                    RemoteAnimationTargetCompat[] appTargets,
+                    RemoteAnimationTargetCompat[] wallpaperTargets,
+                    RemoteAnimationTargetCompat[] nonAppTargets,
+                    AnimationResult result) {
                 AnimatorSet anim = composeRecentsLaunchAnimator(taskView, appTargets,
                         wallpaperTargets);
                 anim.addListener(resetStateListener());
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index ca55468..85b21e0 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -17,6 +17,7 @@
 
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 
+import android.app.PendingIntent;
 import android.app.PictureInPictureParams;
 import android.content.ComponentName;
 import android.content.Context;
@@ -24,15 +25,18 @@
 import android.graphics.Bitmap;
 import android.graphics.Insets;
 import android.graphics.Rect;
+import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.IBinder.DeathRecipient;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.Log;
 import android.view.MotionEvent;
 
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
+import com.android.systemui.shared.recents.ISplitScreenListener;
 import com.android.systemui.shared.recents.ISystemUiProxy;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.system.RemoteTransitionCompat;
@@ -431,4 +435,84 @@
             }
         }
     }
+
+    @Override
+    public void registerSplitScreenListener(ISplitScreenListener listener) {
+        if (mSystemUiProxy != null) {
+            try {
+                mSystemUiProxy.registerSplitScreenListener(listener);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call registerSplitScreenListener");
+            }
+        }
+    }
+
+    @Override
+    public void unregisterSplitScreenListener(ISplitScreenListener listener) {
+        if (mSystemUiProxy != null) {
+            try {
+                mSystemUiProxy.unregisterSplitScreenListener(listener);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call unregisterSplitScreenListener");
+            }
+        }
+    }
+
+    @Override
+    public void setSideStageVisibility(boolean visible) {
+        if (mSystemUiProxy != null) {
+            try {
+                mSystemUiProxy.setSideStageVisibility(visible);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call setSideStageVisibility");
+            }
+        }
+    }
+
+    @Override
+    public void exitSplitScreen() {
+        if (mSystemUiProxy != null) {
+            try {
+                mSystemUiProxy.exitSplitScreen();
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call exitSplitScreen");
+            }
+        }
+    }
+
+    @Override
+    public void startTask(int taskId, int stage, int position, Bundle options) {
+        if (mSystemUiProxy != null) {
+            try {
+                mSystemUiProxy.startTask(taskId, stage, position, options);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call startTask");
+            }
+        }
+    }
+
+    @Override
+    public void startShortcut(String packageName, String shortcutId, int stage, int position,
+            Bundle options, UserHandle user) {
+        if (mSystemUiProxy != null) {
+            try {
+                mSystemUiProxy.startShortcut(packageName, shortcutId, stage, position, options,
+                        user);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call startShortcut");
+            }
+        }
+    }
+
+    @Override
+    public void startIntent(PendingIntent intent, int stage, int position, Bundle options) {
+        if (mSystemUiProxy != null) {
+            try {
+                mSystemUiProxy.startIntent(intent, stage, position, options);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed call startIntent");
+            }
+        }
+    }
+
 }
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 0f40937..8ebea33 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -370,12 +370,14 @@
     @UiThread
     private void onSystemUiFlagsChanged() {
         if (mDeviceState.isUserUnlocked()) {
-            SystemUiProxy.INSTANCE.get(this).setLastSystemUiStateFlags(
-                    mDeviceState.getSystemUiStateFlags());
+            int systemUiStateFlags = mDeviceState.getSystemUiStateFlags();
+            SystemUiProxy.INSTANCE.get(this).setLastSystemUiStateFlags(systemUiStateFlags);
             mOverviewComponentObserver.onSystemUiStateChanged();
+            mOverviewComponentObserver.getActivityInterface().onSystemUiFlagsChanged(
+                    systemUiStateFlags);
 
             // Update the tracing state
-            if ((mDeviceState.getSystemUiStateFlags() & SYSUI_STATE_TRACING_ENABLED) != 0) {
+            if ((systemUiStateFlags & SYSUI_STATE_TRACING_ENABLED) != 0) {
                 Log.d(TAG, "Starting tracing.");
                 ProtoTracer.INSTANCE.get(this).start();
             } else {
@@ -512,9 +514,14 @@
             }
         }
 
-        boolean cleanUpConsumer = (action == ACTION_UP || action == ACTION_CANCEL)
+        boolean cancelGesture = mGestureState.getActivityInterface() != null
+                && mGestureState.getActivityInterface().shouldCancelCurrentGesture();
+        boolean cleanUpConsumer = (action == ACTION_UP || action == ACTION_CANCEL || cancelGesture)
                 && mConsumer != null
                 && !mConsumer.getActiveConsumerInHierarchy().isConsumerDetachedFromGesture();
+        if (cancelGesture) {
+            event.setAction(ACTION_CANCEL);
+        }
         mUncheckedConsumer.onMotionEvent(event);
 
         if (cleanUpConsumer) {
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index d949126..d22496d 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -16,6 +16,7 @@
 
 package com.android.quickstep.logging;
 
+import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.EXTENDED_CONTAINERS;
 import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.FOLDER;
 import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.SEARCH_RESULT_CONTAINER;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WORKSPACE_SNAPSHOT;
@@ -72,6 +73,7 @@
     private static final int DEFAULT_PAGE_INDEX = -2;
     private static final int FOLDER_HIERARCHY_OFFSET = 100;
     private static final int SEARCH_RESULT_HIERARCHY_OFFSET = 200;
+    private static final int EXTENDED_CONTAINERS_HIERARCHY_OFFSET = 300;
 
     public static final CopyOnWriteArrayList<StatsLogConsumer> LOGS_CONSUMER =
             new CopyOnWriteArrayList<>();
@@ -397,6 +399,9 @@
         } else if (info.getContainerInfo().getContainerCase() == SEARCH_RESULT_CONTAINER) {
             return info.getContainerInfo().getSearchResultContainer().getParentContainerCase()
                     .getNumber() + SEARCH_RESULT_HIERARCHY_OFFSET;
+        } else if (info.getContainerInfo().getContainerCase() == EXTENDED_CONTAINERS) {
+            return info.getContainerInfo().getExtendedContainers().getContainerCase().getNumber()
+                    + EXTENDED_CONTAINERS_HIERARCHY_OFFSET;
         } else {
             return info.getContainerInfo().getContainerCase().getNumber();
         }
diff --git a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
index 19c6588..3adb459 100644
--- a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
+++ b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
@@ -43,8 +43,11 @@
             }
 
             @Override
-            public void onCreateAnimation(RemoteAnimationTargetCompat[] appTargets,
-                    RemoteAnimationTargetCompat[] wallpaperTargets, AnimationResult result) {
+            public void onCreateAnimation(int transit,
+                    RemoteAnimationTargetCompat[] appTargets,
+                    RemoteAnimationTargetCompat[] wallpaperTargets,
+                    RemoteAnimationTargetCompat[] nonApps,
+                    AnimationResult result) {
                 result.setAnimation(createWindowAnimation(appTargets, wallpaperTargets), context);
             }
         };
diff --git a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
index 4120331..5bae3c7 100644
--- a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
+++ b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
@@ -24,6 +24,7 @@
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_COMPONENTS;
 import static com.android.launcher3.states.StateAnimationConfig.SKIP_DEPTH_CONTROLLER;
 import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
+import static com.android.launcher3.states.StateAnimationConfig.SKIP_TASKBAR;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -158,7 +159,8 @@
      */
     private void prepareToAnimate(Launcher launcher, boolean animateOverviewScrim) {
         StateAnimationConfig config = new StateAnimationConfig();
-        config.animFlags = ANIM_ALL_COMPONENTS | SKIP_OVERVIEW | SKIP_DEPTH_CONTROLLER;
+        config.animFlags = ANIM_ALL_COMPONENTS | SKIP_OVERVIEW | SKIP_DEPTH_CONTROLLER
+                | SKIP_TASKBAR;
         config.duration = 0;
         // setRecentsAttachedToAppWindow() will animate recents out.
         launcher.getStateManager().createAtomicAnimation(BACKGROUND_APP, NORMAL, config).start();
diff --git a/quickstep/src/com/android/quickstep/views/LiveTileOverlay.java b/quickstep/src/com/android/quickstep/views/LiveTileOverlay.java
index 747c3f2..8210ab0 100644
--- a/quickstep/src/com/android/quickstep/views/LiveTileOverlay.java
+++ b/quickstep/src/com/android/quickstep/views/LiveTileOverlay.java
@@ -85,6 +85,11 @@
         mIcon = icon;
     }
 
+    // TODO: consider cleaning this up and drawing icon in another way. Previously we place app
+    // below launcher during the initial swipe up and render the icon in this live tile overlay.
+    // However, this resulted in a bunch of touch input issues caused by Launcher getting the input
+    // events during transition (to overview / to another app (quick switch). So now our new
+    // solution places app on top in live tile until it fully settles in Overview.
     public void startIconAnimation() {
         if (mIconAnimator != null) {
             mIconAnimator.cancel();
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 2f2b566..d9d0a93 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -2277,7 +2277,9 @@
     }
 
     public void redrawLiveTile() {
-        mLiveTileTaskViewSimulator.apply(mLiveTileParams);
+        if (mLiveTileParams.getTargetSet() != null) {
+            mLiveTileTaskViewSimulator.apply(mLiveTileParams);
+        }
     }
 
     public TaskViewSimulator getLiveTileTaskViewSimulator() {
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index e8f590f..59cf3b2 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -356,10 +356,6 @@
         return false;
     }
 
-    public void setIsClickableAsLiveTile(boolean isClickableAsLiveTile) {
-        mIsClickableAsLiveTile = isClickableAsLiveTile;
-    }
-
     private void computeAndSetIconTouchDelegate() {
         float iconHalfSize = mIconView.getWidth() / 2f;
         mIconCenterCoords[0] = mIconCenterCoords[1] = iconHalfSize;
diff --git a/res/values-v31/colors.xml b/res/values-v31/colors.xml
new file mode 100644
index 0000000..b8600a6
--- /dev/null
+++ b/res/values-v31/colors.xml
@@ -0,0 +1,33 @@
+<?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.
+*/
+-->
+<resources>
+    <color name="popup_color_primary_light">@android:color/system_main_50</color>
+    <color name="popup_color_secondary_light">@android:color/system_main_100</color>
+    <color name="popup_color_tertiary_light">@android:color/system_main_300</color>
+    <color name="popup_color_primary_dark">@android:color/system_main_800</color>
+    <color name="popup_color_secondary_dark">@android:color/system_main_900</color>
+    <color name="popup_color_tertiary_dark">@android:color/system_main_700</color>
+
+    <color name="workspace_text_color_light">@android:color/system_main_50</color>
+    <color name="workspace_text_color_dark">@android:color/system_main_900</color>
+
+    <color name="text_color_primary_dark">@android:color/system_main_50</color>
+    <color name="text_color_secondary_dark">@android:color/system_main_200</color>
+    <color name="text_color_tertiary_dark">@android:color/system_main_400</color>
+</resources>
\ No newline at end of file
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 96c30b5..e593fb4 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -57,6 +57,7 @@
             <enum name="widget_section" value="3" />
             <enum name="shortcut_popup" value="4" />
             <enum name="hero_app" value="5" />
+            <enum name="taskbar" value="6" />
         </attr>
         <attr name="centerVertically" format="boolean" />
     </declare-styleable>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 78c2df6..0b30253 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -40,4 +40,19 @@
     <color name="gesture_tutorial_fake_previous_task_view_color">#9CCC65</color> <!-- Light Green -->
     <color name="gesture_tutorial_action_button_label_color">#FFFFFFFF</color>
     <color name="gesture_tutorial_primary_color">#1A73E8</color> <!-- Blue -->
+
+    <color name="popup_color_primary_light">#FFF</color>
+    <color name="popup_color_secondary_light">#F1F3F4</color>
+    <color name="popup_color_tertiary_light">#E0E0E0</color> <!-- Gray 300 -->
+    <color name="popup_color_primary_dark">#3C4043</color> <!-- Gray 800 -->
+    <color name="popup_color_secondary_dark">#202124</color>
+    <color name="popup_color_tertiary_dark">#757575</color> <!-- Gray 600 -->
+
+    <color name="workspace_text_color_light">#FFF</color>
+    <color name="workspace_text_color_dark">#FF212121</color>
+
+    <color name="text_color_primary_dark">#FFFFFFFF</color>
+    <color name="text_color_secondary_dark">#FFFFFFFF</color>
+    <color name="text_color_tertiary_dark">#CCFFFFFF</color>
+
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 3425f91..447c9ac 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -232,6 +232,8 @@
     <string name="abandoned_promise_explanation">The app for this icon isn\'t installed.
         You can remove it, or search for the app and install it manually.
     </string>
+    <!-- Title for an app which is being installed. -->
+    <string name="app_installing_title"><xliff:g id="name" example="Messenger">%1$s</xliff:g> installing, <xliff:g id="progress" example="30%">%2$s</xliff:g> complete</string>
     <!-- Title for an app which is being downloaded. -->
     <string name="app_downloading_title"><xliff:g id="name" example="Messenger">%1$s</xliff:g> downloading, <xliff:g id="progress" example="30%">%2$s</xliff:g> complete</string>
     <!-- Title for an app whose download has been started. -->
diff --git a/res/values/styles.xml b/res/values/styles.xml
index dc7182f..adc2238 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -35,12 +35,12 @@
         <item name="allAppsInterimScrimAlpha">46</item>
         <item name="allAppsNavBarScrimColor">#66FFFFFF</item>
         <item name="allAppsTheme">@style/AllAppsTheme</item>
-        <item name="popupColorPrimary">#FFF</item>
-        <item name="popupColorSecondary">#F1F3F4</item>
-        <item name="popupColorTertiary">#E0E0E0</item> <!-- Gray 300 -->
+        <item name="popupColorPrimary">@color/popup_color_primary_light</item>
+        <item name="popupColorSecondary">@color/popup_color_secondary_light</item>
+        <item name="popupColorTertiary">@color/popup_color_tertiary_light</item>
         <item name="isMainColorDark">false</item>
         <item name="isWorkspaceDarkText">false</item>
-        <item name="workspaceTextColor">@android:color/white</item>
+        <item name="workspaceTextColor">@color/workspace_text_color_light</item>
         <item name="workspaceShadowColor">#B0000000</item>
         <item name="workspaceAmbientShadowColor">#33000000</item>
         <item name="workspaceKeyShadowColor">#44000000</item>
@@ -74,7 +74,7 @@
     </style>
 
     <style name="LauncherTheme.DarkText" parent="@style/LauncherTheme">
-        <item name="workspaceTextColor">#FF212121</item>
+        <item name="workspaceTextColor">@color/workspace_text_color_dark</item>
         <item name="allAppsInterimScrimAlpha">128</item>
         <item name="workspaceShadowColor">@android:color/transparent</item>
         <item name="workspaceAmbientShadowColor">@android:color/transparent</item>
@@ -88,9 +88,9 @@
     </style>
 
     <style name="LauncherTheme.Dark" parent="@style/LauncherTheme">
-        <item name="android:textColorPrimary">#FFFFFFFF</item>
-        <item name="android:textColorSecondary">#FFFFFFFF</item>
-        <item name="android:textColorTertiary">#CCFFFFFF</item>
+        <item name="android:textColorPrimary">@color/text_color_primary_dark</item>
+        <item name="android:textColorSecondary">@color/text_color_secondary_dark</item>
+        <item name="android:textColorTertiary">@color/text_color_tertiary_dark</item>
         <item name="android:textColorHint">#A0FFFFFF</item>
         <item name="android:colorControlHighlight">#A0FFFFFF</item>
         <item name="android:colorPrimary">#FF212121</item>
@@ -98,9 +98,9 @@
         <item name="allAppsInterimScrimAlpha">102</item>
         <item name="allAppsNavBarScrimColor">#80000000</item>
         <item name="allAppsTheme">@style/AllAppsTheme.Dark</item>
-        <item name="popupColorPrimary">#3C4043</item> <!-- Gray 800 -->
-        <item name="popupColorSecondary">#202124</item>
-        <item name="popupColorTertiary">#757575</item> <!-- Gray 600 -->
+        <item name="popupColorPrimary">@color/popup_color_primary_dark</item>
+        <item name="popupColorSecondary">@color/popup_color_secondary_dark</item>
+        <item name="popupColorTertiary">@color/popup_color_tertiary_dark</item>
         <item name="widgetsTheme">@style/WidgetContainerTheme.Dark</item>
         <item name="folderDotColor">?android:attr/colorPrimary</item>
         <item name="folderFillColor">?android:attr/colorBackground</item>
@@ -125,7 +125,7 @@
         <item name="allAppsInterimScrimAlpha">25</item>
         <item name="folderFillColor">#CDFFFFFF</item>
         <item name="folderTextColor">?attr/workspaceTextColor</item>
-        <item name="workspaceTextColor">#FF212121</item>
+        <item name="workspaceTextColor">@color/workspace_text_color_dark</item>
         <item name="workspaceShadowColor">@android:color/transparent</item>
         <item name="workspaceAmbientShadowColor">@android:color/transparent</item>
         <item name="workspaceKeyShadowColor">@android:color/transparent</item>
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 575d6cd..21297c9 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -51,6 +51,7 @@
 import android.widget.TextView;
 
 import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
 import androidx.core.graphics.ColorUtils;
 
 import com.android.launcher3.Launcher.OnResumeCallback;
@@ -65,9 +66,9 @@
 import com.android.launcher3.graphics.PlaceHolderIconDrawable;
 import com.android.launcher3.graphics.PreloadIconDrawable;
 import com.android.launcher3.icons.DotRenderer;
-import com.android.launcher3.icons.IconCache.IconLoadRequest;
 import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
 import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.icons.cache.HandlerRunnable;
 import com.android.launcher3.model.data.AppInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.ItemInfoWithIcon;
@@ -92,6 +93,7 @@
     private static final int DISPLAY_ALL_APPS = 1;
     private static final int DISPLAY_FOLDER = 2;
     private static final int DISPLAY_HERO_APP = 5;
+    protected static final int DISPLAY_TASKBAR = 6;
 
     private static final int[] STATE_PRESSED = new int[]{android.R.attr.state_pressed};
     private static final float HIGHLIGHT_SCALE = 1.16f;
@@ -140,7 +142,7 @@
     private Drawable mIcon;
     private boolean mCenterVertically;
 
-    private final int mDisplay;
+    protected final int mDisplay;
 
     private final CheckLongPressHelper mLongPressHelper;
 
@@ -169,7 +171,7 @@
     @ViewDebug.ExportedProperty(category = "launcher")
     private boolean mDisableRelayout = false;
 
-    private IconLoadRequest mIconLoadRequest;
+    private HandlerRunnable mIconLoadRequest;
 
     private boolean mEnableIconUpdateAnimation = false;
 
@@ -206,6 +208,8 @@
             defaultIconSize = grid.folderChildIconSizePx;
         } else if (mDisplay == DISPLAY_HERO_APP) {
             defaultIconSize = grid.allAppsIconSizePx;
+        } else if (mDisplay == DISPLAY_TASKBAR) {
+            defaultIconSize = grid.iconSizePx;
         } else {
             // widget_selection or shortcut_popup
             defaultIconSize = grid.iconSizePx;
@@ -268,6 +272,7 @@
         mDotScaleAnim.start();
     }
 
+    @UiThread
     public void applyFromWorkspaceItem(WorkspaceItemInfo info) {
         applyFromWorkspaceItem(info, false);
     }
@@ -284,13 +289,16 @@
         }
     }
 
+    @UiThread
     public void applyFromWorkspaceItem(WorkspaceItemInfo info, boolean promiseStateChanged) {
         applyIconAndLabel(info);
         setTag(info);
         applyLoadingState(promiseStateChanged);
         applyDotState(info, false /* animate */);
+        setDownloadStateContentDescription(info, info.getProgressLevel());
     }
 
+    @UiThread
     public void applyFromApplicationInfo(AppInfo info) {
         applyIconAndLabel(info);
 
@@ -304,11 +312,13 @@
             applyProgressLevel();
         }
         applyDotState(info, false /* animate */);
+        setDownloadStateContentDescription(info, info.getProgressLevel());
     }
 
     /**
      * Apply label and tag using a generic {@link ItemInfoWithIcon}
      */
+    @UiThread
     public void applyFromItemInfoWithIcon(ItemInfoWithIcon info) {
         applyIconAndLabel(info);
         // We don't need to check the info since it's not a WorkspaceItemInfo
@@ -316,16 +326,20 @@
 
         // Verify high res immediately
         verifyHighRes();
+
+        setDownloadStateContentDescription(info, info.getProgressLevel());
     }
 
     /**
      * Apply label and tag using a {@link SearchActionItemInfo}
      */
+    @UiThread
     public void applyFromSearchActionItemInfo(SearchActionItemInfo searchActionItemInfo) {
         applyIconAndLabel(searchActionItemInfo);
         setTag(searchActionItemInfo);
     }
 
+    @UiThread
     protected void applyIconAndLabel(ItemInfoWithIcon info) {
         FastBitmapDrawable iconDrawable = newIcon(getContext(), info);
         mDotParams.color = IconPalette.getMutedColor(info.bitmap.color, 0.54f);
@@ -334,6 +348,7 @@
         applyLabel(info);
     }
 
+    @UiThread
     private void applyLabel(ItemInfoWithIcon info) {
         setText(info.title);
         if (info.contentDescription != null) {
@@ -344,6 +359,16 @@
     }
 
     /**
+     * Directly set the icon and label.
+     */
+    @UiThread
+    public void applyIconAndLabel(Drawable icon, CharSequence label) {
+        setIcon(icon);
+        setText(label);
+        setContentDescription(label);
+    }
+
+    /**
      * Overrides the default long press timeout.
      */
     public void setLongPressTimeoutFactor(float longPressTimeoutFactor) {
@@ -483,6 +508,10 @@
      * @param canvas The canvas to draw to.
      */
     protected void drawDotIfNecessary(Canvas canvas) {
+        if (mDisplay == DISPLAY_TASKBAR) {
+            // TODO: support notification dots in Taskbar
+            return;
+        }
         if (!mForceHideDot && (hasDot() || mDotParams.scale > 0)) {
             getIconBounds(mDotParams.iconBounds);
             Utilities.scaleRectAboutCenter(mDotParams.iconBounds,
@@ -635,9 +664,7 @@
             setContentDescription(info.contentDescription != null
                     ? info.contentDescription : "");
         } else if (progressLevel > 0) {
-            setContentDescription(getContext()
-                    .getString(R.string.app_downloading_title, info.title,
-                            NumberFormat.getPercentInstance().format(progressLevel * 0.01)));
+            setDownloadStateContentDescription(info, progressLevel);
         } else {
             setContentDescription(getContext()
                     .getString(R.string.app_waiting_download_title, info.title));
@@ -713,6 +740,24 @@
         }
     }
 
+    private void setDownloadStateContentDescription(ItemInfoWithIcon info, int progressLevel) {
+        if ((info.runtimeStatusFlags & ItemInfoWithIcon.FLAG_SHOW_DOWNLOAD_PROGRESS_MASK)
+                != 0) {
+            String percentageString = NumberFormat.getPercentInstance()
+                    .format(progressLevel * 0.01);
+            if ((info.runtimeStatusFlags & ItemInfoWithIcon.FLAG_INSTALL_SESSION_ACTIVE) != 0) {
+                setContentDescription(getContext()
+                        .getString(
+                            R.string.app_installing_title, info.title, percentageString));
+            } else if ((info.runtimeStatusFlags
+                    & ItemInfoWithIcon.FLAG_INCREMENTAL_DOWNLOAD_ACTIVE) != 0) {
+                setContentDescription(getContext()
+                        .getString(
+                            R.string.app_downloading_title, info.title, percentageString));
+            }
+        }
+    }
+
     /**
      * Sets the icon for this view based on the layout direction.
      */
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index f681d75..4d5bd5d 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -168,8 +168,10 @@
         // Constants from resources
         float swDPs = Utilities.dpiFromPx(
                 Math.min(info.smallestSize.x, info.smallestSize.y), info.metrics);
-        isTablet = swDPs >= TABLET_MIN_DPS;
-        isLargeTablet = swDPs >= LARGE_TABLET_MIN_DPS;
+        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;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 70a0be9..344ae0a 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -98,13 +98,9 @@
 import android.widget.Toast;
 
 import androidx.annotation.CallSuper;
-import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.StringRes;
 import androidx.annotation.VisibleForTesting;
-import androidx.lifecycle.Lifecycle;
-import androidx.lifecycle.LifecycleOwner;
-import androidx.lifecycle.LifecycleRegistry;
 
 import com.android.launcher3.DropTarget.DragObject;
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
@@ -211,8 +207,7 @@
  * Default launcher application.
  */
 public class Launcher extends StatefulActivity<LauncherState> implements LauncherExterns,
-        Callbacks, InvariantDeviceProfile.OnIDPChangeListener, PluginListener<OverlayPlugin>,
-        LifecycleOwner {
+        Callbacks, InvariantDeviceProfile.OnIDPChangeListener, PluginListener<OverlayPlugin> {
     public static final String TAG = "Launcher";
 
     public static final ActivityTracker<Launcher> ACTIVITY_TRACKER = new ActivityTracker<>();
@@ -275,8 +270,6 @@
     private LauncherAppTransitionManager mAppTransitionManager;
     private Configuration mOldConfig;
 
-    private LifecycleRegistry mLifecycleRegistry;
-
     private LiveSearchManager mLiveSearchManager;
 
     @Thunk
@@ -392,12 +385,12 @@
         mIconCache = app.getIconCache();
         mAccessibilityDelegate = new LauncherAccessibilityDelegate(this);
 
-        mLiveSearchManager = new LiveSearchManager(this);
-
         mDragController = new DragController(this);
         mAllAppsController = new AllAppsTransitionController(this);
         mStateManager = new StateManager<>(this, NORMAL);
 
+        mLiveSearchManager = new LiveSearchManager(this);
+
         mOnboardingPrefs = createOnboardingPrefs(mSharedPrefs);
 
         mAppWidgetManager = new WidgetManagerHelper(this);
@@ -486,15 +479,6 @@
         if (Utilities.ATLEAST_R) {
             getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
         }
-
-        mLifecycleRegistry = new LifecycleRegistry(this);
-        mLifecycleRegistry.setCurrentState(Lifecycle.State.CREATED);
-    }
-
-    @NonNull
-    @Override
-    public Lifecycle getLifecycle() {
-        return mLifecycleRegistry;
     }
 
     public LiveSearchManager getLiveSearchManager() {
@@ -913,7 +897,6 @@
 
     @Override
     protected void onStop() {
-        mLifecycleRegistry.setCurrentState(Lifecycle.State.CREATED);
         super.onStop();
         if (mDeferOverlayCallbacks) {
             checkIfOverlayStillDeferred();
@@ -922,7 +905,7 @@
         }
 
         logStopAndResume(false /* isResume */);
-        mAppWidgetHost.setListenIfResumed(false);
+        mAppWidgetHost.setActivityStarted(false);
         NotificationListener.removeNotificationsChangedListener();
     }
 
@@ -935,9 +918,8 @@
             mOverlayManager.onActivityStarted(this);
         }
 
-        mAppWidgetHost.setListenIfResumed(true);
+        mAppWidgetHost.setActivityStarted(true);
         TraceHelper.INSTANCE.endSection(traceToken);
-        mLifecycleRegistry.setCurrentState(Lifecycle.State.STARTED);
     }
 
     @Override
@@ -956,6 +938,7 @@
         NotificationListener.setNotificationsChangedListener(mPopupDataProvider);
 
         DiscoveryBounce.showForHomeIfNeeded(this);
+        mAppWidgetHost.setActivityResumed(true);
     }
 
     private void logStopAndResume(boolean isResume) {
@@ -1049,7 +1032,7 @@
     @Override
     public void onStateSetEnd(LauncherState state) {
         super.onStateSetEnd(state);
-        getAppWidgetHost().setResumed(state == LauncherState.NORMAL);
+        getAppWidgetHost().setStateIsNormal(state == LauncherState.NORMAL);
         getWorkspace().setClipChildren(!state.hasFlag(FLAG_MULTI_PAGE));
 
         finishAutoCancelActionMode();
@@ -1091,7 +1074,6 @@
         }
 
         TraceHelper.INSTANCE.endSection(traceToken);
-        mLifecycleRegistry.setCurrentState(Lifecycle.State.RESUMED);
     }
 
     @Override
@@ -1099,7 +1081,6 @@
         // Ensure that items added to Launcher are queued until Launcher returns
         ItemInstallQueue.INSTANCE.get(this).pauseModelPush(FLAG_ACTIVITY_PAUSED);
 
-        mLifecycleRegistry.setCurrentState(Lifecycle.State.STARTED);
         super.onPause();
         mDragController.cancelDrag();
         mLastTouchUpTime = -1;
@@ -1108,6 +1089,7 @@
         if (!mDeferOverlayCallbacks) {
             mOverlayManager.onActivityPaused(this);
         }
+        mAppWidgetHost.setActivityResumed(false);
     }
 
     class LauncherOverlayCallbacksImpl implements LauncherOverlayCallbacks {
@@ -1598,7 +1580,6 @@
         mAppTransitionManager.unregisterRemoteAnimations();
         mAppTransitionManager.unregisterRemoteTransitions();
         mUserChangedCallbackCloseable.close();
-        mLifecycleRegistry.setCurrentState(Lifecycle.State.DESTROYED);
         mLiveSearchManager.stop();
     }
 
@@ -1917,6 +1898,13 @@
 
     @Override
     public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
+        if (isViewInTaskbar(v)) {
+            // Start the activity without the hacky workarounds below, which assume the View was
+            // clicked when Launcher was resumed and will be hidden until Launcher is re-resumed
+            // (this isn't the case for Taskbar).
+            return super.startActivitySafely(v, intent, item);
+        }
+
         if (!hasBeenResumed()) {
             // Workaround an issue where the WM launch animation is clobbered when finishing the
             // recents animation into launcher. Defer launching the activity until Launcher is
@@ -2819,6 +2807,13 @@
                 .start();
     }
 
+    /**
+     * @return Whether the View is in the same window as the Taskbar window.
+     */
+    public boolean isViewInTaskbar(View v) {
+        return false;
+    }
+
     private static class NonConfigInstance {
         public Configuration config;
         public Bitmap snapshot;
diff --git a/src/com/android/launcher3/LauncherAppTransitionManager.java b/src/com/android/launcher3/LauncherAppTransitionManager.java
index ac3ad9f..0fa441a 100644
--- a/src/com/android/launcher3/LauncherAppTransitionManager.java
+++ b/src/com/android/launcher3/LauncherAppTransitionManager.java
@@ -50,7 +50,7 @@
         return ActivityOptions.makeClipRevealAnimation(v, left, top, width, height);
     }
 
-    public boolean supportsAdaptiveIconAnimation() {
+    public boolean supportsAdaptiveIconAnimation(View clickedView) {
         return false;
     }
 
diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java
index 7ea6851..fea26df 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHost.java
@@ -49,8 +49,11 @@
 public class LauncherAppWidgetHost extends AppWidgetHost {
 
     private static final int FLAG_LISTENING = 1;
-    private static final int FLAG_RESUMED = 1 << 1;
-    private static final int FLAG_LISTEN_IF_RESUMED = 1 << 2;
+    private static final int FLAG_STATE_IS_NORMAL = 1 << 1;
+    private static final int FLAG_ACTIVITY_STARTED = 1 << 2;
+    private static final int FLAG_ACTIVITY_RESUMED = 1 << 3;
+    private static final int FLAGS_SHOULD_LISTEN =
+            FLAG_STATE_IS_NORMAL | FLAG_ACTIVITY_STARTED | FLAG_ACTIVITY_RESUMED;
 
     public static final int APPWIDGET_HOST_ID = 1024;
 
@@ -59,7 +62,7 @@
     private final SparseArray<PendingAppWidgetHostView> mPendingViews = new SparseArray<>();
 
     private final Context mContext;
-    private int mFlags = FLAG_RESUMED;
+    private int mFlags = FLAG_STATE_IS_NORMAL;
 
     private IntConsumer mAppWidgetRemovedCallback = null;
 
@@ -130,49 +133,45 @@
     }
 
     /**
-     * Updates the resumed state of the host.
-     * When a host is not resumed, it defers calls to startListening until host is resumed again.
-     * But if the host was already listening, it will not call stopListening.
-     *
-     * @see #setListenIfResumed(boolean)
+     * Sets or unsets a flag the can change whether the widget host should be in the listening
+     * state.
      */
-    public void setResumed(boolean isResumed) {
-        if (isResumed == ((mFlags & FLAG_RESUMED) != 0)) {
-            return;
-        }
-        if (isResumed) {
-            mFlags |= FLAG_RESUMED;
-            // Start listening if we were supposed to start listening on resume
-            if ((mFlags & FLAG_LISTEN_IF_RESUMED) != 0 && (mFlags & FLAG_LISTENING) == 0) {
-                startListening();
-            }
+    private void setShouldListenFlag(int flag, boolean on) {
+        if (on) {
+            mFlags |= flag;
         } else {
-            mFlags &= ~FLAG_RESUMED;
+            mFlags &= ~flag;
+        }
+
+        final boolean listening = isListening();
+        if (!listening && (mFlags & FLAGS_SHOULD_LISTEN) == FLAGS_SHOULD_LISTEN) {
+            // Postpone starting listening until all flags are on.
+            startListening();
+        } else if (listening && (mFlags & FLAG_ACTIVITY_STARTED) == 0) {
+            // Postpone stopping listening until the activity is stopped.
+            stopListening();
         }
     }
 
     /**
-     * Updates the listening state of the host. If the host is not resumed, startListening is
-     * deferred until next resume.
-     *
-     * @see #setResumed(boolean)
+     * Registers an "entering/leaving Normal state" event.
      */
-    public void setListenIfResumed(boolean listenIfResumed) {
-        if (listenIfResumed == ((mFlags & FLAG_LISTEN_IF_RESUMED) != 0)) {
-            return;
-        }
-        if (listenIfResumed) {
-            mFlags |= FLAG_LISTEN_IF_RESUMED;
-            if ((mFlags & FLAG_RESUMED) != 0) {
-                // If we are resumed, start listening immediately. Note we do not check for
-                // duplicate calls before calling startListening as startListening is safe to call
-                // multiple times.
-                startListening();
-            }
-        } else {
-            mFlags &= ~FLAG_LISTEN_IF_RESUMED;
-            stopListening();
-        }
+    public void setStateIsNormal(boolean isNormal) {
+        setShouldListenFlag(FLAG_STATE_IS_NORMAL, isNormal);
+    }
+
+    /**
+     * Registers an "activity started/stopped" event.
+     */
+    public void setActivityStarted(boolean isStarted) {
+        setShouldListenFlag(FLAG_ACTIVITY_STARTED, isStarted);
+    }
+
+    /**
+     * Registers an "activity paused/resumed" event.
+     */
+    public void setActivityResumed(boolean isResumed) {
+        setShouldListenFlag(FLAG_ACTIVITY_RESUMED, isResumed);
     }
 
     @Override
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 79476fc..f9a1ded 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -57,6 +57,7 @@
     public static final int ALL_APPS_CONTENT = 1 << 4;
     public static final int VERTICAL_SWIPE_INDICATOR = 1 << 5;
     public static final int OVERVIEW_BUTTONS = 1 << 6;
+    public static final int TASKBAR = 1 << 7;
 
     /** Mask of all the items that are contained in the apps view. */
     public static final int APPS_VIEW_ITEM_MASK =
@@ -186,7 +187,7 @@
     }
 
     public int getVisibleElements(Launcher launcher) {
-        int flags = HOTSEAT_ICONS | VERTICAL_SWIPE_INDICATOR;
+        int flags = HOTSEAT_ICONS | VERTICAL_SWIPE_INDICATOR | TASKBAR;
         if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()
                 && !launcher.getDeviceProfile().isVerticalBarLayout()) {
             flags |= HOTSEAT_SEARCH_BOX;
diff --git a/src/com/android/launcher3/allapps/AllAppsInsetTransitionController.java b/src/com/android/launcher3/allapps/AllAppsInsetTransitionController.java
index 1cf98e1..f6e54aa 100644
--- a/src/com/android/launcher3/allapps/AllAppsInsetTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsInsetTransitionController.java
@@ -164,8 +164,11 @@
                     @Override
                     public void onCancelled(@Nullable WindowInsetsAnimationController controller) {
                         if (DEBUG) {
-                            Log.d(TAG, "Listener.onCancelled ctrl=" + controller
-                                    + " mAnimationController=" + mAnimationController);
+                            // Keep the verbose logging to chase down IME not showing up issue.
+                            // b/178904132
+                            Log.e(TAG, "Listener.onCancelled ctrl=" + controller
+                                    + " mAnimationController=" + mAnimationController,
+                                    new Exception());
                         }
                         if (mState == State.DRAG_START_BOTTOM) {
                             mState = State.DRAG_START_BOTTOM_IME_CANCELLED;
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 2f805fd..a4e1f27 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -268,13 +268,10 @@
                 && !FeatureFlags.DISABLE_INITIAL_IME_IN_ALLAPPS.get() && BuildCompat.isAtLeastR()) {
             mInsetController.onAnimationEnd(mProgress);
             if (Float.compare(mProgress, 0f) == 0) {
-                mLauncher.getLiveSearchManager().start();
                 EditText editText = mAppsView.getSearchUiManager().getEditText();
                 if (editText != null && !mInsetController.showSearchEduIfNecessary()) {
                     editText.requestFocus();
                 }
-            } else {
-                mLauncher.getLiveSearchManager().stop();
             }
             // TODO: should make the controller hide synchronously
         }
diff --git a/src/com/android/launcher3/allapps/search/LiveSearchManager.java b/src/com/android/launcher3/allapps/search/LiveSearchManager.java
index ec33908..e52c790 100644
--- a/src/com/android/launcher3/allapps/search/LiveSearchManager.java
+++ b/src/com/android/launcher3/allapps/search/LiveSearchManager.java
@@ -15,8 +15,16 @@
  */
 package com.android.launcher3.allapps.search;
 
+import static com.android.launcher3.LauncherState.ALL_APPS;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_ENTRY;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_EXIT;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR;
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 import static com.android.launcher3.widget.WidgetHostViewLoader.getDefaultOptionsForWidget;
 
+import android.app.Activity;
+import android.app.Application.ActivityLifecycleCallbacks;
 import android.appwidget.AppWidgetHost;
 import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetManager;
@@ -26,35 +34,52 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.UserHandle;
+import android.util.Log;
 
-import androidx.lifecycle.LiveData;
+import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
+import androidx.annotation.WorkerThread;
+import androidx.lifecycle.Observer;
 import androidx.slice.Slice;
-import androidx.slice.widget.SliceLiveData;
+import androidx.slice.SliceViewManager;
+import androidx.slice.SliceViewManager.SliceCallback;
 
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.logging.InstanceId;
+import com.android.launcher3.logging.InstanceIdSequence;
+import com.android.launcher3.logging.StatsLogManager.StatsLogger;
+import com.android.launcher3.statemanager.StateManager.StateListener;
 import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.SafeCloseable;
 import com.android.launcher3.widget.PendingAddWidgetInfo;
 
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.Optional;
 
 /**
  * Manages Lifecycle for Live search results
  */
-public class LiveSearchManager {
+public class LiveSearchManager implements StateListener<LauncherState> {
+
+    private static final String TAG = "LiveSearchManager";
 
     public static final int SEARCH_APPWIDGET_HOST_ID = 2048;
 
     private final Launcher mLauncher;
-    private final AppWidgetManager mAppWidgetManger;
+    private final HashMap<Uri, SliceLifeCycle> mUriSliceMap = new HashMap<>();
+
     private final HashMap<ComponentKey, SearchWidgetInfoContainer> mWidgetPlaceholders =
             new HashMap<>();
-    private final HashMap<Uri, LiveData<Slice>> mUriSliceMap = new HashMap<>();
     private SearchWidgetHost mSearchWidgetHost;
+    private InstanceId mLogInstanceId;
+    private LauncherState mPrevLauncherState;
 
     public LiveSearchManager(Launcher launcher) {
         mLauncher = launcher;
-        mAppWidgetManger = AppWidgetManager.getInstance(launcher);
+        mLauncher.getStateManager().addStateListener(this);
     }
 
     /**
@@ -63,77 +88,102 @@
      */
     public SearchWidgetInfoContainer getPlaceHolderWidget(AppWidgetProviderInfo providerInfo) {
         if (mSearchWidgetHost == null) {
-            throw new RuntimeException("AppWidgetHost has not been created yet");
+            mSearchWidgetHost = new SearchWidgetHost(mLauncher);
+            mSearchWidgetHost.startListening();
         }
 
         ComponentName provider = providerInfo.provider;
         UserHandle userHandle = providerInfo.getProfile();
 
         ComponentKey key = new ComponentKey(provider, userHandle);
-        SearchWidgetInfoContainer view = mWidgetPlaceholders.getOrDefault(key, null);
         if (mWidgetPlaceholders.containsKey(key)) {
             return mWidgetPlaceholders.get(key);
         }
+
         LauncherAppWidgetProviderInfo pinfo = LauncherAppWidgetProviderInfo.fromProviderInfo(
                 mLauncher, providerInfo);
         PendingAddWidgetInfo pendingAddWidgetInfo = new PendingAddWidgetInfo(pinfo);
 
         Bundle options = getDefaultOptionsForWidget(mLauncher, pendingAddWidgetInfo);
         int appWidgetId = mSearchWidgetHost.allocateAppWidgetId();
-        boolean success = mAppWidgetManger.bindAppWidgetIdIfAllowed(appWidgetId, userHandle,
-                provider, options);
+        boolean success = AppWidgetManager.getInstance(mLauncher)
+                .bindAppWidgetIdIfAllowed(appWidgetId, userHandle, provider, options);
         if (!success) {
+            mSearchWidgetHost.deleteAppWidgetId(appWidgetId);
             mWidgetPlaceholders.put(key, null);
             return null;
         }
 
-        view = (SearchWidgetInfoContainer) mSearchWidgetHost.createView(mLauncher, appWidgetId,
-                providerInfo);
+        SearchWidgetInfoContainer view = (SearchWidgetInfoContainer) mSearchWidgetHost.createView(
+                mLauncher, appWidgetId, providerInfo);
         view.setTag(pendingAddWidgetInfo);
         mWidgetPlaceholders.put(key, view);
         return view;
     }
 
     /**
-     * Creates {@link LiveData<Slice>} from Slice Uri. Caches created live data to be reused
-     * within the same search session. Removes previous observers when new SliceView request a
-     * live data for observation.
-     */
-    public LiveData<Slice> getSliceForUri(Uri sliceUri) {
-        LiveData<Slice> sliceLiveData = mUriSliceMap.getOrDefault(sliceUri, null);
-        if (sliceLiveData == null) {
-            sliceLiveData = SliceLiveData.fromUri(mLauncher, sliceUri);
-            mUriSliceMap.put(sliceUri, sliceLiveData);
-        }
-        return sliceLiveData;
-    }
-
-    /**
-     * Start search session
-     */
-    public void start() {
-        stop();
-        mSearchWidgetHost = new SearchWidgetHost(mLauncher);
-        mSearchWidgetHost.startListening();
-    }
-
-    /**
      * Stop search session
      */
     public void stop() {
+        clearWidgetHost();
+    }
+
+    private void clearWidgetHost() {
         if (mSearchWidgetHost != null) {
             mSearchWidgetHost.stopListening();
+            mSearchWidgetHost.clearViews();
             mSearchWidgetHost.deleteHost();
-            for (SearchWidgetInfoContainer placeholder : mWidgetPlaceholders.values()) {
-                placeholder.clearListeners();
-            }
             mWidgetPlaceholders.clear();
             mSearchWidgetHost = null;
         }
-        for (LiveData<Slice> liveData : mUriSliceMap.values()) {
-            liveData.removeObservers(mLauncher);
+    }
+
+    @Override
+    public void onStateTransitionStart(LauncherState toState) {
+        mPrevLauncherState = mLauncher.getStateManager().getCurrentStableState();
+    }
+
+    @Override
+    public void onStateTransitionComplete(LauncherState finalState) {
+        if (finalState != ALL_APPS) {
+            // Clear all search session related objects
+            mUriSliceMap.values().forEach(SliceLifeCycle::destroy);
+            mUriSliceMap.clear();
+
+            clearWidgetHost();
         }
-        mUriSliceMap.clear();
+
+        StatsLogger logger = mLauncher.getStatsLogManager().logger();
+        if (finalState.equals(ALL_APPS)) {
+            mLogInstanceId = new InstanceIdSequence().newInstanceId();
+            logger.withInstanceId(mLogInstanceId).log(LAUNCHER_ALLAPPS_ENTRY);
+        } else if (mPrevLauncherState.equals(ALL_APPS)) {
+            logger.withInstanceId(mLogInstanceId).log(LAUNCHER_ALLAPPS_EXIT);
+            mLogInstanceId = null;
+        }
+    }
+
+    /**
+     * Adds a new observer for the provided uri and returns a callback to cancel this observer
+     */
+    public SafeCloseable addObserver(Uri uri, Observer<Slice> listener) {
+        SliceLifeCycle slc = mUriSliceMap.get(uri);
+        if (slc == null) {
+            slc = new SliceLifeCycle(uri, mLauncher);
+            mUriSliceMap.put(uri, slc);
+        }
+        slc.addListener(listener);
+
+        final SliceLifeCycle sliceLifeCycle = slc;
+        return () -> sliceLifeCycle.removeListener(listener);
+    }
+
+    /**
+     * Returns {@link InstanceId} that should be used for logging events within search session, if
+     * available.
+     */
+    public Optional<InstanceId> getLogInstanceId() {
+        return Optional.ofNullable(mLogInstanceId);
     }
 
     static class SearchWidgetHost extends AppWidgetHost {
@@ -146,5 +196,121 @@
                 AppWidgetProviderInfo appWidget) {
             return new SearchWidgetInfoContainer(context);
         }
+
+        @Override
+        public void clearViews() {
+            super.clearViews();
+        }
+    }
+
+    private static class SliceLifeCycle
+            implements ActivityLifecycleCallbacks, SliceCallback {
+
+        private final Uri mUri;
+        private final Launcher mLauncher;
+        private final SliceViewManager mSliceViewManager;
+        private final ArrayList<Observer<Slice>> mListeners = new ArrayList<>();
+
+        private boolean mDestroyed = false;
+        private boolean mWasListening = false;
+
+        SliceLifeCycle(Uri uri, Launcher launcher) {
+            mUri = uri;
+            mLauncher = launcher;
+            mSliceViewManager = SliceViewManager.getInstance(launcher);
+            launcher.registerActivityLifecycleCallbacks(this);
+
+            if (launcher.isDestroyed()) {
+                onActivityDestroyed(launcher);
+            } else if (launcher.isStarted()) {
+                onActivityStarted(launcher);
+            }
+        }
+
+        @Override
+        public void onActivityDestroyed(Activity activity) {
+            destroy();
+        }
+
+        @Override
+        public void onActivityStarted(Activity activity) {
+            updateListening();
+        }
+
+        @Override
+        public void onActivityStopped(Activity activity) {
+            updateListening();
+        }
+
+        private void updateListening() {
+            boolean isListening = mDestroyed
+                    ? false
+                    : (mLauncher.isStarted() && !mListeners.isEmpty());
+            UI_HELPER_EXECUTOR.execute(() -> uploadListeningBg(isListening));
+        }
+
+        @WorkerThread
+        private void uploadListeningBg(boolean isListening) {
+            if (mWasListening != isListening) {
+                mWasListening = isListening;
+                if (isListening) {
+                    mSliceViewManager.registerSliceCallback(mUri, MAIN_EXECUTOR, this);
+                    // Update slice one-time on the different thread so that we can display
+                    // multiple slices in parallel
+                    THREAD_POOL_EXECUTOR.execute(this::updateSlice);
+                } else {
+                    mSliceViewManager.unregisterSliceCallback(mUri, this);
+                }
+            }
+        }
+
+        @UiThread
+        private void addListener(Observer<Slice> listener) {
+            mListeners.add(listener);
+            updateListening();
+        }
+
+        @UiThread
+        private void removeListener(Observer<Slice> listener) {
+            mListeners.remove(listener);
+            updateListening();
+        }
+
+        @WorkerThread
+        private void updateSlice() {
+            try {
+                Slice s = mSliceViewManager.bindSlice(mUri);
+                MAIN_EXECUTOR.execute(() -> onSliceUpdated(s));
+            } catch (Exception e) {
+                Log.d(TAG, "Error fetching slice", e);
+            }
+        }
+
+        @UiThread
+        @Override
+        public void onSliceUpdated(@Nullable Slice s) {
+            mListeners.forEach(l -> l.onChanged(s));
+        }
+
+        private void destroy() {
+            if (mDestroyed) {
+                return;
+            }
+            mDestroyed = true;
+            mLauncher.unregisterActivityLifecycleCallbacks(this);
+            mListeners.clear();
+        }
+
+        @Override
+        public void onActivityCreated(Activity activity, Bundle bundle) { }
+
+        @Override
+        public void onActivityPaused(Activity activity) { }
+
+        @Override
+        public void onActivityResumed(Activity activity) { }
+
+        @Override
+        public void onActivitySaveInstanceState(Activity activity, Bundle bundle) { }
     }
 }
diff --git a/src/com/android/launcher3/allapps/search/SearchWidgetInfoContainer.java b/src/com/android/launcher3/allapps/search/SearchWidgetInfoContainer.java
index b5c2268..8e5f8cb 100644
--- a/src/com/android/launcher3/allapps/search/SearchWidgetInfoContainer.java
+++ b/src/com/android/launcher3/allapps/search/SearchWidgetInfoContainer.java
@@ -70,10 +70,4 @@
         mListeners.remove(hostView);
     }
 
-    /**
-     * Removes all AppWidgetHost update listeners
-     */
-    public void clearListeners() {
-        mListeners.clear();
-    }
 }
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 086d665..b61799f 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -98,12 +98,12 @@
     public static final BooleanFlag ENABLE_DEVICE_SEARCH = new DeviceFlag(
             "ENABLE_DEVICE_SEARCH", false, "Allows on device search in all apps");
 
-    public static final BooleanFlag USE_SEARCH_API = getDebugFlag(
-            "USE_SEARCH_API", true, "Use SearchUIManager api for device search");
-
     public static final BooleanFlag DISABLE_INITIAL_IME_IN_ALLAPPS = getDebugFlag(
             "DISABLE_INITIAL_IME_IN_ALLAPPS", false, "Disable default IME state in all apps");
 
+    public static final BooleanFlag DISABLE_SLICE_IN_ALLAPPS = getDebugFlag(
+            "DISABLE_SLICE_IN_ALLAPPS", true, "Disable slice in all apps");
+
     public static final BooleanFlag FOLDER_NAME_SUGGEST = new DeviceFlag(
             "FOLDER_NAME_SUGGEST", true,
             "Suggests folder names instead of blank text.");
diff --git a/src/com/android/launcher3/graphics/PreloadIconDrawable.java b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
index 304d496..ce824df 100644
--- a/src/com/android/launcher3/graphics/PreloadIconDrawable.java
+++ b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
@@ -24,6 +24,7 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
 import android.content.Context;
+import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Matrix;
@@ -34,10 +35,12 @@
 import android.util.Pair;
 import android.util.Property;
 import android.util.SparseArray;
+import android.view.ContextThemeWrapper;
 
 import com.android.launcher3.FastBitmapDrawable;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.model.data.ItemInfoWithIcon;
+import com.android.launcher3.util.Themes;
 
 import java.lang.ref.WeakReference;
 
@@ -77,6 +80,9 @@
     private static final SparseArray<WeakReference<Pair<Path, Bitmap>>> sShadowCache =
             new SparseArray<>();
 
+    private static final int PRELOAD_ACCENT_COLOR_INDEX = 0;
+    private static final int PRELOAD_BACKGROUND_COLOR_INDEX = 1;
+
     private final Matrix mTmpMatrix = new Matrix();
     private final PathMeasure mPathMeasure = new PathMeasure();
 
@@ -91,6 +97,9 @@
 
     private Bitmap mShadowBitmap;
     private final int mIndicatorColor;
+    private final int mSystemAccentColor;
+    private final int mSystemBackgroundColor;
+    private final boolean mIsDarkMode;
 
     private int mTrackAlpha;
     private float mTrackLength;
@@ -104,11 +113,23 @@
 
     private ObjectAnimator mCurrentAnim;
 
+    private boolean mIsStartable;
+
     public PreloadIconDrawable(ItemInfoWithIcon info, Context context) {
-        this(info, IconPalette.getPreloadProgressColor(context, info.bitmap.color));
+        this(
+                info,
+                IconPalette.getPreloadProgressColor(context, info.bitmap.color),
+                getPreloadColors(context),
+            (context.getResources().getConfiguration().uiMode
+                    & Configuration.UI_MODE_NIGHT_MASK
+                    & Configuration.UI_MODE_NIGHT_YES) != 0) /* isDarkMode */;
     }
 
-    public PreloadIconDrawable(ItemInfoWithIcon info, int indicatorColor) {
+    public PreloadIconDrawable(
+            ItemInfoWithIcon info,
+            int indicatorColor,
+            int[] preloadColors,
+            boolean isDarkMode) {
         super(info.bitmap);
         mItem = info;
         mShapePath = getShapePath();
@@ -120,9 +141,12 @@
         mProgressPaint.setStrokeCap(Paint.Cap.ROUND);
         mIndicatorColor = indicatorColor;
 
-        setInternalProgress(0);
+        mSystemAccentColor = preloadColors[PRELOAD_ACCENT_COLOR_INDEX];
+        mSystemBackgroundColor = preloadColors[PRELOAD_BACKGROUND_COLOR_INDEX];
+        mIsDarkMode = isDarkMode;
 
-        setIsDisabled(!info.isAppStartable());
+        setInternalProgress(info.getProgressLevel());
+        setIsStartable(info.isAppStartable());
     }
 
     @Override
@@ -148,7 +172,7 @@
     }
 
     private Bitmap getShadowBitmap(int width, int height, float shadowRadius) {
-        int key = (width << 16) | height;
+        int key = ((width << 16) | height) * (mIsDarkMode ? -1 : 1);
         WeakReference<Pair<Path, Bitmap>> shadowRef = sShadowCache.get(key);
         Pair<Path, Bitmap> cache = shadowRef != null ? shadowRef.get() : null;
         Bitmap shadow = cache != null && cache.first.equals(mShapePath) ? cache.second : null;
@@ -157,8 +181,9 @@
         }
         shadow = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
         Canvas c = new Canvas(shadow);
-        mProgressPaint.setShadowLayer(shadowRadius, 0, 0, COLOR_SHADOW);
-        mProgressPaint.setColor(COLOR_TRACK);
+        mProgressPaint.setShadowLayer(shadowRadius, 0, 0, mIsStartable
+                ? COLOR_SHADOW : mSystemAccentColor);
+        mProgressPaint.setColor(mIsStartable ? COLOR_TRACK : mSystemBackgroundColor);
         mProgressPaint.setAlpha(MAX_PAINT_ALPHA);
         c.drawPath(mScaledTrackPath, mProgressPaint);
         mProgressPaint.clearShadowLayer();
@@ -176,7 +201,7 @@
         }
 
         // Draw track.
-        mProgressPaint.setColor(mIndicatorColor);
+        mProgressPaint.setColor(mIsStartable ? mIndicatorColor : mSystemAccentColor);
         mProgressPaint.setAlpha(mTrackAlpha);
         if (mShadowBitmap != null) {
             canvas.drawBitmap(mShadowBitmap, bounds.left, bounds.top, mProgressPaint);
@@ -215,6 +240,14 @@
         return !mRanFinishAnimation;
     }
 
+    /** Sets whether this icon should display the startable app UI. */
+    public void setIsStartable(boolean isStartable) {
+        if (mIsStartable != isStartable) {
+            mIsStartable = isStartable;
+            setIsDisabled(!isStartable);
+        }
+    }
+
     private void updateInternalState(float finalProgress, boolean shouldAnimate, boolean isFinish) {
         if (mCurrentAnim != null) {
             mCurrentAnim.cancel();
@@ -295,6 +328,18 @@
         invalidateSelf();
     }
 
+    private static int[] getPreloadColors(Context context) {
+        Context dayNightThemeContext = new ContextThemeWrapper(
+                context, android.R.style.Theme_DeviceDefault_DayNight);
+        int[] preloadColors = new int[2];
+
+        preloadColors[PRELOAD_ACCENT_COLOR_INDEX] = Themes.getColorAccent(dayNightThemeContext);
+        preloadColors[PRELOAD_BACKGROUND_COLOR_INDEX] = Themes.getColorBackgroundFloating(
+                dayNightThemeContext);
+
+        return preloadColors;
+    }
+
     /**
      * Returns a FastBitmapDrawable with the icon.
      */
@@ -305,13 +350,21 @@
     @Override
     public ConstantState getConstantState() {
         return new PreloadIconConstantState(
-                mBitmap, mIconColor, !mItem.isAppStartable(), mItem, mIndicatorColor);
+                mBitmap,
+                mIconColor,
+                !mItem.isAppStartable(),
+                mItem,
+                mIndicatorColor,
+                new int[] {mSystemAccentColor, mSystemBackgroundColor},
+                mIsDarkMode);
     }
 
     protected static class PreloadIconConstantState extends FastBitmapConstantState {
 
         protected final ItemInfoWithIcon mInfo;
         protected final int mIndicatorColor;
+        protected final int[] mPreloadColors;
+        protected final boolean mIsDarkMode;
         protected final int mLevel;
 
         public PreloadIconConstantState(
@@ -319,19 +372,24 @@
                 int iconColor,
                 boolean isDisabled,
                 ItemInfoWithIcon info,
-                int indicatorcolor) {
+                int indicatorColor,
+                int[] preloadColors,
+                boolean isDarkMode) {
             super(bitmap, iconColor, isDisabled);
             mInfo = info;
-            mIndicatorColor = indicatorcolor;
+            mIndicatorColor = indicatorColor;
+            mPreloadColors = preloadColors;
+            mIsDarkMode = isDarkMode;
             mLevel = info.getProgressLevel();
         }
 
         @Override
         public PreloadIconDrawable newDrawable() {
-            PreloadIconDrawable drawable = new PreloadIconDrawable(mInfo, mIndicatorColor);
-            drawable.setLevel(mLevel);
-            drawable.setIsDisabled(mIsDisabled);
-            return drawable;
+            return new PreloadIconDrawable(
+                    mInfo,
+                    mIndicatorColor,
+                    mPreloadColors,
+                    mIsDarkMode);
         }
 
         @Override
diff --git a/src/com/android/launcher3/icons/IconCache.java b/src/com/android/launcher3/icons/IconCache.java
index 8013557..61f2c2a 100644
--- a/src/com/android/launcher3/icons/IconCache.java
+++ b/src/com/android/launcher3/icons/IconCache.java
@@ -31,7 +31,6 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ShortcutInfo;
 import android.graphics.drawable.Drawable;
-import android.os.Handler;
 import android.os.Process;
 import android.os.UserHandle;
 import android.util.Log;
@@ -134,7 +133,7 @@
      * Fetches high-res icon for the provided ItemInfo and updates the caller when done.
      * @return a request ID that can be used to cancel the request.
      */
-    public IconLoadRequest updateIconInBackground(final ItemInfoUpdateReceiver caller,
+    public HandlerRunnable updateIconInBackground(final ItemInfoUpdateReceiver caller,
             final ItemInfoWithIcon info) {
         Preconditions.assertUIThread();
         if (mPendingIconRequestCount <= 0) {
@@ -142,20 +141,18 @@
         }
         mPendingIconRequestCount ++;
 
-        IconLoadRequest request = new IconLoadRequest(mWorkerHandler, this::onIconRequestEnd) {
-            @Override
-            public void run() {
-                if (info instanceof AppInfo || info instanceof WorkspaceItemInfo) {
-                    getTitleAndIcon(info, false);
-                } else if (info instanceof PackageItemInfo) {
-                    getTitleAndIconForApp((PackageItemInfo) info, false);
-                }
-                MAIN_EXECUTOR.execute(() -> {
-                    caller.reapplyItemInfo(info);
-                    onEnd();
-                });
-            }
-        };
+        HandlerRunnable<ItemInfoWithIcon> request = new HandlerRunnable<>(mWorkerHandler,
+                () -> {
+                    if (info instanceof AppInfo || info instanceof WorkspaceItemInfo) {
+                        getTitleAndIcon(info, false);
+                    } else if (info instanceof PackageItemInfo) {
+                        getTitleAndIconForApp((PackageItemInfo) info, false);
+                    }
+                    return info;
+                },
+                MAIN_EXECUTOR,
+                caller::reapplyItemInfo,
+                this::onIconRequestEnd);
         Utilities.postAsyncCallback(mWorkerHandler, request);
         return request;
     }
@@ -336,12 +333,6 @@
         return super.getEntryFromDB(cacheKey, entry, lowRes);
     }
 
-    public static abstract class IconLoadRequest extends HandlerRunnable {
-        IconLoadRequest(Handler handler, Runnable endRunnable) {
-            super(handler, endRunnable);
-        }
-    }
-
     /**
      * Interface for receiving itemInfo with high-res icon.
      */
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 2066cd3..0292d20 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -344,6 +344,12 @@
 
         @UiEvent(doc = "Current grid size is changed to 2.")
         LAUNCHER_GRID_SIZE_2(665),
+
+        @UiEvent(doc = "Launcher entered into AllApps state.")
+        LAUNCHER_ALLAPPS_ENTRY(692),
+
+        @UiEvent(doc = "Launcher exited from AllApps state.")
+        LAUNCHER_ALLAPPS_EXIT(693),
         ;
 
         // ADD MORE
diff --git a/src/com/android/launcher3/model/AllAppsList.java b/src/com/android/launcher3/model/AllAppsList.java
index d4fa278..e3e4b69 100644
--- a/src/com/android/launcher3/model/AllAppsList.java
+++ b/src/com/android/launcher3/model/AllAppsList.java
@@ -203,11 +203,16 @@
     /**
      * Add the icons for the supplied apk called packageName.
      */
-    public void addPackage(Context context, String packageName, UserHandle user) {
-        for (LauncherActivityInfo info : context.getSystemService(LauncherApps.class)
-                .getActivityList(packageName, user)) {
+    public List<LauncherActivityInfo> addPackage(
+            Context context, String packageName, UserHandle user) {
+        List<LauncherActivityInfo> activities = context.getSystemService(LauncherApps.class)
+                .getActivityList(packageName, user);
+
+        for (LauncherActivityInfo info : activities) {
             add(new AppInfo(context, info, user), info);
         }
+
+        return activities;
     }
 
     /**
@@ -250,7 +255,8 @@
     /**
      * Add and remove icons for this package which has been updated.
      */
-    public void updatePackage(Context context, String packageName, UserHandle user) {
+    public List<LauncherActivityInfo> updatePackage(
+            Context context, String packageName, UserHandle user) {
         final List<LauncherActivityInfo> matches = context.getSystemService(LauncherApps.class)
                 .getActivityList(packageName, user);
         if (matches.size() > 0) {
@@ -297,6 +303,8 @@
                 }
             }
         }
+
+        return matches;
     }
 
     /**
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 3275d59..f13a109 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -22,6 +22,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.LauncherActivityInfo;
 import android.content.pm.LauncherApps;
 import android.content.pm.ShortcutInfo;
 import android.os.UserHandle;
@@ -51,6 +52,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 
@@ -95,6 +97,7 @@
                 ? ItemInfoMatcher.ofUser(mUser) // We want to update all packages for this user
                 : ItemInfoMatcher.ofPackages(packageSet, mUser);
         final HashSet<ComponentName> removedComponents = new HashSet<>();
+        final HashMap<String, List<LauncherActivityInfo>> activitiesLists = new HashMap<>();
 
         switch (mOp) {
             case OP_ADD: {
@@ -104,7 +107,8 @@
                     if (FeatureFlags.PROMISE_APPS_IN_ALL_APPS.get()) {
                         appsList.removePackage(packages[i], mUser);
                     }
-                    appsList.addPackage(context, packages[i], mUser);
+                    activitiesLists.put(
+                            packages[i], appsList.addPackage(context, packages[i], mUser));
                 }
                 flagOp = FlagOp.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE);
                 break;
@@ -115,7 +119,8 @@
                     for (int i = 0; i < N; i++) {
                         if (DEBUG) Log.d(TAG, "mAllAppsList.updatePackage " + packages[i]);
                         iconCache.updateIconsForPkg(packages[i], mUser);
-                        appsList.updatePackage(context, packages[i], mUser);
+                        activitiesLists.put(
+                                packages[i], appsList.updatePackage(context, packages[i], mUser));
                         app.getWidgetCache().removePackage(packages[i], mUser);
                     }
                 }
@@ -247,7 +252,14 @@
 
                         if (isNewApkAvailable
                                 && si.itemType == Favorites.ITEM_TYPE_APPLICATION) {
-                            si.setProgressLevel(100, PackageInstallInfo.STATUS_INSTALLED);
+                            List<LauncherActivityInfo> activities = activitiesLists.get(
+                                    packageName);
+                            si.setProgressLevel(
+                                    activities == null || activities.isEmpty()
+                                            ? 100
+                                            : PackageManagerHelper.getLoadingProgress(
+                                                    activities.get(0)),
+                                    PackageInstallInfo.STATUS_INSTALLED_DOWNLOADING);
                             iconCache.getTitleAndIcon(si, si.usingLowResIcon());
                             infoUpdated = true;
                         }
diff --git a/src/com/android/launcher3/model/data/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java
index b11b419..3851ab0 100644
--- a/src/com/android/launcher3/model/data/ItemInfo.java
+++ b/src/com/android/launcher3/model/data/ItemInfo.java
@@ -225,7 +225,7 @@
     protected String dumpProperties() {
         return "id=" + id
                 + " type=" + LauncherSettings.Favorites.itemTypeToString(itemType)
-                + " container=" + LauncherSettings.Favorites.containerToString(container)
+                + " container=" + getContainerInfo()
                 + " targetComponent=" + getTargetComponent()
                 + " screen=" + screenId
                 + " cell(" + cellX + "," + cellY + ")"
@@ -352,7 +352,10 @@
         return itemBuilder;
     }
 
-    protected ContainerInfo getContainerInfo() {
+    /**
+     * Returns {@link ContainerInfo} used when logging this item.
+     */
+    public ContainerInfo getContainerInfo() {
         switch (container) {
             case CONTAINER_HOTSEAT:
                 return ContainerInfo.newBuilder()
diff --git a/src/com/android/launcher3/model/data/SearchActionItemInfo.java b/src/com/android/launcher3/model/data/SearchActionItemInfo.java
index 36afe50..8469569 100644
--- a/src/com/android/launcher3/model/data/SearchActionItemInfo.java
+++ b/src/com/android/launcher3/model/data/SearchActionItemInfo.java
@@ -30,7 +30,7 @@
 
     public static final int FLAG_SHOULD_START = 1 << 1;
     public static final int FLAG_SHOULD_START_FOR_RESULT = FLAG_SHOULD_START | 1 << 2;
-    public static final int FLAG_BADGE_FROM_ICON = 1 << 3;
+    public static final int FLAG_BADGE_WITH_PACKAGE = 1 << 3;
     public static final int FLAG_PRIMARY_ICON_FROM_TITLE = 1 << 4;
 
     private final String mFallbackPackageName;
diff --git a/src/com/android/launcher3/settings/DeveloperOptionsFragment.java b/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
index 47d214d..c9fb956 100644
--- a/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
+++ b/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
@@ -15,7 +15,10 @@
  */
 package com.android.launcher3.settings;
 
+import static android.content.pm.PackageManager.GET_RESOLVED_FILTER;
 import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
+import static android.view.View.GONE;
+import static android.view.View.VISIBLE;
 
 import static com.android.launcher3.uioverrides.plugins.PluginManagerWrapper.PLUGIN_CHANGED;
 import static com.android.launcher3.uioverrides.plugins.PluginManagerWrapper.pluginEnabledKey;
@@ -26,10 +29,8 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
 import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
@@ -130,7 +131,7 @@
     public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
         super.onViewCreated(view, savedInstanceState);
         EditText filterBox = view.findViewById(R.id.filter_box);
-        filterBox.setVisibility(View.VISIBLE);
+        filterBox.setVisibility(VISIBLE);
         filterBox.addTextChangedListener(new TextWatcher() {
             @Override
             public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
@@ -212,7 +213,7 @@
 
         Set<String> pluginActions = manager.getPluginActions();
 
-        ArrayMap<Pair<String, String>, ArrayList<Pair<String, ServiceInfo>>> plugins =
+        ArrayMap<Pair<String, String>, ArrayList<Pair<String, ResolveInfo>>> plugins =
                 new ArrayMap<>();
 
         Set<String> pluginPermissionApps = pm.getPackagesHoldingPermissions(
@@ -224,7 +225,7 @@
         for (String action : pluginActions) {
             String name = toName(action);
             List<ResolveInfo> result = pm.queryIntentServices(
-                    new Intent(action), MATCH_DISABLED_COMPONENTS);
+                    new Intent(action), MATCH_DISABLED_COMPONENTS | GET_RESOLVED_FILTER);
             for (ResolveInfo info : result) {
                 String packageName = info.serviceInfo.packageName;
                 if (!pluginPermissionApps.contains(packageName)) {
@@ -235,7 +236,7 @@
                 if (!plugins.containsKey(key)) {
                     plugins.put(key, new ArrayList<>());
                 }
-                plugins.get(key).add(Pair.create(name, info.serviceInfo));
+                plugins.get(key).add(Pair.create(name, info));
             }
         }
 
@@ -243,11 +244,11 @@
         plugins.forEach((key, si) -> {
             String packageName = key.first;
             List<ComponentName> componentNames = si.stream()
-                    .map(p -> new ComponentName(packageName, p.second.name))
+                    .map(p -> new ComponentName(packageName, p.second.serviceInfo.name))
                     .collect(Collectors.toList());
             if (!componentNames.isEmpty()) {
                 SwitchPreference pref = new PluginPreference(
-                        prefContext, si.get(0).second.applicationInfo, enabler, componentNames);
+                        prefContext, si.get(0).second, enabler, componentNames);
                 pref.setSummary("Plugins: "
                         + si.stream().map(p -> p.first).collect(Collectors.joining(", ")));
                 mPluginsCategory.addPreference(pref);
@@ -341,21 +342,33 @@
     }
 
     private static class PluginPreference extends SwitchPreference {
-        private final boolean mHasSettings;
-        private final PreferenceDataStore mPluginEnabler;
         private final String mPackageName;
+        private final ResolveInfo mSettingsInfo;
+        private final PreferenceDataStore mPluginEnabler;
         private final List<ComponentName> mComponentNames;
 
-        PluginPreference(Context prefContext, ApplicationInfo info,
+        PluginPreference(Context prefContext, ResolveInfo pluginInfo,
                 PreferenceDataStore pluginEnabler, List<ComponentName> componentNames) {
             super(prefContext);
             PackageManager pm = prefContext.getPackageManager();
-            mHasSettings = pm.resolveActivity(new Intent(ACTION_PLUGIN_SETTINGS)
-                    .setPackage(info.packageName), 0) != null;
-            mPackageName = info.packageName;
-            mComponentNames = componentNames;
+            mPackageName = pluginInfo.serviceInfo.applicationInfo.packageName;
+            Intent settingsIntent = new Intent(ACTION_PLUGIN_SETTINGS).setPackage(mPackageName);
+            // If any Settings activity in app has category filters, set plugin action as category.
+            List<ResolveInfo> settingsInfos =
+                    pm.queryIntentActivities(settingsIntent, GET_RESOLVED_FILTER);
+            if (pluginInfo.filter != null) {
+                for (ResolveInfo settingsInfo : settingsInfos) {
+                    if (settingsInfo.filter != null && settingsInfo.filter.countCategories() > 0) {
+                        settingsIntent.addCategory(pluginInfo.filter.getAction(0));
+                        break;
+                    }
+                }
+            }
+
+            mSettingsInfo = pm.resolveActivity(settingsIntent, 0);
             mPluginEnabler = pluginEnabler;
-            setTitle(info.loadLabel(pm));
+            mComponentNames = componentNames;
+            setTitle(pluginInfo.loadLabel(pm));
             setChecked(isPluginEnabled());
             setWidgetLayoutResource(R.layout.switch_preference_with_settings);
         }
@@ -396,17 +409,14 @@
         @Override
         public void onBindViewHolder(PreferenceViewHolder holder) {
             super.onBindViewHolder(holder);
-            holder.findViewById(R.id.settings).setVisibility(mHasSettings ? View.VISIBLE
-                    : View.GONE);
-            holder.findViewById(R.id.divider).setVisibility(mHasSettings ? View.VISIBLE
-                    : View.GONE);
+            boolean hasSettings = mSettingsInfo != null;
+            holder.findViewById(R.id.settings).setVisibility(hasSettings ? VISIBLE : GONE);
+            holder.findViewById(R.id.divider).setVisibility(hasSettings ? VISIBLE : GONE);
             holder.findViewById(R.id.settings).setOnClickListener(v -> {
-                ResolveInfo result = v.getContext().getPackageManager().resolveActivity(
-                        new Intent(ACTION_PLUGIN_SETTINGS).setPackage(mPackageName), 0);
-                if (result != null) {
+                if (hasSettings) {
                     v.getContext().startActivity(new Intent().setComponent(
-                            new ComponentName(result.activityInfo.packageName,
-                                    result.activityInfo.name)));
+                            new ComponentName(mSettingsInfo.activityInfo.packageName,
+                                    mSettingsInfo.activityInfo.name)));
                 }
             });
             holder.itemView.setOnLongClickListener(v -> {
diff --git a/src/com/android/launcher3/states/SpringLoadedState.java b/src/com/android/launcher3/states/SpringLoadedState.java
index 45172b5..44bcc34 100644
--- a/src/com/android/launcher3/states/SpringLoadedState.java
+++ b/src/com/android/launcher3/states/SpringLoadedState.java
@@ -90,4 +90,9 @@
     public float getWorkspaceScrimAlpha(Launcher launcher) {
         return 0.3f;
     }
+
+    @Override
+    public int getVisibleElements(Launcher launcher) {
+        return super.getVisibleElements(launcher) & ~TASKBAR;
+    }
 }
diff --git a/src/com/android/launcher3/states/StateAnimationConfig.java b/src/com/android/launcher3/states/StateAnimationConfig.java
index 8b72177..ec949eb 100644
--- a/src/com/android/launcher3/states/StateAnimationConfig.java
+++ b/src/com/android/launcher3/states/StateAnimationConfig.java
@@ -37,7 +37,8 @@
             PLAY_ATOMIC_OVERVIEW_SCALE,
             PLAY_ATOMIC_OVERVIEW_PEEK,
             SKIP_OVERVIEW,
-            SKIP_DEPTH_CONTROLLER
+            SKIP_DEPTH_CONTROLLER,
+            SKIP_TASKBAR,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface AnimationFlags {}
@@ -46,6 +47,7 @@
     public static final int PLAY_ATOMIC_OVERVIEW_PEEK = 1 << 2;
     public static final int SKIP_OVERVIEW = 1 << 3;
     public static final int SKIP_DEPTH_CONTROLLER = 1 << 4;
+    public static final int SKIP_TASKBAR = 1 << 5;
 
     public long duration;
     public boolean userControlled;
@@ -72,6 +74,7 @@
             ANIM_OVERVIEW_MODAL,
             ANIM_DEPTH,
             ANIM_OVERVIEW_ACTIONS_FADE,
+            ANIM_TASKBAR_FADE,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface AnimType {}
@@ -91,8 +94,9 @@
     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_TASKBAR_FADE = 16;
 
-    private static final int ANIM_TYPES_COUNT = 16;
+    private static final int ANIM_TYPES_COUNT = 17;
 
     protected final Interpolator[] mInterpolators = new Interpolator[ANIM_TYPES_COUNT];
 
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index 3122c68..2647d6f 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -302,7 +302,7 @@
                 intent.setPackage(null);
             }
         }
-        if (v != null && launcher.getAppTransitionManager().supportsAdaptiveIconAnimation()) {
+        if (v != null && launcher.getAppTransitionManager().supportsAdaptiveIconAnimation(v)) {
             // Preload the icon to reduce latency b/w swapping the floating view with the original.
             FloatingIconView.fetchIcon(launcher, v, item, true /* isOpening */);
         }
diff --git a/src/com/android/launcher3/util/Themes.java b/src/com/android/launcher3/util/Themes.java
index b74686f..55d17fc 100644
--- a/src/com/android/launcher3/util/Themes.java
+++ b/src/com/android/launcher3/util/Themes.java
@@ -81,6 +81,11 @@
         return getAttrColor(context, android.R.attr.colorAccent);
     }
 
+    /** Returns the floating background color attribute. */
+    public static int getColorBackgroundFloating(Context context) {
+        return getAttrColor(context, android.R.attr.colorBackgroundFloating);
+    }
+
     public static int getAttrColor(Context context, int attr) {
         TypedArray ta = context.obtainStyledAttributes(new int[]{attr});
         int colorAccent = ta.getColor(0, 0);
diff --git a/src_plugins/com/android/systemui/plugins/AllAppsSearchPlugin.java b/src_plugins/com/android/systemui/plugins/AllAppsSearchPlugin.java
deleted file mode 100644
index 0b48c07..0000000
--- a/src_plugins/com/android/systemui/plugins/AllAppsSearchPlugin.java
+++ /dev/null
@@ -1,105 +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.systemui.plugins;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.os.CancellationSignal;
-import android.os.Parcelable;
-import android.view.View;
-
-import com.android.systemui.plugins.annotations.ProvidesInterface;
-import com.android.systemui.plugins.shared.SearchTargetEventLegacy;
-import com.android.systemui.plugins.shared.SearchTargetLegacy;
-
-import java.util.List;
-import java.util.function.Consumer;
-
-/**
- * Implement this plugin interface to fetch search result data from the plugin side.
- */
-@ProvidesInterface(action = AllAppsSearchPlugin.ACTION, version = AllAppsSearchPlugin.VERSION)
-public interface AllAppsSearchPlugin extends Plugin {
-    String ACTION = "com.android.systemui.action.PLUGIN_ALL_APPS_SEARCH_ACTIONS";
-    int VERSION = 9;
-
-    /**
-     * init plugin
-     */
-    void setup(Activity activity, View view, boolean useLegacy);
-
-    /**
-     * Send launcher state related signals.
-     */
-    void onStateTransitionStart(int fromState, int toState);
-
-    void onStateTransitionComplete(int state);
-
-    /**
-     * Send launcher window focus and visibility changed signals.
-     */
-    void onWindowFocusChanged(boolean hasFocus);
-
-    void onWindowVisibilityChanged(int visibility);
-
-    /**
-     * Send signal when user starts typing, perform search, notify search target
-     * event when search ends.
-     */
-    void startedSearchSession();
-
-    /**
-     * Main function that triggers search.
-     *
-     * @param input              string that has been typed by a user
-     * @param inputArgs          extra info that may be relevant for the input query
-     * @param results            contains the result that will be rendered in all apps search
-     *                           surface
-     * @param cancellationSignal {@link CancellationSignal} can be used to share status of current
-     */
-    void queryLegacy(String input, Bundle inputArgs, Consumer<List<SearchTargetLegacy>> results,
-            CancellationSignal cancellationSignal);
-
-    /**
-     * Main function that triggers search.
-     *
-     * @param input              string that has been typed by a user
-     * @param inputArgs          extra info that may be relevant for the input query
-     * @param results            contains the result that will be rendered in all apps search
-     *                           surface
-     * @param cancellationSignal {@link CancellationSignal} can be used to share status of current
-     */
-    void query(String input, Bundle inputArgs, Consumer<List<Parcelable>> results,
-            CancellationSignal cancellationSignal);
-
-    /**
-     * Send over search target interaction events to Plugin
-     */
-    void notifySearchTargetEventLegacy(SearchTargetEventLegacy event);
-
-    /**
-     * Send over search target interaction events to Plugin
-     */
-    void notifySearchTargetEvent(Parcelable event);
-
-    /**
-     * Launcher activity lifecycle callbacks
-     */
-    void onResume(int state);
-
-    void onStop(int state);
-}
\ No newline at end of file
diff --git a/src_plugins/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/src_plugins/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
new file mode 100644
index 0000000..b90e43b
--- /dev/null
+++ b/src_plugins/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
@@ -0,0 +1,44 @@
+/*
+ * 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.systemui.plugins;
+
+import android.os.Parcelable;
+
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+
+import java.util.List;
+
+/**
+ * Interface to provide SmartspaceTargets to BcSmartspace.
+ */
+@ProvidesInterface(action = BcSmartspaceDataPlugin.ACTION, version = BcSmartspaceDataPlugin.VERSION)
+public interface BcSmartspaceDataPlugin extends Plugin {
+    String ACTION = "com.android.systemui.action.PLUGIN_BC_SMARTSPACE_DATA";
+    int VERSION = 1;
+
+    /** Register a listener to get Smartspace data. */
+    void registerListener(SmartspaceTargetListener listener);
+
+    /** Unregister a listener. */
+    void unregisterListener(SmartspaceTargetListener listener);
+
+    /** Provides Smartspace data to registered listeners. */
+    interface SmartspaceTargetListener {
+        /** Each Parcelable is a SmartspaceTarget that represents a card. */
+        void onSmartspaceTargetsUpdated(List<Parcelable> targets);
+    }
+}
diff --git a/src_plugins/com/android/systemui/plugins/shared/SearchTargetEventLegacy.java b/src_plugins/com/android/systemui/plugins/shared/SearchTargetEventLegacy.java
deleted file mode 100644
index 7fbd6ac..0000000
--- a/src_plugins/com/android/systemui/plugins/shared/SearchTargetEventLegacy.java
+++ /dev/null
@@ -1,95 +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.systemui.plugins.shared;
-
-import android.os.Bundle;
-
-/**
- * Event used for the feedback loop to the plugin. (and future aiai)
- *
- * @deprecated Use SearchTargetEvent
- */
-@Deprecated
-public class SearchTargetEventLegacy {
-    public static final int POSITION_NONE = -1;
-
-    public static final int SELECT = 0;
-    public static final int QUICK_SELECT = 1;
-    public static final int LONG_PRESS = 2;
-    public static final int CHILD_SELECT = 3;
-
-    private final SearchTargetLegacy mSearchTarget;
-    private final int mEventType;
-    private final int mShortcutPosition;
-    private final Bundle mExtras;
-
-    public SearchTargetEventLegacy(SearchTargetLegacy searchTarget, int eventType,
-            int shortcutPosition,
-            Bundle extras) {
-        mSearchTarget = searchTarget;
-        mEventType = eventType;
-        mShortcutPosition = shortcutPosition;
-        mExtras = extras;
-    }
-
-
-    public SearchTargetLegacy getSearchTarget() {
-        return mSearchTarget;
-    }
-
-    public int getShortcutPosition() {
-        return mShortcutPosition;
-    }
-
-    public int getEventType() {
-        return mEventType;
-    }
-
-    public Bundle getExtras() {
-        return mExtras;
-    }
-
-    /**
-     * A builder for {@link SearchTargetLegacy}
-     */
-    public static final class Builder {
-        private final SearchTargetLegacy mSearchTarget;
-        private final int mEventType;
-        private int mShortcutPosition = POSITION_NONE;
-        private Bundle mExtras;
-
-        public Builder(SearchTargetLegacy searchTarget, int eventType) {
-            mSearchTarget = searchTarget;
-            mEventType = eventType;
-        }
-
-        public Builder setShortcutPosition(int shortcutPosition) {
-            mShortcutPosition = shortcutPosition;
-            return this;
-        }
-
-        public Builder setExtras(Bundle extras) {
-            mExtras = extras;
-            return this;
-        }
-
-        public SearchTargetEventLegacy build() {
-            return new SearchTargetEventLegacy(mSearchTarget, mEventType, mShortcutPosition,
-                    mExtras);
-        }
-    }
-
-}
diff --git a/src_plugins/com/android/systemui/plugins/shared/SearchTargetLegacy.java b/src_plugins/com/android/systemui/plugins/shared/SearchTargetLegacy.java
deleted file mode 100644
index 2a6ba88..0000000
--- a/src_plugins/com/android/systemui/plugins/shared/SearchTargetLegacy.java
+++ /dev/null
@@ -1,175 +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.systemui.plugins.shared;
-
-import android.app.RemoteAction;
-import android.content.ComponentName;
-import android.content.pm.ShortcutInfo;
-import android.os.Bundle;
-import android.os.UserHandle;
-
-import java.util.List;
-
-/**
- * Used to return all apps search targets.
- *
- * @deprecated Use SearchTarget
- */
-@Deprecated
-public class SearchTargetLegacy implements Comparable<SearchTargetLegacy> {
-
-    private final String mItemId;
-    private final String mItemType;
-    private final float mScore;
-
-    private final ComponentName mComponentName;
-    private final UserHandle mUserHandle;
-    private final List<ShortcutInfo> mShortcutInfos;
-    //TODO: (sfufa) replace with a list of a custom type
-    private final RemoteAction mRemoteAction;
-    private final Bundle mExtras;
-
-    private SearchTargetLegacy(String itemId, String itemType, float score,
-            ComponentName componentName, UserHandle userHandle, List<ShortcutInfo> shortcutInfos,
-            RemoteAction remoteAction, Bundle extras) {
-        mItemId = itemId;
-        mItemType = itemType;
-        mScore = score;
-        mComponentName = componentName;
-        mUserHandle = userHandle;
-        mShortcutInfos = shortcutInfos;
-        mExtras = extras;
-        mRemoteAction = remoteAction;
-    }
-
-    public String getItemId() {
-        return mItemId;
-    }
-
-    public String getItemType() {
-        return mItemType;
-    }
-
-    public ComponentName getComponentName() {
-        return mComponentName;
-    }
-
-    public UserHandle getUserHandle() {
-        return mUserHandle;
-    }
-
-    public float getScore() {
-        return mScore;
-    }
-
-    public List<ShortcutInfo> getShortcutInfos() {
-        return mShortcutInfos;
-    }
-
-    public Bundle getExtras() {
-        return mExtras;
-    }
-
-    public RemoteAction getRemoteAction() {
-        return mRemoteAction;
-    }
-
-    @Override
-    public int compareTo(SearchTargetLegacy o) {
-        return Float.compare(o.mScore, mScore);
-    }
-
-    /**
-     * A builder for {@link SearchTargetLegacy}
-     */
-    public static final class Builder {
-
-
-        private String mItemId;
-
-        private final String mItemType;
-        private final float mScore;
-
-
-        private ComponentName mComponentName;
-        private UserHandle mUserHandle;
-        private List<ShortcutInfo> mShortcutInfos;
-        private Bundle mExtras;
-        private RemoteAction mRemoteAction;
-
-        public Builder(String itemType, float score) {
-            this(itemType, score, null, null);
-        }
-
-        public Builder(String itemType, float score, ComponentName cn,
-                UserHandle user) {
-            mItemType = itemType;
-            mScore = score;
-            mComponentName = cn;
-            mUserHandle = user;
-        }
-
-        public String getItemId() {
-            return mItemId;
-        }
-
-        public float getScore() {
-            return mScore;
-        }
-
-        public Builder setItemId(String itemId) {
-            mItemId = itemId;
-            return this;
-        }
-
-        public Builder setComponentName(ComponentName componentName) {
-            mComponentName = componentName;
-            return this;
-        }
-
-        public Builder setUserHandle(UserHandle userHandle) {
-            mUserHandle = userHandle;
-            return this;
-        }
-
-        public Builder setShortcutInfos(List<ShortcutInfo> shortcutInfos) {
-            mShortcutInfos = shortcutInfos;
-            return this;
-        }
-
-        public Builder setExtras(Bundle extras) {
-            mExtras = extras;
-            return this;
-        }
-
-        public Builder setRemoteAction(RemoteAction remoteAction) {
-            mRemoteAction = remoteAction;
-            return this;
-        }
-
-        /**
-         * Builds a {@link SearchTargetLegacy}
-         */
-        public SearchTargetLegacy build() {
-            if (mItemId == null) {
-                throw new IllegalStateException("Item ID is required for building SearchTarget");
-            }
-            return new SearchTargetLegacy(mItemId, mItemType, mScore, mComponentName, mUserHandle,
-                    mShortcutInfos,
-                    mRemoteAction, mExtras);
-        }
-    }
-}