Merge "Add rotation support to launch animations" into sc-dev
diff --git a/Android.bp b/Android.bp
index 002f6fe..92cc36b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -94,27 +94,35 @@
     min_sdk_version: "28",
 }
 
+// Library with all the dependencies for building Launcher3
+android_library {
+    name: "Launcher3ResLib",
+    srcs: [ ],
+    resource_dirs: ["res"],
+    static_libs: [
+        "LauncherPluginLib",
+        "launcher_quickstep_log_protos_lite",
+        "androidx-constraintlayout_constraintlayout",
+        "androidx.recyclerview_recyclerview",
+        "androidx.dynamicanimation_dynamicanimation",
+        "androidx.fragment_fragment",
+        "androidx.preference_preference",
+        "androidx.slice_slice-view",
+        "androidx.cardview_cardview",
+        "iconloader_base",
+    ],
+    manifest: "AndroidManifest-common.xml",
+    sdk_version: "current",
+    min_sdk_version: "26",
+}
+
 //
 // Build rule for Launcher3 dependencies lib.
 //
 android_library {
     name: "Launcher3CommonDepsLib",
-    static_libs: [
-        "androidx.recyclerview_recyclerview",
-        "androidx.dynamicanimation_dynamicanimation",
-        "androidx.preference_preference",
-        "androidx.slice_slice-view",
-        "iconloader_base",
-        "LauncherPluginLib",
-        "launcher_quickstep_log_protos_lite"
-    ],
-    srcs: [
-        "src_build_config/**/*.java",
-    ],
-    resource_dirs: ["res"],
-    optimize: {
-        enabled: false,
-    },
+    srcs: ["src_build_config/**/*.java"],
+    static_libs: ["Launcher3ResLib"],
     sdk_version: "current",
     min_sdk_version: "26",
     manifest: "AndroidManifest-common.xml",
@@ -164,22 +172,42 @@
     ],
 }
 
