Merge "Using recents animation and swipe handler for 3-button mode" 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 343b87e..7404dee 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;
@@ -429,9 +424,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/model/AppEventProducer.java b/quickstep/src/com/android/launcher3/model/AppEventProducer.java
index 9944270..eed493d 100644
--- a/quickstep/src/com/android/launcher3/model/AppEventProducer.java
+++ b/quickstep/src/com/android/launcher3/model/AppEventProducer.java
@@ -42,6 +42,7 @@
 import android.app.prediction.AppTargetId;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.pm.ShortcutInfo;
 import android.os.Build;
 import android.os.Handler;
 import android.os.Message;
@@ -62,9 +63,11 @@
 import com.android.launcher3.logging.StatsLogManager.EventEnum;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.pm.UserCache;
+import com.android.launcher3.shortcuts.ShortcutRequest;
 import com.android.quickstep.logging.StatsLogCompatManager.StatsLogConsumer;
 
 import java.util.Locale;
+import java.util.Optional;
 import java.util.function.ObjIntConsumer;
 import java.util.function.Predicate;
 
@@ -174,6 +177,7 @@
             return null;
         }
         ComponentName cn = null;
+        ShortcutInfo shortcutInfo = null;
         String id = null;
 
         switch (info.getItemCase()) {
@@ -188,6 +192,14 @@
                 LauncherAtom.Shortcut si = info.getShortcut();
                 if (!TextUtils.isEmpty(si.getShortcutId())
                         && (cn = parseNullable(si.getShortcutName())) != null) {
+                    Optional<ShortcutInfo> opt = new ShortcutRequest(mContext,
+                            userHandle).forPackage(cn.getPackageName(), si.getShortcutId()).query(
+                            ShortcutRequest.ALL).stream().findFirst();
+                    if (opt.isPresent()) {
+                        shortcutInfo = opt.get();
+                    } else {
+                        return null;
+                    }
                     id = "shortcut:" + si.getShortcutId();
                 }
                 break;
@@ -210,6 +222,9 @@
                 return createTempFolderTarget();
         }
         if (id != null && cn != null) {
+            if (shortcutInfo != null) {
+                return new AppTarget.Builder(new AppTargetId(id), shortcutInfo).build();
+            }
             return new AppTarget.Builder(new AppTargetId(id), cn.getPackageName(), userHandle)
                     .setClassName(cn.getClassName())
                     .build();
@@ -217,6 +232,7 @@
         return null;
     }
 
+
     private AppTarget createTempFolderTarget() {
         return new AppTarget.Builder(new AppTargetId("folder:" + SystemClock.uptimeMillis()),
                 mContext.getPackageName(), Process.myUserHandle())
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/PredictedAppIcon.java b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
index d330a68..b4aa596 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
@@ -34,11 +34,11 @@
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherSettings;
 import com.android.launcher3.R;
 import com.android.launcher3.graphics.IconPalette;
 import com.android.launcher3.icons.IconNormalizer;
 import com.android.launcher3.icons.LauncherIcons;
-import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.touch.ItemLongClickListener;
@@ -85,8 +85,13 @@
     public void onDraw(Canvas canvas) {
         int count = canvas.save();
         if (!mIsPinned) {
-            boolean isBadged = getTag() instanceof WorkspaceItemInfo
-                    && !Process.myUserHandle().equals(((ItemInfo) getTag()).user);
+            boolean isBadged = false;
+            if (getTag() instanceof WorkspaceItemInfo) {
+                WorkspaceItemInfo info = (WorkspaceItemInfo) getTag();
+                isBadged = !Process.myUserHandle().equals(info.user)
+                        || info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT
+                        || info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
+            }
             drawEffect(canvas, isBadged);
             canvas.translate(getWidth() * RING_EFFECT_RATIO, getHeight() * RING_EFFECT_RATIO);
             canvas.scale(1 - 2 * RING_EFFECT_RATIO, 1 - 2 * RING_EFFECT_RATIO);
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/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/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index 6e8a5f1..8b5d498 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -271,10 +271,12 @@
             mSizeStrategy.calculateGridSize(mContext, mDp, mGridRect);
             mThumbnailData.rotation = mOrientationState.getDisplayRotation();
 
+            // mIsRecentsRtl is the inverse of TaskView RTL.
+            boolean isRtlEnabled = !mIsRecentsRtl;
             mPositionHelper.updateThumbnailMatrix(
                     mThumbnailPosition, mThumbnailData,
                     mTaskRect.width(), mTaskRect.height(),
-                    mDp, mOrientationState.getRecentsActivityRotation());
+                    mDp, mOrientationState.getRecentsActivityRotation(), isRtlEnabled);
             mPositionHelper.getMatrix().invert(mInversePositionMatrix);
 
             PagedOrientationHandler poh = mOrientationState.getOrientationHandler();
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 ce97fdc..882241e 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -614,9 +614,6 @@
     @Override
     protected void onWindowVisibilityChanged(int visibility) {
         super.onWindowVisibilityChanged(visibility);
-        if (visibility != VISIBLE && LIVE_TILE.get()) {
-            finishRecentsAnimation(true /* toRecents */, null);
-        }
         updateTaskStackListenerState();
     }
 
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
index 36a5f03..17d075f 100644
--- a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -379,9 +379,10 @@
                     mThumbnailData.thumbnail.getHeight());
             int currentRotation = getTaskView().getRecentsView().getPagedViewOrientedState()
                     .getRecentsActivityRotation();
+            boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
             mPreviewPositionHelper.updateThumbnailMatrix(mPreviewRect, mThumbnailData,
                     getMeasuredWidth(), getMeasuredHeight(), mActivity.getDeviceProfile(),
-                    currentRotation);
+                    currentRotation, isRtl);
 
             mBitmapShader.setLocalMatrix(mPreviewPositionHelper.mMatrix);
             mPaint.setShader(mBitmapShader);
@@ -466,7 +467,8 @@
          * Updates the matrix based on the provided parameters
          */
         public void updateThumbnailMatrix(Rect thumbnailBounds, ThumbnailData thumbnailData,
-                int canvasWidth, int canvasHeight, DeviceProfile dp, int currentRotation) {
+                int canvasWidth, int canvasHeight, DeviceProfile dp, int currentRotation,
+                boolean isRtl) {
             boolean isRotated = false;
             boolean isOrientationDifferent;
 
@@ -500,6 +502,17 @@
                 float availableHeight = surfaceHeight
                         - (thumbnailClipHint.top + thumbnailClipHint.bottom);
 
+                if (isRotated) {
+                    float canvasAspect = canvasWidth / (float) canvasHeight;
+                    float availableAspect = availableHeight / availableWidth;
+                    // Do not rotate thumbnail if it would not improve fit
+                    if (Utilities.isRelativePercentDifferenceGreaterThan(canvasAspect,
+                            availableAspect, 0.1f)) {
+                        isRotated = false;
+                        isOrientationDifferent = false;
+                    }
+                }
+
                 final float targetW, targetH;
                 if (isOrientationDifferent) {
                     targetW = canvasHeight;
@@ -535,28 +548,25 @@
                     }
                 }
 
-                // Update the clip hints
-                float halfExtraW = (availableWidth - croppedWidth) / 2;
-                thumbnailClipHint.left += halfExtraW;
-                thumbnailClipHint.right += halfExtraW;
-                if (thumbnailClipHint.left < 0) {
-                    thumbnailClipHint.right += thumbnailClipHint.left;
-                    thumbnailClipHint.left = 0;
-                } else if (thumbnailClipHint.right < 0) {
-                    thumbnailClipHint.left += thumbnailClipHint.right;
+                // Update the clip hints. Align to 0,0, crop the remaining.
+                if (isRtl) {
+                    if (thumbnailClipHint.right < 0) {
+                        thumbnailClipHint.left += thumbnailClipHint.right;
+                    }
                     thumbnailClipHint.right = 0;
+                    thumbnailClipHint.left += availableWidth - croppedWidth;
+                } else {
+                    if (thumbnailClipHint.left < 0) {
+                        thumbnailClipHint.right += thumbnailClipHint.left;
+                    }
+                    thumbnailClipHint.left = 0;
+                    thumbnailClipHint.right += availableWidth - croppedWidth;
                 }
-
-                float halfExtraH = (availableHeight - croppedHeight) / 2;
-                thumbnailClipHint.top += halfExtraH;
-                thumbnailClipHint.bottom += halfExtraH;
                 if (thumbnailClipHint.top < 0) {
                     thumbnailClipHint.bottom += thumbnailClipHint.top;
-                    thumbnailClipHint.top = 0;
-                } else if (thumbnailClipHint.bottom < 0) {
-                    thumbnailClipHint.top += thumbnailClipHint.bottom;
-                    thumbnailClipHint.bottom = 0;
                 }
+                thumbnailClipHint.top = 0;
+                thumbnailClipHint.bottom += availableHeight - croppedHeight;
 
                 thumbnailScale = targetW / (croppedWidth * scale);
             }
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/app_widget_resize_frame.xml b/res/layout/app_widget_resize_frame.xml
index dfce946..2e476df 100644
--- a/res/layout/app_widget_resize_frame.xml
+++ b/res/layout/app_widget_resize_frame.xml
@@ -34,6 +34,7 @@
 
         <!-- Left -->
         <ImageView