-//
-// Launcher Robolectric test target.
-//
-java_library {
-    name: "Launcher3TestCommon",
-    libs: [
-        "Launcher3CommonDepsLib",
+// Library with all the dependencies for building quickstep
+android_library {
+    name: "QuickstepResLib",
+    srcs: [ ],
+    resource_dirs: [
+        "quickstep/res",
+        "quickstep/overview_ui_overrides/res",
     ],
+    static_libs: [
+        "Launcher3ResLib",
+        "SystemUISharedLib",
+        "SystemUI-statsd",
+    ],
+    manifest: "quickstep/AndroidManifest.xml",
+    min_sdk_version: "28",
+}
+
+
+// Source code used for test helpers
+filegroup {
+    name: "launcher-src-ext-tests",
+    srcs: ["ext_tests/src/**/*.java"],
+}
+
+// Common source files used to build launcher
+filegroup {
+    name: "launcher-src-no-build-config",
     srcs: [
         "src/**/*.java",
         "src_shortcuts_overrides/**/*.java",
-        "src_ui_overrides/**/*.java",
-        "ext_tests/src/**/*.java",
-        "tests/src_common/**/*.java",
+        "quickstep/src/**/*.java",
     ],
-    target_sdk_version: "29",
-    sdk_version: "current",
-    min_sdk_version: "26",
+}
+
+// Proguard files for Launcher3
+filegroup {
+    name: "launcher-proguard-rules",
+    srcs: ["proguard.flags"],
 }
diff --git a/SharedLibWrapper/build.gradle b/SharedLibWrapper/build.gradle
deleted file mode 100644
index 674e38a..0000000
--- a/SharedLibWrapper/build.gradle
+++ /dev/null
@@ -1,17 +0,0 @@
-apply plugin: 'java'
-
-final String ANDROID_TOP = "${rootDir}/../../.."
-final String FRAMEWORK_PREBUILTS_DIR = "${ANDROID_TOP}/prebuilts/framework_intermediates/"
-
-sourceSets {
-    main {
-        java.srcDirs = ["${ANDROID_TOP}/frameworks/lib/systemui/SharedLibWrapper/src"]
-    }
-}
-
-sourceCompatibility = 1.8
-
-dependencies {
-    implementation fileTree(dir: "${FRAMEWORK_PREBUILTS_DIR}/quickstep/libs", include: 'sysui_shared.jar')
-    compileOnly fileTree(dir: "$ANDROID_TOP/prebuilts/fullsdk-${org.gradle.internal.os.OperatingSystem.current().isMacOsX() ? "darwin" : "linux"}/platforms/${COMPILE_SDK}", include: 'android.jar')
-}
diff --git a/quickstep/Android.bp b/quickstep/Android.bp
new file mode 100644
index 0000000..585b6ad
--- /dev/null
+++ b/quickstep/Android.bp
@@ -0,0 +1,19 @@
+// 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.
+
+filegroup {
+    name: "launcher3-quickstep-robolectric-src",
+    path: "robolectric_tests",
+    srcs: ["robolectric_tests/src/**/*.java"],
+}
diff --git a/quickstep/robolectric_tests/src/com/android/quickstep/RecentsActivityTest.java b/quickstep/robolectric_tests/src/com/android/quickstep/RecentsActivityTest.java
index 7049af0..9df9ab1 100644
--- a/quickstep/robolectric_tests/src/com/android/quickstep/RecentsActivityTest.java
+++ b/quickstep/robolectric_tests/src/com/android/quickstep/RecentsActivityTest.java
@@ -37,6 +37,7 @@
 
 @RunWith(RobolectricTestRunner.class)
 @LooperMode(Mode.PAUSED)
+@org.junit.Ignore
 public class RecentsActivityTest {
 
     @Test
diff --git a/quickstep/robolectric_tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java b/quickstep/robolectric_tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
index 688f323..88079ae 100644
--- a/quickstep/robolectric_tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
+++ b/quickstep/robolectric_tests/src/com/android/quickstep/util/TaskViewSimulatorTest.java
@@ -17,6 +17,8 @@
 
 import static android.view.Display.DEFAULT_DISPLAY;
 
+import static org.mockito.Mockito.mock;
+
 import android.content.Context;
 import android.graphics.Rect;
 import android.graphics.RectF;
@@ -29,6 +31,7 @@
 import com.android.launcher3.shadows.LShadowDisplay;
 import com.android.launcher3.util.DisplayController;
 import com.android.quickstep.LauncherActivityInterface;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
 
 import org.hamcrest.Description;
@@ -162,7 +165,7 @@
         @Override
         public SurfaceParams[] createSurfaceParams(BuilderProxy proxy) {
             SurfaceParams.Builder builder = new SurfaceParams.Builder((SurfaceControl) null);
-            proxy.onBuildTargetParams(builder, null, this);
+            proxy.onBuildTargetParams(builder, mock(RemoteAnimationTargetCompat.class), this);
             return new SurfaceParams[] {builder.build()};
         }
 
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 5dcec68..2f8b79e 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -27,7 +27,6 @@
 import static com.android.launcher3.LauncherState.BACKGROUND_APP;
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.Utilities.postAsyncCallback;
-import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
 import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
 import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
 import static com.android.launcher3.anim.Interpolators.EXAGGERATED_EASE;
@@ -67,7 +66,6 @@
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
-import com.android.launcher3.allapps.AllAppsTransitionController;
 import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.shortcuts.DeepShortcutView;
@@ -148,9 +146,6 @@
     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.
-    public static final float ALL_APPS_PROGRESS_OFF_SCREEN = 1.3059858f;
-
     private static final int MAX_NUM_TASKS = 5;
 
     protected final BaseQuickstepLauncher mLauncher;
@@ -441,9 +436,6 @@
                 appsView.setLayerType(View.LAYER_TYPE_NONE, null);
             };
         } else if (mLauncher.isInState(OVERVIEW)) {
-            AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
-            launcherAnimator.play(ObjectAnimator.ofFloat(allAppsController, ALL_APPS_PROGRESS,
-                    allAppsController.getProgress(), ALL_APPS_PROGRESS_OFF_SCREEN));
             endListener = composeViewContentAnimator(launcherAnimator, alphas, trans);
         } else {
             mDragLayerAlpha.setValue(alphas[0]);
diff --git a/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java b/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java
index 66b1a86..d17a5ae 100644
--- a/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java
@@ -16,7 +16,6 @@
 
 package com.android.launcher3.appprediction;
 
-import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
 import static com.android.launcher3.LauncherState.ALL_APPS;
 
 import android.annotation.TargetApi;
@@ -30,7 +29,6 @@
 import android.text.TextPaint;
 import android.util.AttributeSet;
 import android.view.View;
-import android.view.animation.Interpolator;
 
 import androidx.annotation.ColorInt;
 import androidx.core.content.ContextCompat;
@@ -41,7 +39,6 @@
 import com.android.launcher3.R;
 import com.android.launcher3.allapps.FloatingHeaderRow;
 import com.android.launcher3.allapps.FloatingHeaderView;
-import com.android.launcher3.anim.PropertySetter;
 import com.android.launcher3.statemanager.StateManager.StateListener;
 import com.android.launcher3.util.Themes;
 
@@ -287,13 +284,6 @@
     }
 
     @Override
-    public void setContentVisibility(boolean hasHeaderExtra, boolean hasAllAppsContent,
-            PropertySetter setter, Interpolator headerFade, Interpolator allAppsFade) {
-        // Don't use setViewAlpha as we want to control the visibility ourselves.
-        setter.setFloat(this, VIEW_ALPHA, hasAllAppsContent ? 1 : 0, allAppsFade);
-    }
-
-    @Override
     public void setVerticalScroll(int scroll, boolean isScrolledOut) {
         setTranslationY(scroll);
         mIsScrolledOut = isScrolledOut;
diff --git a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
index 83234e3..8e92b59 100644
--- a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -16,20 +16,14 @@
 
 package com.android.launcher3.appprediction;
 
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
-
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.graphics.Canvas;
-import android.graphics.Color;
 import android.graphics.Rect;
 import android.os.Build;
 import android.util.AttributeSet;
-import android.util.IntProperty;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.view.animation.Interpolator;
 import android.widget.LinearLayout;
 
 import androidx.annotation.NonNull;
@@ -44,7 +38,6 @@
 import com.android.launcher3.allapps.FloatingHeaderRow;
 import com.android.launcher3.allapps.FloatingHeaderView;
 import com.android.launcher3.anim.AlphaUpdateListener;
-import com.android.launcher3.anim.PropertySetter;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.keyboard.FocusIndicatorHelper;
 import com.android.launcher3.keyboard.FocusIndicatorHelper.SimpleFocusIndicatorHelper;
@@ -53,8 +46,6 @@
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.touch.ItemLongClickListener;
-import com.android.launcher3.util.Themes;
-import com.android.quickstep.AnimatedFloat;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -63,22 +54,6 @@
 public class PredictionRowView extends LinearLayout implements
         OnDeviceProfileChangeListener, FloatingHeaderRow {
 
-    private static final IntProperty<PredictionRowView> TEXT_ALPHA =
-            new IntProperty<PredictionRowView>("textAlpha") {
-                @Override
-                public void setValue(PredictionRowView view, int alpha) {
-                    view.setTextAlpha(alpha);
-                }
-
-                @Override
-                public Integer get(PredictionRowView view) {
-                    return view.mIconLastSetTextAlpha;
-                }
-            };
-
-    private static final Interpolator ALPHA_FACTOR_INTERPOLATOR =
-            (t) -> (t < 0.8f) ? 0 : (t - 0.8f) / 0.2f;
-
     private final Launcher mLauncher;
     private int mNumPredictedAppsPerRow;
 
@@ -88,21 +63,9 @@
     // The set of predicted apps resolved from the component names and the current set of apps
     private final List<WorkspaceItemInfo> mPredictedApps = new ArrayList<>();
 
-    private final int mIconTextColor;
-    private final int mIconFullTextAlpha;
-    private int mIconLastSetTextAlpha;
-    // Might use mIconFullTextAlpha instead of mIconLastSetTextAlpha if we are translucent.
-    private int mIconCurrentTextAlpha;
-
     private FloatingHeaderView mParent;
     private boolean mScrolledOut;
 
-    private float mScrollTranslation = 0;
-    private final AnimatedFloat mContentAlphaFactor =
-            new AnimatedFloat(this::updateTranslationAndAlpha);
-    private final AnimatedFloat mOverviewScrollFactor =
-            new AnimatedFloat(this::updateTranslationAndAlpha);
-
     private boolean mPredictionsEnabled = false;
 
     @Nullable
@@ -117,15 +80,9 @@
         setOrientation(LinearLayout.HORIZONTAL);
 
         mFocusHelper = new SimpleFocusIndicatorHelper(this);
-
         mNumPredictedAppsPerRow = LauncherAppState.getIDP(context).numAllAppsColumns;
         mLauncher = Launcher.getLauncher(context);
         mLauncher.addOnDeviceProfileChangeListener(this);
-
-        mIconTextColor = Themes.getAttrColor(context, android.R.attr.textColorSecondary);
-        mIconFullTextAlpha = Color.alpha(mIconTextColor);
-        mIconCurrentTextAlpha = mIconFullTextAlpha;
-
         updateVisibility();
     }
 
@@ -246,7 +203,6 @@
         }
 
         int predictionCount = mPredictedApps.size();
-        int iconColor = setColorAlphaBound(mIconTextColor, mIconCurrentTextAlpha);
 
         for (int i = 0; i < getChildCount(); i++) {
             BubbleTextView icon = (BubbleTextView) getChildAt(i);
@@ -254,7 +210,6 @@
             if (predictionCount > i) {
                 icon.setVisibility(View.VISIBLE);
                 icon.applyFromWorkspaceItem(mPredictedApps.get(i));
-                icon.setTextColor(iconColor);
             } else {
                 icon.setVisibility(predictionCount == 0 ? GONE : INVISIBLE);
             }
@@ -269,27 +224,6 @@
         mParent.onHeightUpdated();
     }
 
-    public void setTextAlpha(int textAlpha) {
-        mIconLastSetTextAlpha = textAlpha;
-        if (getAlpha() < 1 && textAlpha > 0) {
-            // If the entire header is translucent, make sure the text is at full opacity so it's
-            // not double-translucent. However, we support keeping the text invisible (alpha == 0).
-            textAlpha = mIconFullTextAlpha;
-        }
-        mIconCurrentTextAlpha = textAlpha;
-        int iconColor = setColorAlphaBound(mIconTextColor, mIconCurrentTextAlpha);
-        for (int i = 0; i < getChildCount(); i++) {
-            ((BubbleTextView) getChildAt(i)).setTextColor(iconColor);
-        }
-    }
-
-    @Override
-    public void setAlpha(float alpha) {
-        super.setAlpha(alpha);
-        // Reapply text alpha so that we update it to be full alpha if the row is now translucent.
-        setTextAlpha(mIconLastSetTextAlpha);
-    }
-
     @Override
     public boolean hasOverlappingRendering() {
         return false;
@@ -299,34 +233,11 @@
     @Override
     public void setVerticalScroll(int scroll, boolean isScrolledOut) {
         mScrolledOut = isScrolledOut;
-        updateTranslationAndAlpha();
         if (!isScrolledOut) {
-            mScrollTranslation = scroll;
-            updateTranslationAndAlpha();
+            setTranslationY(scroll);
         }
-    }
-
-    private void updateTranslationAndAlpha() {
-        if (mPredictionsEnabled) {
-            setTranslationY((1 - mOverviewScrollFactor.value) * mScrollTranslation);
-
-            float factor = ALPHA_FACTOR_INTERPOLATOR.getInterpolation(mOverviewScrollFactor.value);
-            float endAlpha = factor + (1 - factor) * (mScrolledOut ? 0 : 1);
-            setAlpha(mContentAlphaFactor.value * endAlpha);
-            AlphaUpdateListener.updateVisibility(this);
-        }
-    }
-
-    @Override
-    public void setContentVisibility(boolean hasHeaderExtra, boolean hasAllAppsContent,
-            PropertySetter setter, Interpolator headerFade, Interpolator allAppsFade) {
-        // Text follows all apps visibility
-        int textAlpha = hasHeaderExtra && hasAllAppsContent ? mIconFullTextAlpha : 0;
-        setter.setInt(this, TEXT_ALPHA, textAlpha, allAppsFade);
-        setter.setFloat(mOverviewScrollFactor, AnimatedFloat.VALUE,
-                (hasHeaderExtra && !hasAllAppsContent) ? 1 : 0, LINEAR);
-        setter.setFloat(mContentAlphaFactor, AnimatedFloat.VALUE, hasHeaderExtra ? 1 : 0,
-                headerFade);
+        setAlpha(mScrolledOut ? 0 : 1);
+        AlphaUpdateListener.updateVisibility(this);
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
index 3ae8581..a2b2ddd 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarController.java
@@ -42,6 +42,7 @@
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.BaseQuickstepLauncher;
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Hotseat;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.QuickstepTransitionManager;
@@ -470,8 +471,12 @@
      */
     public void alignRealHotseatWithTaskbar() {
         Rect hotseatBounds = new Rect();
+        DeviceProfile grid = mLauncher.getDeviceProfile();
+        int hotseatHeight = grid.workspacePadding.bottom + grid.getInsets().bottom;
+        int hotseatTopDiff = hotseatHeight - grid.taskbarSize;
+
         mTaskbarView.getHotseatBoundsAtScale(getTaskbarScaleOnHome()).roundOut(hotseatBounds);
-        mLauncher.getHotseat().setPadding(hotseatBounds.left, hotseatBounds.top,
+        mLauncher.getHotseat().setPadding(hotseatBounds.left, hotseatBounds.top + hotseatTopDiff,
                 mTaskbarView.getWidth() - hotseatBounds.right,
                 mTaskbarView.getHeight() - hotseatBounds.bottom);
     }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index e02f2c2..bae97d7 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -234,7 +234,7 @@
     @Override
     public void onDestroy() {
         super.onDestroy();
-        getAppsView().getSearchUiManager().destroy();
+        getAppsView().getSearchUiManager().destroySearch();
         mHotseatPredictionController.destroy();
     }
 
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
index 37c774b..b2f8a40 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -78,7 +78,7 @@
 
     @Override
     public int getVisibleElements(Launcher launcher) {
-        return ALL_APPS_HEADER | ALL_APPS_HEADER_EXTRA | ALL_APPS_CONTENT;
+        return ALL_APPS_CONTENT;
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
index 45cb46f..6c71995 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonNavbarToOverviewTouchController.java
@@ -155,7 +155,7 @@
             super.onDragEnd(velocity);
         }
 
-        View searchView = mLauncher.getAppsView().getSearchView();
+        View searchView = mLauncher.getHotseat().getQsb();
         if (searchView instanceof FeedbackHandler) {
             ((FeedbackHandler) searchView).resetFeedback();
         }
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index a2f15f5..6da2201 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -48,6 +48,7 @@
 import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE;
 import static com.android.quickstep.views.RecentsView.RECENTS_GRID_PROGRESS;
 import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
+import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
 
 import android.animation.Animator;
@@ -616,7 +617,7 @@
         final boolean passed = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW;
         if (passed != mPassedOverviewThreshold) {
             mPassedOverviewThreshold = passed;
-            if (!mDeviceState.isFullyGesturalNavMode()) {
+            if (!mDeviceState.isTwoButtonNavMode()) {
                 performHapticFeedback();
             }
         }
@@ -722,6 +723,9 @@
 
     @UiThread
     public void onGestureStarted(boolean isLikelyToStartNewTask) {
+        mActivityInterface.closeOverlay();
+        TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
+
         if (mRecentsView != null) {
             InteractionJankMonitorWrapper.begin(mRecentsView,
                     InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH, 2000 /* ms timeout */);
@@ -849,6 +853,10 @@
 
     private GestureEndTarget calculateEndTarget(PointF velocity, float endVelocity, boolean isFling,
             boolean isCancel) {
+        if (mDeviceState.isButtonNavMode()) {
+            // Button mode, this is only used to go to recents
+            return RECENTS;
+        }
         final GestureEndTarget endTarget;
         final boolean goingToNewTask;
         if (mRecentsView != null) {
@@ -953,15 +961,20 @@
         } else if (endTarget == RECENTS) {
             if (mRecentsView != null) {
                 int nearestPage = mRecentsView.getDestinationPage();
+                boolean isScrolling = false;
                 if (mRecentsView.getNextPage() != nearestPage) {
                     // We shouldn't really scroll to the next page when swiping up to recents.
                     // Only allow settling on the next page if it's nearest to the center.
                     mRecentsView.snapToPage(nearestPage, Math.toIntExact(duration));
+                    isScrolling = true;
                 }
                 if (mRecentsView.getScroller().getDuration() > MAX_SWIPE_DURATION) {
                     mRecentsView.snapToPage(mRecentsView.getNextPage(), (int) MAX_SWIPE_DURATION);
+                    isScrolling = true;
                 }
-                duration = Math.max(duration, mRecentsView.getScroller().getDuration());
+                if (!mDeviceState.isButtonNavMode() || isScrolling) {
+                    duration = Math.max(duration, mRecentsView.getScroller().getDuration());
+                }
             }
         }
 
@@ -1764,7 +1777,6 @@
 
     public interface Factory {
 
-        AbsSwipeUpHandler newHandler(
-                GestureState gestureState, long touchTimeMs, boolean continuingLastGesture);
+        AbsSwipeUpHandler newHandler(GestureState gestureState, long touchTimeMs);
     }
 }
diff --git a/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
deleted file mode 100644
index d159fa0..0000000
--- a/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep;
-
-import static com.android.launcher3.LauncherState.BACKGROUND_APP;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
-import static com.android.launcher3.anim.Interpolators.clampToProgress;
-import static com.android.launcher3.statehandlers.DepthController.DEPTH;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
-
-import android.animation.Animator;
-import android.animation.AnimatorSet;
-import android.app.ActivityManager.RunningTaskInfo;
-import android.util.Log;
-import android.view.animation.Interpolator;
-
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.anim.AnimationSuccessListener;
-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;
-import com.android.quickstep.util.TransformParams;
-import com.android.quickstep.views.RecentsView;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-
-/**
- * Provider for the atomic (for 3-button mode) remote window animation from the app to the overview.
- *
- * @param <T> activity that contains the overview
- */
-final class AppToOverviewAnimationProvider<T extends StatefulActivity<?>> extends
-        RemoteAnimationProvider {
-
-    private static final long RECENTS_LAUNCH_DURATION = 250;
-    private static final String TAG = "AppToOverviewAnimationProvider";
-
-    private final BaseActivityInterface<?, T> mActivityInterface;
-    // The id of the currently running task that is transitioning to overview.
-    private final RunningTaskInfo mTargetTask;
-    private final RecentsAnimationDeviceState mDeviceState;
-
-    private T mActivity;
-    private RecentsView mRecentsView;
-
-    AppToOverviewAnimationProvider(
-            BaseActivityInterface<?, T> activityInterface, RunningTaskInfo targetTask,
-            RecentsAnimationDeviceState deviceState) {
-        mActivityInterface = activityInterface;
-        mTargetTask = targetTask;
-        mDeviceState = deviceState;
-    }
-
-    /**
-     * Callback for when the activity is ready/initialized.
-     *
-     * @param activity the activity that is ready
-     * @param wasVisible true if it was visible before
-     */
-    boolean onActivityReady(T activity, Boolean wasVisible) {
-        activity.<RecentsView>getOverviewPanel().showCurrentTask(mTargetTask);
-        AbstractFloatingView.closeAllOpenViews(activity, wasVisible);
-        BaseActivityInterface.AnimationFactory factory = mActivityInterface.prepareRecentsUI(
-                mDeviceState,
-                wasVisible, (controller) -> {
-                    controller.getNormalController().dispatchOnStart();
-                    controller.getNormalController().getAnimationPlayer().end();
-                });
-        factory.createActivityInterface(RECENTS_LAUNCH_DURATION);
-        factory.setRecentsAttachedToAppWindow(true, false);
-        mActivity = activity;
-        mRecentsView = mActivity.getOverviewPanel();
-        return false;
-    }
-
-    /**
-     * Create remote window animation from the currently running app to the overview panel.
-     *
-     * @param appTargets the target apps
-     * @return animation from app to overview
-     */
-    @Override
-    public AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
-            RemoteAnimationTargetCompat[] wallpaperTargets) {
-        PendingAnimation pa = new PendingAnimation(RECENTS_LAUNCH_DURATION);
-        if (mActivity == null) {
-            Log.e(TAG, "Animation created, before activity");
-            return pa.buildAnim();
-        }
-
-        mRecentsView.setRunningTaskIconScaledDown(true);
-        pa.addListener(new AnimationSuccessListener() {
-            @Override
-            public void onAnimationSuccess(Animator animator) {
-                mActivityInterface.onSwipeUpToRecentsComplete();
-                mRecentsView.animateUpRunningTaskIconScale();
-            }
-        });
-
-        DepthController depthController = mActivityInterface.getDepthController();
-        if (depthController != null) {
-            pa.addFloat(depthController, DEPTH, BACKGROUND_APP.getDepth(mActivity),
-                    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);
-
-        // Use the top closing app to determine the insets for the animation
-        RemoteAnimationTargetCompat runningTaskTarget = mTargetTask == null ? null
-                : targets.findTask(mTargetTask.taskId);
-        if (runningTaskTarget == null) {
-            Log.e(TAG, "No closing app");
-            return pa.buildAnim();
-        }
-
-        TaskViewSimulator tsv = new TaskViewSimulator(mActivity, mRecentsView.getSizeStrategy());
-        tsv.setDp(mActivity.getDeviceProfile());
-        tsv.setOrientationState(mRecentsView.getPagedViewOrientedState());
-        tsv.setPreview(runningTaskTarget);
-
-        TransformParams params = new TransformParams()
-                .setTargetSet(targets)
-                .setSyncTransactionApplier(new SurfaceTransactionApplier(mActivity.getRootView()));
-
-        AnimatedFloat recentsAlpha = new AnimatedFloat(() -> { });
-        params.setBaseBuilderProxy((builder, app, p)
-                -> builder.withAlpha(recentsAlpha.value));
-
-        Interpolator taskInterpolator;
-        if (targets.isAnimatingHome()) {
-            params.setHomeBuilderProxy((builder, app, p) -> builder.withAlpha(1 - p.getProgress()));
-
-            taskInterpolator = TOUCH_RESPONSE_INTERPOLATOR;
-            pa.addFloat(recentsAlpha, AnimatedFloat.VALUE, 0, 1, TOUCH_RESPONSE_INTERPOLATOR);
-        } else {
-            // When animation from app to recents, the recents layer is drawn on top of the app. To
-            // prevent the overlap, we animate the task first and then quickly fade in the recents.
-            taskInterpolator = clampToProgress(TOUCH_RESPONSE_INTERPOLATOR, 0, 0.8f);
-            pa.addFloat(recentsAlpha, AnimatedFloat.VALUE, 0, 1,
-                    clampToProgress(TOUCH_RESPONSE_INTERPOLATOR, 0.8f, 1));
-        }
-
-        pa.addFloat(params, TransformParams.PROGRESS, 0, 1, taskInterpolator);
-        tsv.addAppToOverviewAnim(pa, taskInterpolator);
-        pa.addOnFrameCallback(() -> tsv.apply(params));
-        return pa.buildAnim();
-    }
-
-    /**
-     * Get duration of animation from app to overview.
-     *
-     * @return duration of animation
-     */
-    long getRecentsLaunchDuration() {
-        return RECENTS_LAUNCH_DURATION;
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/FallbackActivityInterface.java b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
index bffe3a1..e13d1a4 100644
--- a/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/FallbackActivityInterface.java
@@ -105,7 +105,7 @@
     @Override
     public RecentsView getVisibleRecentsView() {
         RecentsActivity activity = getCreatedActivity();
-        if (activity != null && activity.hasWindowFocus()) {
+        if (activity != null && activity.hasBeenResumed()) {
             return activity.getOverviewPanel();
         }
         return null;
diff --git a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index ca5765e..98b96b2 100644
--- a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -177,7 +177,7 @@
     @UiThread
     private Launcher getVisibleLauncher() {
         Launcher launcher = getCreatedActivity();
-        return (launcher != null) && launcher.isStarted() && launcher.hasWindowFocus()
+        return (launcher != null) && launcher.isStarted() && launcher.hasBeenResumed()
                 ? launcher : null;
     }
 
@@ -188,6 +188,7 @@
             return false;
         }
 
+        closeOverlay();
         launcher.getStateManager().goToState(OVERVIEW,
                 launcher.getStateManager().shouldAnimateStateChange(), onCompleteCallback);
         return true;
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index 63fdd0b..923d4f1 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -16,29 +16,28 @@
 package com.android.quickstep;
 
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
+import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
 import android.annotation.TargetApi;
-import android.content.Context;
+import android.content.Intent;
+import android.graphics.PointF;
 import android.os.Build;
 import android.os.SystemClock;
 import android.os.Trace;
-import android.view.ViewConfiguration;
 
 import androidx.annotation.BinderThread;
+import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
 
 import com.android.launcher3.statemanager.StatefulActivity;
-import com.android.quickstep.util.ActivityInitListener;
-import com.android.quickstep.util.RemoteAnimationProvider;
+import com.android.launcher3.util.RunnableList;
+import com.android.quickstep.RecentsAnimationCallbacks.RecentsAnimationListener;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskView;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
-import com.android.systemui.shared.system.LatencyTrackerCompat;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+
+import java.util.ArrayList;
 
 /**
  * Helper class to handle various atomic commands for switching between Overview.
@@ -46,66 +45,191 @@
 @TargetApi(Build.VERSION_CODES.P)
 public class OverviewCommandHelper {
 
-    private final Context mContext;
-    private final RecentsAnimationDeviceState mDeviceState;
+    public static final int TYPE_SHOW = 1;
+    public static final int TYPE_SHOW_NEXT_FOCUS = 2;
+    public static final int TYPE_HIDE = 3;
+    public static final int TYPE_TOGGLE = 4;
+
+    private static final String TRANSITION_NAME = "Transition:toOverview";
+
+    private final TouchInteractionService mService;
     private final OverviewComponentObserver mOverviewComponentObserver;
+    private final TaskAnimationManager mTaskAnimationManager;
+    private final ArrayList<CommandInfo> mPendingCommands = new ArrayList<>();
 
-    private long mLastToggleTime;
-
-    public OverviewCommandHelper(Context context, RecentsAnimationDeviceState deviceState,
-            OverviewComponentObserver observer) {
-        mContext = context;
-        mDeviceState = deviceState;
+    public OverviewCommandHelper(TouchInteractionService service,
+            OverviewComponentObserver observer,
+            TaskAnimationManager taskAnimationManager) {
+        mService = service;
         mOverviewComponentObserver = observer;
+        mTaskAnimationManager = taskAnimationManager;
     }
 
-    @BinderThread
-    public void onOverviewToggle() {
-        // If currently screen pinning, do not enter overview
-        if (mDeviceState.isScreenPinningActive()) {
+    /**
+     * Called when the command finishes execution.
+     */
+    private void scheduleNextTask(CommandInfo command) {
+        if (!mPendingCommands.isEmpty() && mPendingCommands.get(0) == command) {
+            mPendingCommands.remove(0);
+            executeNext();
+        }
+    }
+
+    /**
+     * Executes the next command from the queue. If the command finishes immediately (returns true),
+     * it continues to execute the next command, until the queue is empty of a command defer's its
+     * completion (returns false).
+     */
+    @UiThread
+    private void executeNext() {
+        if (mPendingCommands.isEmpty()) {
             return;
         }
-
-        TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
-        MAIN_EXECUTOR.execute(new RecentsActivityCommand<>());
+        CommandInfo cmd = mPendingCommands.get(0);
+        if (executeCommand(cmd)) {
+            scheduleNextTask(cmd);
+        }
     }
 
+    @UiThread
+    private void addCommand(CommandInfo cmd) {
+        boolean wasEmpty = mPendingCommands.isEmpty();
+        mPendingCommands.add(cmd);
+        if (wasEmpty) {
+            executeNext();
+        }
+    }
+
+    /**
+     * Adds a command to be executed next, after all pending tasks are completed
+     */
     @BinderThread
-    public void onOverviewShown(boolean triggeredFromAltTab) {
-        if (triggeredFromAltTab) {
-            TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
-        }
-        MAIN_EXECUTOR.execute(new ShowRecentsCommand(triggeredFromAltTab));
+    public void addCommand(int type) {
+        CommandInfo cmd = new CommandInfo(type);
+        MAIN_EXECUTOR.execute(() -> addCommand(cmd));
     }
 
-    @BinderThread
-    public void onOverviewHidden() {
-        MAIN_EXECUTOR.execute(new HideRecentsCommand());
+    private TaskView getNextTask(RecentsView view) {
+        final TaskView runningTaskView = view.getRunningTaskView();
+
+        if (runningTaskView == null) {
+            return view.getTaskViewCount() > 0 ? view.getTaskViewAt(0) : null;
+        } else {
+            final TaskView nextTask = view.getNextTaskView();
+            return nextTask != null ? nextTask : runningTaskView;
+        }
     }
 
-    private class ShowRecentsCommand extends RecentsActivityCommand {
-
-        private final boolean mTriggeredFromAltTab;
-
-        ShowRecentsCommand(boolean triggeredFromAltTab) {
-            mTriggeredFromAltTab = triggeredFromAltTab;
+    private boolean launchTask(RecentsView recents, @Nullable TaskView taskView, CommandInfo cmd) {
+        RunnableList callbackList = null;
+        if (taskView != null) {
+            taskView.setEndQuickswitchCuj(true);
+            callbackList = taskView.launchTaskAnimated();
         }
 
-        @Override
-        protected boolean handleCommand(long elapsedTime) {
-            // TODO: Go to the next page if started from alt-tab.
-            return mActivityInterface.getVisibleRecentsView() != null;
+        if (callbackList != null) {
+            callbackList.add(() -> scheduleNextTask(cmd));
+            return false;
+        } else {
+            recents.startHome();
+            return true;
         }
+    }
 
-        @Override
-        protected void onTransitionComplete() {
-            // TODO(b/138729100) This doesn't execute first time launcher is run
-            if (mTriggeredFromAltTab) {
-                RecentsView rv =  mActivityInterface.getVisibleRecentsView();
-                if (rv == null) {
-                    return;
+    /**
+     * Executes the task and returns true if next task can be executed. If false, then the next
+     * task is deferred until {@link #scheduleNextTask} is called
+     */
+    private <T extends StatefulActivity<?>> boolean executeCommand(CommandInfo cmd) {
+        BaseActivityInterface<?, T> activityInterface =
+                mOverviewComponentObserver.getActivityInterface();
+        RecentsView recents = activityInterface.getVisibleRecentsView();
+        if (recents == null) {
+            if (cmd.type == TYPE_HIDE) {
+                // already hidden
+                return true;
+            }
+        } else {
+            switch (cmd.type) {
+                case TYPE_SHOW:
+                    // already visible
+                    return true;
+                case TYPE_HIDE: {
+                    int currentPage = recents.getNextPage();
+                    TaskView tv = (currentPage >= 0 && currentPage < recents.getTaskViewCount())
+                            ? (TaskView) recents.getPageAt(currentPage)
+                            : null;
+                    return launchTask(recents, tv, cmd);
                 }
+                case TYPE_TOGGLE:
+                    return launchTask(recents, getNextTask(recents), cmd);
+            }
+        }
 
+        if (activityInterface.switchToRecentsIfVisible(() -> scheduleNextTask(cmd))) {
+            // If successfully switched, wait until animation finishes
+            return false;
+        }
+
+        final T activity = activityInterface.getCreatedActivity();
+        if (activity != null) {
+            InteractionJankMonitorWrapper.begin(
+                    activity.getRootView(),
+                    InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH);
+        }
+
+        GestureState gestureState = mService.createGestureState(GestureState.DEFAULT_STATE);
+        AbsSwipeUpHandler interactionHandler = mService.getSwipeUpHandlerFactory()
+                .newHandler(gestureState, cmd.createTime);
+        interactionHandler.setGestureEndCallback(
+                () -> onTransitionComplete(cmd, interactionHandler));
+
+        Intent intent = new Intent(interactionHandler.getLaunchIntent());
+        interactionHandler.initWhenReady(intent);
+
+        RecentsAnimationListener recentAnimListener = new RecentsAnimationListener() {
+            @Override
+            public void onRecentsAnimationStart(RecentsAnimationController controller,
+                    RecentsAnimationTargets targets) {
+                interactionHandler.onGestureEnded(0, new PointF(), new PointF());
+                cmd.removeListener(this);
+            }
+
+            @Override
+            public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
+                interactionHandler.onGestureCancelled();
+                cmd.removeListener(this);
+            }
+        };
+
+        if (mTaskAnimationManager.isRecentsAnimationRunning()) {
+            cmd.mActiveCallbacks = mTaskAnimationManager.continueRecentsAnimation(gestureState);
+            cmd.mActiveCallbacks.addListener(interactionHandler);
+            mTaskAnimationManager.notifyRecentsAnimationState(interactionHandler);
+            interactionHandler.onGestureStarted(true /*isLikelyToStartNewTask*/);
+
+            cmd.mActiveCallbacks.addListener(recentAnimListener);
+            mTaskAnimationManager.notifyRecentsAnimationState(recentAnimListener);
+        } else {
+            intent.putExtra(INTENT_EXTRA_LOG_TRACE_ID, gestureState.getGestureId());
+            cmd.mActiveCallbacks = mTaskAnimationManager.startRecentsAnimation(
+                    gestureState, intent, interactionHandler);
+            interactionHandler.onGestureStarted(false /*isLikelyToStartNewTask*/);
+            cmd.mActiveCallbacks.addListener(recentAnimListener);
+        }
+
+        Trace.beginAsyncSection(TRANSITION_NAME, 0);
+        return false;
+    }
+
+    private void onTransitionComplete(CommandInfo cmd, AbsSwipeUpHandler handler) {
+        cmd.removeListener(handler);
+        Trace.endAsyncSection(TRANSITION_NAME, 0);
+
+        if (cmd.type == TYPE_SHOW_NEXT_FOCUS) {
+            RecentsView rv =
+                    mOverviewComponentObserver.getActivityInterface().getVisibleRecentsView();
+            if (rv != null) {
                 // Ensure that recents view has focus so that it receives the followup key inputs
                 TaskView taskView = rv.getNextTaskView();
                 if (taskView == null) {
@@ -120,130 +244,22 @@
                 }
             }
         }
+        scheduleNextTask(cmd);
     }
 
-    private class HideRecentsCommand extends RecentsActivityCommand {
+    private static class CommandInfo {
+        public final long createTime = SystemClock.elapsedRealtime();
+        public final int type;
+        RecentsAnimationCallbacks mActiveCallbacks;
 
-        @Override
-        protected boolean handleCommand(long elapsedTime) {
-            RecentsView recents = mActivityInterface.getVisibleRecentsView();
-            if (recents == null) {
-                return false;
-            }
-            int currentPage = recents.getNextPage();
-            if (currentPage >= 0 && currentPage < recents.getTaskViewCount()) {
-                ((TaskView) recents.getPageAt(currentPage)).launchTaskAnimated();
-            } else {
-                recents.startHome();
-            }
-            return true;
-        }
-    }
-
-    private class RecentsActivityCommand<T extends StatefulActivity<?>> implements Runnable {
-
-        private static final String TRANSITION_NAME = "Transition:toOverview";
-        protected final BaseActivityInterface<?, T> mActivityInterface;
-        private final long mCreateTime;
-        private final AppToOverviewAnimationProvider<T> mAnimationProvider;
-
-        private final long mToggleClickedTime = SystemClock.uptimeMillis();
-        private ActivityInitListener mListener;
-
-        public RecentsActivityCommand() {
-            mActivityInterface = mOverviewComponentObserver.getActivityInterface();
-            mCreateTime = SystemClock.elapsedRealtime();
-            mAnimationProvider = new AppToOverviewAnimationProvider<>(mActivityInterface,
-                    ActivityManagerWrapper.getInstance().getRunningTask(), mDeviceState);
-
-            // Preload the plan
-            RecentsModel.INSTANCE.get(mContext).getTasks(null);
+        CommandInfo(int type) {
+            this.type = type;
         }
 
-        @Override
-        public void run() {
-            long elapsedTime = mCreateTime - mLastToggleTime;
-            mLastToggleTime = mCreateTime;
-
-            if (handleCommand(elapsedTime)) {
-                // Command already handled.
-                return;
+        void removeListener(RecentsAnimationListener listener) {
+            if (mActiveCallbacks != null) {
+                mActiveCallbacks.removeListener(listener);
             }
-
-            if (mActivityInterface.switchToRecentsIfVisible(this::onTransitionComplete)) {
-                // If successfully switched, then return
-                return;
-            }
-
-            final T activity = mActivityInterface.getCreatedActivity();
-            if (activity != null) {
-                InteractionJankMonitorWrapper.begin(
-                        activity.getRootView(),
-                        InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH);
-            }
-
-            // Otherwise, start overview.
-            mListener = mActivityInterface.createActivityInitListener(this::onActivityReady);
-            mListener.registerAndStartActivity(mOverviewComponentObserver.getOverviewIntent(),
-                    new RemoteAnimationProvider() {
-                        @Override
-                        public AnimatorSet createWindowAnimation(
-                                RemoteAnimationTargetCompat[] appTargets,
-                                RemoteAnimationTargetCompat[] wallpaperTargets) {
-                            return RecentsActivityCommand.this.createWindowAnimation(appTargets,
-                                    wallpaperTargets);
-                        }
-                    }, mContext, MAIN_EXECUTOR.getHandler(),
-                    mAnimationProvider.getRecentsLaunchDuration());
         }
-
-        protected boolean handleCommand(long elapsedTime) {
-            // TODO: We need to fix this case with PIP, when an activity first enters PIP, it shows
-            //       the menu activity which takes window focus, preventing the right condition from
-            //       being run below
-            RecentsView recents = mActivityInterface.getVisibleRecentsView();
-            if (recents != null) {
-                // Launch the next task
-                recents.showNextTask();
-                return true;
-            } else if (elapsedTime < ViewConfiguration.getDoubleTapTimeout()) {
-                // The user tried to launch back into overview too quickly, either after
-                // launching an app, or before overview has actually shown, just ignore for now
-                return true;
-            }
-            return false;
-        }
-
-        private boolean onActivityReady(Boolean wasVisible) {
-            final T activity = mActivityInterface.getCreatedActivity();
-            return mAnimationProvider.onActivityReady(activity, wasVisible);
-        }
-
-        private AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
-                RemoteAnimationTargetCompat[] wallpaperTargets) {
-            LatencyTrackerCompat.logToggleRecents(
-                    mContext, (int) (SystemClock.uptimeMillis() - mToggleClickedTime));
-
-            mListener.unregister();
-
-            AnimatorSet animatorSet = mAnimationProvider.createWindowAnimation(appTargets,
-                    wallpaperTargets);
-            animatorSet.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationStart(Animator animation) {
-                    Trace.beginAsyncSection(TRANSITION_NAME, 0);
-                    super.onAnimationStart(animation);
-                }
-
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    onTransitionComplete();
-                    Trace.endAsyncSection(TRANSITION_NAME, 0);
-                }
-            });
-            return animatorSet;
-        }
-
-        protected void onTransitionComplete() { }
     }
 }
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index f99b7e6..458f45a 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -17,12 +17,13 @@
 
 import static android.content.Intent.ACTION_USER_UNLOCKED;
 
-import static com.android.launcher3.util.SettingsCache.ONE_HANDED_ENABLED;
-import static com.android.launcher3.util.SettingsCache.ONE_HANDED_SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED;
 import static com.android.launcher3.util.DisplayController.DisplayHolder.CHANGE_ALL;
 import static com.android.launcher3.util.DisplayController.DisplayHolder.CHANGE_FRAME_DELAY;
+import static com.android.launcher3.util.SettingsCache.ONE_HANDED_ENABLED;
+import static com.android.launcher3.util.SettingsCache.ONE_HANDED_SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED;
 import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
 import static com.android.quickstep.SysUINavigationMode.Mode.THREE_BUTTONS;
+import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY;
@@ -60,11 +61,11 @@
 
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.util.SettingsCache;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.DisplayController.DisplayHolder;
 import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
 import com.android.launcher3.util.DisplayController.Info;
+import com.android.launcher3.util.SettingsCache;
 import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
 import com.android.quickstep.SysUINavigationMode.OneHandedModeChangeListener;
 import com.android.quickstep.util.NavBarPosition;
@@ -304,6 +305,13 @@
     }
 
     /**
+     * @return whether the current nav mode is 2-button-based.
+     */
+    public boolean isTwoButtonNavMode() {
+        return mMode == TWO_BUTTONS;
+    }
+
+    /**
      * @return whether the current nav mode is button-based.
      */
     public boolean isButtonNavMode() {
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index fc805d0..8e6f663 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -24,6 +24,7 @@
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.quickstep.GestureState.DEFAULT_STATE;
 import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE;
+import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED;
 
@@ -157,13 +158,23 @@
         @BinderThread
         public void onOverviewToggle() {
             TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onOverviewToggle");
-            mOverviewCommandHelper.onOverviewToggle();
+            // If currently screen pinning, do not enter overview
+            if (mDeviceState.isScreenPinningActive()) {
+                return;
+            }
+            TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
+            mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_TOGGLE);
         }
 
         @BinderThread
         @Override
         public void onOverviewShown(boolean triggeredFromAltTab) {
-            mOverviewCommandHelper.onOverviewShown(triggeredFromAltTab);
+            if (triggeredFromAltTab) {
+                TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
+                mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_SHOW_NEXT_FOCUS);
+            } else {
+                mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_SHOW);
+            }
         }
 
         @BinderThread
@@ -171,7 +182,7 @@
         public void onOverviewHidden(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
             if (triggeredFromAltTab && !triggeredFromHomeKey) {
                 // onOverviewShownFromAltTab hides the overview and ends at the target app
-                mOverviewCommandHelper.onOverviewHidden();
+                mOverviewCommandHelper.addCommand(OverviewCommandHelper.TYPE_HIDE);
             }
         }
 
@@ -326,8 +337,8 @@
     public void onUserUnlocked() {
         mTaskAnimationManager = new TaskAnimationManager(this);
         mOverviewComponentObserver = new OverviewComponentObserver(this, mDeviceState);
-        mOverviewCommandHelper = new OverviewCommandHelper(this, mDeviceState,
-                mOverviewComponentObserver);
+        mOverviewCommandHelper = new OverviewCommandHelper(this,
+                mOverviewComponentObserver, mTaskAnimationManager);
         mResetGestureInputConsumer = new ResetGestureInputConsumer(mTaskAnimationManager);
         mInputConsumer = InputConsumerController.getRecentsAnimationInputConsumer();
         mInputConsumer.registerInputConsumer();
@@ -555,7 +566,7 @@
         ProtoTracer.INSTANCE.get(this).scheduleFrameUpdate();
     }
 
-    private GestureState createGestureState(GestureState previousGestureState) {
+    public GestureState createGestureState(GestureState previousGestureState) {
         GestureState gestureState = new GestureState(mOverviewComponentObserver,
                 ActiveGestureLog.INSTANCE.generateAndSetLogId());
         if (mTaskAnimationManager.isRecentsAnimationRunning()) {
@@ -704,16 +715,15 @@
         }
     }
 
+    public AbsSwipeUpHandler.Factory getSwipeUpHandlerFactory() {
+        return !mOverviewComponentObserver.isHomeAndOverviewSame()
+                ? mFallbackSwipeHandlerFactory : mLauncherSwipeHandlerFactory;
+    }
+
     private InputConsumer createOtherActivityInputConsumer(GestureState gestureState,
             MotionEvent event) {
 
-        final AbsSwipeUpHandler.Factory factory;
-        if (!mOverviewComponentObserver.isHomeAndOverviewSame()) {
-            factory = mFallbackSwipeHandlerFactory;
-        } else {
-            factory = mLauncherSwipeHandlerFactory;
-        }
-
+        final AbsSwipeUpHandler.Factory factory = getSwipeUpHandlerFactory();
         final boolean shouldDefer = !mOverviewComponentObserver.isHomeAndOverviewSame()
                 || gestureState.getActivityInterface().deferStartingActivity(mDeviceState, event);
         final boolean disableHorizontalSwipe = mDeviceState.isInExclusionRegion(event);
@@ -886,15 +896,17 @@
     }
 
     private AbsSwipeUpHandler createLauncherSwipeHandler(
-            GestureState gestureState, long touchTimeMs, boolean continuingLastGesture) {
+            GestureState gestureState, long touchTimeMs) {
         return new LauncherSwipeHandlerV2(this, mDeviceState, mTaskAnimationManager,
-                gestureState, touchTimeMs, continuingLastGesture, mInputConsumer);
+                gestureState, touchTimeMs, mTaskAnimationManager.isRecentsAnimationRunning(),
+                mInputConsumer);
     }
 
     private AbsSwipeUpHandler createFallbackSwipeHandler(
-            GestureState gestureState, long touchTimeMs, boolean continuingLastGesture) {
+            GestureState gestureState, long touchTimeMs) {
         return new FallbackSwipeHandler(this, mDeviceState, mTaskAnimationManager,
-                gestureState, touchTimeMs, continuingLastGesture, mInputConsumer);
+                gestureState, touchTimeMs, mTaskAnimationManager.isRecentsAnimationRunning(),
+                mInputConsumer);
     }
 
     protected boolean shouldNotifyBackGesture() {
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index 5baf518..9878d45 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -31,7 +31,6 @@
 import static com.android.launcher3.util.VelocityUtils.PX_PER_MS;
 import static com.android.quickstep.GestureState.STATE_OVERSCROLL_WINDOW_CREATED;
 import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
-import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
 
 import android.annotation.TargetApi;
 import android.content.Context;
@@ -65,7 +64,6 @@
 import com.android.quickstep.RecentsAnimationDeviceState;
 import com.android.quickstep.RotationTouchHelper;
 import com.android.quickstep.TaskAnimationManager;
-import com.android.quickstep.TaskUtils;
 import com.android.quickstep.TouchInteractionService;
 import com.android.quickstep.util.ActiveGestureLog;
 import com.android.quickstep.util.CachedEventDispatcher;
@@ -381,9 +379,6 @@
         // Once we detect the gesture, we can enable batching to reduce further updates
         mInputEventReceiver.setBatchingEnabled(true);
 
-        mActivityInterface.closeOverlay();
-        TaskUtils.closeSystemWindowsAsync(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
-
         // Notify the handler that the gesture has actually started
         mInteractionHandler.onGestureStarted(isLikelyToStartNewTask);
     }
@@ -391,8 +386,7 @@
     private void startTouchTrackingForWindowAnimation(long touchTimeMs) {
         ActiveGestureLog.INSTANCE.addLog("startRecentsAnimation");
 
-        mInteractionHandler = mHandlerFactory.newHandler(mGestureState, touchTimeMs,
-                mTaskAnimationManager.isRecentsAnimationRunning());
+        mInteractionHandler = mHandlerFactory.newHandler(mGestureState, touchTimeMs);
         mInteractionHandler.setGestureEndCallback(this::onInteractionGestureFinished);
         mMotionPauseDetector.setOnMotionPauseListener(mInteractionHandler.getMotionPauseListener());
         Intent intent = new Intent(mInteractionHandler.getLaunchIntent());
diff --git a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
index 932ff27..ae644cd 100644
--- a/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
+++ b/quickstep/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
@@ -37,6 +37,7 @@
 import com.android.launcher3.BaseQuickstepLauncher;
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Hotseat;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.R;
@@ -84,7 +85,7 @@
         Workspace workspace = launcher.getWorkspace();
         CellLayout cellLayout = (CellLayout) workspace.getChildAt(workspace.getCurrentPage());
         ShortcutAndWidgetContainer currentPage = cellLayout.getShortcutsAndWidgets();
-        ViewGroup hotseat = launcher.getHotseat();
+        Hotseat hotseat = launcher.getHotseat();
 
         boolean workspaceClipChildren = workspace.getClipChildren();
         boolean workspaceClipToPadding = workspace.getClipToPadding();
@@ -124,11 +125,7 @@
                 addStaggeredAnimationForView(child, grid.inv.numRows + 1, totalRows);
             }
 
-            if (launcher.getAppsView().getSearchUiManager()
-                    .isQsbVisible(NORMAL.getVisibleElements(launcher))) {
-                addStaggeredAnimationForView(launcher.getAppsView().getSearchView(),
-                        grid.inv.numRows + 2, totalRows);
-            }
+            addStaggeredAnimationForView(hotseat.getQsb(), grid.inv.numRows + 2, totalRows);
         }
 
         if (animateOverviewScrim) {
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index da24b59..e042b35 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -15,18 +15,13 @@
  */
 package com.android.quickstep.views;
 
-import static com.android.launcher3.LauncherState.ALL_APPS_HEADER_EXTRA;
 import static com.android.launcher3.LauncherState.CLEAR_ALL_BUTTON;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK;
 import static com.android.launcher3.LauncherState.SPRING_LOADED;
-import static com.android.launcher3.QuickstepTransitionManager.ALL_APPS_PROGRESS_OFF_SCREEN;
-import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
 import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE;
 
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.os.Build;
@@ -41,7 +36,6 @@
 import com.android.launcher3.statemanager.StateManager.StateListener;
 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
 import com.android.quickstep.LauncherActivityInterface;
-import com.android.quickstep.SysUINavigationMode;
 import com.android.quickstep.util.OverviewToHomeAnim;
 import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.plugins.RecentsExtraCard;
@@ -104,31 +98,6 @@
         }
     }
 
-    /**
-     * Animates adjacent tasks and translate hotseat off screen as well.
-     */
-    @Override
-    public AnimatorSet createAdjacentPageAnimForTaskLaunch(TaskView tv) {
-        AnimatorSet anim = super.createAdjacentPageAnimForTaskLaunch(tv);
-
-        if (!SysUINavigationMode.getMode(mActivity).hasGestures) {
-            // Hotseat doesn't move when opening recents with the button,
-            // so don't animate it here either.
-            return anim;
-        }
-
-        float allAppsProgressOffscreen = ALL_APPS_PROGRESS_OFF_SCREEN;
-        LauncherState state = mActivity.getStateManager().getState();
-        if ((state.getVisibleElements(mActivity) & ALL_APPS_HEADER_EXTRA) != 0) {
-            float maxShiftRange = mActivity.getDeviceProfile().heightPx;
-            float currShiftRange = mActivity.getAllAppsController().getShiftRange();
-            allAppsProgressOffscreen = 1f + (maxShiftRange - currShiftRange) / maxShiftRange;
-        }
-        anim.play(ObjectAnimator.ofFloat(
-                mActivity.getAllAppsController(), ALL_APPS_PROGRESS, allAppsProgressOffscreen));
-        return anim;
-    }
-
     @Override
     protected void onTaskLaunchAnimationEnd(boolean success) {
         if (success) {
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 8f35ea6..ce97fdc 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -1538,29 +1538,6 @@
         }
     }
 
-    public void showNextTask() {
-        final TaskView runningTaskView = getRunningTaskView();
-        final TaskView targetTask;
-
-        if (runningTaskView == null) {
-            // Launch the first task
-            if (getTaskViewCount() > 0) {
-                targetTask = getTaskViewAt(0);
-            } else {
-                return;
-            }
-        } else {
-            final TaskView nextTask = getNextTaskView();
-            if (nextTask != null) {
-                targetTask = nextTask;
-            } else {
-                targetTask = runningTaskView;
-            }
-        }
-        targetTask.setEndQuickswitchCuj(true);
-        targetTask.launchTaskAnimated();
-    }
-
     public void setRunningTaskIconScaledDown(boolean isScaledDown) {
         if (mRunningTaskIconScaledDown != isScaledDown) {
             mRunningTaskIconScaledDown = isScaledDown;
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index 0fe5432..5ffe315 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -120,6 +120,7 @@
                 getCurrentOverviewPage(launcher) < currentTaskAfterFlingForward));
 
         // Test opening a task.
+        startTestActivity(2);
         OverviewTask task = mLauncher.pressHome().switchToOverview().getCurrentTask();
         assertNotNull("overview.getCurrentTask() returned null (1)", task);
         assertNotNull("OverviewTask.open returned null", task.open());
diff --git a/res/drawable/ic_expand_less.xml b/res/drawable/ic_expand_less.xml
index 8360cee..cc16083 100644
--- a/res/drawable/ic_expand_less.xml
+++ b/res/drawable/ic_expand_less.xml
@@ -18,7 +18,7 @@
     android:height="24dp"
     android:viewportWidth="24"
     android:viewportHeight="24"
-    android:tint="?android:attr/textColorHint">
+    android:tint="?android:attr/textColorSecondary">
     <path
         android:fillColor="#FF000000"
         android:pathData="M18.59,16.41L20,15l-8,-8 -8,8 1.41,1.41L12,9.83"/>
diff --git a/res/drawable/ic_expand_more.xml b/res/drawable/ic_expand_more.xml
index 49e24f6..ecbce7f 100644
--- a/res/drawable/ic_expand_more.xml
+++ b/res/drawable/ic_expand_more.xml
@@ -18,7 +18,7 @@
     android:height="24dp"
     android:viewportWidth="24"
     android:viewportHeight="24"
-    android:tint="?android:attr/textColorHint">
+    android:tint="?android:attr/textColorSecondary">
     <path
         android:fillColor="#FF000000"
         android:pathData="M5.41,7.59L4,9l8,8 8,-8 -1.41,-1.41L12,14.17"/>
diff --git a/res/drawable/widgets_list_bottom_ripple.xml b/res/drawable/widgets_list_bottom_ripple.xml
new file mode 100644
index 0000000..3a26091
--- /dev/null
+++ b/res/drawable/widgets_list_bottom_ripple.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2021, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="?android:attr/colorControlHighlight">
+    <item android:id="@android:id/mask">
+        <shape android:shape="rectangle">
+            <corners
+                android:topLeftRadius="@dimen/widget_list_content_corner_radius"
+                android:topRightRadius="@dimen/widget_list_content_corner_radius"
+                android:bottomLeftRadius="@dimen/widget_list_top_bottom_corner_radius"
+                android:bottomRightRadius="@dimen/widget_list_top_bottom_corner_radius" />
+        </shape>
+    </item>
+    <item android:id="@android:id/background">
+        <shape android:shape="rectangle">
+            <solid android:color="?android:attr/colorBackground" />
+            <corners
+                android:topLeftRadius="@dimen/widget_list_content_corner_radius"
+                android:topRightRadius="@dimen/widget_list_content_corner_radius"
+                android:bottomLeftRadius="@dimen/widget_list_top_bottom_corner_radius"
+                android:bottomRightRadius="@dimen/widget_list_top_bottom_corner_radius" />
+        </shape>
+    </item>
+</ripple>
\ No newline at end of file
diff --git a/res/drawable/widgets_list_middle_ripple.xml b/res/drawable/widgets_list_middle_ripple.xml
new file mode 100644
index 0000000..da025d7
--- /dev/null
+++ b/res/drawable/widgets_list_middle_ripple.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2021, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="?android:attr/colorControlHighlight">
+    <item android:id="@android:id/mask">
+        <shape android:shape="rectangle">
+            <corners
+                android:topLeftRadius="@dimen/widget_list_content_corner_radius"
+                android:topRightRadius="@dimen/widget_list_content_corner_radius"
+                android:bottomLeftRadius="@dimen/widget_list_content_corner_radius"
+                android:bottomRightRadius="@dimen/widget_list_content_corner_radius" />
+        </shape>
+    </item>
+
+    <item android:id="@android:id/background">
+        <shape android:shape="rectangle">
+            <solid android:color="?android:attr/colorBackground" />
+            <corners
+                android:topLeftRadius="@dimen/widget_list_content_corner_radius"
+                android:topRightRadius="@dimen/widget_list_content_corner_radius"
+                android:bottomLeftRadius="@dimen/widget_list_content_corner_radius"
+                android:bottomRightRadius="@dimen/widget_list_content_corner_radius" />
+        </shape>
+    </item>
+</ripple>
\ No newline at end of file
diff --git a/res/drawable/widgets_list_top_ripple.xml b/res/drawable/widgets_list_top_ripple.xml
new file mode 100644
index 0000000..6efc3e1
--- /dev/null
+++ b/res/drawable/widgets_list_top_ripple.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2021, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="?android:attr/colorControlHighlight">
+    <item android:id="@android:id/mask">
+        <shape android:shape="rectangle">
+            <corners
+                android:topLeftRadius="@dimen/widget_list_top_bottom_corner_radius"
+                android:topRightRadius="@dimen/widget_list_top_bottom_corner_radius"
+                android:bottomLeftRadius="@dimen/widget_list_content_corner_radius"
+                android:bottomRightRadius="@dimen/widget_list_content_corner_radius" />
+        </shape>
+    </item>
+
+    <item android:id="@android:id/background">
+        <shape android:shape="rectangle">
+            <solid android:color="?android:attr/colorBackground" />
+            <corners
+                android:topLeftRadius="@dimen/widget_list_top_bottom_corner_radius"
+                android:topRightRadius="@dimen/widget_list_top_bottom_corner_radius"
+                android:bottomLeftRadius="@dimen/widget_list_content_corner_radius"
+                android:bottomRightRadius="@dimen/widget_list_content_corner_radius" />
+        </shape>
+    </item>
+</ripple>
\ No newline at end of file
diff --git a/res/layout/all_apps.xml b/res/layout/all_apps.xml
index 8ed16c7..24d764e 100644
--- a/res/layout/all_apps.xml
+++ b/res/layout/all_apps.xml
@@ -44,7 +44,6 @@
     </com.android.launcher3.allapps.FloatingHeaderView>
 
     <include
-        android:id="@id/search_container_all_apps"
         layout="@layout/search_container_all_apps"/>
 
     <include layout="@layout/all_apps_fast_scroller" />
diff --git a/res/layout/search_container_hotseat.xml b/res/layout/search_container_hotseat.xml
new file mode 100644
index 0000000..8f12ca0
--- /dev/null
+++ b/res/layout/search_container_hotseat.xml
@@ -0,0 +1,20 @@
+<?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="match_parent"
+    android:layout_height="0dp" />
\ No newline at end of file
diff --git a/res/layout/widget_cell_content.xml b/res/layout/widget_cell_content.xml
index 65a49ab..50908a4 100644
--- a/res/layout/widget_cell_content.xml
+++ b/res/layout/widget_cell_content.xml
@@ -36,7 +36,7 @@
         android:gravity="center_horizontal"
         android:singleLine="true"
         android:maxLines="1"
-        android:textColor="?android:attr/textColorSecondary"
+        android:textColor="?android:attr/textColorPrimary"
         android:textSize="14sp" />
 
     <!-- The original dimensions of the widget (can't be the same text as above due to different
@@ -46,7 +46,7 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:gravity="center_horizontal"
-        android:textColor="?android:attr/textColorSecondary"
+        android:textColor="?android:attr/textColorTertiary"
         android:textSize="14sp"
         android:alpha="0.8" />
 
@@ -55,7 +55,8 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:gravity="center_horizontal"
-        android:textSize="12sp"
+        android:textSize="14sp"
+        android:textColor="?android:attr/textColorTertiary"
         android:maxLines="2"
         android:ellipsize="end"
         android:fadingEdge="horizontal" />
diff --git a/res/layout/widgets_bottom_sheet.xml b/res/layout/widgets_bottom_sheet.xml
index c1b2cbf..d18ba56 100644
--- a/res/layout/widgets_bottom_sheet.xml
+++ b/res/layout/widgets_bottom_sheet.xml
@@ -19,12 +19,17 @@
     android:orientation="vertical"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:paddingTop="28dp"
+    android:paddingTop="16dp"
     android:background="@drawable/top_round_rect_primary"
     android:elevation="@dimen/deep_shortcuts_elevation"
     android:layout_gravity="bottom"
     android:theme="?attr/widgetsTheme">
-
+    <View
+        android:layout_width="48dp"
+        android:layout_height="2dp"
+        android:layout_gravity="center_horizontal"
+        android:layout_marginBottom="16dp"
+        android:background="?android:attr/textColorSecondary"/>
     <TextView
         style="@style/TextHeadline"
         android:id="@+id/title"
@@ -48,8 +53,8 @@
         android:id="@+id/widgets_table_scroll_view"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_marginTop="45dp"
-        android:layout_marginBottom="40dp">
+        android:fadeScrollbars="false"
+        android:layout_marginVertical="16dp">
         <include layout="@layout/widgets_table_container"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
diff --git a/res/layout/widgets_full_sheet.xml b/res/layout/widgets_full_sheet.xml
index 226c4f7..172284b 100644
--- a/res/layout/widgets_full_sheet.xml
+++ b/res/layout/widgets_full_sheet.xml
@@ -25,7 +25,7 @@
         android:id="@+id/container"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:background="?android:attr/colorPrimary"
+        android:background="?android:attr/colorBackgroundFloating"
         android:elevation="4dp">
 
         <TextView
diff --git a/res/layout/widgets_full_sheet_search_and_recommendations.xml b/res/layout/widgets_full_sheet_search_and_recommendations.xml
index 6182255..1219f57 100644
--- a/res/layout/widgets_full_sheet_search_and_recommendations.xml
+++ b/res/layout/widgets_full_sheet_search_and_recommendations.xml
@@ -25,7 +25,7 @@
         android:layout_width="48dp"
         android:layout_height="2dp"
         android:layout_gravity="center_horizontal"
-        android:background="@color/popup_color_primary_dark"/>
+        android:background="?android:attr/textColorSecondary"/>
     <TextView
         android:id="@+id/title"
         android:layout_width="match_parent"
@@ -33,6 +33,7 @@
         android:gravity="center_horizontal"
         android:textSize="24sp"
         android:layout_marginTop="16dp"
+        android:textColor="?android:attr/textColorSecondary"
         android:text="@string/widget_button_text"/>
     <include layout="@layout/widgets_search_bar"/>
 </LinearLayout>
diff --git a/res/layout/widgets_list_row_header.xml b/res/layout/widgets_list_row_header.xml
index 1590286..62345b3 100644
--- a/res/layout/widgets_list_row_header.xml
+++ b/res/layout/widgets_list_row_header.xml
@@ -18,7 +18,9 @@
     android:id="@+id/widgets_list_header"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:background="?android:attr/selectableItemBackground"
+    android:layout_marginHorizontal="8dp"
+    android:background="@drawable/widgets_list_middle_ripple"
+    android:layout_marginBottom="@dimen/widget_list_entry_bottom_margin"
     android:paddingVertical="@dimen/widget_list_header_view_vertical_padding"
     android:orientation="horizontal">
 
@@ -54,6 +56,7 @@
             android:layout_height="wrap_content"
             android:ellipsize="end"
             android:maxLines="1"
+            android:textColor="?android:attr/textColorTertiary"
             tools:text="m widgets, n shortcuts" />
 
     </LinearLayout>
diff --git a/res/layout/widgets_search_bar.xml b/res/layout/widgets_search_bar.xml
index 450e5f1..cf693bb 100644
--- a/res/layout/widgets_search_bar.xml
+++ b/res/layout/widgets_search_bar.xml
@@ -6,13 +6,13 @@
     android:layout_height="wrap_content"
     android:orientation="horizontal"
     android:layout_marginTop="16dp"
-    android:background="@drawable/bg_widgets_searchbox"
-    android:padding="12dp">
+    android:background="@drawable/bg_widgets_searchbox">
 
-    <EditText
+    <com.android.launcher3.ExtendedEditText
         android:id="@+id/widgets_search_bar_edit_text"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
+        android:padding="12dp"
         android:drawablePadding="8dp"
         android:drawableStart="@drawable/ic_allapps_search"
         android:background="@null"
diff --git a/res/layout/widgets_table_container.xml b/res/layout/widgets_table_container.xml
index c4dfe7e..0b5f0b9 100644
--- a/res/layout/widgets_table_container.xml
+++ b/res/layout/widgets_table_container.xml
@@ -19,4 +19,5 @@
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:layout_marginHorizontal="8dp"
-    android:background="?android:attr/colorPrimaryDark" />
+    android:background="@drawable/widgets_list_middle_ripple"
+    android:layout_marginBottom="@dimen/widget_list_entry_bottom_margin"/>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index d25cdb6..1bace48 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -110,7 +110,12 @@
     <dimen name="widget_cell_vertical_padding">8dp</dimen>
     <dimen name="widget_cell_horizontal_padding">16dp</dimen>
 
+
+    <dimen name="widget_list_top_bottom_corner_radius">28dp</dimen>
+    <dimen name="widget_list_content_corner_radius">4dp</dimen>
+
     <dimen name="widget_list_header_view_vertical_padding">20dp</dimen>
+    <dimen name="widget_list_entry_bottom_margin">2dp</dimen>
 
     <dimen name="widget_preview_shadow_blur">0.5dp</dimen>
     <dimen name="widget_preview_key_shadow_distance">1dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 0600cae..1eb123b 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -84,12 +84,13 @@
     <!-- Text shown when there is no widgets shown in the popup view showing all available widgets
          installed on the device. [CHAR_LIMIT=none] -->
     <string name="no_widgets_available">No widgets available</string>
+    <!-- Text shown when there are no matching widget search results for user's search query.
+         [CHAR_LIMIT=none] -->
+    <string name="no_search_results">No search results</string>
 
     <!-- All Apps -->
     <!-- Search bar text in the apps view. [CHAR_LIMIT=50] -->
     <string name="all_apps_search_bar_hint">Search apps</string>
-    <!-- Search bar text in the apps view. [CHAR_LIMIT=50] -->
-    <string name="all_apps_on_device_search_bar_hint">Search this phone and more…</string>
     <!-- Loading apps text. [CHAR_LIMIT=50] -->
     <string name="all_apps_loading_message">Loading apps&#8230;</string>
     <!-- No-search-results text. [CHAR_LIMIT=50] -->
diff --git a/robolectric_tests/Android.bp b/robolectric_tests/Android.bp
index c738df9..50309b7 100644
--- a/robolectric_tests/Android.bp
+++ b/robolectric_tests/Android.bp
@@ -16,27 +16,31 @@
 // Launcher Robolectric test target.
 //
 //        "robolectric_android-all-stub", not needed, we write our own stubs
+filegroup {
+    name: "launcher3-robolectric-resources",
+    path: "resources",
+    srcs: ["resources/*"],
+}
+
+filegroup {
+    name: "launcher3-robolectric-src",
+    srcs: ["src/**/*.java"],
+}
+
 android_robolectric_test {
     name: "LauncherRoboTests",
     srcs: [
-        "src/**/*.java",
+        ":launcher3-robolectric-src",
+        ":launcher3-test-src-common",
     ],
-    java_resource_dirs: [
-        "resources",
-        "res",
-        "config",
-    ],
+    java_resources: [":launcher3-robolectric-resources"],
     static_libs: [
         "truth-prebuilt",
-        "Launcher3TestCommon",
         "androidx.test.runner",
         "androidx.test.rules",
         "mockito-robolectric-prebuilt",
     ],
-    //robolectric_prebuilt_version: "4.4",
-    libs: [
-        "platform-robolectric-4.4-prebuilt",
-    ],
+    robolectric_prebuilt_version: "4.5.1",
     instrumentation_for: "Launcher3",
 
     test_options: {
diff --git a/robolectric_tests/config/robolectric.properties b/robolectric_tests/resources/robolectric.properties
similarity index 98%
rename from robolectric_tests/config/robolectric.properties
rename to robolectric_tests/resources/robolectric.properties
index 1b170e1..abb6968 100644
--- a/robolectric_tests/config/robolectric.properties
+++ b/robolectric_tests/resources/robolectric.properties
@@ -1,4 +1,4 @@
-sdk=29
+sdk=30
 
 shadows= \
     com.android.launcher3.shadows.LShadowAppPredictionManager \
diff --git a/robolectric_tests/src/com/android/launcher3/ui/LauncherUIScrollTest.java b/robolectric_tests/src/com/android/launcher3/ui/LauncherUIScrollTest.java
index 34cb2ad..4d151f1 100644
--- a/robolectric_tests/src/com/android/launcher3/ui/LauncherUIScrollTest.java
+++ b/robolectric_tests/src/com/android/launcher3/ui/LauncherUIScrollTest.java
@@ -54,6 +54,7 @@
  */
 @RunWith(RobolectricTestRunner.class)
 @LooperMode(Mode.PAUSED)
+@org.junit.Ignore
 public class LauncherUIScrollTest {
 
     private Context mTargetContext;
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java
index e8c11da..84a03d5 100644
--- a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java
+++ b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinderTest.java
@@ -34,6 +34,7 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.R;
+import com.android.launcher3.WidgetPreviewLoader;
 import com.android.launcher3.icons.BitmapInfo;
 import com.android.launcher3.icons.ComponentWithLabel;
 import com.android.launcher3.icons.IconCache;
@@ -78,6 +79,8 @@
     @Mock
     private DeviceProfile mDeviceProfile;
     @Mock
+    private WidgetPreviewLoader mWidgetPreviewLoader;
+    @Mock
     private OnHeaderClickListener mOnHeaderClickListener;
 
     @Before
@@ -97,8 +100,14 @@
             return componentWithLabel.getComponent().getShortClassName();
         }).when(mIconCache).getTitleNoCache(any());
 
+        WidgetsListAdapter widgetsListAdapter = new WidgetsListAdapter(mContext,
+                LayoutInflater.from(mTestActivity),
+                mWidgetPreviewLoader,
+                mIconCache,
+                /* iconClickListener= */ view -> {},
+                /* iconLongClickListener= */ view -> false);
         mViewHolderBinder = new WidgetsListHeaderViewHolderBinder(
-                LayoutInflater.from(mTestActivity), mOnHeaderClickListener);
+                LayoutInflater.from(mTestActivity), mOnHeaderClickListener, widgetsListAdapter);
     }
 
     @After
@@ -115,7 +124,7 @@
                 APP_NAME,
                 TEST_PACKAGE,
                 /* numOfWidgets= */ 3);
-        mViewHolderBinder.bindViewHolder(viewHolder, entry);
+        mViewHolderBinder.bindViewHolder(viewHolder, entry, /* position= */ 0);
 
         TextView appTitle = widgetsListHeader.findViewById(R.id.app_title);
         TextView appSubtitle = widgetsListHeader.findViewById(R.id.app_subtitle);
@@ -133,7 +142,7 @@
                 TEST_PACKAGE,
                 /* numOfWidgets= */ 3);
 
-        mViewHolderBinder.bindViewHolder(viewHolder, entry);
+        mViewHolderBinder.bindViewHolder(viewHolder, entry, /* position= */ 0);
         widgetsListHeader.callOnClick();
 
         verify(mOnHeaderClickListener).onHeaderClicked(eq(true),
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinderTest.java b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinderTest.java
index 07fbfd2..075c58d 100644
--- a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinderTest.java
+++ b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinderTest.java
@@ -34,6 +34,7 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.R;
+import com.android.launcher3.WidgetPreviewLoader;
 import com.android.launcher3.icons.BitmapInfo;
 import com.android.launcher3.icons.ComponentWithLabel;
 import com.android.launcher3.icons.IconCache;
@@ -78,6 +79,8 @@
     @Mock
     private DeviceProfile mDeviceProfile;
     @Mock
+    private WidgetPreviewLoader mWidgetPreviewLoader;
+    @Mock
     private OnHeaderClickListener mOnHeaderClickListener;
 
     @Before
@@ -97,8 +100,14 @@
             return componentWithLabel.getComponent().getShortClassName();
         }).when(mIconCache).getTitleNoCache(any());
 
+        WidgetsListAdapter widgetsListAdapter = new WidgetsListAdapter(mContext,
+                LayoutInflater.from(mTestActivity),
+                mWidgetPreviewLoader,
+                mIconCache,
+                /* iconClickListener= */ view -> {},
+                /* iconLongClickListener= */ view -> false);
         mViewHolderBinder = new WidgetsListSearchHeaderViewHolderBinder(
-                LayoutInflater.from(mTestActivity), mOnHeaderClickListener);
+                LayoutInflater.from(mTestActivity), mOnHeaderClickListener, widgetsListAdapter);
     }
 
     @After
@@ -115,7 +124,7 @@
                 APP_NAME,
                 TEST_PACKAGE,
                 /* numOfWidgets= */ 3);
-        mViewHolderBinder.bindViewHolder(viewHolder, entry);
+        mViewHolderBinder.bindViewHolder(viewHolder, entry, /* position= */ 0);
 
         TextView appTitle = widgetsListHeader.findViewById(R.id.app_title);
         TextView appSubtitle = widgetsListHeader.findViewById(R.id.app_subtitle);
@@ -134,7 +143,7 @@
                 TEST_PACKAGE,
                 /* numOfWidgets= */ 3);
 
-        mViewHolderBinder.bindViewHolder(viewHolder, entry);
+        mViewHolderBinder.bindViewHolder(viewHolder, entry, /* position= */ 0);
         widgetsListHeader.callOnClick();
 
         verify(mOnHeaderClickListener).onHeaderClicked(eq(true),
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
index 8a0cf34..0c6e717 100644
--- a/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
+++ b/robolectric_tests/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinderTest.java
@@ -106,12 +106,19 @@
             return componentWithLabel.getComponent().getShortClassName();
         }).when(mIconCache).getTitleNoCache(any());
 
+        WidgetsListAdapter widgetsListAdapter = new WidgetsListAdapter(mContext,
+                LayoutInflater.from(mTestActivity),
+                mWidgetPreviewLoader,
+                mIconCache,
+                /* iconClickListener= */ view -> {},
+                /* iconLongClickListener= */ view -> false);
         mViewHolderBinder = new WidgetsListTableViewHolderBinder(
                 mContext,
                 LayoutInflater.from(mTestActivity),
                 mOnIconClickListener,
                 mOnLongClickListener,
-                mWidgetPreviewLoader);
+                mWidgetPreviewLoader,
+                widgetsListAdapter);
     }
 
     @After
@@ -127,7 +134,7 @@
                 APP_NAME,
                 TEST_PACKAGE,
                 /* numOfWidgets= */ 3);