+            android:id="@+id/widget_resize_left_handle"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_gravity="left|center_vertical"
@@ -43,6 +44,7 @@
 
         <!-- Top -->
         <ImageView
+            android:id="@+id/widget_resize_top_handle"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_gravity="top|center_horizontal"
@@ -52,6 +54,7 @@
 
         <!-- Right -->
         <ImageView
+            android:id="@+id/widget_resize_right_handle"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_gravity="right|center_vertical"
@@ -61,6 +64,7 @@
 
         <!-- Bottom -->
         <ImageView
+            android:id="@+id/widget_resize_bottom_handle"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_gravity="bottom|center_horizontal"
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 252637d..cf693bb 100644
--- a/res/layout/widgets_search_bar.xml
+++ b/res/layout/widgets_search_bar.xml
@@ -1,19 +1,18 @@
 <?xml version="1.0" encoding="utf-8"?>
-<com.android.launcher3.widget.picker.search.WidgetsSearchBar
+<com.android.launcher3.widget.picker.search.LauncherWidgetsSearchBar
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/widgets_search_bar"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:orientation="horizontal"
     android:layout_marginTop="16dp"
-    android:background="@drawable/bg_widgets_searchbox"
-    android:padding="12dp"
-    android:visibility="gone">
+    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"
@@ -30,4 +29,4 @@
         android:background="?android:selectableItemBackground"
         android:layout_gravity="center"
         android:visibility="gone"/>
-</com.android.launcher3.widget.picker.search.WidgetsSearchBar>
\ No newline at end of file
+</com.android.launcher3.widget.picker.search.LauncherWidgetsSearchBar>
\ No newline at end of file
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 d135b43..1bace48 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -29,6 +29,8 @@
     <dimen name="dynamic_grid_cell_layout_padding">5.5dp</dimen>
     <dimen name="dynamic_grid_cell_padding_x">8dp</dimen>
 
+    <dimen name="two_panel_home_side_padding">18dp</dimen>
+
     <!-- Hotseat -->
     <dimen name="dynamic_grid_hotseat_top_padding">8dp</dimen>
     <dimen name="dynamic_grid_hotseat_bottom_padding">2dp</dimen>
@@ -108,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/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index 9d6af9f..8071782 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -20,7 +20,6 @@
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.ViewGroup;
 
 import androidx.annotation.Nullable;
 
@@ -139,10 +138,10 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
 
-        ViewGroup content = (ViewGroup) getChildAt(0);
-        for (int i = 0; i < HANDLE_COUNT; i ++) {
-            mDragHandles[i] = content.getChildAt(i);
-        }
+        mDragHandles[INDEX_LEFT] = findViewById(R.id.widget_resize_left_handle);
+        mDragHandles[INDEX_TOP] = findViewById(R.id.widget_resize_top_handle);
+        mDragHandles[INDEX_RIGHT] = findViewById(R.id.widget_resize_right_handle);
+        mDragHandles[INDEX_BOTTOM] = findViewById(R.id.widget_resize_bottom_handle);
     }
 
     @Override
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/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 90cc384..02571a0 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -251,7 +251,12 @@
         int cellLayoutPadding = isScalableGrid
                 ? 0
                 : res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_layout_padding);
-        if (isLandscape) {
+
+        if (FeatureFlags.ENABLE_TWO_PANEL_HOME.get() && isTablet) {
+            cellLayoutPaddingLeftRightPx =
+                    res.getDimensionPixelSize(R.dimen.two_panel_home_side_padding);
+            cellLayoutBottomPaddingPx = 0;
+        } else if (isLandscape) {
             cellLayoutPaddingLeftRightPx = 0;
             cellLayoutBottomPaddingPx = cellLayoutPadding;
         } else {
@@ -660,6 +665,10 @@
                         - (2 * inv.numRows * cellHeightPx) - hotseatVerticalPadding);
                 padding.set(availablePaddingX / 2, edgeMarginPx + availablePaddingY / 2,
                         availablePaddingX / 2, paddingBottom + availablePaddingY / 2);
+
+                if (FeatureFlags.ENABLE_TWO_PANEL_HOME.get()) {
+                    padding.set(0, padding.top, 0, padding.bottom);
+                }
             } else {
                 // Pad the top and bottom of the workspace with search/hotseat bar sizes
                 padding.set(desiredWorkspaceLeftRightMarginPx,
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/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index bb60557..348d9ee 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -19,6 +19,7 @@
 import static com.android.launcher3.Utilities.getDevicePrefs;
 import static com.android.launcher3.Utilities.getPointString;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_FOUR_COLUMNS;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_TWO_PANEL_HOME;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.PackageManagerHelper.getPackageFilter;
 
@@ -235,6 +236,9 @@
     }
 
     public static String getCurrentGridName(Context context) {
+        if (ENABLE_TWO_PANEL_HOME.get()) {
+            return ENABLE_TWO_PANEL_HOME.key;
+        }
         if (ENABLE_FOUR_COLUMNS.get()) {
             return ENABLE_FOUR_COLUMNS.key;
         }
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/PagedView.java b/src/com/android/launcher3/PagedView.java
index 50f1e44..b084eb1 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -282,7 +282,32 @@
     private int validateNewPage(int newPage) {
         newPage = ensureWithinScrollBounds(newPage);
         // Ensure that it is clamped by the actual set of children in all cases
-        return Utilities.boundToRange(newPage, 0, getPageCount() - 1);
+        newPage = Utilities.boundToRange(newPage, 0, getPageCount() - 1);
+
+        if (getPanelCount() > 1) {
+            // Always return left panel as new page
+            newPage = getLeftmostVisiblePageForIndex(newPage);
+        }
+        return newPage;
+    }
+
+    private int getLeftmostVisiblePageForIndex(int pageIndex) {
+        int panelCount = getPanelCount();
+        return (pageIndex / panelCount) * panelCount;
+    }
+
+    /**
+     * Returns the number of pages that are shown at the same time.
+     */
+    protected int getPanelCount() {
+        return 1;
+    }
+
+    /**
+     * Returns true if the view is on one of the current pages, false otherwise.
+     */
+    public boolean isVisible(View child) {
+        return getLeftmostVisiblePageForIndex(indexOfChild(child)) == mCurrentPage;
     }
 
     /**
@@ -548,6 +573,10 @@
         super.forceLayout();
     }
 
+    private int getPageWidthSize(int widthSize) {
+        return (widthSize - mInsets.left - mInsets.right) / getPanelCount();
+    }
+
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         if (getChildCount() == 0) {
@@ -578,7 +607,7 @@
         if (DEBUG) Log.d(TAG, "PagedView.onMeasure(): " + widthSize + ", " + heightSize);
 
         int myWidthSpec = MeasureSpec.makeMeasureSpec(
-                widthSize - mInsets.left - mInsets.right, MeasureSpec.EXACTLY);
+                getPageWidthSize(widthSize), MeasureSpec.EXACTLY);
         int myHeightSpec = MeasureSpec.makeMeasureSpec(
                 heightSize - mInsets.top - mInsets.bottom, MeasureSpec.EXACTLY);
 
@@ -672,9 +701,11 @@
 
                 // In case the pages are of different width, align the page to left or right edge
                 // based on the orientation.
+                // In case we have multiple panels on the screen, scrollOffsetEnd is the scroll
+                // needed for the whole visible area, so we have to divide it by panelCount.
                 final int pageScroll = mIsRtl
-                    ? (childStart - scrollOffsetStart)
-                    : Math.max(0, childPrimaryEnd  - scrollOffsetEnd);
+                        ? (childStart - scrollOffsetStart)
+                        : Math.max(0, childPrimaryEnd - scrollOffsetEnd / getPanelCount());
                 if (outPageScrolls[i] != pageScroll) {
                     pageScrollChanged = true;
                     outPageScrolls[i] = pageScroll;
@@ -682,6 +713,19 @@
                 childStart += primaryDimension + mPageSpacing + getChildGap();
             }
         }
+
+        int panelCount = getPanelCount();
+        if (panelCount > 1) {
+            for (int i = 0; i < childCount; i++) {
+                // In case we have multiple panels, always use left panel's page scroll for all
+                // panels on the screen.
+                int adjustedScroll = outPageScrolls[getLeftmostVisiblePageForIndex(i)];
+                if (outPageScrolls[i] != adjustedScroll) {
+                    outPageScrolls[i] = adjustedScroll;
+                    pageScrollChanged = true;
+                }
+            }
+        }
         return pageScrollChanged;
     }
 
@@ -794,14 +838,16 @@
         }
         if (direction == View.FOCUS_LEFT) {
             if (getCurrentPage() > 0) {
-                snapToPage(getCurrentPage() - 1);
-                getChildAt(getCurrentPage() - 1).requestFocus(direction);
+                int nextPage = validateNewPage(getCurrentPage() - 1);
+                snapToPage(nextPage);
+                getChildAt(nextPage).requestFocus(direction);
                 return true;
             }
         } else if (direction == View.FOCUS_RIGHT) {
             if (getCurrentPage() < getPageCount() - 1) {
-                snapToPage(getCurrentPage() + 1);
-                getChildAt(getCurrentPage() + 1).requestFocus(direction);
+                int nextPage = validateNewPage(getCurrentPage() + 1);
+                snapToPage(nextPage);
+                getChildAt(nextPage).requestFocus(direction);
                 return true;
             }
         }
@@ -820,11 +866,13 @@
         }
         if (direction == View.FOCUS_LEFT) {
             if (mCurrentPage > 0) {
-                getPageAt(mCurrentPage - 1).addFocusables(views, direction, focusableMode);
+                int nextPage = validateNewPage(mCurrentPage - 1);
+                getPageAt(nextPage).addFocusables(views, direction, focusableMode);
             }
-        } else if (direction == View.FOCUS_RIGHT){
+        } else if (direction == View.FOCUS_RIGHT) {
             if (mCurrentPage < getPageCount() - 1) {
-                getPageAt(mCurrentPage + 1).addFocusables(views, direction, focusableMode);
+                int nextPage = validateNewPage(mCurrentPage + 1);
+                getPageAt(nextPage).addFocusables(views, direction, focusableMode);
             }
         }
     }
@@ -1255,12 +1303,14 @@
 
                     if (((isSignificantMove && !isDeltaLeft && !isFling) ||
                             (isFling && !isVelocityLeft)) && mCurrentPage > 0) {
-                        finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage - 1;
+                        finalPage = returnToOriginalPage
+                                ? mCurrentPage : mCurrentPage - getPanelCount();
                         snapToPageWithVelocity(finalPage, velocity);
                     } else if (((isSignificantMove && isDeltaLeft && !isFling) ||
                             (isFling && isVelocityLeft)) &&
                             mCurrentPage < getChildCount() - 1) {
-                        finalPage = returnToOriginalPage ? mCurrentPage : mCurrentPage + 1;
+                        finalPage = returnToOriginalPage
+                                ? mCurrentPage : mCurrentPage + getPanelCount();
                         snapToPageWithVelocity(finalPage, velocity);
                     } else {
                         snapToDestination();
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index c440303..94c6574 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -690,6 +690,16 @@
         };
     }
 
+    /**
+     * Compares the ratio of two quantities and returns whether that ratio is greater than the
+     * provided bound. Order of quantities does not matter. Bound should be a decimal representation
+     * of a percentage.
+     */
+    public static boolean isRelativePercentDifferenceGreaterThan(float first, float second,
+            float bound) {
+        return (Math.abs(first - second) / Math.abs((first + second) / 2.0f)) > bound;
+    }
+
     private static class FixedSizeEmptyDrawable extends ColorDrawable {
 
         private final int mSize;
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index d1daac8..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;
@@ -313,7 +314,9 @@
         // Increase our bottom insets so we don't overlap with the taskbar.
         mInsets.bottom += grid.nonOverlappingTaskbarInset;
 
-        if (mWorkspaceFadeInAdjacentScreens) {
+        if (isTwoPanelEnabled()) {
+            setPageSpacing(0); // we have two pages and we don't want any spacing
+        } else if (mWorkspaceFadeInAdjacentScreens) {
             // In landscape mode the page spacing is set to the default.
             setPageSpacing(grid.edgeMarginPx);
         } else {
@@ -325,12 +328,30 @@
             setPageSpacing(Math.max(maxInsets, maxPadding));
         }
 
-
         int paddingLeftRight = grid.cellLayoutPaddingLeftRightPx;
         int paddingBottom = grid.cellLayoutBottomPaddingPx;
+        int twoPanelLandscapeSidePadding = paddingLeftRight * 2;
+        int twoPanelPortraitSidePadding = paddingLeftRight / 2;
+
+        int panelCount = getPanelCount();
         for (int i = mWorkspaceScreens.size() - 1; i >= 0; i--) {
-            mWorkspaceScreens.valueAt(i)
-                    .setPadding(paddingLeftRight, 0, paddingLeftRight, paddingBottom);
+            int paddingLeft = paddingLeftRight;
+            int paddingRight = paddingLeftRight;
+            if (panelCount > 1) {
+                if (i % panelCount == 0) { // left side panel
+                    paddingLeft = grid.isLandscape ? twoPanelLandscapeSidePadding
+                            : twoPanelPortraitSidePadding;
+                    paddingRight = 0;
+                } else if (i % panelCount == panelCount - 1) { // right side panel
+                    paddingLeft = 0;
+                    paddingRight = grid.isLandscape ? twoPanelLandscapeSidePadding
+                            : twoPanelPortraitSidePadding;
+                } else { // middle panel
+                    paddingLeft = 0;
+                    paddingRight = 0;
+                }
+            }
+            mWorkspaceScreens.valueAt(i).setPadding(paddingLeft, 0, paddingRight, paddingBottom);
         }
     }
 
@@ -437,6 +458,15 @@
                 .log(LauncherEvent.LAUNCHER_ITEM_DRAG_STARTED);
     }
 
+    private boolean isTwoPanelEnabled() {
+        return mLauncher.mDeviceProfile.isTablet && FeatureFlags.ENABLE_TWO_PANEL_HOME.get();
+    }
+
+    @Override
+    protected int getPanelCount() {
+        return isTwoPanelEnabled() ? 2 : super.getPanelCount();
+    }
+
     public void deferRemoveExtraEmptyScreen() {
         mDeferRemoveExtraEmptyScreen = true;
     }
@@ -824,7 +854,7 @@
 
     private boolean shouldConsumeTouch(View v) {
         return !workspaceIconsCanBeDragged()
-                || (!workspaceInModalState() && indexOfChild(v) != mCurrentPage);
+                || (!workspaceInModalState() && !isVisible(v));
     }
 
     public boolean isSwitchingState() {
@@ -1527,6 +1557,9 @@
             }
         }
 
+        if (drawable instanceof AppWidgetHostViewDrawable) {
+            mDragController.addDragListener(new AppWidgetHostViewDragListener(mLauncher));
+        }
         DragView dv = mDragController.startDrag(
                 drawable,
                 draggableView,
@@ -2260,19 +2293,27 @@
 
         int nextPage = getNextPage();
         if (layout == null && !isPageInTransition()) {
-            // Check if the item is dragged over left page
+            // Check if the item is dragged over currentPage - 1 page
             mTempTouchCoordinates[0] = Math.min(centerX, d.x);
             mTempTouchCoordinates[1] = d.y;
             layout = verifyInsidePage(nextPage + (mIsRtl ? 1 : -1), mTempTouchCoordinates);
         }
 
         if (layout == null && !isPageInTransition()) {
-            // Check if the item is dragged over right page
+            // Check if the item is dragged over currentPage + 1 page
             mTempTouchCoordinates[0] = Math.max(centerX, d.x);
             mTempTouchCoordinates[1] = d.y;
             layout = verifyInsidePage(nextPage + (mIsRtl ? -1 : 1), mTempTouchCoordinates);
         }
 
+        // If two panel is enabled, users can also drag items to currentPage + 2
+        if (isTwoPanelEnabled() && layout == null && !isPageInTransition()) {
+            // Check if the item is dragged over currentPage + 2 page
+            mTempTouchCoordinates[0] = Math.max(centerX, d.x);
+            mTempTouchCoordinates[1] = d.y;
+            layout = verifyInsidePage(nextPage + (mIsRtl ? -2 : 2), mTempTouchCoordinates);
+        }
+
         // Always pick the current page.
         if (layout == null && nextPage >= 0 && nextPage < getPageCount()) {
             layout = (CellLayout) getChildAt(nextPage);
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/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 48e41d5..637bf60 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -85,7 +85,7 @@
             "ADAPTIVE_ICON_WINDOW_ANIM", true, "Use adaptive icons for window animations.");
 
     public static final BooleanFlag ENABLE_QUICKSTEP_LIVE_TILE = getDebugFlag(
-            "ENABLE_QUICKSTEP_LIVE_TILE", false, "Enable live tile in Quickstep overview");
+            "ENABLE_QUICKSTEP_LIVE_TILE", true, "Enable live tile in Quickstep overview");
 
     // Keep as DeviceFlag to allow remote disable in emergency.
     public static final BooleanFlag ENABLE_SUGGESTED_ACTIONS_OVERVIEW = new DeviceFlag(
@@ -140,6 +140,9 @@
     public static final BooleanFlag ENABLE_OVERVIEW_SELECTIONS = new DeviceFlag(
             "ENABLE_OVERVIEW_SELECTIONS", true, "Show Select Mode button in Overview Actions");
 
+    public static final BooleanFlag ENABLE_WIDGETS_PICKER_AIAI_SEARCH = new DeviceFlag(
+            "ENABLE_WIDGETS_PICKER_AIAI_SEARCH", false, "Enable AiAi search in the widgets picker");
+
     public static final BooleanFlag ENABLE_OVERVIEW_SHARE = getDebugFlag(
             "ENABLE_OVERVIEW_SHARE", false, "Show Share button in Overview Actions");
 
@@ -205,6 +208,10 @@
             "ENABLE_OVERVIEW_GRID", false, "Uses grid overview layout. "
             + "Only applicable on large screen devices.");
 
+    public static final BooleanFlag ENABLE_TWO_PANEL_HOME = getDebugFlag(
+            "ENABLE_TWO_PANEL_HOME", false,
+            "Uses two panel on home screen. Only applicable on large screen devices.");
+
     public static final BooleanFlag ENABLE_SPLIT_SELECT = getDebugFlag(
             "ENABLE_SPLIT_SELECT", false, "Uses new split screen selection overview UI");
 
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/dragndrop/SpringLoadedDragController.java b/src/com/android/launcher3/dragndrop/SpringLoadedDragController.java
index 37200a6..6325877 100644
--- a/src/com/android/launcher3/dragndrop/SpringLoadedDragController.java
+++ b/src/com/android/launcher3/dragndrop/SpringLoadedDragController.java
@@ -56,9 +56,8 @@
         if (mScreen != null) {
             // Snap to the screen that we are hovering over now
             Workspace w = mLauncher.getWorkspace();
-            int page = w.indexOfChild(mScreen);
-            if (page != w.getCurrentPage()) {
-                w.snapToPage(page);
+            if (!w.isVisible(mScreen)) {
+                w.snapToPage(w.indexOfChild(mScreen));
             }
         } else {
             mLauncher.getDragController().cancelDrag();
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/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java
index ce97d2e..8689fbf 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetProviderInfo.java
@@ -1,5 +1,7 @@
 package com.android.launcher3.widget;
 
+import static com.android.launcher3.Utilities.ATLEAST_S;
+
 import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.ComponentName;
@@ -33,6 +35,8 @@
     public int spanY;
     public int minSpanX;
     public int minSpanY;
+    public int maxSpanX;
+    public int maxSpanY;
 
     public static LauncherAppWidgetProviderInfo fromProviderInfo(Context context,
             AppWidgetProviderInfo info) {
@@ -78,15 +82,40 @@
                 || !idp.portraitProfile.shouldInsetWidgets()) {
             AppWidgetHostView.getDefaultPaddingForWidget(context, provider, widgetPadding);
         }
-        spanX = Math.max(1, (int) Math.ceil(
-                        (minWidth + widgetPadding.left + widgetPadding.right) / smallestCellWidth));
-        spanY = Math.max(1, (int) Math.ceil(
-                (minHeight + widgetPadding.top + widgetPadding.bottom) / smallestCellHeight));
 
-        minSpanX = Math.max(1, (int) Math.ceil(
-                (minResizeWidth + widgetPadding.left + widgetPadding.right) / smallestCellWidth));
-        minSpanY = Math.max(1, (int) Math.ceil(
-                (minResizeHeight + widgetPadding.top + widgetPadding.bottom) / smallestCellHeight));
+        minSpanX = getSpanX(widgetPadding, minResizeWidth, smallestCellWidth);
+        minSpanY = getSpanY(widgetPadding, minResizeHeight, smallestCellHeight);
+
+        // Use maxResizeWidth/Height if they are defined and we're on S or above.
+        maxSpanX =
+                (ATLEAST_S && maxResizeWidth > 0)
+                        ? getSpanX(widgetPadding, maxResizeWidth, smallestCellWidth)
+                        : idp.numColumns;
+        maxSpanY =
+                (ATLEAST_S && maxResizeHeight > 0)
+                        ? getSpanY(widgetPadding, maxResizeHeight, smallestCellHeight)
+                        : idp.numRows;
+
+        // Use targetCellWidth/Height if it is within the min/max ranges and we're on S or above.
+        // Otherwise, fall back to minWidth/Height.
+        if (ATLEAST_S && targetCellWidth >= minSpanX && targetCellWidth <= maxSpanX
+                && targetCellHeight >= minSpanY && targetCellHeight <= maxSpanY) {
+            spanX = targetCellWidth;
+            spanY = targetCellHeight;
+        } else {
+            spanX = getSpanX(widgetPadding, minWidth, smallestCellWidth);
+            spanY = getSpanY(widgetPadding, minHeight, smallestCellHeight);
+        }
+    }
+
+    private int getSpanX(Rect widgetPadding, int widgetWidth, float cellWidth) {
+        return Math.max(1, (int) Math.ceil(
+                (widgetWidth + widgetPadding.left + widgetPadding.right) / cellWidth));
+    }
+
+    private int getSpanY(Rect widgetPadding, int widgetHeight, float cellHeight) {
+        return Math.max(1, (int) Math.ceil(
+                (widgetHeight + widgetPadding.top + widgetPadding.bottom) / cellHeight));
     }
 
     public String getLabel(PackageManager packageManager) {
@@ -124,4 +153,4 @@
     public Drawable getFullResIcon(IconCache cache) {
         return cache.getFullResIcon(provider.getPackageName(), icon);
     }
-}
+}
\ No newline at end of file
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 6b3c71a..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) {
@@ -439,7 +457,8 @@
     public int getHeaderViewHeight() {
         return measureHeightWithVerticalMargins(mSearchAndRecommendationViewHolder.mCollapseHandle)
                 + measureHeightWithVerticalMargins(mSearchAndRecommendationViewHolder.mHeaderTitle)
-                + measureHeightWithVerticalMargins(mSearchAndRecommendationViewHolder.mSearchBar);
+                + measureHeightWithVerticalMargins(
+                (View) mSearchAndRecommendationViewHolder.mSearchBar);
     }
 
     /** private the height, in pixel, + the vertical margins of a given view. */
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 9ab6424..b016b4f 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
@@ -34,6 +34,7 @@
 import com.android.launcher3.widget.model.WidgetsListBaseEntry;
 import com.android.launcher3.widget.model.WidgetsListContentEntry;
 import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
+import com.android.launcher3.widget.model.WidgetsListSearchHeaderEntry;
 
 /**
  * The widgets recycler view.
@@ -219,7 +220,8 @@
         int totalItemsHeight = 0;
         for (int i = 0; i < untilIndex; i++) {
             WidgetsListBaseEntry entry = mAdapter.getItems().get(i);
-            if (entry instanceof WidgetsListHeaderEntry) {
+            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
new file mode 100644
index 0000000..5520826
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/search/LauncherWidgetsSearchBar.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.widget.picker.search;
+
+import android.content.Context;
+import android.util.AttributeSet;
+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;
+
+import java.util.List;
+
+/**
+ * View for a search bar with an edit text with a cancel button.
+ */
+public class LauncherWidgetsSearchBar extends LinearLayout implements WidgetsSearchBar {
+    private WidgetsSearchBarController mController;
+    private ExtendedEditText mEditText;
+    private ImageButton mCancelButton;
+
+    public LauncherWidgetsSearchBar(Context context) {
+        this(context, null, 0);
+    }
+
+    public LauncherWidgetsSearchBar(@NonNull Context context,
+            @Nullable AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public LauncherWidgetsSearchBar(@NonNull Context context, @Nullable AttributeSet attrs,
+            int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    @Override
+    public void initialize(List<WidgetsListBaseEntry> allWidgets,
+            SearchModeListener searchModeListener) {
+        SearchAlgorithm<WidgetsListBaseEntry> algo =
+                new SimpleWidgetsSearchAlgorithm(new SimpleWidgetsSearchPipeline(allWidgets));
+        mController = new WidgetsSearchBarController(
+                algo, mEditText, mCancelButton, searchModeListener);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mEditText = findViewById(R.id.widgets_search_bar_edit_text);
+        mCancelButton = findViewById(R.id.widgets_search_cancel_button);
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        mController.onDestroy();
+    }
+}
diff --git a/src/com/android/launcher3/widget/picker/search/WidgetsSearchBar.java b/src/com/android/launcher3/widget/picker/search/WidgetsSearchBar.java
index d8e9733..af6dc48 100644
--- a/src/com/android/launcher3/widget/picker/search/WidgetsSearchBar.java
+++ b/src/com/android/launcher3/widget/picker/search/WidgetsSearchBar.java
@@ -16,64 +16,21 @@
 
 package com.android.launcher3.widget.picker.search;
 
-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.R;
-import com.android.launcher3.search.SearchAlgorithm;
 import com.android.launcher3.widget.model.WidgetsListBaseEntry;
 
 import java.util.List;
 
 /**
- * View for a search bar with an edit text with a cancel button.
+ * Interface for a widgets picker search bar.
  */
-public class WidgetsSearchBar extends LinearLayout {
-    private WidgetsSearchBarController mController;
-    private EditText mEditText;
-    private ImageButton mCancelButton;
-
-    public WidgetsSearchBar(Context context) {
-        this(context, null, 0);
-    }
-
-    public WidgetsSearchBar(@NonNull Context context,
-            @Nullable AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public WidgetsSearchBar(@NonNull Context context, @Nullable AttributeSet attrs,
-            int defStyleAttr) {
-        super(context, attrs, defStyleAttr);
-    }
-
+public interface WidgetsSearchBar {
     /**
      * Attaches a controller to the search bar which interacts with {@code searchModeListener}.
      */
-    public void initialize(List<WidgetsListBaseEntry> allWidgets,
-            SearchModeListener searchModeListener) {
-        SearchAlgorithm<WidgetsListBaseEntry> algo =
-                new SimpleWidgetsSearchAlgorithm(new SimpleWidgetsSearchPipeline(allWidgets));
-        mController = new WidgetsSearchBarController(
-                algo, mEditText, mCancelButton, searchModeListener);
-    }
+    void initialize(List<WidgetsListBaseEntry> allWidgets, SearchModeListener searchModeListener);
 
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        mEditText = findViewById(R.id.widgets_search_bar_edit_text);
-        mCancelButton = findViewById(R.id.widgets_search_cancel_button);
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        mController.onDestroy();
-    }
+    /**
+     * Sets the vertical location, in pixels, of this search bar relative to its top position.
+     */
+    void setTranslationY(float translationY);
 }
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/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);