-        mViewHolderBinder.bindViewHolder(viewHolder, entry);
+        mViewHolderBinder.bindViewHolder(viewHolder, entry, /* position= */ 0);
         shadowOf(getMainLooper()).idle();
 
         // THEN the table container has one row, which contains 3 widgets.
diff --git a/robolectric_tests/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarControllerTest.java b/robolectric_tests/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarControllerTest.java
index 7fc9650..4e6f17c 100644
--- a/robolectric_tests/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarControllerTest.java
+++ b/robolectric_tests/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarControllerTest.java
@@ -24,9 +24,9 @@
 
 import android.content.Context;
 import android.view.View;
-import android.widget.EditText;
 import android.widget.ImageButton;
 
+import com.android.launcher3.ExtendedEditText;
 import com.android.launcher3.search.SearchAlgorithm;
 import com.android.launcher3.widget.model.WidgetsListBaseEntry;
 
@@ -45,7 +45,7 @@
 
     private WidgetsSearchBarController mController;
     private Context mContext;
-    private EditText mEditText;
+    private ExtendedEditText mEditText;
     private ImageButton mCancelButton;
     @Mock
     private SearchModeListener mSearchModeListener;
@@ -56,7 +56,7 @@
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mContext = RuntimeEnvironment.application;
-        mEditText = new EditText(mContext);
+        mEditText = new ExtendedEditText(mContext);
         mCancelButton = new ImageButton(mContext);
         mController = new WidgetsSearchBarController(
                 mSearchAlgorithm, mEditText, mCancelButton, mSearchModeListener);
@@ -116,11 +116,10 @@
     }
 
     @Test
-    public void cancelSearch_shouldInformSearchModeListenerToExitSearch() {
+    public void cancelSearch_shouldInformSearchModeListenerToClearResultsAndExitSearch() {
         mCancelButton.performClick();
 
         verify(mSearchModeListener).exitSearchMode();
-        verifyNoMoreInteractions(mSearchModeListener);
     }
 
     @Test
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index b8833cf..cc4bfe8 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -60,6 +60,7 @@
 import com.android.launcher3.accessibility.DragAndDropAccessibilityDelegate;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dragndrop.AppWidgetHostViewDrawable;
 import com.android.launcher3.dragndrop.DraggableView;
 import com.android.launcher3.folder.PreviewBackground;
 import com.android.launcher3.graphics.DragPreviewProvider;
@@ -180,6 +181,9 @@
     private final ArrayList<View> mIntersectingViews = new ArrayList<>();
     private final Rect mOccupiedRect = new Rect();
     private final int[] mDirectionVector = new int[2];
+    private final Workspace mWorkspace;
+    private final DeviceProfile mDeviceProfile;
+
     final int[] mPreviousReorderDirection = new int[2];
     private static final int INVALID_DIRECTION = -100;
 
@@ -209,15 +213,15 @@
         setWillNotDraw(false);
         setClipToPadding(false);
         mActivity = ActivityContext.lookupContext(context);
+        mWorkspace = Launcher.cast(mActivity).getWorkspace();
+        mDeviceProfile = mActivity.getDeviceProfile();
 
-        DeviceProfile grid = mActivity.getDeviceProfile();
-
-        mBorderSpacing = grid.cellLayoutBorderSpacingPx;
+        mBorderSpacing = mDeviceProfile.cellLayoutBorderSpacingPx;
         mCellWidth = mCellHeight = -1;
         mFixedCellWidth = mFixedCellHeight = -1;
 
-        mCountX = grid.inv.numColumns;
-        mCountY = grid.inv.numRows;
+        mCountX = mDeviceProfile.inv.numColumns;
+        mCountY = mDeviceProfile.inv.numRows;
         mOccupied =  new GridOccupancy(mCountX, mCountY);
         mTmpOccupied = new GridOccupancy(mCountX, mCountY);
 
@@ -234,7 +238,7 @@
         mBackground.setCallback(this);
         mBackground.setAlpha(0);
 
-        mReorderPreviewAnimationMagnitude = (REORDER_PREVIEW_MAGNITUDE * grid.iconSizePx);
+        mReorderPreviewAnimationMagnitude = (REORDER_PREVIEW_MAGNITUDE * mDeviceProfile.iconSizePx);
 
         // Initialize the data structures used for the drag visualization.
         mEaseOutInterpolator = Interpolators.DEACCEL_2_5; // Quint ease out
@@ -961,15 +965,18 @@
         final int oldDragCellX = mDragCell[0];
         final int oldDragCellY = mDragCell[1];
 
-        if (outlineProvider == null || outlineProvider.generatedDragOutline == null) {
-            return;
-        }
-
-        Bitmap dragOutline = outlineProvider.generatedDragOutline;
         if (cellX != oldDragCellX || cellY != oldDragCellY) {
             mDragCell[0] = cellX;
             mDragCell[1] = cellY;
 
+            applyColorExtraction(dragObject, mDragCell, spanX, spanY);
+
+            if (outlineProvider == null || outlineProvider.generatedDragOutline == null) {
+                return;
+            }
+
+            Bitmap dragOutline = outlineProvider.generatedDragOutline;
+
             final int oldIndex = mDragOutlineCurrent;
             mDragOutlineAnims[oldIndex].animateOut();
             mDragOutlineCurrent = (oldIndex + 1) % mDragOutlines.length;
@@ -1011,6 +1018,20 @@
         }
     }
 
+    /** Applies the local color extraction to a dragging widget object. */
+    private void applyColorExtraction(DropTarget.DragObject dragObject, int[] targetCell, int spanX,
+            int spanY) {
+        // Apply local extracted color if the DragView is an AppWidgetHostViewDrawable.
+        Drawable drawable = dragObject.dragView.getDrawable();
+        if (drawable instanceof AppWidgetHostViewDrawable) {
+            int screenId = mWorkspace.getIdForScreen(this);
+            int pageId = mWorkspace.getPageIndexForScreenId(screenId);
+            AppWidgetHostViewDrawable hostViewDrawable = ((AppWidgetHostViewDrawable) drawable);
+            cellToRect(targetCell[0], targetCell[1], spanX, spanY, mTempRect);
+            hostViewDrawable.getAppWidgetHostView().handleDrag(mTempRect, pageId);
+        }
+    }
+
     @SuppressLint("StringFormatMatches")
     public String getItemMoveDescription(int cellX, int cellY) {
         if (mContainerType == HOTSEAT) {
@@ -2076,7 +2097,7 @@
     private void commitTempPlacement() {
         mTmpOccupied.copyTo(mOccupied);
 
-        int screenId = Launcher.cast(mActivity).getWorkspace().getIdForScreen(this);
+        int screenId = mWorkspace.getIdForScreen(this);
         int container = Favorites.CONTAINER_DESKTOP;
 
         if (mContainerType == HOTSEAT) {
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index e5b75c1..0e9de45 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -20,6 +20,7 @@
 import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.view.Gravity;
+import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewDebug;
@@ -42,6 +43,9 @@
     private static final int ALPHA_INDEX_REPLACE_TASKBAR = 1;
     private static final int NUM_ALPHA_CHANNELS = 2;
 
+    // Ratio of empty space, qsb should take up to appear visually centered.
+    public static final float QSB_CENTER_FACTOR = .325f;
+
     @ViewDebug.ExportedProperty(category = "launcher")
     private boolean mHasVerticalHotseat;
     private Workspace mWorkspace;
@@ -50,6 +54,8 @@
     private Consumer<Boolean> mOnVisibilityAggregatedCallback;
 
     private final MultiValueAlpha mMultiValueAlpha;
+    private final View mQsb;
+    private final int mQsbHeight;
 
     public Hotseat(Context context) {
         this(context, null);
@@ -63,6 +69,10 @@
         super(context, attrs, defStyle);
         mMultiValueAlpha = new MultiValueAlpha(this, NUM_ALPHA_CHANNELS, MultiValueAlpha.Mode.MAX);
         mMultiValueAlpha.setUpdateVisibility(true);
+
+        mQsb = LayoutInflater.from(context).inflate(R.layout.search_container_hotseat, this, false);
+        mQsbHeight = mQsb.getLayoutParams().height;
+        addView(mQsb);
     }
 
     /**
@@ -97,6 +107,7 @@
         DeviceProfile grid = mActivity.getDeviceProfile();
 
         if (grid.isVerticalBarLayout()) {
+            mQsb.setVisibility(View.GONE);
             lp.height = ViewGroup.LayoutParams.MATCH_PARENT;
             if (grid.isSeascape()) {
                 lp.gravity = Gravity.LEFT;
@@ -106,12 +117,15 @@
                 lp.width = grid.hotseatBarSizePx + insets.right;
             }
         } else {
+            mQsb.setVisibility(View.VISIBLE);
             lp.gravity = Gravity.BOTTOM;
             lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
-            lp.height = grid.isTaskbarPresent
-                    ? grid.taskbarSize
-                    : grid.hotseatBarSizePx + insets.bottom;
+            lp.height = (grid.isTaskbarPresent
+                        ? grid.workspacePadding.bottom
+                        : grid.hotseatBarSizePx)
+                    + insets.bottom;
         }
+
         if (!grid.isTaskbarPresent) {
             // When taskbar is present, we set the padding separately to ensure a seamless visual
             // handoff between taskbar and hotseat during drag and drop.
@@ -177,6 +191,34 @@
         //Does nothing
     }
 
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+        int width = getShortcutsAndWidgets().getMeasuredWidth();
+        mQsb.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
+                MeasureSpec.makeMeasureSpec(mQsbHeight, MeasureSpec.EXACTLY));
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        super.onLayout(changed, l, t, r, b);
+
+        int qsbWidth = mQsb.getMeasuredWidth();
+        int left = (r - l - qsbWidth) / 2;
+        int right = left + qsbWidth;
+
+        DeviceProfile dp = mActivity.getDeviceProfile();
+        int freeSpace = dp.isTaskbarPresent
+                ? dp.workspacePadding.bottom
+                : dp.hotseatBarSizePx - dp.hotseatCellHeightPx - mQsbHeight;
+        int bottom = b - t
+                - (int) (freeSpace * QSB_CENTER_FACTOR)
+                - dp.getInsets().bottom;
+        int top = bottom - mQsbHeight;
+        mQsb.layout(left, top, right, bottom);
+    }
+
     /**
      * Returns the first View for which the given itemOperator returns true, or null.
      */
@@ -191,4 +233,11 @@
     public MultiValueAlpha.AlphaProperty getReplaceTaskbarAlpha() {
         return mMultiValueAlpha.getProperty(ALPHA_INDEX_REPLACE_TASKBAR);
     }
+
+    /**
+     * Returns the QSB inside hotseat
+     */
+    public View getQsb() {
+        return mQsb;
+    }
 }
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index ae75b51..e9a3495 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -30,7 +30,6 @@
 import android.content.Context;
 import android.view.animation.Interpolator;
 
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.statemanager.BaseState;
 import com.android.launcher3.statemanager.StateManager;
 import com.android.launcher3.states.HintState;
@@ -52,20 +51,13 @@
      */
     public static final int NONE = 0;
     public static final int HOTSEAT_ICONS = 1 << 0;
-    public static final int HOTSEAT_SEARCH_BOX = 1 << 1;
-    public static final int ALL_APPS_HEADER = 1 << 2;
-    public static final int ALL_APPS_HEADER_EXTRA = 1 << 3; // e.g. app predictions
-    public static final int ALL_APPS_CONTENT = 1 << 4;
-    public static final int VERTICAL_SWIPE_INDICATOR = 1 << 5;
-    public static final int OVERVIEW_ACTIONS = 1 << 6;
-    public static final int TASKBAR = 1 << 7;
-    public static final int CLEAR_ALL_BUTTON = 1 << 8;
-    public static final int WORKSPACE_PAGE_INDICATOR = 1 << 9;
-    public static final int SPLIT_PLACHOLDER_VIEW = 1 << 10;
-
-    /** Mask of all the items that are contained in the apps view. */
-    public static final int APPS_VIEW_ITEM_MASK =
-            HOTSEAT_SEARCH_BOX | ALL_APPS_HEADER | ALL_APPS_HEADER_EXTRA | ALL_APPS_CONTENT;
+    public static final int ALL_APPS_CONTENT = 1 << 1;
+    public static final int VERTICAL_SWIPE_INDICATOR = 1 << 2;
+    public static final int OVERVIEW_ACTIONS = 1 << 3;
+    public static final int TASKBAR = 1 << 4;
+    public static final int CLEAR_ALL_BUTTON = 1 << 5;
+    public static final int WORKSPACE_PAGE_INDICATOR = 1 << 6;
+    public static final int SPLIT_PLACHOLDER_VIEW = 1 << 7;
 
     // Flag indicating workspace has multiple pages visible.
     public static final int FLAG_MULTI_PAGE = BaseState.getFlag(0);
@@ -195,9 +187,6 @@
     public int getVisibleElements(Launcher launcher) {
         DeviceProfile deviceProfile = launcher.getDeviceProfile();
         int flags = WORKSPACE_PAGE_INDICATOR | VERTICAL_SWIPE_INDICATOR | TASKBAR;
-        if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get() && !deviceProfile.isVerticalBarLayout()) {
-            flags |= HOTSEAT_SEARCH_BOX;
-        }
         if (!deviceProfile.isTaskbarPresent) {
             flags |= HOTSEAT_ICONS;
         }
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index a94fab7..c84724f 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -111,6 +111,7 @@
 import com.android.launcher3.widget.PendingAddWidgetInfo;
 import com.android.launcher3.widget.PendingAppWidgetHostView;
 import com.android.launcher3.widget.WidgetManagerHelper;
+import com.android.launcher3.widget.dragndrop.AppWidgetHostViewDragListener;
 import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlay;
 
 import java.util.ArrayList;
@@ -1556,6 +1557,9 @@
             }
         }
 
+        if (drawable instanceof AppWidgetHostViewDrawable) {
+            mDragController.addDragListener(new AppWidgetHostViewDragListener(mLauncher));
+        }
         DragView dv = mDragController.startDrag(
                 drawable,
                 draggableView,
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index d6d2f73..412754e 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -47,7 +47,6 @@
 
 import com.android.launcher3.LauncherState.PageAlphaProvider;
 import com.android.launcher3.LauncherState.ScaleAndTranslation;
-import com.android.launcher3.allapps.AllAppsContainerView;
 import com.android.launcher3.anim.PendingAnimation;
 import com.android.launcher3.anim.PropertySetter;
 import com.android.launcher3.anim.SpringAnimationBuilder;
@@ -111,9 +110,6 @@
                 pageAlphaProvider.interpolator);
         boolean playAtomicComponent = config.playAtomicOverviewScaleComponent();
         Hotseat hotseat = mWorkspace.getHotseat();
-        // Since we set the pivot relative to mWorkspace, we need to scale a sibling of Workspace.
-        AllAppsContainerView qsbScaleView = mLauncher.getAppsView();
-        View qsbView = qsbScaleView.getSearchView();
         if (playAtomicComponent) {
             Interpolator scaleInterpolator = config.getInterpolator(ANIM_WORKSPACE_SCALE, ZOOM_OUT);
             LauncherState fromState = mLauncher.getStateManager().getState();
@@ -127,20 +123,15 @@
             }
 
             setPivotToScaleWithWorkspace(hotseat);
-            setPivotToScaleWithWorkspace(qsbScaleView);
             float hotseatScale = hotseatScaleAndTranslation.scale;
             if (shouldSpring) {
                 PendingAnimation pa = (PendingAnimation) propertySetter;
                 pa.add(getSpringScaleAnimator(mLauncher, hotseat, hotseatScale));
-                pa.add(getSpringScaleAnimator(mLauncher, qsbScaleView,
-                        qsbScaleAndTranslation.scale));
             } else {
                 Interpolator hotseatScaleInterpolator = config.getInterpolator(ANIM_HOTSEAT_SCALE,
                         scaleInterpolator);
                 propertySetter.setFloat(hotseat, SCALE_PROPERTY, hotseatScale,
                         hotseatScaleInterpolator);
-                propertySetter.setFloat(qsbScaleView, SCALE_PROPERTY, qsbScaleAndTranslation.scale,
-                        hotseatScaleInterpolator);
             }
 
             float hotseatIconsAlpha = (elements & HOTSEAT_ICONS) != 0 ? 1 : 0;
@@ -170,8 +161,6 @@
                 hotseatScaleAndTranslation.translationY, hotseatTranslationInterpolator);
         propertySetter.setFloat(mWorkspace.getPageIndicator(), VIEW_TRANSLATE_Y,
                 hotseatScaleAndTranslation.translationY, hotseatTranslationInterpolator);
-        propertySetter.setFloat(qsbView, VIEW_TRANSLATE_Y,
-                qsbScaleAndTranslation.translationY, hotseatTranslationInterpolator);
 
         setScrim(propertySetter, state);
     }
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index fdc69ec..78c404f 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -343,7 +343,7 @@
 
         mSearchContainer = findViewById(R.id.search_container_all_apps);
         mSearchUiManager = (SearchUiManager) mSearchContainer;
-        mSearchUiManager.initialize(this);
+        mSearchUiManager.initializeSearch(this);
     }
 
     public SearchUiManager getSearchUiManager() {
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 1e6f829..16ecd58 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -16,16 +16,11 @@
 package com.android.launcher3.allapps;
 
 import static com.android.launcher3.LauncherState.ALL_APPS_CONTENT;
-import static com.android.launcher3.LauncherState.ALL_APPS_HEADER_EXTRA;
-import static com.android.launcher3.LauncherState.APPS_VIEW_ITEM_MASK;
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
-import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
-import static com.android.launcher3.anim.Interpolators.INSTANT;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FADE;
-import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_HEADER_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
 import static com.android.launcher3.util.SystemUiController.UI_STATE_ALLAPPS;
@@ -194,25 +189,10 @@
      */
     public void setAlphas(LauncherState state, StateAnimationConfig config, PropertySetter setter) {
         int visibleElements = state.getVisibleElements(mLauncher);
-        boolean hasHeaderExtra = (visibleElements & ALL_APPS_HEADER_EXTRA) != 0;
         boolean hasAllAppsContent = (visibleElements & ALL_APPS_CONTENT) != 0;
 
-        boolean hasAnyVisibleItem = (visibleElements & APPS_VIEW_ITEM_MASK) != 0;
-
         Interpolator allAppsFade = config.getInterpolator(ANIM_ALL_APPS_FADE, LINEAR);
-        Interpolator headerFade = config.getInterpolator(ANIM_ALL_APPS_HEADER_FADE, allAppsFade);
-
-
-        setter.setViewAlpha(mAppsView.getContentView(), hasAllAppsContent ? 1 : 0, allAppsFade);
-        setter.setViewAlpha(mAppsView.getScrollBar(), hasAllAppsContent ? 1 : 0, allAppsFade);
-        mAppsView.getFloatingHeaderView().setContentVisibility(hasHeaderExtra,
-                hasAllAppsContent, setter, headerFade, allAppsFade);
-
-        mAppsView.getSearchUiManager().setContentVisibility(visibleElements, setter, allAppsFade);
-
-        // Set visibility of the container at the very beginning or end of the transition.
-        setter.setViewAlpha(mAppsView, hasAnyVisibleItem ? 1 : 0,
-                hasAnyVisibleItem ? INSTANT : FINAL_FRAME);
+        setter.setViewAlpha(mAppsView, hasAllAppsContent ? 1 : 0, allAppsFade);
     }
 
     public AnimatorListenerAdapter getProgressAnimatorListener() {
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderRow.java b/src/com/android/launcher3/allapps/FloatingHeaderRow.java
index 31c6cc7..9bf6043 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderRow.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderRow.java
@@ -17,10 +17,8 @@
 
 import android.graphics.Rect;
 import android.view.View;
-import android.view.animation.Interpolator;
 
 import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.anim.PropertySetter;
 
 /**
  * A abstract representation of a row in all-apps view
@@ -47,9 +45,6 @@
      */
     boolean hasVisibleContent();
 
-    void setContentVisibility(boolean hasHeaderExtra, boolean hasAllAppsContent,
-            PropertySetter setter, Interpolator headerFade, Interpolator allAppsFade);
-
     /**
      * Scrolls the content vertically.
      */
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java
index 9056e8a..86f330c 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderView.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java
@@ -15,8 +15,6 @@
  */
 package com.android.launcher3.allapps;
 
-import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
-
 import android.animation.ValueAnimator;
 import android.content.Context;
 import android.graphics.Point;
@@ -26,7 +24,6 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.animation.Interpolator;
 import android.widget.LinearLayout;
 
 import androidx.annotation.NonNull;
@@ -37,7 +34,6 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Insettable;
 import com.android.launcher3.R;
-import com.android.launcher3.anim.PropertySetter;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
 import com.android.systemui.plugins.AllAppsRow;
@@ -88,7 +84,6 @@
     private int mSnappedScrolledY;
     private int mTranslationY;
 
-    private boolean mAllowTouchForwarding;
     private boolean mForwardToRecyclerView;
 
     protected boolean mTabsHidden;
@@ -350,10 +345,6 @@
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
-        if (!mAllowTouchForwarding) {
-            mForwardToRecyclerView = false;
-            return super.onInterceptTouchEvent(ev);
-        }
         calcOffset(mTempOffset);
         ev.offsetLocation(mTempOffset.x, mTempOffset.y);
         mForwardToRecyclerView = mCurrentRV.onInterceptTouchEvent(ev);
@@ -382,20 +373,6 @@
         p.y = getTop() - mCurrentRV.getTop() - mParent.getTop();
     }
 
-    public void setContentVisibility(boolean hasHeader, boolean hasAllAppsContent,
-            PropertySetter setter, Interpolator headerFade, Interpolator allAppsFade) {
-        for (FloatingHeaderRow row : mAllRows) {
-            row.setContentVisibility(hasHeader, hasAllAppsContent, setter, headerFade, allAppsFade);
-        }
-
-        allowTouchForwarding(hasAllAppsContent);
-        setter.setFloat(mTabLayout, VIEW_ALPHA, hasAllAppsContent ? 1 : 0, headerFade);
-    }
-
-    protected void allowTouchForwarding(boolean allow) {
-        mAllowTouchForwarding = allow;
-    }
-
     public boolean hasVisibleContent() {
         for (FloatingHeaderRow row : mAllRows) {
             if (row.hasVisibleContent()) {
diff --git a/src/com/android/launcher3/allapps/PluginHeaderRow.java b/src/com/android/launcher3/allapps/PluginHeaderRow.java
index cf7142c..5b5fbb7 100644
--- a/src/com/android/launcher3/allapps/PluginHeaderRow.java
+++ b/src/com/android/launcher3/allapps/PluginHeaderRow.java
@@ -18,14 +18,10 @@
 import static android.view.View.INVISIBLE;
 import static android.view.View.VISIBLE;
 
-import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
-
 import android.graphics.Rect;
 import android.view.View;
-import android.view.animation.Interpolator;
 
 import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.anim.PropertySetter;
 import com.android.systemui.plugins.AllAppsRow;
 
 /**
@@ -65,13 +61,6 @@
     }
 
     @Override
-    public void setContentVisibility(boolean hasHeaderExtra, boolean hasAllAppsContent,
-            PropertySetter setter, Interpolator headerFade, Interpolator allAppsFade) {
-        // Don't use setViewAlpha as we want to control the visibility ourselves.
-        setter.setFloat(mView, VIEW_ALPHA, hasAllAppsContent ? 1 : 0, headerFade);
-    }
-
-    @Override
     public void setVerticalScroll(int scroll, boolean isScrolledOut) {
         mView.setVisibility(isScrolledOut ? INVISIBLE : VISIBLE);
         if (!isScrolledOut) {
diff --git a/src/com/android/launcher3/allapps/SearchUiManager.java b/src/com/android/launcher3/allapps/SearchUiManager.java
index 0d42950..0a2dea9 100644
--- a/src/com/android/launcher3/allapps/SearchUiManager.java
+++ b/src/com/android/launcher3/allapps/SearchUiManager.java
@@ -15,16 +15,12 @@
  */
 package com.android.launcher3.allapps;
 
-import static com.android.launcher3.LauncherState.ALL_APPS_HEADER;
-
 import android.graphics.Rect;
 import android.view.KeyEvent;
-import android.view.animation.Interpolator;
 
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.ExtendedEditText;
-import com.android.launcher3.anim.PropertySetter;
 
 /**
  * Interface for controlling the Apps search UI.
@@ -34,7 +30,7 @@
     /**
      * Initializes the search manager.
      */
-    void initialize(AllAppsContainerView containerView);
+    void initializeSearch(AllAppsContainerView containerView);
 
     /**
      * Notifies the search manager to close any active search session.
@@ -45,7 +41,7 @@
      * Called before dispatching a key event, in case the search manager wants to initialize
      * some UI beforehand.
      */
-    void preDispatchKeyEvent(KeyEvent keyEvent);
+    default void preDispatchKeyEvent(KeyEvent keyEvent) { };
 
     /**
      * Returns the vertical shift for the all-apps view, so that it aligns with the hotseat.
@@ -53,23 +49,9 @@
     float getScrollRangeDelta(Rect insets);
 
     /**
-     * Called as part of state transition to update the content UI
-     */
-    void setContentVisibility(int visibleElements, PropertySetter setter,
-            Interpolator interpolator);
-
-    /**
      * Called when activity is destroyed. Used to close search system services
      */
-    default void destroy() {
-    }
-
-    /**
-     * Returns true if the QSB should be visible for the given set of visible elements
-     */
-    default boolean isQsbVisible(int visibleElements) {
-        return (visibleElements & ALL_APPS_HEADER) != 0;
-    }
+    default void destroySearch() { }
 
     /**
      * @return the edit text object
diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
index 2261d51..bfcc1c7 100644
--- a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
+++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
@@ -31,7 +31,6 @@
 import android.view.KeyEvent;
 import android.view.View;
 import android.view.ViewGroup.MarginLayoutParams;
-import android.view.animation.Interpolator;
 
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.DeviceProfile;
@@ -44,7 +43,6 @@
 import com.android.launcher3.allapps.AllAppsStore;
 import com.android.launcher3.allapps.AlphabeticalAppsList;
 import com.android.launcher3.allapps.SearchUiManager;
-import com.android.launcher3.anim.PropertySetter;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.search.SearchCallback;
 
@@ -134,7 +132,7 @@
     }
 
     @Override
-    public void initialize(AllAppsContainerView appsView) {
+    public void initializeSearch(AllAppsContainerView appsView) {
         mApps = appsView.getApps();
         mAppsView = appsView;
         mSearchBarController.initialize(
@@ -223,12 +221,6 @@
     }
 
     @Override
-    public void setContentVisibility(int visibleElements, PropertySetter setter,
-            Interpolator interpolator) {
-        setter.setViewAlpha(this, isQsbVisible(visibleElements) ? 1 : 0, interpolator);
-    }
-
-    @Override
     public ExtendedEditText getEditText() {
         return this;
     }
diff --git a/src/com/android/launcher3/dragndrop/AppWidgetHostViewDrawable.java b/src/com/android/launcher3/dragndrop/AppWidgetHostViewDrawable.java
index 477bc6e..92ae670 100644
--- a/src/com/android/launcher3/dragndrop/AppWidgetHostViewDrawable.java
+++ b/src/com/android/launcher3/dragndrop/AppWidgetHostViewDrawable.java
@@ -76,4 +76,9 @@
     public ColorFilter getColorFilter() {
         return mPaint.getColorFilter();
     }
+
+    /** Returns the {@link LauncherAppWidgetHostView}. */
+    public LauncherAppWidgetHostView getAppWidgetHostView() {
+        return mAppWidgetHostView;
+    }
 }
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index df4d811..e2816f4 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -561,6 +561,11 @@
         return mInitialScale;
     }
 
+    /** Returns the current {@link Drawable} that is rendered in this view. */
+    public Drawable getDrawable() {
+        return mDrawable;
+    }
+
     private static class SpringFloatValue {
 
         private static final FloatPropertyCompat<SpringFloatValue> VALUE =
diff --git a/src/com/android/launcher3/recyclerview/ViewHolderBinder.java b/src/com/android/launcher3/recyclerview/ViewHolderBinder.java
index 4653774..5b8d5bc 100644
--- a/src/com/android/launcher3/recyclerview/ViewHolderBinder.java
+++ b/src/com/android/launcher3/recyclerview/ViewHolderBinder.java
@@ -33,7 +33,7 @@
     V newViewHolder(ViewGroup parent);
 
     /** Populate UI references in {@link ViewHolder} with data. */
-    void bindViewHolder(V viewHolder, T data);
+    void bindViewHolder(V viewHolder, T data, int position);
 
     /**
      * Called when the view is recycled. Views are recycled in batches once they are sufficiently
diff --git a/src/com/android/launcher3/util/MultiValueAlpha.java b/src/com/android/launcher3/util/MultiValueAlpha.java
index c79b1f6..0ea0290 100644
--- a/src/com/android/launcher3/util/MultiValueAlpha.java
+++ b/src/com/android/launcher3/util/MultiValueAlpha.java
@@ -46,25 +46,20 @@
      * Determines how each alpha should factor into the final alpha.
      */
     public enum Mode {
-        BLEND(1f) {
+        BLEND() {
             @Override
             public float calculateNewAlpha(float currentAlpha, float otherAlpha) {
                 return currentAlpha * otherAlpha;
             }
         },
 
-        MAX(0f) {
+        MAX() {
             @Override
             public float calculateNewAlpha(float currentAlpha, float otherAlpha) {
                 return Math.max(currentAlpha, otherAlpha);
             }
         };
 
-        Mode(float startAlpha) {
-            mStartAlpha = startAlpha;
-        }
-
-        protected final float mStartAlpha;
         protected abstract float calculateNewAlpha(float currentAlpha, float otherAlpha);
     }
 
@@ -84,7 +79,6 @@
         mView = view;
         mMyProperties = new AlphaProperty[size];
         mMode = mode;
-        mView.setAlpha(mMode.mStartAlpha);
 
         mValidMask = 0;
         for (int i = 0; i < size; i++) {
@@ -112,9 +106,9 @@
 
         private final int mMyMask;
 
-        private float mValue = mMode.mStartAlpha;
+        private float mValue = 1;
         // Factor of all other alpha channels, only valid if mMyMask is present in mValidMask.
-        private float mOthers = mMode.mStartAlpha;
+        private float mOthers = 1;
 
         AlphaProperty(int myMask) {
             mMyMask = myMask;
@@ -127,7 +121,7 @@
 
             if ((mValidMask & mMyMask) == 0) {
                 // Our cache value is not correct, recompute it.
-                mOthers = mMode.mStartAlpha;
+                mOthers = 1;
                 for (AlphaProperty prop : mMyProperties) {
                     if (prop != this) {
                         mOthers = mMode.calculateNewAlpha(mOthers, prop.mValue);
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
index df01295..8df70fb 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
@@ -16,7 +16,6 @@
 
 package com.android.launcher3.widget;
 
-import android.app.WallpaperManager;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -50,6 +49,7 @@
 import com.android.launcher3.util.Executors;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.BaseDragLayer.TouchCompleteListener;
+import com.android.launcher3.widget.dragndrop.AppWidgetHostViewDragListener;
 
 import java.util.List;
 
@@ -74,7 +74,6 @@
     private final CheckLongPressHelper mLongPressHelper;
     protected final Launcher mLauncher;
     private final Workspace mWorkspace;
-    private final WallpaperManager mWallpaperManager;
 
     @ViewDebug.ExportedProperty(category = "launcher")
     private boolean mReinflateOnConfigChange;
@@ -85,10 +84,14 @@
     private boolean mIsScrollable;
     private boolean mIsAttachedToWindow;
     private boolean mIsAutoAdvanceRegistered;
+    private boolean mIsInDragMode = false;
     private Runnable mAutoAdvanceRunnable;
     private RectF mLastLocationRegistered = null;
+    @Nullable private AppWidgetHostViewDragListener mDragListener;
+
     // Used to store the widget size during onLayout.
     private final Rect mCurrentWidgetSize = new Rect();
+    private final Rect mWidgetSizeAtDrag = new Rect();
     private final RectF mTempRectF = new RectF();
     private final boolean mIsRtl;
 
@@ -106,7 +109,6 @@
             setOnLightBackground(true);
         }
         mIsRtl = Utilities.isRtl(context.getResources());
-        mWallpaperManager = WallpaperManager.getInstance(getContext());
         mColorExtractor = LocalColorExtractor.newInstance(getContext());
         mColorExtractor.setListener(this);
     }
@@ -118,12 +120,16 @@
         } else {
             super.setColorResources(colors);
         }
+
+        if (mDragListener != null) {
+            mDragListener.onDragContentChanged();
+        }
     }
 
     @Override
     public boolean onLongClick(View view) {
         if (mIsScrollable) {
-            DragLayer dragLayer = Launcher.getLauncher(getContext()).getDragLayer();
+            DragLayer dragLayer = mLauncher.getDragLayer();
             dragLayer.requestDisallowInterceptTouchEvent(false);
         }
         view.performLongClick();
@@ -172,7 +178,7 @@
 
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
-            DragLayer dragLayer = Launcher.getLauncher(getContext()).getDragLayer();
+            DragLayer dragLayer = mLauncher.getDragLayer();
             if (mIsScrollable) {
                 dragLayer.requestDisallowInterceptTouchEvent(true);
             }
@@ -252,70 +258,89 @@
 
         mIsScrollable = checkScrollableRecursively(this);
 
-        mCurrentWidgetSize.left = left;
-        mCurrentWidgetSize.top = top;
-        mCurrentWidgetSize.right = right;
-        mCurrentWidgetSize.bottom = bottom;
-        updateColorExtraction(mCurrentWidgetSize);
+        if (!mIsInDragMode && getTag() instanceof LauncherAppWidgetInfo) {
+            mCurrentWidgetSize.left = left;
+            mCurrentWidgetSize.top = top;
+            mCurrentWidgetSize.right = right;
+            mCurrentWidgetSize.bottom = bottom;
+            LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) getTag();
+            int pageId = mWorkspace.getPageIndexForScreenId(info.screenId);
+            updateColorExtraction(mCurrentWidgetSize, pageId);
+        }
     }
 
-    private void updateColorExtraction(Rect widgetLocation) {
+    /** Starts the drag mode. */
+    public void startDrag(AppWidgetHostViewDragListener dragListener) {
+        mIsInDragMode = true;
+        mDragListener = dragListener;
+    }
+
+    /** Handles a drag event occurred on a workspace page, {@code pageId}. */
+    public void handleDrag(Rect rect, int pageId) {
+        mWidgetSizeAtDrag.set(rect);
+        updateColorExtraction(mWidgetSizeAtDrag, pageId);
+    }
+
+    /** Ends the drag mode. */
+    public void endDrag() {
+        mIsInDragMode = false;
+        mDragListener = null;
+        mWidgetSizeAtDrag.setEmpty();
+        requestLayout();
+    }
+
+    private void updateColorExtraction(Rect widgetLocation, int pageId) {
         // If the widget hasn't been measured and laid out, we cannot do this.
         if (widgetLocation.isEmpty()) {
             return;
         }
-        LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) getTag();
-        if (info != null) {
-            int screenWidth = mLauncher.getDeviceProfile().widthPx;
-            int screenHeight = mLauncher.getDeviceProfile().heightPx;
-            int numScreens = mWorkspace.getNumPagesForWallpaperParallax();
-            int screenId = mIsRtl ? numScreens - info.screenId : info.screenId;
-            float relativeScreenWidth = 1f / numScreens;
-            float absoluteTop = widgetLocation.top;
-            float absoluteBottom = widgetLocation.bottom;
-            for (View v = (View) getParent();
-                    v != null && v.getId() != R.id.launcher;
-                    v = (View) v.getParent()) {
-                absoluteBottom += v.getTop();
-                absoluteTop += v.getTop();
-            }
-            float xOffset = 0;
-            View parentView = (View) getParent();
-            // The layout depends on the orientation.
-            if (getResources().getConfiguration().orientation
-                    == Configuration.ORIENTATION_LANDSCAPE) {
-                xOffset = screenHeight - mWorkspace.getPaddingRight()
-                        - parentView.getWidth();
-            } else {
-                xOffset = mWorkspace.getPaddingLeft() + parentView.getPaddingLeft();
-            }
-            // This is the position of the widget relative to the wallpaper, as expected by the
-            // local color extraction of the WallpaperManager.
-            // The coordinate system is such that, on the horizontal axis, each screen has a
-            // distinct range on the [0,1] segment. So if there are 3 screens, they will have the
-            // ranges [0, 1/3], [1/3, 2/3] and [2/3, 1]. The position on the subrange should be
-            // the position of the widget relative to the screen. For the vertical axis, this is
-            // simply the location of the widget relative to the screen.
-            mTempRectF.left = ((widgetLocation.left + xOffset) / screenWidth + screenId)
-                    * relativeScreenWidth;
-            mTempRectF.right = ((widgetLocation.right + xOffset) / screenWidth + screenId)
-                    * relativeScreenWidth;
-            mTempRectF.top = absoluteTop / screenHeight;
-            mTempRectF.bottom = absoluteBottom / screenHeight;
-            if (mTempRectF.left < 0 || mTempRectF.right > 1 || mTempRectF.top < 0
-                    || mTempRectF.bottom > 1) {
-                Log.e(LOG_TAG, "   Error, invalid relative position");
-                return;
-            }
-            if (!mTempRectF.equals(mLastLocationRegistered)) {
-                if (mLastLocationRegistered != null) {
-                    mColorExtractor.removeLocations();
-                }
-                mLastLocationRegistered = new RectF(mTempRectF);
-                mColorExtractor.addLocation(List.of(mLastLocationRegistered));
-            }
+        int screenWidth = mLauncher.getDeviceProfile().widthPx;
+        int screenHeight = mLauncher.getDeviceProfile().heightPx;
+        int numScreens = mWorkspace.getNumPagesForWallpaperParallax();
+        pageId = mIsRtl ? numScreens - pageId - 1 : pageId;
+        float relativeScreenWidth = 1f / numScreens;
+        float absoluteTop = widgetLocation.top;
+        float absoluteBottom = widgetLocation.bottom;
+        for (View v = (View) getParent();
+                v != null && v.getId() != R.id.launcher;
+                v = (View) v.getParent()) {
+            absoluteBottom += v.getTop();
+            absoluteTop += v.getTop();
+        }
+        float xOffset = 0;
+        View parentView = (View) getParent();
+        // The layout depends on the orientation.
+        if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
+            int parentViewWidth = parentView == null ? 0 : parentView.getWidth();
+            xOffset = screenHeight - mWorkspace.getPaddingRight() - parentViewWidth;
         } else {
-            mColorExtractor.removeLocations();
+            int parentViewPaddingLeft = parentView == null ? 0 : parentView.getPaddingLeft();
+            xOffset = mWorkspace.getPaddingLeft() + parentViewPaddingLeft;
+        }
+        // This is the position of the widget relative to the wallpaper, as expected by the
+        // local color extraction of the WallpaperManager.
+        // The coordinate system is such that, on the horizontal axis, each screen has a
+        // distinct range on the [0,1] segment. So if there are 3 screens, they will have the
+        // ranges [0, 1/3], [1/3, 2/3] and [2/3, 1]. The position on the subrange should be
+        // the position of the widget relative to the screen. For the vertical axis, this is
+        // simply the location of the widget relative to the screen.
+        mTempRectF.left = ((widgetLocation.left + xOffset) / screenWidth + pageId)
+                * relativeScreenWidth;
+        mTempRectF.right = ((widgetLocation.right + xOffset) / screenWidth + pageId)
+                * relativeScreenWidth;
+        mTempRectF.top = absoluteTop / screenHeight;
+        mTempRectF.bottom = absoluteBottom / screenHeight;
+        if (mTempRectF.left < 0 || mTempRectF.right > 1 || mTempRectF.top < 0
+                || mTempRectF.bottom > 1) {
+            Log.e(LOG_TAG, "   Error, invalid relative position");
+            return;
+        }
+        if (!mTempRectF.equals(mLastLocationRegistered)) {
+            if (mLastLocationRegistered != null) {
+                mColorExtractor.removeLocations();
+            }
+            mLastLocationRegistered = new RectF(mTempRectF);
+            mColorExtractor.addLocation(List.of(mLastLocationRegistered));
         }
     }
 
diff --git a/src/com/android/launcher3/widget/PendingItemDragHelper.java b/src/com/android/launcher3/widget/PendingItemDragHelper.java
index 8961f36..247a748 100644
--- a/src/com/android/launcher3/widget/PendingItemDragHelper.java
+++ b/src/com/android/launcher3/widget/PendingItemDragHelper.java
@@ -39,6 +39,7 @@
 import com.android.launcher3.dragndrop.DraggableView;
 import com.android.launcher3.graphics.DragPreviewProvider;
 import com.android.launcher3.icons.LauncherIcons;
+import com.android.launcher3.widget.dragndrop.AppWidgetHostViewDragListener;
 
 /**
  * Extension of {@link DragPreviewProvider} with logic specific to pending widgets/shortcuts
@@ -110,6 +111,8 @@
             }
             if (mAppWidgetHostViewPreview != null) {
                 preview = new AppWidgetHostViewDrawable(mAppWidgetHostViewPreview);
+                launcher.getDragController()
+                        .addDragListener(new AppWidgetHostViewDragListener(launcher));
             }
             if (preview == null) {
                 preview = new FastBitmapDrawable(
diff --git a/src/com/android/launcher3/widget/dragndrop/AppWidgetHostViewDragListener.java b/src/com/android/launcher3/widget/dragndrop/AppWidgetHostViewDragListener.java
new file mode 100644
index 0000000..c5e6fbd
--- /dev/null
+++ b/src/com/android/launcher3/widget/dragndrop/AppWidgetHostViewDragListener.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.widget.dragndrop;
+
+import com.android.launcher3.DropTarget;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.dragndrop.AppWidgetHostViewDrawable;
+import com.android.launcher3.dragndrop.DragController;
+import com.android.launcher3.dragndrop.DragOptions;
+import com.android.launcher3.widget.LauncherAppWidgetHostView;
+
+/** A drag listener of {@link LauncherAppWidgetHostView}. */
+public final class AppWidgetHostViewDragListener implements DragController.DragListener {
+    private final Launcher mLauncher;
+    private DropTarget.DragObject mDragObject;
+    private AppWidgetHostViewDrawable mAppWidgetHostViewDrawable;
+    private LauncherAppWidgetHostView mAppWidgetHostView;
+
+    public AppWidgetHostViewDragListener(Launcher launcher) {
+        mLauncher = launcher;
+    }
+
+    @Override
+    public void onDragStart(DropTarget.DragObject dragObject, DragOptions unused) {
+        if (dragObject.dragView.getDrawable() instanceof AppWidgetHostViewDrawable) {
+            mDragObject = dragObject;
+            mAppWidgetHostViewDrawable =
+                    (AppWidgetHostViewDrawable) mDragObject.dragView.getDrawable();
+            mAppWidgetHostView = mAppWidgetHostViewDrawable.getAppWidgetHostView();
+            mAppWidgetHostView.startDrag(this);
+        } else {
+            mLauncher.getDragController().removeDragListener(this);
+        }
+    }
+
+    @Override
+    public void onDragEnd() {
+        mAppWidgetHostView.endDrag();
+        mLauncher.getDragController().removeDragListener(this);
+    }
+
+    /** Notifies when there is a content change in the drag view. */
+    public void onDragContentChanged() {
+        mDragObject.dragView.invalidate();
+    }
+}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index 92994be..5747690 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -57,6 +57,7 @@
 import com.android.launcher3.workprofile.PersonalWorkPagedView;
 import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip.OnActivePageChangedListener;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.function.Predicate;
 
@@ -161,8 +162,8 @@
                 mAdapters.get(currentActivePage).mWidgetsRecyclerView;
 
         updateNoWidgetsView(currentAdapterHolder);
-
         attachScrollbarToRecyclerView(currentRecyclerView);
+        resetExpandedHeaders();
     }
 
     private void attachScrollbarToRecyclerView(WidgetsRecyclerView recyclerView) {
@@ -180,6 +181,13 @@
         mNoWidgetsView.setVisibility(isWidgetAvailable ? GONE : VISIBLE);
     }
 
+    private void updateNoSearchResultsView(boolean isVisible) {
+        mNoWidgetsView.setVisibility(isVisible ? VISIBLE : GONE);
+        if (isVisible) {
+            mNoWidgetsView.setText(R.string.no_search_results);
+        }
+    }
+
     private void reset() {
         mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView.scrollToTop();
         if (mHasWorkProfile) {
@@ -323,10 +331,12 @@
         if (mIsInSearchMode) return;
         setViewVisibilityBasedOnSearch(/*isInSearchMode= */ true);
         attachScrollbarToRecyclerView(mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView);
+        resetExpandedHeaders();
     }
 
     @Override
     public void exitSearchMode() {
+        onSearchResults(new ArrayList<>());
         setViewVisibilityBasedOnSearch(/*isInSearchMode=*/ false);
         if (mHasWorkProfile) {
             mViewPager.snapToPage(AdapterHolder.PRIMARY);
@@ -337,6 +347,8 @@
     @Override
     public void onSearchResults(List<WidgetsListBaseEntry> entries) {
         mAdapters.get(AdapterHolder.SEARCH).mWidgetsListAdapter.setWidgetsOnSearch(entries);
+        updateNoSearchResultsView(
+                mAdapters.get(AdapterHolder.SEARCH).mWidgetsListAdapter.getItemCount() == 0);
     }
 
     private void setViewVisibilityBasedOnSearch(boolean isInSearchMode) {
@@ -350,6 +362,12 @@
         }
         mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView
                 .setVisibility(mIsInSearchMode ? VISIBLE : GONE);
+        mNoWidgetsView.setVisibility(GONE);
+    }
+
+    private void resetExpandedHeaders() {
+        mAdapters.get(AdapterHolder.PRIMARY).mWidgetsListAdapter.resetExpandedHeader();
+        mAdapters.get(AdapterHolder.WORK).mWidgetsListAdapter.resetExpandedHeader();
     }
 
     private void open(boolean animate) {
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
index 9009eb1..cab1e02 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
@@ -16,6 +16,7 @@
 package com.android.launcher3.widget.picker;
 
 import android.content.Context;
+import android.os.Process;
 import android.util.Log;
 import android.util.SparseArray;
 import android.view.LayoutInflater;
@@ -81,7 +82,7 @@
             entry instanceof WidgetsListHeaderEntry
                     || entry instanceof WidgetsListSearchHeaderEntry
                     || new PackageUserKey(entry.mPkgItem.packageName, entry.mPkgItem.user)
-                    .equals(mWidgetsContentVisiblePackageUserKey);
+                            .equals(mWidgetsContentVisiblePackageUserKey);
     @Nullable private Predicate<WidgetsListBaseEntry> mFilter = null;
 
     public WidgetsListAdapter(Context context, LayoutInflater layoutInflater,
@@ -89,16 +90,17 @@
             OnClickListener iconClickListener, OnLongClickListener iconLongClickListener) {
         mDiffReporter = new WidgetsDiffReporter(iconCache, this);
         mWidgetsListTableViewHolderBinder = new WidgetsListTableViewHolderBinder(context,
-                layoutInflater, iconClickListener, iconLongClickListener, widgetPreviewLoader);
+                layoutInflater, iconClickListener, iconLongClickListener,
+                widgetPreviewLoader, /* listAdapter= */ this);
         mViewHolderBinders.put(VIEW_TYPE_WIDGETS_LIST, mWidgetsListTableViewHolderBinder);
         mViewHolderBinders.put(
                 VIEW_TYPE_WIDGETS_HEADER,
                 new WidgetsListHeaderViewHolderBinder(
-                        layoutInflater, /*onHeaderClickListener=*/this));
+                        layoutInflater, /* onHeaderClickListener= */this, /* listAdapter= */ this));
         mViewHolderBinders.put(
                 VIEW_TYPE_WIDGETS_SEARCH_HEADER,
                 new WidgetsListSearchHeaderViewHolderBinder(
-                        layoutInflater, /*onHeaderClickListener=*/ this));
+                        layoutInflater, /*onHeaderClickListener=*/ this, /* listAdapter= */ this));
     }
 
     public void setFilter(Predicate<WidgetsListBaseEntry> filter) {
@@ -175,10 +177,18 @@
         mDiffReporter.process(mVisibleEntries, newVisibleEntries, mRowComparator);
     }
 
+    /**
+     * Resets any expanded widget header.
+     */
+    public void resetExpandedHeader() {
+        mWidgetsContentVisiblePackageUserKey = null;
+        updateVisibleEntries();
+    }
+
     @Override
     public void onBindViewHolder(ViewHolder holder, int pos) {
         ViewHolderBinder viewHolderBinder = mViewHolderBinders.get(getItemViewType(pos));
-        viewHolderBinder.bindViewHolder(holder, mVisibleEntries.get(pos));
+        viewHolderBinder.bindViewHolder(holder, mVisibleEntries.get(pos), pos);
     }
 
     @Override
@@ -258,7 +268,14 @@
 
         @Override
         public int compare(WidgetsListBaseEntry a, WidgetsListBaseEntry b) {
-            return mComparator.compare(a.mPkgItem.title.toString(), b.mPkgItem.title.toString());
+            int i = mComparator.compare(a.mPkgItem.title.toString(), b.mPkgItem.title.toString());
+            if (i != 0) {
+                return i;
+            }
+            // Prioritize entries from current user over other users if the entries are same.
+            if (a.mPkgItem.user.equals(b.mPkgItem.user)) return 0;
+            if (a.mPkgItem.user.equals(Process.myUserHandle())) return -1;
+            return 1;
         }
     }
 }
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListHeader.java b/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
index 119d094..75dd409 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
@@ -29,6 +29,7 @@
 
 import androidx.annotation.Nullable;
 import androidx.annotation.UiThread;
+import androidx.recyclerview.widget.RecyclerView;
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.FastBitmapDrawable;
@@ -58,6 +59,7 @@
     @Nullable private HandlerRunnable mIconLoadRequest;
     @Nullable private Drawable mIconDrawable;
     private final int mIconSize;
+    private final int mBottomMarginSize;
 
     private ImageView mAppIcon;
     private TextView mTitle;
@@ -83,6 +85,8 @@
                 R.styleable.WidgetsListRowHeader, defStyleAttr, /* defStyleRes= */ 0);
         mIconSize = a.getDimensionPixelSize(R.styleable.WidgetsListRowHeader_appIconSize,
                 grid.iconSizePx);
+        mBottomMarginSize =
+                getResources().getDimensionPixelSize(R.dimen.widget_list_entry_bottom_margin);
     }
 
     @Override
@@ -113,6 +117,13 @@
     public void setExpanded(boolean isExpanded) {
         this.mIsExpanded = isExpanded;
         mExpandToggle.setChecked(isExpanded);
+        if (getLayoutParams() instanceof RecyclerView.LayoutParams) {
+            int bottomMargin = isExpanded ? 0 : mBottomMarginSize;
+            RecyclerView.LayoutParams layoutParams =
+                    ((RecyclerView.LayoutParams) getLayoutParams());
+            layoutParams.bottomMargin = bottomMargin;
+            setLayoutParams(layoutParams);
+        }
     }
 
     /** Apply app icon, labels and tag using a generic {@link WidgetsListHeaderEntry}. */
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinder.java
index fcefe3a..f126321 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListHeaderViewHolderBinder.java
@@ -30,11 +30,14 @@
         ViewHolderBinder<WidgetsListHeaderEntry, WidgetsListHeaderHolder> {
     private final LayoutInflater mLayoutInflater;
     private final OnHeaderClickListener mOnHeaderClickListener;
+    private final WidgetsListAdapter mWidgetsListAdapter;
 
     public WidgetsListHeaderViewHolderBinder(LayoutInflater layoutInflater,
-            OnHeaderClickListener onHeaderClickListener) {
+            OnHeaderClickListener onHeaderClickListener,
+            WidgetsListAdapter listAdapter) {
         mLayoutInflater = layoutInflater;
         mOnHeaderClickListener = onHeaderClickListener;
+        mWidgetsListAdapter = listAdapter;
     }
 
     @Override
@@ -46,8 +49,16 @@
     }
 
     @Override
-    public void bindViewHolder(WidgetsListHeaderHolder viewHolder, WidgetsListHeaderEntry data) {
+    public void bindViewHolder(WidgetsListHeaderHolder viewHolder, WidgetsListHeaderEntry data,
+            int position) {
         WidgetsListHeader widgetsListHeader = viewHolder.mWidgetsListHeader;
+        if (position == 0) {
+            widgetsListHeader.setBackgroundResource(R.drawable.widgets_list_top_ripple);
+        } else if (position == mWidgetsListAdapter.getItemCount() - 1) {
+            widgetsListHeader.setBackgroundResource(R.drawable.widgets_list_bottom_ripple);
+        } else {
+            widgetsListHeader.setBackgroundResource(R.drawable.widgets_list_middle_ripple);
+        }
         widgetsListHeader.applyFromItemInfoWithIcon(data);
         widgetsListHeader.setExpanded(data.isWidgetListShown());
         widgetsListHeader.setOnExpandChangeListener(isExpanded ->
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinder.java
index 83c7948..37713e1 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinder.java
@@ -31,11 +31,14 @@
         ViewHolderBinder<WidgetsListSearchHeaderEntry, WidgetsListSearchHeaderHolder> {
     private final LayoutInflater mLayoutInflater;
     private final OnHeaderClickListener mOnHeaderClickListener;
+    private final WidgetsListAdapter mWidgetsListAdapter;
 
     public WidgetsListSearchHeaderViewHolderBinder(LayoutInflater layoutInflater,
-            OnHeaderClickListener onHeaderClickListener) {
+            OnHeaderClickListener onHeaderClickListener,
+            WidgetsListAdapter listAdapter) {
         mLayoutInflater = layoutInflater;
         mOnHeaderClickListener = onHeaderClickListener;
+        mWidgetsListAdapter = listAdapter;
     }
 
     @Override
@@ -48,8 +51,15 @@
 
     @Override
     public void bindViewHolder(WidgetsListSearchHeaderHolder viewHolder,
-            WidgetsListSearchHeaderEntry data) {
+            WidgetsListSearchHeaderEntry data, int position) {
         WidgetsListHeader widgetsListHeader = viewHolder.mWidgetsListHeader;
+        if (position == 0) {
+            widgetsListHeader.setBackgroundResource(R.drawable.widgets_list_top_ripple);
+        } else if (position == mWidgetsListAdapter.getItemCount() - 1) {
+            widgetsListHeader.setBackgroundResource(R.drawable.widgets_list_bottom_ripple);
+        } else {
+            widgetsListHeader.setBackgroundResource(R.drawable.widgets_list_middle_ripple);
+        }
         widgetsListHeader.applyFromItemInfoWithIcon(data);
         widgetsListHeader.setExpanded(data.isWidgetListShown());
         widgetsListHeader.setOnExpandChangeListener(isExpanded ->
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
index 47fa71a..d0be35d 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
@@ -52,6 +52,7 @@
     private final OnClickListener mIconClickListener;
     private final OnLongClickListener mIconLongClickListener;
     private final WidgetPreviewLoader mWidgetPreviewLoader;
+    private final WidgetsListAdapter mWidgetsListAdapter;
     private boolean mApplyBitmapDeferred = false;
 
     public WidgetsListTableViewHolderBinder(
@@ -59,12 +60,14 @@
             LayoutInflater layoutInflater,
             OnClickListener iconClickListener,
             OnLongClickListener iconLongClickListener,
-            WidgetPreviewLoader widgetPreviewLoader) {
+            WidgetPreviewLoader widgetPreviewLoader,
+            WidgetsListAdapter listAdapter) {
         mLayoutInflater = layoutInflater;
         mIndent = context.getResources().getDimensionPixelSize(R.dimen.widget_section_indent);
         mIconClickListener = iconClickListener;
         mIconLongClickListener = iconLongClickListener;
         mWidgetPreviewLoader = widgetPreviewLoader;
+        mWidgetsListAdapter = listAdapter;
     }
 
     /**
@@ -97,13 +100,22 @@
     }
 
     @Override
-    public void bindViewHolder(WidgetsRowViewHolder holder, WidgetsListContentEntry entry) {
+    public void bindViewHolder(WidgetsRowViewHolder holder, WidgetsListContentEntry entry,
+            int position) {
         TableLayout table = holder.mTableContainer;
         if (DEBUG) {
             Log.d(TAG, String.format("onBindViewHolder [widget#=%d, table.getChildCount=%d]",
                     entry.mWidgets.size(), table.getChildCount()));
         }
 
+        if (position == mWidgetsListAdapter.getItemCount() - 1) {
+            table.setBackgroundResource(R.drawable.widgets_list_bottom_ripple);
+        } else {
+            // WidgetsListContentEntry is never shown in position 0. There must be a header above
+            // it.
+            table.setBackgroundResource(R.drawable.widgets_list_middle_ripple);
+        }
+
         List<ArrayList<WidgetItem>> widgetItemsTable =
                 WidgetsTableUtils.groupWidgetItemsIntoTable(entry.mWidgets, mMaxSpansPerRow);
         recycleTableBeforeBinding(table, widgetItemsTable);
diff --git a/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
index 12d3f11..b016b4f 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
@@ -220,8 +220,8 @@
         int totalItemsHeight = 0;
         for (int i = 0; i < untilIndex; i++) {
             WidgetsListBaseEntry entry = mAdapter.getItems().get(i);
-            if (entry instanceof WidgetsListHeaderEntry ||
-                    entry instanceof WidgetsListSearchHeaderEntry) {
+            if (entry instanceof WidgetsListHeaderEntry
+                    || entry instanceof WidgetsListSearchHeaderEntry) {
                 totalItemsHeight += mEstimatedWidgetListHeaderHeight;
             } else if (entry instanceof WidgetsListContentEntry) {
                 totalItemsHeight += mLastVisibleWidgetContentTableHeight;
diff --git a/src/com/android/launcher3/widget/picker/search/LauncherWidgetsSearchBar.java b/src/com/android/launcher3/widget/picker/search/LauncherWidgetsSearchBar.java
index d68e87e..5520826 100644
--- a/src/com/android/launcher3/widget/picker/search/LauncherWidgetsSearchBar.java
+++ b/src/com/android/launcher3/widget/picker/search/LauncherWidgetsSearchBar.java
@@ -18,13 +18,13 @@
 
 import android.content.Context;
 import android.util.AttributeSet;
-import android.widget.EditText;
 import android.widget.ImageButton;
 import android.widget.LinearLayout;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import com.android.launcher3.ExtendedEditText;
 import com.android.launcher3.R;
 import com.android.launcher3.search.SearchAlgorithm;
 import com.android.launcher3.widget.model.WidgetsListBaseEntry;
@@ -36,7 +36,7 @@
  */
 public class LauncherWidgetsSearchBar extends LinearLayout implements WidgetsSearchBar {
     private WidgetsSearchBarController mController;
-    private EditText mEditText;
+    private ExtendedEditText mEditText;
     private ImageButton mCancelButton;
 
     public LauncherWidgetsSearchBar(Context context) {
diff --git a/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarController.java b/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarController.java
index 6c37484..6011097 100644
--- a/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarController.java
+++ b/src/com/android/launcher3/widget/picker/search/WidgetsSearchBarController.java
@@ -22,9 +22,11 @@
 import android.text.Editable;
 import android.text.TextWatcher;
 import android.util.Log;
-import android.widget.EditText;
+import android.view.KeyEvent;
+import android.view.View;
 import android.widget.ImageButton;
 
+import com.android.launcher3.ExtendedEditText;
 import com.android.launcher3.search.SearchAlgorithm;
 import com.android.launcher3.search.SearchCallback;
 import com.android.launcher3.widget.model.WidgetsListBaseEntry;
@@ -35,22 +37,25 @@
  * Controller for a search bar with an edit text and a cancel button.
  */
 public class WidgetsSearchBarController implements TextWatcher,
-        SearchCallback<WidgetsListBaseEntry> {
+        SearchCallback<WidgetsListBaseEntry>,  ExtendedEditText.OnBackKeyListener,
+        View.OnKeyListener {
     private static final String TAG = "WidgetsSearchBarController";
     private static final boolean DEBUG = false;
 
     protected SearchAlgorithm<WidgetsListBaseEntry> mSearchAlgorithm;
-    protected EditText mInput;
+    protected ExtendedEditText mInput;
     protected ImageButton mCancelButton;
     protected SearchModeListener mSearchModeListener;
     protected String mQuery;
 
     public WidgetsSearchBarController(
-            SearchAlgorithm<WidgetsListBaseEntry> algo, EditText editText, ImageButton cancelButton,
-            SearchModeListener searchModeListener) {
+            SearchAlgorithm<WidgetsListBaseEntry> algo, ExtendedEditText editText,
+            ImageButton cancelButton, SearchModeListener searchModeListener) {
         mSearchAlgorithm = algo;
         mInput = editText;
         mInput.addTextChangedListener(this);
+        mInput.setOnBackKeyListener(this);
+        mInput.setOnKeyListener(this);
         mCancelButton = cancelButton;
         mCancelButton.setOnClickListener(v -> clearSearchResult());
         mSearchModeListener = searchModeListener;
@@ -99,6 +104,7 @@
         mSearchAlgorithm.cancel(/* interruptActiveRequests= */ true);
         mInput.getText().clear();
         mInput.clearFocus();
+        mInput.hideKeyboard();
         mSearchModeListener.exitSearchMode();
     }
 
@@ -108,4 +114,21 @@
     public void onDestroy() {
         mSearchAlgorithm.destroy();
     }
+
+    @Override
+    public boolean onBackKey() {
+        mInput.clearFocus();
+        mInput.hideKeyboard();
+        return true;
+    }
+
+    @Override
+    public boolean onKey(View view, int keyCode, KeyEvent event) {
+        if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_UP) {
+            mInput.clearFocus();
+            mInput.hideKeyboard();
+            return true;
+        }
+        return false;
+    }
 }
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java b/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java
index a4e53a1..ff28148 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -56,7 +56,7 @@
 
     @Override
     public int getVisibleElements(Launcher launcher) {
-        return ALL_APPS_HEADER | ALL_APPS_CONTENT;
+        return ALL_APPS_CONTENT;
     }
 
     @Override
diff --git a/tests/Android.bp b/tests/Android.bp
new file mode 100644
index 0000000..8a73483
--- /dev/null
+++ b/tests/Android.bp
@@ -0,0 +1,17 @@
+// 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.
+filegroup {
+    name: "launcher3-test-src-common",
+    srcs: ["src_common/**/*.java"],
+}
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index 4c9a8e7..6f47df0 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -334,20 +334,31 @@
         // 1. Open all apps and wait for load complete.
         // 2. Find the app and long press it to show shortcuts.
         // 3. Press icon center until shortcuts appear
-        final AllApps allApps = mLauncher.
-                getWorkspace().
-                switchToAllApps();
+        final AllApps allApps = mLauncher
+                .getWorkspace()
+                .switchToAllApps();
         allApps.freeze();
         try {
-            final AppIconMenuItem menuItem = allApps.
-                    getAppIcon(APP_NAME).
-                    openMenu().
-                    getMenuItem(0);
-            final String shortcutName = menuItem.getText();
-            assertEquals("Wrong menu item", "Shortcut 3", shortcutName);
+            final AppIconMenu menu = allApps
+                    .getAppIcon(APP_NAME)
+                    .openMenu();
+            final AppIconMenuItem menuItem0 = menu.getMenuItem(0);
+            final AppIconMenuItem menuItem2 = menu.getMenuItem(2);
+
+            final AppIconMenuItem menuItem;
+
+            final String expectedShortcutName = "Shortcut 3";
+            if (menuItem0.getText().equals(expectedShortcutName)) {
+                menuItem = menuItem0;
+            } else {
+                final String shortcutName2 = menuItem2.getText();
+                assertEquals("Wrong menu item", expectedShortcutName, shortcutName2);
+                menuItem = menuItem2;
+            }
 
             menuItem.dragToWorkspace(false, false);
-            mLauncher.getWorkspace().getWorkspaceAppIcon(shortcutName).launch(getAppPackageName());
+            mLauncher.getWorkspace().getWorkspaceAppIcon(expectedShortcutName)
+                    .launch(getAppPackageName());
         } finally {
             allApps.unfreeze();
         }
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 5138f02..a7b92b7 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -585,11 +585,7 @@
                 "but the current state is not " + containerType.name())) {
             switch (containerType) {
                 case WORKSPACE: {
-                    if (mDevice.isNaturalOrientation()) {
-                        waitForLauncherObject(APPS_RES_ID);
-                    } else {
-                        waitUntilLauncherObjectGone(APPS_RES_ID);
-                    }
+                    waitUntilLauncherObjectGone(APPS_RES_ID);
                     waitUntilLauncherObjectGone(OVERVIEW_RES_ID);
                     waitUntilLauncherObjectGone(WIDGETS_RES_ID);
                     return waitForLauncherObject(WORKSPACE_RES_ID);