Merge "Pause the AllSetActivity background animation more reliably." into tm-qpr-dev
diff --git a/protos/view_capture.proto b/protos/view_capture.proto
new file mode 100644
index 0000000..98574dd
--- /dev/null
+++ b/protos/view_capture.proto
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+
+package com.android.launcher3.view;
+
+option java_outer_classname = "ViewCaptureData";
+
+message ExportedData {
+
+  repeated FrameData frameData = 1;
+}
+
+message FrameData {
+  optional int64 timestamp = 1;
+  optional ViewNode node = 2;
+}
+
+message ViewNode {
+  optional string classname = 1;
+  optional string id = 2;
+  optional int32 left = 3;
+  optional int32 top = 4;
+  optional int32 width = 5;
+  optional int32 height = 6;
+  optional int32 scrollX = 7;
+  optional int32 scrollY = 8;
+
+  optional float translationX = 9;
+  optional float translationY = 10;
+  optional float scaleX = 11 [default = 1];
+  optional float scaleY = 12 [default = 1];
+  optional float alpha = 13 [default = 1];
+
+  optional bool willNotDraw = 14;
+  optional bool clipChildren = 15;
+  optional int32 visibility = 16;
+
+  repeated ViewNode children = 17;
+}
diff --git a/quickstep/Android.bp b/quickstep/Android.bp
index 70b1438..f739f81 100644
--- a/quickstep/Android.bp
+++ b/quickstep/Android.bp
@@ -26,7 +26,7 @@
 filegroup {
     name: "launcher3-quickstep-tests-src",
     path: "tests",
-    srcs: ["tests/src/**/*.java"],
+    srcs: ["tests/src/**/*.java", "tests/src/**/*.kt"],
 }
 
 filegroup {
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 0fd3c4a..fa4eaed 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -36,6 +36,7 @@
     <dimen name="task_thumbnail_icon_drawable_size">44dp</dimen>
     <dimen name="overview_task_margin">16dp</dimen>
     <dimen name="overview_page_spacing">16dp</dimen>
+    <dimen name="task_icon_cache_default_icon_size">72dp</dimen>
 
     <item name="overview_max_scale" format="float" type="dimen">0.7</item>
     <item name="overview_modal_max_scale" format="float" type="dimen">1.1</item>
@@ -66,7 +67,6 @@
     <dimen name="quickstep_fling_threshold_speed">0.5dp</dimen>
 
     <!-- Launcher app transition -->
-    <item name="content_scale" format="float" type="dimen">0.97</item>
     <dimen name="closing_window_trans_y">115dp</dimen>
 
     <dimen name="quick_switch_scaling_scroll_threshold">100dp</dimen>
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index 95d6dd0..e21dcba 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -20,6 +20,8 @@
 import static com.android.launcher3.LauncherState.FLAG_HIDE_BACK_BUTTON;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.NO_OFFSET;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE;
 import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
@@ -70,6 +72,7 @@
 import com.android.launcher3.util.DisplayController.NavigationMode;
 import com.android.launcher3.util.IntSet;
 import com.android.launcher3.util.ObjectWrapper;
+import com.android.launcher3.util.PendingSplitSelectInfo;
 import com.android.launcher3.util.RunnableList;
 import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
 import com.android.launcher3.util.UiThreadHelper;
@@ -91,10 +94,10 @@
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 import com.android.systemui.unfold.UnfoldTransitionFactory;
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
+import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig;
 import com.android.systemui.unfold.config.UnfoldTransitionConfig;
 import com.android.systemui.unfold.system.ActivityManagerActivityTypeProvider;
 import com.android.systemui.unfold.system.DeviceStateManagerFoldProvider;
-import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -130,9 +133,19 @@
     private @Nullable UnfoldTransitionProgressProvider mUnfoldTransitionProgressProvider;
     private @Nullable LauncherUnfoldAnimationController mLauncherUnfoldAnimationController;
 
+    /**
+     * If Launcher restarted while in the middle of an Overview split select, it needs this data to
+     * recover. In all other cases this will remain null.
+     */
+    private PendingSplitSelectInfo mPendingSplitSelectInfo = null;
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+        if (savedInstanceState != null) {
+            mPendingSplitSelectInfo = ObjectWrapper.unwrap(
+                    savedInstanceState.getIBinder(PENDING_SPLIT_SELECT_INFO));
+        }
         addMultiWindowModeChangedListener(mDepthController);
         initUnfoldTransitionProgressProvider();
     }
@@ -643,4 +656,53 @@
             mDepthController.dump(prefix, writer);
         }
     }
+
+    @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+
+        // If Launcher shuts downs during split select, we save some extra data in the recovery
+        // bundle to allow graceful recovery. The normal LauncherState restore mechanism doesn't
+        // work in this case because restoring straight to OverviewSplitSelect without staging data,
+        // or before the tasks themselves have loaded into Overview, causes a crash. So we tell
+        // Launcher to first restore into Overview state, wait for the relevant tasks and icons to
+        // load in, and then proceed to OverviewSplitSelect.
+        if (isInState(OVERVIEW_SPLIT_SELECT)) {
+            SplitSelectStateController splitSelectStateController =
+                    ((RecentsView) getOverviewPanel()).getSplitPlaceholder();
+            // Launcher will restart in Overview and then transition to OverviewSplitSelect.
+            outState.putIBinder(PENDING_SPLIT_SELECT_INFO, ObjectWrapper.wrap(
+                    new PendingSplitSelectInfo(
+                            splitSelectStateController.getInitialTaskId(),
+                            splitSelectStateController.getActiveSplitStagePosition()
+                    )
+            ));
+            outState.putInt(RUNTIME_STATE, OVERVIEW.ordinal);
+        }
+    }
+
+    /**
+     * When Launcher restarts, it sometimes needs to recover to a split selection state.
+     * This function checks if such a recovery is needed.
+     * @return a boolean representing whether the launcher is waiting to recover to
+     * OverviewSplitSelect state.
+     */
+    public boolean hasPendingSplitSelectInfo() {
+        return mPendingSplitSelectInfo != null;
+    }
+
+    /**
+     * See {@link #hasPendingSplitSelectInfo()}
+     */
+    public @Nullable PendingSplitSelectInfo getPendingSplitSelectInfo() {
+        return mPendingSplitSelectInfo;
+    }
+
+    /**
+     * When the launcher has successfully recovered to OverviewSplitSelect state, this function
+     * deletes the recovery data, returning it to a null state.
+     */
+    public void finishSplitSelectRecovery() {
+        mPendingSplitSelectInfo = null;
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index b20752d..bb79c1b 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -200,7 +200,6 @@
 
     final Handler mHandler;
 
-    private final float mContentScale;
     private final float mClosingWindowTransY;
     private final float mMaxShadowRadius;
 
@@ -245,7 +244,6 @@
         mBackAnimationController = new LauncherBackAnimationController(mLauncher, this);
 
         Resources res = mLauncher.getResources();
-        mContentScale = res.getFloat(R.dimen.content_scale);
         mClosingWindowTransY = res.getDimensionPixelSize(R.dimen.closing_window_trans_y);
         mMaxShadowRadius = res.getDimensionPixelSize(R.dimen.max_shadow_radius);
 
@@ -483,8 +481,8 @@
                 : new float[]{0, 1};
 
         float[] scales = isAppOpening
-                ? new float[]{1, mContentScale}
-                : new float[]{mContentScale, 1};
+                ? new float[]{1, mDeviceProfile.workspaceContentScale}
+                : new float[]{mDeviceProfile.workspaceContentScale, 1};
 
         // Pause expensive view updates as they can lead to layer thrashing and skipped frames.
         mLauncher.pauseExpensiveViewUpdates();
diff --git a/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java b/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java
index 0284ae4..f42b39f 100644
--- a/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/AppsDividerView.java
@@ -21,7 +21,6 @@
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.graphics.Canvas;
-import android.graphics.Rect;
 import android.graphics.Typeface;
 import android.os.Build;
 import android.text.Layout;
@@ -33,7 +32,6 @@
 import androidx.annotation.ColorInt;
 import androidx.core.content.ContextCompat;
 
-import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
 import com.android.launcher3.allapps.FloatingHeaderRow;
 import com.android.launcher3.allapps.FloatingHeaderView;
@@ -239,12 +237,6 @@
     }
 
     @Override
-    public void setInsets(Rect insets, DeviceProfile grid) {
-        int leftRightPadding = grid.allAppsLeftRightPadding;
-        setPadding(leftRightPadding, getPaddingTop(), leftRightPadding, getPaddingBottom());
-    }
-
-    @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 1dec737..351a3bc 100644
--- a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -19,7 +19,6 @@
 import android.annotation.TargetApi;
 import android.content.Context;
 import android.graphics.Canvas;
-import android.graphics.Rect;
 import android.os.Build;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
@@ -252,12 +251,6 @@
     }
 
     @Override
-    public void setInsets(Rect insets, DeviceProfile grid) {
-        int leftRightPadding = grid.allAppsLeftRightPadding;
-        setPadding(leftRightPadding, getPaddingTop(), leftRightPadding, getPaddingBottom());
-    }
-
-    @Override
     public Class<PredictionRowView> getTypeClass() {
         return PredictionRowView.class;
     }
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
index 119ae90..7b48332 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduDialog.java
@@ -41,7 +41,6 @@
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
-import com.android.launcher3.uioverrides.ApiWrapper;
 import com.android.launcher3.uioverrides.PredictedAppIcon;
 import com.android.launcher3.views.AbstractSlideInView;
 
@@ -107,8 +106,7 @@
         mDismissBtn.setOnClickListener(this::onDismiss);
 
         LinearLayout buttonContainer = findViewById(R.id.button_container);
-        int adjustedMarginEnd = ApiWrapper.getHotseatEndOffset(context)
-                - buttonContainer.getPaddingEnd();
+        int adjustedMarginEnd = grid.hotseatBarEndOffset - buttonContainer.getPaddingEnd();
         if (InvariantDeviceProfile.INSTANCE.get(context)
                 .getDeviceProfile(context).isTaskbarPresent && adjustedMarginEnd > 0) {
             ((LinearLayout.LayoutParams) buttonContainer.getLayoutParams()).setMarginEnd(
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index 01cf23b..17da0d9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -108,8 +108,6 @@
     private static final int FLAG_SCREEN_PINNING_ACTIVE = 1 << 11;
     private static final int FLAG_VOICE_INTERACTION_WINDOW_SHOWING = 1 << 12;
 
-    private static final int MASK_IME_SWITCHER_VISIBLE = FLAG_SWITCHER_SUPPORTED | FLAG_IME_VISIBLE;
-
     private static final String NAV_BUTTONS_SEPARATE_WINDOW_TITLE = "Taskbar Nav Buttons";
 
     public static final int ALPHA_INDEX_IMMERSIVE_MODE = 0;
@@ -191,7 +189,7 @@
                     isThreeButtonNav ? mStartContextualContainer : mEndContextualContainer,
                     mControllers.navButtonController, R.id.ime_switcher);
             mPropertyHolders.add(new StatePropertyHolder(imeSwitcherButton,
-                    flags -> ((flags & MASK_IME_SWITCHER_VISIBLE) == MASK_IME_SWITCHER_VISIBLE)
+                    flags -> ((flags & FLAG_SWITCHER_SUPPORTED) != 0)
                             && ((flags & FLAG_ROTATION_BUTTON_VISIBLE) == 0)));
         }
 
@@ -420,7 +418,7 @@
                     return recentsCoords;
                 }, new Handler());
         recentsButton.setOnClickListener(v -> {
-            navButtonController.onButtonClick(BUTTON_RECENTS);
+            navButtonController.onButtonClick(BUTTON_RECENTS, v);
             mHitboxExtender.onRecentsButtonClicked();
         });
         mPropertyHolders.add(new StatePropertyHolder(recentsButton,
@@ -507,6 +505,13 @@
     }
 
     /**
+     * Returns true if IME switcher is visible
+     */
+    public boolean isImeSwitcherVisible() {
+        return (mState & FLAG_SWITCHER_SUPPORTED) != 0;
+    }
+
+    /**
      * Returns true if the home button is disabled
      */
     public boolean isHomeDisabled() {
@@ -634,9 +639,9 @@
         buttonView.setImageResource(drawableId);
         buttonView.setContentDescription(parent.getContext().getString(
                 navButtonController.getButtonContentDescription(buttonType)));
-        buttonView.setOnClickListener(view -> navButtonController.onButtonClick(buttonType));
+        buttonView.setOnClickListener(view -> navButtonController.onButtonClick(buttonType, view));
         buttonView.setOnLongClickListener(view ->
-                navButtonController.onButtonLongClick(buttonType));
+                navButtonController.onButtonLongClick(buttonType, view));
         return buttonView;
     }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index d1994e7..439490e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -263,7 +263,8 @@
                 mLastRequestedNonFullscreenHeight,
                 type,
                 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
-                        | WindowManager.LayoutParams.FLAG_SLIPPERY,
+                        | WindowManager.LayoutParams.FLAG_SLIPPERY
+                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
                 PixelFormat.TRANSLUCENT);
         windowLayoutParams.setTitle(WINDOW_TITLE);
         windowLayoutParams.packageName = getPackageName();
@@ -463,6 +464,8 @@
                 fromInit);
         mControllers.taskbarViewController.setImeIsVisible(
                 mControllers.navbarButtonsViewController.isImeVisible());
+        mControllers.taskbarViewController.setIsImeSwitcherVisible(
+                mControllers.navbarButtonsViewController.isImeSwitcherVisible());
         int shadeExpandedFlags = SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED
                 | SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
         onNotificationShadeExpandChanged((systemUiStateFlags & shadeExpandedFlags) != 0, fromInit);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
index c522888..04fcc44 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
@@ -16,6 +16,7 @@
 package com.android.launcher3.taskbar;
 
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_ALL_APPS;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_PREDICTION;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
 
 import android.animation.Animator;
@@ -435,7 +436,7 @@
         if (tag instanceof ItemInfo) {
             ItemInfo item = (ItemInfo) tag;
             TaskbarViewController taskbarViewController = mControllers.taskbarViewController;
-            if (item.container == CONTAINER_ALL_APPS) {
+            if (item.container == CONTAINER_ALL_APPS || item.container == CONTAINER_PREDICTION) {
                 // Since all apps closes when the drag starts, target the all apps button instead.
                 target = taskbarViewController.getAllAppsButtonView();
             } else if (item.container >= 0) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
index e4f82d2..dc5c22d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
@@ -17,9 +17,11 @@
 
 import android.graphics.Insets
 import android.graphics.Region
+import android.view.InsetsFrameProvider
 import android.view.InsetsState.ITYPE_BOTTOM_MANDATORY_GESTURES
 import android.view.InsetsState
 import android.view.WindowManager
+import android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD
 import com.android.launcher3.AbstractFloatingView
 import com.android.launcher3.AbstractFloatingView.TYPE_TASKBAR_ALL_APPS
 import com.android.launcher3.DeviceProfile
@@ -87,8 +89,14 @@
         }
 
         var imeInsetsSize = Insets.of(0, 0, 0, taskbarHeightForIme)
+        var insetsSizeOverride = arrayOf(
+            InsetsFrameProvider.InsetsSizeOverride(
+                TYPE_INPUT_METHOD,
+                imeInsetsSize
+            )
+        )
         for (provider in windowLayoutParams.providedInsets) {
-            provider.imeInsetsSize = imeInsetsSize
+            provider.insetsSizeOverrides = insetsSizeOverride
         }
     }
 
@@ -141,9 +149,16 @@
         pw.println(prefix + "TaskbarInsetsController:")
         pw.println("$prefix\twindowHeight=${windowLayoutParams.height}")
         for (provider in windowLayoutParams.providedInsets) {
-            pw.println("$prefix\tprovidedInsets: (type=" + InsetsState.typeToString(provider.type)
-                    + " insetsSize=" + provider.insetsSize
-                    + " imeInsetsSize=" + provider.imeInsetsSize + ")")
+            pw.print("$prefix\tprovidedInsets: (type=" + InsetsState.typeToString(provider.type)
+                    + " insetsSize=" + provider.insetsSize)
+            if (provider.insetsSizeOverrides != null) {
+                pw.print(" insetsSizeOverrides={")
+                for (overrideSize in provider.insetsSizeOverrides) {
+                    pw.print(overrideSize)
+                }
+                pw.print("})")
+            }
+            pw.println()
         }
     }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
index 4ff0649..3392b6b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
@@ -33,6 +33,8 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.util.Log;
+import android.view.HapticFeedbackConstants;
+import android.view.View;
 
 import androidx.annotation.IntDef;
 import androidx.annotation.Nullable;
@@ -113,7 +115,9 @@
         mHandler = handler;
     }
 
-    public void onButtonClick(@TaskbarButton int buttonType) {
+    public void onButtonClick(@TaskbarButton int buttonType, View view) {
+        // Provide the same haptic feedback that the system offers for virtual keys.
+        view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
         switch (buttonType) {
             case BUTTON_BACK:
                 logEvent(LAUNCHER_TASKBAR_BACK_BUTTON_TAP);
@@ -144,7 +148,9 @@
         }
     }
 
-    public boolean onButtonLongClick(@TaskbarButton int buttonType) {
+    public boolean onButtonLongClick(@TaskbarButton int buttonType, View view) {
+        // Provide the same haptic feedback that the system offers for virtual keys.
+        view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
         switch (buttonType) {
             case BUTTON_HOME:
                 logEvent(LAUNCHER_TASKBAR_HOME_BUTTON_LONGPRESS);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index fc9f9d0..2b8fdd1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -22,6 +22,7 @@
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_LONGPRESS_SHOW;
 import static com.android.launcher3.taskbar.Utilities.appendFlag;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SWITCHER_SHOWING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
 
 import android.animation.Animator;
@@ -30,13 +31,15 @@
 import android.annotation.Nullable;
 import android.content.SharedPreferences;
 import android.util.Log;
+import android.view.View;
 import android.view.ViewConfiguration;
-import android.view.WindowInsets;
 
+import androidx.annotation.NonNull;
+
+import com.android.internal.jank.InteractionJankMonitor;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimatorListeners;
-import com.android.launcher3.taskbar.allapps.TaskbarAllAppsSlideInView;
 import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
 import com.android.quickstep.AnimatedFloat;
@@ -44,6 +47,8 @@
 import com.android.systemui.shared.system.WindowManagerWrapper;
 
 import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.Optional;
 import java.util.StringJoiner;
 import java.util.function.IntPredicate;
 
@@ -53,6 +58,8 @@
  */
 public class TaskbarStashController implements TaskbarControllers.LoggableTaskbarController {
 
+    private static final String TAG = "TaskbarStashController";
+
     public static final int FLAG_IN_APP = 1 << 0;
     public static final int FLAG_STASHED_IN_APP_MANUAL = 1 << 1; // long press, persisted
     public static final int FLAG_STASHED_IN_APP_PINNED = 1 << 2; // app pinning
@@ -149,6 +156,7 @@
     private @Nullable AnimatorSet mAnimator;
     private boolean mIsSystemGestureInProgress;
     private boolean mIsImeShowing;
+    private boolean mIsImeSwitcherShowing;
 
     private boolean mEnableManualStashingForTests = false;
 
@@ -401,6 +409,7 @@
             mAnimator.cancel();
         }
         mAnimator = new AnimatorSet();
+        addJankMonitorListener(mAnimator, /* appearing= */ !mIsStashed);
 
         if (!supportsVisualStashing()) {
             // Just hide/show the icons and background instead of stashing into a handle.
@@ -496,6 +505,28 @@
         });
     }
 
+    private void addJankMonitorListener(AnimatorSet animator, boolean expanding) {
+        Optional<View> optionalView =
+                Arrays.stream(mControllers.taskbarViewController.getIconViews()).findFirst();
+        if (optionalView.isEmpty()) {
+            Log.wtf(TAG, "No views to start Interaction jank monitor with.", new Exception());
+            return;
+        }
+        View v = optionalView.get();
+        int action = expanding ? InteractionJankMonitor.CUJ_TASKBAR_EXPAND :
+                InteractionJankMonitor.CUJ_TASKBAR_COLLAPSE;
+        animator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(@NonNull Animator animation) {
+                InteractionJankMonitor.getInstance().begin(v, action);
+            }
+
+            @Override
+            public void onAnimationEnd(@NonNull Animator animation) {
+                InteractionJankMonitor.getInstance().end(action);
+            }
+        });
+    }
     /**
      * Creates and starts a partial stash animation, hinting at the new state that will trigger when
      * long press is detected.
@@ -571,9 +602,11 @@
         }
 
         // Only update the following flags when system gesture is not in progress.
-        maybeResetStashedInAppAllApps(hasAnyFlag(FLAG_STASHED_IN_APP_IME) == mIsImeShowing);
-        if (hasAnyFlag(FLAG_STASHED_IN_APP_IME) != mIsImeShowing) {
-            updateStateForFlag(FLAG_STASHED_IN_APP_IME, mIsImeShowing);
+        boolean shouldStashForIme = shouldStashForIme();
+        maybeResetStashedInAppAllApps(
+                hasAnyFlag(FLAG_STASHED_IN_APP_IME) == shouldStashForIme);
+        if (hasAnyFlag(FLAG_STASHED_IN_APP_IME) != shouldStashForIme) {
+            updateStateForFlag(FLAG_STASHED_IN_APP_IME, shouldStashForIme);
             applyState(TASKBAR_STASH_DURATION_FOR_IME, getTaskbarStashStartDelayForIme());
         }
     }
@@ -625,8 +658,9 @@
 
         // Only update FLAG_STASHED_IN_APP_IME when system gesture is not in progress.
         mIsImeShowing = hasAnyFlag(systemUiStateFlags, SYSUI_STATE_IME_SHOWING);
+        mIsImeSwitcherShowing = hasAnyFlag(systemUiStateFlags, SYSUI_STATE_IME_SWITCHER_SHOWING);
         if (!mIsSystemGestureInProgress) {
-            updateStateForFlag(FLAG_STASHED_IN_APP_IME, mIsImeShowing);
+            updateStateForFlag(FLAG_STASHED_IN_APP_IME, shouldStashForIme());
             animDuration = TASKBAR_STASH_DURATION_FOR_IME;
             startDelay = getTaskbarStashStartDelayForIme();
         }
@@ -634,6 +668,10 @@
         applyState(skipAnim ? 0 : animDuration, skipAnim ? 0 : startDelay);
     }
 
+    private boolean shouldStashForIme() {
+        return mIsImeShowing || mIsImeSwitcherShowing;
+    }
+
     /**
      * Updates the proper flag to indicate whether the task bar should be stashed.
      *
@@ -699,6 +737,7 @@
         pw.println(String.format(
                 "%s\tmIsSystemGestureInProgress=%b", prefix, mIsSystemGestureInProgress));
         pw.println(String.format("%s\tmIsImeShowing=%b", prefix, mIsImeShowing));
+        pw.println(String.format("%s\tmIsImeSwitcherShowing=%b", prefix, mIsImeSwitcherShowing));
     }
 
     private static String getStateString(int flags) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index 6a43cc5..ea15acb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -42,7 +42,6 @@
 import com.android.launcher3.model.data.FolderInfo;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
-import com.android.launcher3.uioverrides.ApiWrapper;
 import com.android.launcher3.util.LauncherBindableItemsContainer;
 import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.AllAppsButton;
@@ -292,7 +291,7 @@
             countExcludingQsb--;
         }
         int spaceNeeded = countExcludingQsb * (mItemMarginLeftRight * 2 + mIconTouchSize);
-        int navSpaceNeeded = ApiWrapper.getHotseatEndOffset(getContext());
+        int navSpaceNeeded = deviceProfile.hotseatBarEndOffset;
         boolean layoutRtl = isLayoutRtl();
         int iconEnd = right - (right - left - spaceNeeded) / 2;
         boolean needMoreSpaceForNav = layoutRtl ?
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index 0cbd0d1..db7dc78 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -68,7 +68,8 @@
     public static final int ALPHA_INDEX_RECENTS_DISABLED = 3;
     public static final int ALPHA_INDEX_NOTIFICATION_EXPANDED = 4;
     public static final int ALPHA_INDEX_ASSISTANT_INVOKED = 5;
-    private static final int NUM_ALPHA_CHANNELS = 6;
+    public static final int ALPHA_INDEX_IME_BUTTON_NAV = 6;
+    private static final int NUM_ALPHA_CHANNELS = 7;
 
     private final TaskbarActivityContext mActivity;
     private final TaskbarView mTaskbarView;
@@ -143,6 +144,14 @@
     }
 
     /**
+     * Should be called when the IME switcher visibility changes.
+     */
+    public void setIsImeSwitcherVisible(boolean isImeSwitcherVisible) {
+        mTaskbarIconAlpha.getProperty(ALPHA_INDEX_IME_BUTTON_NAV).setValue(
+                isImeSwitcherVisible ? 0 : 1);
+    }
+
+    /**
      * Should be called when the recents button is disabled, so we can hide taskbar icons as well.
      */
     public void setRecentsButtonDisabled(boolean isDisabled) {
@@ -289,9 +298,7 @@
                         isRtl ? -halfQsbIconWidthDiff : halfQsbIconWidthDiff,
                         hotseatIconCenter - childCenter, LINEAR);
 
-                int qsbContentHeight = child.getHeight() - child.getPaddingTop()
-                        - child.getPaddingBottom();
-                float scale = ((float) taskbarDp.iconSizePx) / qsbContentHeight;
+                float scale = ((float) taskbarDp.iconSizePx) / launcherDp.hotseatQsbVisualHeight;
                 setter.addFloat(child, SCALE_PROPERTY, scale, 1f, LINEAR);
 
                 setter.addFloat(child, VIEW_ALPHA, 0f, 1f,
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
index 61b038e..6c43e50 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsController.java
@@ -207,7 +207,7 @@
     private LayoutParams createLayoutParams() {
         LayoutParams layoutParams = new LayoutParams(
                 TYPE_APPLICATION_OVERLAY,
-                0,
+                WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
                 PixelFormat.TRANSLUCENT);
         layoutParams.setTitle(WINDOW_TITLE);
         layoutParams.gravity = Gravity.BOTTOM;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
index 2f8e4d9..f450496 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
@@ -17,14 +17,9 @@
 package com.android.launcher3.uioverrides;
 
 import android.app.Person;
-import android.content.Context;
 import android.content.pm.ShortcutInfo;
-import android.content.res.Resources;
 
-import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.util.DisplayController;
-import com.android.launcher3.util.DisplayController.NavigationMode;
 
 public class ApiWrapper {
 
@@ -34,24 +29,4 @@
         Person[] persons = si.getPersons();
         return persons == null ? Utilities.EMPTY_PERSON_ARRAY : persons;
     }
-
-    /**
-     * Returns the minimum space that should be left empty at the end of hotseat
-     */
-    public static int getHotseatEndOffset(Context context) {
-        if (DisplayController.getNavigationMode(context) == NavigationMode.THREE_BUTTONS) {
-            Resources res = context.getResources();
-            /*
-            * 3 nav buttons +
-            * Little space at the end for contextual buttons +
-            * Little space between icons and nav buttons
-            */
-            return 3 * res.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size)
-                    + res.getDimensionPixelSize(R.dimen.taskbar_contextual_button_margin)
-                    + res.getDimensionPixelSize(R.dimen.taskbar_hotseat_nav_spacing);
-        } else {
-            return 0;
-        }
-
-    }
 }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
index 84b3839..871ddc6 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
@@ -28,6 +28,7 @@
 import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
 import static com.android.launcher3.testing.TestProtocol.BAD_STATE;
 import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
+import static com.android.quickstep.views.RecentsView.OVERVIEW_PROGRESS;
 import static com.android.quickstep.views.RecentsView.RECENTS_GRID_PROGRESS;
 import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
 import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION;
@@ -74,6 +75,7 @@
         getTaskModalnessProperty().set(mRecentsView, state.getOverviewModalness());
         RECENTS_GRID_PROGRESS.set(mRecentsView,
                 state.displayOverviewTasksAsGrid(mLauncher.getDeviceProfile()) ? 1f : 0f);
+        OVERVIEW_PROGRESS.set(mRecentsView, state == LauncherState.OVERVIEW ? 1f : 0f);
     }
 
     @Override
@@ -117,6 +119,9 @@
         boolean showAsGrid = toState.displayOverviewTasksAsGrid(mLauncher.getDeviceProfile());
         setter.setFloat(mRecentsView, RECENTS_GRID_PROGRESS, showAsGrid ? 1f : 0f,
                 showAsGrid ? INSTANT : FINAL_FRAME);
+
+        setter.setFloat(mRecentsView, OVERVIEW_PROGRESS,
+                toState == LauncherState.OVERVIEW ? 1f : 0f, INSTANT);
     }
 
     abstract FloatProperty getTaskModalnessProperty();
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
index a74774c..9f2efc4 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -31,8 +31,6 @@
  */
 public class AllAppsState extends LauncherState {
 
-    private static final float WORKSPACE_SCALE_FACTOR = 0.97f;
-
     private static final int STATE_FLAGS =
             FLAG_WORKSPACE_INACCESSIBLE | FLAG_CLOSE_POPUPS | FLAG_HOTSEAT_INACCESSIBLE;
 
@@ -60,7 +58,8 @@
 
     @Override
     public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) {
-        return new ScaleAndTranslation(WORKSPACE_SCALE_FACTOR, NO_OFFSET, NO_OFFSET);
+        return new ScaleAndTranslation(launcher.getDeviceProfile().workspaceContentScale, NO_OFFSET,
+                NO_OFFSET);
     }
 
     @Override
@@ -71,7 +70,7 @@
             ScaleAndTranslation overviewScaleAndTranslation = LauncherState.OVERVIEW
                     .getWorkspaceScaleAndTranslation(launcher);
             return new ScaleAndTranslation(
-                    WORKSPACE_SCALE_FACTOR,
+                    launcher.getDeviceProfile().workspaceContentScale,
                     overviewScaleAndTranslation.translationX,
                     overviewScaleAndTranslation.translationY);
         }
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 0972e4e..9eb4d62 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -58,6 +58,7 @@
 import android.annotation.TargetApi;
 import android.app.Activity;
 import android.app.ActivityManager;
+import android.app.WindowConfiguration;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Matrix;
@@ -297,9 +298,12 @@
         mActivityInterface = gestureState.getActivityInterface();
         mActivityInitListener = mActivityInterface.createActivityInitListener(this::onActivityInit);
         mInputConsumerProxy =
-                new InputConsumerProxy(context,
-                        () -> mRecentsView.getPagedViewOrientedState().getRecentsActivityRotation(),
-                        inputConsumer, () -> {
+                new InputConsumerProxy(context, /* rotationSupplier = */ () -> {
+                    if (mRecentsView == null) {
+                        return ROTATION_0;
+                    }
+                    return mRecentsView.getPagedViewOrientedState().getRecentsActivityRotation();
+                }, inputConsumer, /* callback = */ () -> {
                     endRunningWindowAnim(mGestureState.getEndTarget() == HOME /* cancel */);
                     endLauncherTransitionController();
                 }, new InputProxyHandlerFactory(mActivityInterface, mGestureState));
@@ -441,7 +445,7 @@
             });
 
         setupRecentsViewUi();
-        linkRecentsViewScroll();
+        mRecentsView.runOnPageScrollsInitialized(this::linkRecentsViewScroll);
         activity.runOnBindToTouchInteractionService(this::onLauncherBindToService);
 
         mActivity.registerActivityLifecycleCallbacks(mLifecycleCallbacks);
@@ -1418,6 +1422,16 @@
                         runningTaskTarget.taskInfo.pictureInPictureParams,
                         homeRotation,
                         mDp.hotseatBarSizePx);
+        final Rect appBounds = new Rect();
+        final WindowConfiguration winConfig = taskInfo.configuration.windowConfiguration;
+        // Adjust the appBounds for TaskBar by using the calculated window crop Rect
+        // from TaskViewSimulator and fallback to the bounds in TaskInfo when it's originated
+        // from windowing modes other than full-screen.
+        if (winConfig.getWindowingMode() == WindowConfiguration.WINDOWING_MODE_FULLSCREEN) {
+            mRemoteTargetHandles[0].getTaskViewSimulator().getCurrentCropRect().round(appBounds);
+        } else {
+            appBounds.set(winConfig.getBounds());
+        }
         final SwipePipToHomeAnimator.Builder builder = new SwipePipToHomeAnimator.Builder()
                 .setContext(mContext)
                 .setTaskId(runningTaskTarget.taskId)
@@ -1425,7 +1439,7 @@
                 .setLeash(runningTaskTarget.leash)
                 .setSourceRectHint(
                         runningTaskTarget.taskInfo.pictureInPictureParams.getSourceRectHint())
-                .setAppBounds(taskInfo.configuration.windowConfiguration.getBounds())
+                .setAppBounds(appBounds)
                 .setHomeToWindowPositionMap(homeToWindowPositionMap)
                 .setStartBounds(startRect)
                 .setDestinationBounds(destinationBounds)
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 52abb92..6354282 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -60,7 +60,6 @@
 import com.android.quickstep.util.ActivityInitListener;
 import com.android.quickstep.util.AnimatorControllerWithResistance;
 import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
@@ -280,17 +279,8 @@
     public static void getTaskDimension(DeviceProfile dp, PointF out) {
         out.x = dp.widthPx;
         out.y = dp.heightPx;
-        if (TaskView.clipLeft(dp)) {
-            out.x -= dp.getInsets().left;
-        }
-        if (TaskView.clipRight(dp)) {
-            out.x -= dp.getInsets().right;
-        }
-        if (TaskView.clipTop(dp)) {
-            out.y -= dp.getInsets().top;
-        }
-        if (TaskView.clipBottom(dp)) {
-            out.y -= Math.max(dp.getInsets().bottom, dp.taskbarSize);
+        if (dp.isTablet) {
+            out.y -= dp.taskbarSize;
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/ImageActionsApi.java b/quickstep/src/com/android/quickstep/ImageActionsApi.java
index 154848d..2273806 100644
--- a/quickstep/src/com/android/quickstep/ImageActionsApi.java
+++ b/quickstep/src/com/android/quickstep/ImageActionsApi.java
@@ -78,16 +78,17 @@
         addImageAndSendIntent(crop, intent, true, exceptionCallback);
     }
 
-    @UiThread
     private void addImageAndSendIntent(@Nullable Rect crop, Intent intent, boolean setData,
             @Nullable Runnable exceptionCallback) {
-        if (mBitmapSupplier.get() == null) {
-            Log.e(TAG, "No snapshot available, not starting share.");
-            return;
-        }
 
-        UI_HELPER_EXECUTOR.execute(() -> persistBitmapAndStartActivity(mContext,
-                mBitmapSupplier.get(), crop, intent, (uri, intentForUri) -> {
+        UI_HELPER_EXECUTOR.execute(() -> {
+            Bitmap bitmap = mBitmapSupplier.get();
+            if (bitmap == null) {
+                Log.e(TAG, "No snapshot available, not starting share.");
+                return;
+            }
+            persistBitmapAndStartActivity(mContext,
+                    bitmap, crop, intent, (uri, intentForUri) -> {
                     intentForUri.addFlags(FLAG_GRANT_READ_URI_PERMISSION);
                     if (setData) {
                         intentForUri.setData(uri);
@@ -95,7 +96,8 @@
                         intentForUri.putExtra(EXTRA_STREAM, uri);
                     }
                     return new Intent[]{intentForUri};
-                }, TAG, exceptionCallback));
+                }, TAG, exceptionCallback);
+        });
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index 1634c08..442578e 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -36,6 +36,7 @@
 import com.android.launcher3.util.Executors.SimpleThreadFactory;
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.quickstep.util.GroupTask;
+import com.android.quickstep.util.TaskVisualsChangeListener;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -54,7 +55,8 @@
  * Singleton class to load and manage recents model.
  */
 @TargetApi(Build.VERSION_CODES.O)
-public class RecentsModel implements IconChangeListener, TaskStackChangeListener {
+public class RecentsModel implements IconChangeListener, TaskStackChangeListener,
+        TaskVisualsChangeListener {
 
     // We do not need any synchronization for this variable as its only written on UI thread.
     public static final MainThreadInitializedObject<RecentsModel> INSTANCE =
@@ -77,6 +79,7 @@
 
         IconProvider iconProvider = new IconProvider(context);
         mIconCache = new TaskIconCache(context, RECENTS_MODEL_EXECUTOR, iconProvider);
+        mIconCache.registerTaskVisualsChangeListener(this);
         mThumbnailCache = new TaskThumbnailCache(context, RECENTS_MODEL_EXECUTOR);
 
         TaskStackChangeListeners.getInstance().registerTaskStackListener(this);
@@ -204,6 +207,13 @@
     }
 
     @Override
+    public void onTaskIconChanged(int taskId) {
+        for (TaskVisualsChangeListener listener : mThumbnailChangeListeners) {
+            listener.onTaskIconChanged(taskId);
+        }
+    }
+
+    @Override
     public void onSystemIconStateChanged(String iconState) {
         mIconCache.clearCache();
     }
@@ -226,20 +236,4 @@
         writer.println(prefix + "RecentsModel:");
         mTaskList.dump("  ", writer);
     }
-
-    /**
-     * Listener for receiving various task properties changes
-     */
-    public interface TaskVisualsChangeListener {
-
-        /**
-         * Called whn the task thumbnail changes
-         */
-        Task onTaskThumbnailChanged(int taskId, ThumbnailData thumbnailData);
-
-        /**
-         * Called when the icon for a task changes
-         */
-        void onTaskIconChanged(String pkg, UserHandle user);
-    }
 }
diff --git a/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
index b20d488..1bd808d 100644
--- a/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
+++ b/quickstep/src/com/android/quickstep/RemoteAnimationTargets.java
@@ -17,8 +17,6 @@
 
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
 
-import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS;
-
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
 import java.util.ArrayList;
@@ -114,10 +112,6 @@
     }
 
     public void release() {
-        if (ENABLE_SHELL_TRANSITIONS) {
-            mReleaseChecks.clear();
-            return;
-        }
         if (mReleased) {
             return;
         }
diff --git a/quickstep/src/com/android/quickstep/TaskIconCache.java b/quickstep/src/com/android/quickstep/TaskIconCache.java
index 3803f03..dc60875 100644
--- a/quickstep/src/com/android/quickstep/TaskIconCache.java
+++ b/quickstep/src/com/android/quickstep/TaskIconCache.java
@@ -18,6 +18,7 @@
 import static com.android.launcher3.uioverrides.QuickstepLauncher.GO_LOW_RAM_RECENTS_ENABLED;
 import static com.android.launcher3.util.DisplayController.CHANGE_DENSITY;
 
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityManager.TaskDescription;
 import android.content.Context;
@@ -46,6 +47,7 @@
 import com.android.launcher3.util.Preconditions;
 import com.android.quickstep.util.CancellableTask;
 import com.android.quickstep.util.TaskKeyLruCache;
+import com.android.quickstep.util.TaskVisualsChangeListener;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.model.Task.TaskKey;
 import com.android.systemui.shared.system.PackageManagerWrapper;
@@ -70,6 +72,9 @@
 
     private BaseIconFactory mIconFactory;
 
+    @Nullable
+    public TaskVisualsChangeListener mTaskVisualsChangeListener = null;
+
     public TaskIconCache(Context context, Executor bgExecutor, IconProvider iconProvider) {
         mContext = context;
         mBgExecutor = bgExecutor;
@@ -116,6 +121,7 @@
                 task.icon = result.icon;
                 task.titleDescription = result.contentDescription;
                 callback.accept(task);
+                dispatchIconUpdate(task.key.id);
             }
         };
         mBgExecutor.execute(request);
@@ -257,7 +263,8 @@
         if (mIconFactory == null) {
             mIconFactory = new BaseIconFactory(mContext,
                     DisplayController.INSTANCE.get(mContext).getInfo().getDensityDpi(),
-                    mContext.getResources().getDimensionPixelSize(R.dimen.taskbar_icon_size));
+                    mContext.getResources().getDimensionPixelSize(
+                            R.dimen.task_icon_cache_default_icon_size));
         }
         return mIconFactory;
     }
@@ -272,4 +279,18 @@
         public Drawable icon;
         public String contentDescription = "";
     }
+
+    void registerTaskVisualsChangeListener(TaskVisualsChangeListener newListener) {
+        mTaskVisualsChangeListener = newListener;
+    }
+
+    void removeTaskVisualsChangeListener() {
+        mTaskVisualsChangeListener = null;
+    }
+
+    void dispatchIconUpdate(int taskId) {
+        if (mTaskVisualsChangeListener != null) {
+            mTaskVisualsChangeListener.onTaskIconChanged(taskId);
+        }
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
index 749c07b..092854f 100644
--- a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
@@ -261,6 +261,8 @@
      * Does NOT add split options in the following scenarios:
      * * The taskView to add split options is already showing split screen tasks
      * * There aren't at least 2 tasks in overview to show split options for
+     * * Split isn't supported by the task itself (non resizable activity)
+     * * We aren't currently in multi-window
      * * The taskView to show split options for is the focused task AND we haven't started
      * scrolling in overview (if we haven't scrolled, there's a split overview action button so
      * we don't need this menu option)
@@ -270,9 +272,12 @@
         public List<SystemShortcut> getShortcuts(BaseDraggingActivity activity,
                 TaskIdAttributeContainer taskContainer) {
             DeviceProfile deviceProfile = activity.getDeviceProfile();
-            TaskView taskView = taskContainer.getTaskView();
-            RecentsView recentsView = taskView.getRecentsView();
-            PagedOrientationHandler orientationHandler = recentsView.getPagedOrientationHandler();
+            final Task task  = taskContainer.getTask();
+            final TaskView taskView = taskContainer.getTaskView();
+            final RecentsView recentsView = taskView.getRecentsView();
+            final PagedOrientationHandler orientationHandler =
+                    recentsView.getPagedOrientationHandler();
+
             int[] taskViewTaskIds = taskView.getTaskIds();
             boolean taskViewHasMultipleTasks = taskViewTaskIds[0] != -1 &&
                     taskViewTaskIds[1] != -1;
@@ -280,9 +285,14 @@
             boolean isFocusedTask = deviceProfile.isTablet && taskView.isFocusedTask();
             boolean isTaskInExpectedScrollPosition =
                     recentsView.isTaskInExpectedScrollPosition(recentsView.indexOfChild(taskView));
+            boolean isTaskSplitNotSupported = !task.isDockable;
+            boolean hideForExistingMultiWindow = activity.getDeviceProfile().isMultiWindowMode;
 
-            if (taskViewHasMultipleTasks || notEnoughTasksToSplit
-                    || (isFocusedTask && isTaskInExpectedScrollPosition)) {
+            if (taskViewHasMultipleTasks ||
+                    notEnoughTasksToSplit ||
+                    isTaskSplitNotSupported ||
+                    hideForExistingMultiWindow ||
+                    (isFocusedTask && isTaskInExpectedScrollPosition)) {
                 return null;
             }
 
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
index f68bbbc..c7c3441 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
@@ -27,6 +27,7 @@
 import static com.android.quickstep.fallback.RecentsState.OVERVIEW_SPLIT_SELECT;
 import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
 import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
+import static com.android.quickstep.views.RecentsView.OVERVIEW_PROGRESS;
 import static com.android.quickstep.views.RecentsView.RECENTS_GRID_PROGRESS;
 import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
 import static com.android.quickstep.views.RecentsView.TASK_MODALNESS;
@@ -105,6 +106,8 @@
         boolean showAsGrid = state.displayOverviewTasksAsGrid(mActivity.getDeviceProfile());
         setter.setFloat(mRecentsView, RECENTS_GRID_PROGRESS, showAsGrid ? 1f : 0f,
                 showAsGrid ? INSTANT : FINAL_FRAME);
+        setter.setFloat(mRecentsView, OVERVIEW_PROGRESS, state == RecentsState.DEFAULT ? 1f : 0f,
+                INSTANT);
 
         setter.setViewBackgroundColor(mActivity.getScrimView(), state.getScrimColor(mActivity),
                 config.getInterpolator(ANIM_SCRIM_FADE, LINEAR));
diff --git a/quickstep/src/com/android/quickstep/fallback/RecentsState.java b/quickstep/src/com/android/quickstep/fallback/RecentsState.java
index 77db6b7..af9d0cb 100644
--- a/quickstep/src/com/android/quickstep/fallback/RecentsState.java
+++ b/quickstep/src/com/android/quickstep/fallback/RecentsState.java
@@ -15,6 +15,7 @@
  */
 package com.android.quickstep.fallback;
 
+import static com.android.launcher3.LauncherState.FLAG_CLOSE_POPUPS;
 import static com.android.launcher3.uioverrides.states.BackgroundAppState.getOverviewScaleAndOffsetForBackgroundState;
 import static com.android.launcher3.uioverrides.states.OverviewModalTaskState.getOverviewScaleAndOffsetForModalState;
 
@@ -52,7 +53,7 @@
     public static final RecentsState HOME = new RecentsState(3, 0);
     public static final RecentsState BG_LAUNCHER = new LauncherState(4, 0);
     public static final RecentsState OVERVIEW_SPLIT_SELECT = new RecentsState(5,
-            FLAG_SHOW_AS_GRID | FLAG_SCRIM | FLAG_OVERVIEW_UI);
+            FLAG_SHOW_AS_GRID | FLAG_SCRIM | FLAG_OVERVIEW_UI | FLAG_CLOSE_POPUPS);
 
     public final int ordinal;
     private final int mFlags;
diff --git a/quickstep/src/com/android/quickstep/util/ImageActionUtils.java b/quickstep/src/com/android/quickstep/util/ImageActionUtils.java
index 63d5b0d..9fe24de 100644
--- a/quickstep/src/com/android/quickstep/util/ImageActionUtils.java
+++ b/quickstep/src/com/android/quickstep/util/ImageActionUtils.java
@@ -43,7 +43,6 @@
 import android.util.Log;
 import android.view.View;
 
-import androidx.annotation.UiThread;
 import androidx.annotation.WorkerThread;
 import androidx.core.content.FileProvider;
 
@@ -86,67 +85,70 @@
      * Launch the activity to share image for overview sharing. This is to share cropped bitmap
      * with specific share targets (with shortcutInfo and appTarget) rendered in overview.
      */
-    @UiThread
     public static void shareImage(Context context, Supplier<Bitmap> bitmapSupplier, RectF rectF,
             ShortcutInfo shortcutInfo, AppTarget appTarget, String tag) {
-        if (bitmapSupplier.get() == null) {
-            return;
-        }
-        Rect crop = new Rect();
-        rectF.round(crop);
-        Intent intent = new Intent();
-        Uri uri =  getImageUri(bitmapSupplier.get(), crop, context, tag);
-        ClipData clipdata = new ClipData(new ClipDescription("content",
-                new String[]{"image/png"}),
-                new ClipData.Item(uri));
-        intent.setAction(Intent.ACTION_SEND)
-            .setComponent(new ComponentName(appTarget.getPackageName(), appTarget.getClassName()))
-            .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
-            .addFlags(FLAG_GRANT_READ_URI_PERMISSION)
-            .setType("image/png")
-            .putExtra(Intent.EXTRA_STREAM, uri)
-            .putExtra(Intent.EXTRA_SHORTCUT_ID, shortcutInfo.getId())
-            .setClipData(clipdata);
+        UI_HELPER_EXECUTOR.execute(() -> {
+            Bitmap bitmap = bitmapSupplier.get();
+            if (bitmap == null) {
+                return;
+            }
+            Rect crop = new Rect();
+            rectF.round(crop);
+            Intent intent = new Intent();
+            Uri uri = getImageUri(bitmap, crop, context, tag);
+            ClipData clipdata = new ClipData(new ClipDescription("content",
+                    new String[]{"image/png"}),
+                    new ClipData.Item(uri));
+            intent.setAction(Intent.ACTION_SEND)
+                    .setComponent(
+                            new ComponentName(appTarget.getPackageName(), appTarget.getClassName()))
+                    .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+                    .addFlags(FLAG_GRANT_READ_URI_PERMISSION)
+                    .setType("image/png")
+                    .putExtra(Intent.EXTRA_STREAM, uri)
+                    .putExtra(Intent.EXTRA_SHORTCUT_ID, shortcutInfo.getId())
+                    .setClipData(clipdata);
 
-        if (context.getUserId() != appTarget.getUser().getIdentifier()) {
-            intent.prepareToLeaveUser(context.getUserId());
-            intent.fixUris(context.getUserId());
-            context.startActivityAsUser(intent, appTarget.getUser());
-        } else {
-            context.startActivity(intent);
-        }
+            if (context.getUserId() != appTarget.getUser().getIdentifier()) {
+                intent.prepareToLeaveUser(context.getUserId());
+                intent.fixUris(context.getUserId());
+                context.startActivityAsUser(intent, appTarget.getUser());
+            } else {
+                context.startActivity(intent);
+            }
+        });
     }
 
     /**
      * Launch the activity to share image.
      */
-    @UiThread
     public static void startShareActivity(Context context, Supplier<Bitmap> bitmapSupplier,
             Rect crop, Intent intent, String tag) {
-        if (bitmapSupplier.get() == null) {
-            Log.e(tag, "No snapshot available, not starting share.");
-            return;
-        }
-
-        UI_HELPER_EXECUTOR.execute(() -> persistBitmapAndStartActivity(context,
-                bitmapSupplier.get(), crop, intent, ImageActionUtils::getShareIntentForImageUri,
-                tag));
+        UI_HELPER_EXECUTOR.execute(() -> {
+            Bitmap bitmap = bitmapSupplier.get();
+            if (bitmap == null) {
+                Log.e(tag, "No snapshot available, not starting share.");
+                return;
+            }
+            persistBitmapAndStartActivity(context, bitmap, crop, intent,
+                    ImageActionUtils::getShareIntentForImageUri, tag);
+        });
     }
 
     /**
      * Launch the activity to share image with shared element transition.
      */
-    @UiThread
     public static void startShareActivity(Context context, Supplier<Bitmap> bitmapSupplier,
             Rect crop, Intent intent, String tag, View sharedElement) {
-        if (bitmapSupplier.get() == null) {
-            Log.e(tag, "No snapshot available, not starting share.");
-            return;
-        }
-
-        UI_HELPER_EXECUTOR.execute(() -> persistBitmapAndStartActivity(context,
-                bitmapSupplier.get(), crop, intent, ImageActionUtils::getShareIntentForImageUri,
-                tag, sharedElement));
+        UI_HELPER_EXECUTOR.execute(() -> {
+            Bitmap bitmap = bitmapSupplier.get();
+            if (bitmap == null) {
+                Log.e(tag, "No snapshot available, not starting share.");
+                return;
+            }
+            persistBitmapAndStartActivity(context, bitmap,
+                    crop, intent, ImageActionUtils::getShareIntentForImageUri, tag, sharedElement);
+        });
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
index b83e26e..e758f5b 100644
--- a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
+++ b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
@@ -17,13 +17,14 @@
 
 import android.content.Context;
 import android.content.res.Resources;
+import android.util.Log;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
 
 import com.android.launcher3.Alarm;
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.compat.AccessibilityManagerCompat;
-import com.android.launcher3.testing.TestProtocol;
 
 /**
  * Given positions along x- or y-axis, tracks velocity and acceleration and determines when there is
@@ -31,6 +32,8 @@
  */
 public class MotionPauseDetector {
 
+    private static final String TAG = "MotionPauseDetector";
+
     // The percentage of the previous speed that determines whether this is a rapid deceleration.
     // The bigger this number, the easier it is to trigger the first pause.
     private static final float RAPID_DECELERATION_FACTOR = 0.6f;
@@ -43,6 +46,12 @@
      */
     private static final long HARDER_TRIGGER_TIMEOUT = 400;
 
+    /**
+     * When running in a test harness, if no motion is added for this amount of time, assume the
+     * motion has paused. (We use an increased timeout since sometimes test devices can be slow.)
+     */
+    private static final long TEST_HARNESS_TRIGGER_TIMEOUT = 2000;
+
     private final float mSpeedVerySlow;
     private final float mSpeedSlow;
     private final float mSpeedSomewhatFast;
@@ -85,7 +94,8 @@
         mSpeedSomewhatFast = res.getDimension(R.dimen.motion_pause_detector_speed_somewhat_fast);
         mSpeedFast = res.getDimension(R.dimen.motion_pause_detector_speed_fast);
         mForcePauseTimeout = new Alarm();
-        mForcePauseTimeout.setOnAlarmListener(alarm -> updatePaused(true /* isPaused */));
+        mForcePauseTimeout.setOnAlarmListener(alarm -> updatePaused(true /* isPaused */,
+                "Force pause timeout after " +  alarm.getLastSetTimeout() + "ms" /* reason */));
         mMakePauseHarderToTrigger = makePauseHarderToTrigger;
         mVelocityProvider = new SystemVelocityProvider(axis);
     }
@@ -102,7 +112,7 @@
      */
     public void setDisallowPause(boolean disallowPause) {
         mDisallowPause = disallowPause;
-        updatePaused(mIsPaused);
+        updatePaused(mIsPaused, "Set disallowPause=" + disallowPause);
     }
 
     /**
@@ -119,9 +129,11 @@
      * @param pointerIndex Index for the pointer being tracked in the motion event
      */
     public void addPosition(MotionEvent ev, int pointerIndex) {
-        long timeoutMs = TestProtocol.sForcePauseTimeout != null
-                ? TestProtocol.sForcePauseTimeout
-                : mMakePauseHarderToTrigger ? HARDER_TRIGGER_TIMEOUT : FORCE_PAUSE_TIMEOUT;
+        long timeoutMs = Utilities.IS_RUNNING_IN_TEST_HARNESS
+                ? TEST_HARNESS_TRIGGER_TIMEOUT
+                : mMakePauseHarderToTrigger
+                        ? HARDER_TRIGGER_TIMEOUT
+                        : FORCE_PAUSE_TIMEOUT;
         mForcePauseTimeout.setAlarm(timeoutMs);
         float newVelocity = mVelocityProvider.addMotionEvent(ev, ev.getPointerId(pointerIndex));
         if (mPreviousVelocity != null) {
@@ -134,21 +146,27 @@
         float speed = Math.abs(velocity);
         float previousSpeed = Math.abs(prevVelocity);
         boolean isPaused;
+        String isPausedReason = "";
         if (mIsPaused) {
             // Continue to be paused until moving at a fast speed.
             isPaused = speed < mSpeedFast || previousSpeed < mSpeedFast;
+            isPausedReason = "Was paused, but started moving at a fast speed";
         } else {
             if (velocity < 0 != prevVelocity < 0) {
                 // We're just changing directions, not necessarily stopping.
                 isPaused = false;
+                isPausedReason = "Velocity changed directions";
             } else {
                 isPaused = speed < mSpeedVerySlow && previousSpeed < mSpeedVerySlow;
+                isPausedReason = "Pause requires back to back slow speeds";
                 if (!isPaused && !mHasEverBeenPaused) {
                     // We want to be more aggressive about detecting the first pause to ensure it
                     // feels as responsive as possible; getting two very slow speeds back to back
                     // takes too long, so also check for a rapid deceleration.
                     boolean isRapidDeceleration = speed < previousSpeed * RAPID_DECELERATION_FACTOR;
                     isPaused = isRapidDeceleration && speed < mSpeedSomewhatFast;
+                    isPausedReason = "Didn't have back to back slow speeds, checking for rapid"
+                            + " deceleration on first pause only";
                 }
                 if (mMakePauseHarderToTrigger) {
                     if (speed < mSpeedSlow) {
@@ -156,22 +174,27 @@
                             mSlowStartTime = time;
                         }
                         isPaused = time - mSlowStartTime >= HARDER_TRIGGER_TIMEOUT;
+                        isPausedReason = "Maintained slow speed for sufficient duration when making"
+                                + " pause harder to trigger";
                     } else {
                         mSlowStartTime = 0;
                         isPaused = false;
+                        isPausedReason = "Intentionally making pause harder to trigger";
                     }
                 }
             }
         }
-        updatePaused(isPaused);
+        updatePaused(isPaused, isPausedReason);
     }
 
-    private void updatePaused(boolean isPaused) {
+    private void updatePaused(boolean isPaused, String reason) {
         if (mDisallowPause) {
+            reason = "Disallow pause; otherwise, would have been " + isPaused + " due to " + reason;
             isPaused = false;
         }
         if (mIsPaused != isPaused) {
             mIsPaused = isPaused;
+            Log.d(TAG, "onMotionPauseChanged, paused=" + mIsPaused + " reason=" + reason);
             boolean isFirstDetectedPause = !mHasEverBeenPaused && mIsPaused;
             if (mIsPaused) {
                 AccessibilityManagerCompat.sendPauseDetectedEventToTest(mContext);
diff --git a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
index 14190b3..dec934a 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
+++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
@@ -53,7 +53,6 @@
 import com.android.quickstep.BaseActivityInterface;
 import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.TaskAnimationManager;
-import com.android.quickstep.views.TaskView;
 
 import java.lang.annotation.Retention;
 import java.util.function.IntConsumer;
@@ -221,8 +220,7 @@
 
     private boolean updateHandler() {
         mRecentsActivityRotation = inferRecentsActivityRotation(mDisplayRotation);
-        if (mRecentsActivityRotation == mTouchRotation || (isRecentsActivityRotationAllowed()
-                && (mFlags & FLAG_SWIPE_UP_NOT_RUNNING) != 0)) {
+        if (mRecentsActivityRotation == mTouchRotation || isRecentsActivityRotationAllowed()) {
             mOrientationHandler = PagedOrientationHandler.PORTRAIT;
         } else if (mTouchRotation == ROTATION_90) {
             mOrientationHandler = PagedOrientationHandler.LANDSCAPE;
@@ -400,31 +398,10 @@
      * Returns the scale and pivot so that the provided taskRect can fit the provided full size
      */
     public float getFullScreenScaleAndPivot(Rect taskView, DeviceProfile dp, PointF outPivot) {
-        Rect insets = dp.getInsets();
-        float fullWidth = dp.widthPx;
-        float fullHeight = dp.heightPx;
-        if (TaskView.clipLeft(dp)) {
-            fullWidth -= insets.left;
-        }
-        if (TaskView.clipRight(dp)) {
-            fullWidth -= insets.right;
-        }
-        if (TaskView.clipTop(dp)) {
-            fullHeight -= insets.top;
-        }
-        if (TaskView.clipBottom(dp)) {
-            fullHeight -= insets.bottom;
-        }
-
         getTaskDimension(dp, outPivot);
         float scale = Math.min(outPivot.x / taskView.width(), outPivot.y / taskView.height());
-        // We also scale the preview as part of fullScreenParams, so account for that as well.
-        if (fullWidth > 0) {
-            scale = scale * dp.widthPx / fullWidth;
-        }
-
         if (scale == 1) {
-            outPivot.set(fullWidth / 2, fullHeight / 2);
+            outPivot.set(taskView.centerX(), taskView.centerY());
         } else {
             float factor = scale / (scale - 1);
             outPivot.set(taskView.left * factor, taskView.top * factor);
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index 2502359..f1189c9 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -330,4 +330,8 @@
     private boolean isInitialTaskIntentSet() {
         return (mInitialTaskId != INVALID_TASK_ID || mInitialTaskIntent != null);
     }
+
+    public int getInitialTaskId() {
+        return mInitialTaskId;
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
index 833d705..74e4acc 100644
--- a/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
+++ b/quickstep/src/com/android/quickstep/util/SwipePipToHomeAnimator.java
@@ -253,7 +253,7 @@
                     rotatedPosition.degree, rotatedPosition.positionX, rotatedPosition.positionY);
         } else {
             return mSurfaceTransactionHelper.scaleAndCrop(tx, mLeash, mSourceRectHint, mAppBounds,
-                    bounds, insets);
+                    bounds, insets, progress);
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index 1acdec1..8c48443 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -391,7 +391,7 @@
                 .withCornerRadius(getCurrentCornerRadius());
 
         if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
-            builder.withLayer(mDrawsBelowRecents ? Integer.MIN_VALUE : 0);
+            builder.withLayer(mDrawsBelowRecents ? Integer.MIN_VALUE + 1 : Integer.MAX_VALUE);
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/util/TaskVisualsChangeListener.java b/quickstep/src/com/android/quickstep/util/TaskVisualsChangeListener.java
new file mode 100644
index 0000000..66bff73
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/TaskVisualsChangeListener.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.util;
+
+import android.os.UserHandle;
+
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.model.ThumbnailData;
+
+/**
+ * Listener for receiving various task properties changes
+ */
+public interface TaskVisualsChangeListener {
+
+    /**
+     * Called when the task thumbnail changes
+     */
+    default Task onTaskThumbnailChanged(int taskId, ThumbnailData thumbnailData) {
+        return null;
+    }
+
+    /**
+     * Called when the icon for a task changes
+     */
+    default void onTaskIconChanged(String pkg, UserHandle user) {}
+
+    /**
+     * Called when the icon for a task changes
+     */
+    default void onTaskIconChanged(int taskId) {}
+}
diff --git a/quickstep/src/com/android/quickstep/util/TransformParams.java b/quickstep/src/com/android/quickstep/util/TransformParams.java
index 75d6001..a7f25d4 100644
--- a/quickstep/src/com/android/quickstep/util/TransformParams.java
+++ b/quickstep/src/com/android/quickstep/util/TransformParams.java
@@ -139,10 +139,12 @@
 
     public SurfaceParams[] createSurfaceParams(BuilderProxy proxy) {
         RemoteAnimationTargets targets = mTargetSet;
-        SurfaceParams[] surfaceParams = new SurfaceParams[targets.unfilteredApps.length];
+        final int appLength =  targets.unfilteredApps.length;
+        final int wallpaperLength = targets.wallpapers != null ? targets.wallpapers.length : 0;
+        SurfaceParams[] surfaceParams = new SurfaceParams[appLength + wallpaperLength];
         mRecentsSurface = getRecentsSurface(targets);
 
-        for (int i = 0; i < targets.unfilteredApps.length; i++) {
+        for (int i = 0; i < appLength; i++) {
             RemoteAnimationTargetCompat app = targets.unfilteredApps[i];
             SurfaceParams.Builder builder = new SurfaceParams.Builder(app.leash);
 
@@ -166,6 +168,12 @@
             }
             surfaceParams[i] = builder.build();
         }
+        // always put wallpaper layer to bottom.
+        for (int i = 0; i < wallpaperLength; i++) {
+            RemoteAnimationTargetCompat wallpaper = targets.wallpapers[i];
+            surfaceParams[appLength + i] = new SurfaceParams.Builder(wallpaper.leash)
+                    .withLayer(Integer.MIN_VALUE).build();
+        }
         return surfaceParams;
     }
 
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
index cb88068..2dff18e 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
@@ -1,5 +1,6 @@
 package com.android.quickstep.views;
 
+import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.util.SplitConfigurationOptions.DEFAULT_SPLIT_RATIO;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
@@ -313,4 +314,11 @@
         mSnapshotView2.setDimAlpha(amount);
         mDigitalWellBeingToast2.setBannerColorTint(tintColor, amount);
     }
+
+    @Override
+    protected void applyThumbnailSplashAlpha() {
+        super.applyThumbnailSplashAlpha();
+        mSnapshotView2.setSplashAlpha(
+                Utilities.mapToRange(mOverviewProgress, 0f, 1f, 1f, 0f, LINEAR));
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index 306ebd7..a736583 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -39,6 +39,7 @@
 import com.android.launcher3.popup.QuickstepSystemShortcut;
 import com.android.launcher3.statehandlers.DepthController;
 import com.android.launcher3.statemanager.StateManager.StateListener;
+import com.android.launcher3.util.PendingSplitSelectInfo;
 import com.android.launcher3.util.SplitConfigurationOptions;
 import com.android.quickstep.LauncherActivityInterface;
 import com.android.quickstep.util.SplitSelectStateController;
@@ -89,6 +90,21 @@
     }
 
     @Override
+    public void onTaskIconChanged(int taskId) {
+        // If Launcher needs to return to split select state, do it now, after the icon has updated.
+        if (mActivity.hasPendingSplitSelectInfo()) {
+            PendingSplitSelectInfo recoveryData = mActivity.getPendingSplitSelectInfo();
+            if (recoveryData.getStagedTaskId() == taskId) {
+                initiateSplitSelect(
+                        getTaskViewByTaskId(recoveryData.getStagedTaskId()),
+                        recoveryData.getStagePosition()
+                );
+                mActivity.finishSplitSelectRecovery();
+            }
+        }
+    }
+
+    @Override
     public void reset() {
         super.reset();
 
diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
index 3133453..9b585fc 100644
--- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
@@ -31,7 +31,6 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Insettable;
 import com.android.launcher3.R;
-import com.android.launcher3.uioverrides.ApiWrapper;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.DisplayController.NavigationMode;
 import com.android.launcher3.util.MultiValueAlpha;
@@ -78,8 +77,9 @@
     private static final int INDEX_VISIBILITY_ALPHA = 1;
     private static final int INDEX_FULLSCREEN_ALPHA = 2;
     private static final int INDEX_HIDDEN_FLAGS_ALPHA = 3;
+    private static final int INDEX_SHARE_TARGET_ALPHA = 4;
 
-    private final MultiValueAlpha mMultiValueAlpha;
+    private MultiValueAlpha mMultiValueAlpha;
     private Button mSplitButton;
 
     @ActionsHiddenFlags
@@ -105,13 +105,14 @@
 
     public OverviewActionsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr, 0);
-        mMultiValueAlpha = new MultiValueAlpha(this, 5);
-        mMultiValueAlpha.setUpdateVisibility(true);
     }
 
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
+        mMultiValueAlpha = new MultiValueAlpha(findViewById(R.id.action_buttons), 5);
+        mMultiValueAlpha.setUpdateVisibility(true);
+
         findViewById(R.id.action_screenshot).setOnClickListener(this);
 
         mSplitButton = findViewById(R.id.action_split);
@@ -193,6 +194,10 @@
         return mMultiValueAlpha.getProperty(INDEX_FULLSCREEN_ALPHA);
     }
 
+    public AlphaProperty getShareTargetAlpha() {
+        return mMultiValueAlpha.getProperty(INDEX_SHARE_TARGET_ALPHA);
+    }
+
     /**
      * Offsets OverviewActionsView horizontal position based on 3 button nav container in taskbar.
      */
@@ -200,10 +205,9 @@
         if (mDp == null) {
             return;
         }
-        boolean alignFor3ButtonTaskbar = mDp.isTaskbarPresent && !mDp.isGestureMode;
-        if (alignFor3ButtonTaskbar) {
+        if (mDp.areNavButtonsInline) {
             // Add extra horizontal spacing
-            int additionalPadding = ApiWrapper.getHotseatEndOffset(getContext());
+            int additionalPadding = mDp.hotseatBarEndOffset;
             if (isLayoutRtl()) {
                 setPadding(mInsets.left + additionalPadding, 0, mInsets.right, 0);
             } else {
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 12ddc38..583d097 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -152,7 +152,6 @@
 import com.android.quickstep.RecentsAnimationController;
 import com.android.quickstep.RecentsAnimationTargets;
 import com.android.quickstep.RecentsModel;
-import com.android.quickstep.RecentsModel.TaskVisualsChangeListener;
 import com.android.quickstep.RemoteAnimationTargets;
 import com.android.quickstep.RemoteTargetGluer;
 import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
@@ -170,6 +169,7 @@
 import com.android.quickstep.util.SplitSelectStateController;
 import com.android.quickstep.util.SurfaceTransactionApplier;
 import com.android.quickstep.util.TaskViewSimulator;
+import com.android.quickstep.util.TaskVisualsChangeListener;
 import com.android.quickstep.util.TransformParams;
 import com.android.quickstep.util.VibratorWrapper;
 import com.android.systemui.plugins.ResourceProvider;
@@ -363,6 +363,10 @@
                 }
             };
 
+    /**
+     * Progress of Recents view from carousel layout to grid layout. If Recents is not shown as a
+     * grid, then the value remains 0.
+     */
     public static final FloatProperty<RecentsView> RECENTS_GRID_PROGRESS =
             new FloatProperty<RecentsView>("recentsGrid") {
                 @Override
@@ -376,6 +380,23 @@
                 }
             };
 
+    /**
+     * Progress to and from the OVERVIEW state, where being in OverviewState has a value of 1, and
+     * being in any other LauncherState has a value of 0.
+     */
+    public static final FloatProperty<RecentsView> OVERVIEW_PROGRESS =
+            new FloatProperty<RecentsView>("overviewProgress") {
+                @Override
+                public void setValue(RecentsView view, float overviewProgress) {
+                    view.setOverviewProgress(overviewProgress);
+                }
+
+                @Override
+                public Float get(RecentsView view) {
+                    return view.mOverviewProgress;
+                }
+            };
+
     // OverScroll constants
     private static final int OVERSCROLL_PAGE_SNAP_ANIMATION_DURATION = 270;
 
@@ -466,6 +487,7 @@
     protected float mTaskViewsSecondarySplitTranslation = 0;
     // Progress from 0 to 1 where 0 is a carousel and 1 is a 2 row grid.
     private float mGridProgress = 0;
+    private float mOverviewProgress = 0;
     private boolean mShowAsGridLastOnLayout = false;
     private final IntSet mTopRowIdSet = new IntSet();
 
@@ -2303,7 +2325,7 @@
         boolean runningTaskTileHidden = mRunningTaskTileHidden;
         setCurrentTask(runningTaskViewId);
         mFocusedTaskViewId = runningTaskViewId;
-        setCurrentPage(getRunningTaskIndex());
+        runOnPageScrollsInitialized(() -> setCurrentPage(getRunningTaskIndex()));
         setRunningTaskViewShowScreenshot(false);
         setRunningTaskHidden(runningTaskTileHidden);
         // Update task size after setting current task.
@@ -2661,6 +2683,18 @@
         mClearAllButton.setGridProgress(gridProgress);
     }
 
+    private void setOverviewProgress(float overviewProgress) {
+        int taskCount = getTaskViewCount();
+        if (taskCount == 0) {
+            return;
+        }
+
+        mOverviewProgress = overviewProgress;
+        for (int i = 0; i < taskCount; i++) {
+            requireTaskViewAt(i).setOverviewProgress(overviewProgress);
+        }
+    }
+
     private void enableLayoutTransitions() {
         if (mLayoutTransition == null) {
             mLayoutTransition = new LayoutTransition();
@@ -4014,7 +4048,8 @@
                 stagePosition);
         mSplitHiddenTaskViewIndex = indexOfChild(taskView);
         if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
-            finishRecentsAnimation(true, null);
+            finishRecentsAnimation(true /* toRecents */, false /* shouldPip */,
+                    null /* onFinishComplete */);
         }
     }
 
@@ -4300,6 +4335,7 @@
                         properties));
             }
         }
+        anim.play(ObjectAnimator.ofFloat(recentsView, OVERVIEW_PROGRESS, 1, 0));
         return anim;
     }
 
@@ -4359,6 +4395,8 @@
                     BACKGROUND_APP.getDepth(mActivity));
             anim.play(depthAnimator);
         }
+        anim.play(ObjectAnimator.ofFloat(this, OVERVIEW_PROGRESS, 1f, 0f));
+
         anim.play(progressAnim);
         anim.setInterpolator(interpolator);
 
@@ -4873,10 +4911,10 @@
     }
 
     private void updateEnabledOverlays() {
-        int overlayEnabledPage = mOverlayEnabled ? getNextPage() : -1;
         int taskCount = getTaskViewCount();
         for (int i = 0; i < taskCount; i++) {
-            requireTaskViewAt(i).setOverlayEnabled(i == overlayEnabledPage);
+            TaskView taskView = requireTaskViewAt(i);
+            taskView.setOverlayEnabled(mOverlayEnabled && isTaskViewFullyVisible(taskView));
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
index 1629bb7..727504a 100644
--- a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -36,12 +36,14 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.Shader;
+import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
 import android.util.Property;
 import android.view.Surface;
 import android.view.View;
+import android.widget.ImageView;
 
 import androidx.annotation.Nullable;
 import androidx.annotation.RequiresApi;
@@ -50,6 +52,7 @@
 import com.android.launcher3.BaseActivity;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.touch.PagedOrientationHandler;
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.SystemUiController;
 import com.android.launcher3.util.SystemUiController.SystemUiControllerFlags;
@@ -64,6 +67,7 @@
 public class TaskThumbnailView extends View {
     private static final MainThreadInitializedObject<FullscreenDrawParams> TEMP_PARAMS =
             new MainThreadInitializedObject<>(FullscreenDrawParams::new);
+    private static final float MAX_PCT_BEFORE_ASPECT_RATIOS_CONSIDERED_DIFFERENT = 0.1f;
 
     public static final Property<TaskThumbnailView, Float> DIM_ALPHA =
             new FloatProperty<TaskThumbnailView>("dimAlpha") {
@@ -83,6 +87,7 @@
     private TaskOverlay mOverlay;
     private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
     private final Paint mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+    private final Paint mSplashBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
     private final Paint mClearPaint = new Paint();
     private final Paint mDimmingPaintAfterClearing = new Paint();
     private final int mDimColor;
@@ -91,6 +96,8 @@
     private final Rect mPreviewRect = new Rect();
     private final PreviewPositionHelper mPreviewPositionHelper = new PreviewPositionHelper();
     private TaskView.FullscreenDrawParams mFullscreenParams;
+    private ImageView mSplashView;
+    private Drawable mSplashViewDrawable;
 
     @Nullable
     private Task mTask;
@@ -101,6 +108,8 @@
 
     /** How much this thumbnail is dimmed, 0 not dimmed at all, 1 totally dimmed. */
     private float mDimAlpha = 0f;
+    /** Controls visibility of the splash view, 0 is transparent, 255 fully opaque. */
+    private int mSplashAlpha = 0;
 
     private boolean mOverlayEnabled;
 
@@ -116,6 +125,7 @@
         super(context, attrs, defStyleAttr);
         mPaint.setFilterBitmap(true);
         mBackgroundPaint.setColor(Color.WHITE);
+        mSplashBackgroundPaint.setColor(Color.WHITE);
         mClearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
         mActivity = BaseActivity.fromContext(context);
         // Initialize with placeholder value. It is overridden later by TaskView
@@ -135,6 +145,8 @@
         int color = task == null ? Color.BLACK : task.colorBackground | 0xFF000000;
         mPaint.setColor(color);
         mBackgroundPaint.setColor(color);
+        mSplashBackgroundPaint.setColor(color);
+        updateSplashView(mTask.icon);
     }
 
     /**
@@ -152,6 +164,9 @@
         boolean thumbnailWasNull = mThumbnailData == null;
         mThumbnailData =
                 (thumbnailData != null && thumbnailData.thumbnail != null) ? thumbnailData : null;
+        if (mTask != null) {
+            updateSplashView(mTask.icon);
+        }
         if (refreshNow) {
             refresh(thumbnailWasNull && mThumbnailData != null);
         }
@@ -202,6 +217,18 @@
         updateThumbnailPaintFilter();
     }
 
+    /**
+     * Sets the alpha of the splash view.
+     */
+    public void setSplashAlpha(float splashAlpha) {
+        mSplashAlpha = (int) (splashAlpha * 255);
+        if (mSplashViewDrawable != null) {
+            mSplashViewDrawable.setAlpha(mSplashAlpha);
+        }
+        mSplashBackgroundPaint.setAlpha(mSplashAlpha);
+        invalidate();
+    }
+
     public TaskOverlay getTaskOverlay() {
         if (mOverlay == null) {
             mOverlay = getTaskView().getRecentsView().getTaskOverlayFactory().createOverlay(this);
@@ -238,13 +265,9 @@
         boundsToBitmapSpace.mapRect(boundsInBitmapSpace, viewRect);
 
         DeviceProfile dp = mActivity.getDeviceProfile();
-        int leftInset = TaskView.clipLeft(dp) ? Math.round(boundsInBitmapSpace.left) : 0;
-        int topInset = TaskView.clipTop(dp) ? Math.round(boundsInBitmapSpace.top) : 0;
-        int rightInset = TaskView.clipRight(dp) ? Math.round(
-                bitmapRect.right - boundsInBitmapSpace.right) : 0;
-        int bottomInset = TaskView.clipBottom(dp)
+        int bottomInset = dp.isTablet
                 ? Math.round(bitmapRect.bottom - boundsInBitmapSpace.bottom) : 0;
-        return Insets.of(leftInset, topInset, rightInset, bottomInset);
+        return Insets.of(0, 0, 0, bottomInset);
     }
 
 
@@ -264,6 +287,12 @@
     }
 
     @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        updateSplashView(mSplashViewDrawable);
+    }
+
+    @Override
     protected void onDraw(Canvas canvas) {
         RectF currentDrawnInsets = mFullscreenParams.mCurrentDrawnInsets;
         canvas.save();
@@ -313,6 +342,17 @@
         }
 
         canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius, mPaint);
+
+        // Draw splash above thumbnail to hide inconsistencies in rotation and aspect ratios.
+        if (shouldShowSplashView()) {
+            if (mSplashView != null) {
+                canvas.drawRoundRect(x, y, width + 1, height + 1, cornerRadius,
+                        cornerRadius, mSplashBackgroundPaint);
+
+                mSplashView.layout((int) x, (int) (y + 1), (int) width, (int) height - 1);
+                mSplashView.draw(canvas);
+            }
+        }
     }
 
     public TaskView getTaskView() {
@@ -328,6 +368,78 @@
     }
 
     /**
+     * Determine if the splash should be shown over top of the thumbnail.
+     *
+     * <p>We want to show the splash if the aspect ratio or rotation of the thumbnail would be
+     * different from the task.
+     */
+    boolean shouldShowSplashView() {
+        return isThumbnailAspectRatioDifferentFromThumbnailData()
+                || isThumbnailRotationDifferentFromTask();
+    }
+
+    private void updateSplashView(Drawable icon) {
+        if (icon == null || icon.getConstantState() == null) {
+            return;
+        }
+        mSplashViewDrawable = icon.getConstantState().newDrawable().mutate();
+        mSplashViewDrawable.setAlpha(mSplashAlpha);
+        ImageView imageView = mSplashView == null ? new ImageView(getContext()) : mSplashView;
+        imageView.setImageDrawable(mSplashViewDrawable);
+
+        imageView.setScaleType(ImageView.ScaleType.MATRIX);
+        Matrix matrix = new Matrix();
+
+        float drawableWidth = mSplashViewDrawable.getIntrinsicWidth();
+        float drawableHeight = mSplashViewDrawable.getIntrinsicHeight();
+        float viewWidth = getMeasuredWidth();
+        float viewCenterX = viewWidth / 2f;
+        float viewHeight = getMeasuredHeight();
+        float viewCenterY = viewHeight / 2f;
+        float centeredDrawableLeft = (viewWidth - drawableWidth) / 2f;
+        float centeredDrawableTop = (viewHeight - drawableHeight) / 2f;
+        float nonGridScale = getTaskView() == null ? 1 : 1 / getTaskView().getNonGridScale();
+        float recentsMaxScale = getTaskView() == null || getTaskView().getRecentsView() == null
+                ? 1 : 1 / getTaskView().getRecentsView().getMaxScaleForFullScreen();
+        float scale = nonGridScale * recentsMaxScale;
+
+        // Center the image in the view.
+        matrix.setTranslate(centeredDrawableLeft, centeredDrawableTop);
+        // Apply scale transformation after translation, pivoting around center of view.
+        matrix.postScale(scale, scale, viewCenterX, viewCenterY);
+
+        imageView.setImageMatrix(matrix);
+        mSplashView = imageView;
+    }
+
+    private boolean isThumbnailAspectRatioDifferentFromThumbnailData() {
+        if (mThumbnailData == null || mThumbnailData.thumbnail == null) {
+            return false;
+        }
+
+        float thumbnailViewAspect = getWidth() / (float) getHeight();
+        float thumbnailDataAspect =
+                mThumbnailData.thumbnail.getWidth() / (float) mThumbnailData.thumbnail.getHeight();
+
+        return Utilities.isRelativePercentDifferenceGreaterThan(thumbnailViewAspect,
+                thumbnailDataAspect, MAX_PCT_BEFORE_ASPECT_RATIOS_CONSIDERED_DIFFERENT);
+    }
+
+    private boolean isThumbnailRotationDifferentFromTask() {
+        RecentsView recents = getTaskView().getRecentsView();
+        if (recents == null || mThumbnailData == null) {
+            return false;
+        }
+
+        if (recents.getPagedOrientationHandler() == PagedOrientationHandler.PORTRAIT) {
+            int currentRotation = recents.getPagedViewOrientedState().getRecentsActivityRotation();
+            return (currentRotation - mThumbnailData.rotation) % 2 != 0;
+        } else {
+            return recents.getPagedOrientationHandler().getRotation() != mThumbnailData.rotation;
+        }
+    }
+
+    /**
      * Potentially re-init the task overlay. Be cautious when calling this as the overlay may
      * do processing on initialization.
      */
@@ -435,18 +547,9 @@
             int thumbnailRotation = thumbnailData.rotation;
             int deltaRotate = getRotationDelta(currentRotation, thumbnailRotation);
             RectF thumbnailClipHint = new RectF();
-            if (TaskView.clipLeft(dp)) {
-                thumbnailClipHint.left = thumbnailData.insets.left;
-            }
-            if (TaskView.clipRight(dp)) {
-                thumbnailClipHint.right = thumbnailData.insets.right;
-            }
-            if (TaskView.clipTop(dp)) {
-                thumbnailClipHint.top = thumbnailData.insets.top;
-            }
-            if (TaskView.clipBottom(dp)) {
-                thumbnailClipHint.bottom = thumbnailData.insets.bottom;
-            }
+            float canvasScreenRatio = canvasWidth / (float) dp.widthPx;
+            float scaledTaskbarSize = dp.taskbarSize * canvasScreenRatio;
+            thumbnailClipHint.bottom = dp.isTablet ? scaledTaskbarSize : 0;
 
             float scale = thumbnailData.scale;
             final float thumbnailScale;
@@ -476,8 +579,9 @@
                 float availableAspect = isRotated
                         ? availableHeight / availableWidth
                         : availableWidth / availableHeight;
-                boolean isAspectLargelyDifferent = Utilities.isRelativePercentDifferenceGreaterThan(
-                        canvasAspect, availableAspect, 0.1f);
+                boolean isAspectLargelyDifferent =
+                        Utilities.isRelativePercentDifferenceGreaterThan(canvasAspect,
+                                availableAspect, MAX_PCT_BEFORE_ASPECT_RATIOS_CONSIDERED_DIFFERENT);
                 if (isRotated && isAspectLargelyDifferent) {
                     // Do not rotate thumbnail if it would not improve fit
                     isRotated = false;
@@ -486,18 +590,10 @@
 
                 if (isAspectLargelyDifferent) {
                     // Crop letterbox insets if insets isn't already clipped
-                    if (!TaskView.clipLeft(dp)) {
-                        thumbnailClipHint.left = thumbnailData.letterboxInsets.left;
-                    }
-                    if (!TaskView.clipRight(dp)) {
-                        thumbnailClipHint.right = thumbnailData.letterboxInsets.right;
-                    }
-                    if (!TaskView.clipTop(dp)) {
-                        thumbnailClipHint.top = thumbnailData.letterboxInsets.top;
-                    }
-                    if (!TaskView.clipBottom(dp)) {
-                        thumbnailClipHint.bottom = thumbnailData.letterboxInsets.bottom;
-                    }
+                    thumbnailClipHint.left = thumbnailData.letterboxInsets.left;
+                    thumbnailClipHint.right = thumbnailData.letterboxInsets.right;
+                    thumbnailClipHint.top = thumbnailData.letterboxInsets.top;
+                    thumbnailClipHint.bottom = thumbnailData.letterboxInsets.bottom;
                     availableWidth = surfaceWidth
                             - (thumbnailClipHint.left + thumbnailClipHint.right);
                     availableHeight = surfaceHeight
@@ -560,33 +656,15 @@
                 thumbnailScale = targetW / (croppedWidth * scale);
             }
 
-            Rect splitScreenInsets = dp.getInsets();
             if (!isRotated) {
-                // No Rotation
-                mClippedInsets.offsetTo(thumbnailClipHint.left * scale,
-                        thumbnailClipHint.top * scale);
                 mMatrix.setTranslate(
                         -thumbnailClipHint.left * scale,
                         -thumbnailClipHint.top * scale);
             } else {
-                setThumbnailRotation(deltaRotate, thumbnailClipHint, scale, thumbnailBounds, dp);
+                setThumbnailRotation(deltaRotate, thumbnailBounds);
             }
 
-            final float widthWithInsets;
-            final float heightWithInsets;
-            if (isOrientationDifferent) {
-                widthWithInsets = thumbnailBounds.height() * thumbnailScale;
-                heightWithInsets = thumbnailBounds.width() * thumbnailScale;
-            } else {
-                widthWithInsets = thumbnailBounds.width() * thumbnailScale;
-                heightWithInsets = thumbnailBounds.height() * thumbnailScale;
-            }
-            mClippedInsets.left *= thumbnailScale;
-            mClippedInsets.top *= thumbnailScale;
-            mClippedInsets.right = Math.max(0,
-                    widthWithInsets - mClippedInsets.left - canvasWidth);
-            mClippedInsets.bottom = Math.max(0,
-                    heightWithInsets - mClippedInsets.top - canvasHeight);
+            mClippedInsets.set(0, 0, 0, scaledTaskbarSize);
 
             mMatrix.postScale(thumbnailScale, thumbnailScale);
             mIsOrientationChanged = isOrientationDifferent;
@@ -607,44 +685,32 @@
             return deltaRotation == Surface.ROTATION_90 || deltaRotation == Surface.ROTATION_270;
         }
 
-        private void setThumbnailRotation(int deltaRotate, RectF thumbnailInsets, float scale,
-                Rect thumbnailPosition, DeviceProfile dp) {
-            float newLeftInset = 0;
-            float newTopInset = 0;
+        private void setThumbnailRotation(int deltaRotate, Rect thumbnailPosition) {
             float translateX = 0;
             float translateY = 0;
 
             mMatrix.setRotate(90 * deltaRotate);
             switch (deltaRotate) { /* Counter-clockwise */
                 case Surface.ROTATION_90:
-                    newLeftInset = thumbnailInsets.bottom;
-                    newTopInset = thumbnailInsets.left;
                     translateX = thumbnailPosition.height();
                     break;
                 case Surface.ROTATION_270:
-                    newLeftInset = thumbnailInsets.top;
-                    newTopInset = thumbnailInsets.right;
                     translateY = thumbnailPosition.width();
                     break;
                 case Surface.ROTATION_180:
-                    newLeftInset = -thumbnailInsets.top;
-                    newTopInset = -thumbnailInsets.left;
                     translateX = thumbnailPosition.width();
                     translateY = thumbnailPosition.height();
                     break;
             }
-            mClippedInsets.offsetTo(newLeftInset * scale, newTopInset * scale);
             mMatrix.postTranslate(translateX, translateY);
-            if (TaskView.useFullThumbnail(dp)) {
-                mMatrix.postTranslate(-mClippedInsets.left, -mClippedInsets.top);
-            }
         }
 
         /**
          * Insets to used for clipping the thumbnail (in case it is drawing outside its own space)
          */
         public RectF getInsetsToDrawInFullscreen(DeviceProfile dp) {
-            return TaskView.useFullThumbnail(dp) ? mClippedInsets : EMPTY_RECT_F;
+            return dp.isTaskbarPresent && !dp.isTaskbarPresentInApps
+                    ? mClippedInsets : EMPTY_RECT_F;
         }
     }
 }
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 377467f..ded0ea6 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -18,6 +18,7 @@
 
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.widget.Toast.LENGTH_SHORT;
+import static android.window.SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR;
 
 import static com.android.launcher3.Utilities.comp;
 import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor;
@@ -87,7 +88,6 @@
 import com.android.quickstep.RecentsModel;
 import com.android.quickstep.RemoteAnimationTargets;
 import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
-import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.TaskIconCache;
 import com.android.quickstep.TaskOverlayFactory;
 import com.android.quickstep.TaskThumbnailCache;
@@ -137,41 +137,6 @@
     /** The maximum amount that a task view can be scrimmed, dimmed or tinted. */
     public static final float MAX_PAGE_SCRIM_ALPHA = 0.4f;
 
-    /**
-     * Should the TaskView display clip off the left inset in RecentsView.
-     */
-    public static boolean clipLeft(DeviceProfile deviceProfile) {
-        return false;
-    }
-
-    /**
-     * Should the TaskView display clip off the top inset in RecentsView.
-     */
-    public static boolean clipTop(DeviceProfile deviceProfile) {
-        return false;
-    }
-
-    /**
-     * Should the TaskView display clip off the right inset in RecentsView.
-     */
-    public static boolean clipRight(DeviceProfile deviceProfile) {
-        return false;
-    }
-
-    /**
-     * Should the TaskView display clip off the bottom inset in RecentsView.
-     */
-    public static boolean clipBottom(DeviceProfile deviceProfile) {
-        return deviceProfile.isTablet;
-    }
-
-    /**
-     * Should the TaskView scale down to fit whole thumbnail in fullscreen.
-     */
-    public static boolean useFullThumbnail(DeviceProfile deviceProfile) {
-        return deviceProfile.isTablet && !deviceProfile.isTaskbarPresentInApps;
-    }
-
     private static final float EDGE_SCALE_DOWN_FACTOR_CAROUSEL = 0.03f;
     private static final float EDGE_SCALE_DOWN_FACTOR_GRID = 0.00f;
 
@@ -369,6 +334,7 @@
     protected final DigitalWellBeingToast mDigitalWellBeingToast;
     private float mFullscreenProgress;
     private float mGridProgress;
+    protected float mOverviewProgress;
     private float mNonGridScale = 1;
     private float mDismissScale = 1;
     protected final FullscreenDrawParams mCurrentFullscreenParams;
@@ -428,7 +394,6 @@
 
     private boolean mIsClickableAsLiveTile = true;
 
-
     public TaskView(Context context) {
         this(context, null);
     }
@@ -696,6 +661,9 @@
             if (freezeTaskList) {
                 ActivityOptionsCompat.setFreezeRecentTasksList(opts);
             }
+            // TODO(b/202826469): Replace setSplashScreenStyle with setDisableStartingWindow.
+            opts.setSplashScreenStyle(mSnapshotView.shouldShowSplashView()
+                    ? SPLASH_SCREEN_STYLE_SOLID_COLOR : opts.getSplashScreenStyle());
             Task.TaskKey key = mTask.key;
             UI_HELPER_EXECUTOR.execute(() -> {
                 if (!ActivityManagerWrapper.getInstance().startActivityFromRecents(key, opts)) {
@@ -1096,6 +1064,21 @@
         return scale;
     }
 
+    /**
+     * Updates progress of task view for entering/exiting overview on swipe up/down.
+     *
+     * <p>Updates the alpha of any splash screen over the thumbnail if it exists.
+     */
+    public void setOverviewProgress(float overviewProgress) {
+        mOverviewProgress = overviewProgress;
+        applyThumbnailSplashAlpha();
+    }
+
+    protected void applyThumbnailSplashAlpha() {
+        mSnapshotView.setSplashAlpha(
+                Utilities.mapToRange(mOverviewProgress, 0f, 1f, 1f, 0f, LINEAR));
+    }
+
     private void setSplitSelectTranslationX(float x) {
         mSplitSelectTranslationX = x;
         applyTranslationX();
@@ -1577,13 +1560,11 @@
             RectF insets = pph.getInsetsToDrawInFullscreen(dp);
 
             float currentInsetsLeft = insets.left * fullscreenProgress;
+            float currentInsetsTop = insets.top * fullscreenProgress;
             float currentInsetsRight = insets.right * fullscreenProgress;
-            float insetsBottom = insets.bottom;
-            if (dp.isTaskbarPresentInApps) {
-                insetsBottom = Math.max(0, insetsBottom - dp.taskbarSize);
-            }
-            mCurrentDrawnInsets.set(currentInsetsLeft, insets.top * fullscreenProgress,
-                    currentInsetsRight, insetsBottom * fullscreenProgress);
+            float currentInsetsBottom = insets.bottom * fullscreenProgress;
+            mCurrentDrawnInsets.set(
+                    currentInsetsLeft, currentInsetsTop, currentInsetsRight, currentInsetsBottom);
 
             mCurrentDrawnCornerRadius =
                     Utilities.mapRange(fullscreenProgress, mCornerRadius, mWindowCornerRadius)
diff --git a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java
index d8be307..4eec319 100644
--- a/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java
+++ b/quickstep/tests/src/com/android/launcher3/taskbar/TaskbarNavButtonControllerTest.java
@@ -23,6 +23,7 @@
 import static org.mockito.Mockito.when;
 
 import android.os.Handler;
+import android.view.View;
 
 import androidx.test.runner.AndroidJUnit4;
 
@@ -58,6 +59,8 @@
     TaskbarControllers mockTaskbarControllers;
     @Mock
     TaskbarActivityContext mockTaskbarActivityContext;
+    @Mock
+    View mockView;
 
     private TaskbarNavButtonController mNavButtonController;
 
@@ -76,110 +79,110 @@
 
     @Test
     public void testPressBack() {
-        mNavButtonController.onButtonClick(BUTTON_BACK);
+        mNavButtonController.onButtonClick(BUTTON_BACK, mockView);
         verify(mockSystemUiProxy, times(1)).onBackPressed();
     }
 
     @Test
     public void testPressImeSwitcher() {
-        mNavButtonController.onButtonClick(BUTTON_IME_SWITCH);
+        mNavButtonController.onButtonClick(BUTTON_IME_SWITCH, mockView);
         verify(mockSystemUiProxy, times(1)).onImeSwitcherPressed();
     }
 
     @Test
     public void testPressA11yShortClick() {
-        mNavButtonController.onButtonClick(BUTTON_A11Y);
+        mNavButtonController.onButtonClick(BUTTON_A11Y, mockView);
         verify(mockSystemUiProxy, times(1))
                 .notifyAccessibilityButtonClicked(DISPLAY_ID);
     }
 
     @Test
     public void testPressA11yLongClick() {
-        mNavButtonController.onButtonLongClick(BUTTON_A11Y);
+        mNavButtonController.onButtonLongClick(BUTTON_A11Y, mockView);
         verify(mockSystemUiProxy, times(1)).notifyAccessibilityButtonLongClicked();
     }
 
     @Test
     public void testLongPressHome() {
-        mNavButtonController.onButtonLongClick(BUTTON_HOME);
+        mNavButtonController.onButtonLongClick(BUTTON_HOME, mockView);
         verify(mockSystemUiProxy, times(1)).startAssistant(any());
     }
 
     @Test
     public void testPressHome() {
-        mNavButtonController.onButtonClick(BUTTON_HOME);
+        mNavButtonController.onButtonClick(BUTTON_HOME, mockView);
         verify(mockCommandHelper, times(1)).addCommand(TYPE_HOME);
     }
 
     @Test
     public void testPressRecents() {
-        mNavButtonController.onButtonClick(BUTTON_RECENTS);
+        mNavButtonController.onButtonClick(BUTTON_RECENTS, mockView);
         verify(mockCommandHelper, times(1)).addCommand(TYPE_TOGGLE);
     }
 
     @Test
     public void testPressRecentsWithScreenPinned() {
         mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING);
-        mNavButtonController.onButtonClick(BUTTON_RECENTS);
+        mNavButtonController.onButtonClick(BUTTON_RECENTS, mockView);
         verify(mockCommandHelper, times(0)).addCommand(TYPE_TOGGLE);
     }
 
     @Test
     public void testLongPressBackRecentsNotPinned() {
-        mNavButtonController.onButtonLongClick(BUTTON_RECENTS);
-        mNavButtonController.onButtonLongClick(BUTTON_BACK);
+        mNavButtonController.onButtonLongClick(BUTTON_RECENTS, mockView);
+        mNavButtonController.onButtonLongClick(BUTTON_BACK, mockView);
         verify(mockSystemUiProxy, times(0)).stopScreenPinning();
     }
 
     @Test
     public void testLongPressBackRecentsPinned() {
         mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING);
-        mNavButtonController.onButtonLongClick(BUTTON_RECENTS);
-        mNavButtonController.onButtonLongClick(BUTTON_BACK);
+        mNavButtonController.onButtonLongClick(BUTTON_RECENTS, mockView);
+        mNavButtonController.onButtonLongClick(BUTTON_BACK, mockView);
         verify(mockSystemUiProxy, times(1)).stopScreenPinning();
     }
 
     @Test
     public void testLongPressBackRecentsTooLongPinned() {
         mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING);
-        mNavButtonController.onButtonLongClick(BUTTON_RECENTS);
+        mNavButtonController.onButtonLongClick(BUTTON_RECENTS, mockView);
         try {
             Thread.sleep(SCREEN_PIN_LONG_PRESS_THRESHOLD + 5);
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
-        mNavButtonController.onButtonLongClick(BUTTON_BACK);
+        mNavButtonController.onButtonLongClick(BUTTON_BACK, mockView);
         verify(mockSystemUiProxy, times(0)).stopScreenPinning();
     }
 
     @Test
     public void testLongPressBackRecentsMultipleAttemptPinned() {
         mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING);
-        mNavButtonController.onButtonLongClick(BUTTON_RECENTS);
+        mNavButtonController.onButtonLongClick(BUTTON_RECENTS, mockView);
         try {
             Thread.sleep(SCREEN_PIN_LONG_PRESS_THRESHOLD + 5);
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
-        mNavButtonController.onButtonLongClick(BUTTON_BACK);
+        mNavButtonController.onButtonLongClick(BUTTON_BACK, mockView);
         verify(mockSystemUiProxy, times(0)).stopScreenPinning();
 
         // Try again w/in threshold
-        mNavButtonController.onButtonLongClick(BUTTON_RECENTS);
-        mNavButtonController.onButtonLongClick(BUTTON_BACK);
+        mNavButtonController.onButtonLongClick(BUTTON_RECENTS, mockView);
+        mNavButtonController.onButtonLongClick(BUTTON_BACK, mockView);
         verify(mockSystemUiProxy, times(1)).stopScreenPinning();
     }
 
     @Test
     public void testLongPressHomeScreenPinned() {
         mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING);
-        mNavButtonController.onButtonLongClick(BUTTON_HOME);
+        mNavButtonController.onButtonLongClick(BUTTON_HOME, mockView);
         verify(mockSystemUiProxy, times(0)).startAssistant(any());
     }
 
     @Test
     public void testNoCallsToNullLogger() {
-        mNavButtonController.onButtonClick(BUTTON_HOME);
+        mNavButtonController.onButtonClick(BUTTON_HOME, mockView);
         verify(mockStatsLogManager, times(0)).logger();
         verify(mockStatsLogger, times(0)).log(any());
     }
@@ -187,9 +190,9 @@
     @Test
     public void testNoCallsAfterNullingOut() {
         mNavButtonController.init(mockTaskbarControllers);
-        mNavButtonController.onButtonClick(BUTTON_HOME);
+        mNavButtonController.onButtonClick(BUTTON_HOME, mockView);
         mNavButtonController.onDestroy();
-        mNavButtonController.onButtonClick(BUTTON_HOME);
+        mNavButtonController.onButtonClick(BUTTON_HOME, mockView);
         verify(mockStatsLogger, times(1)).log(LAUNCHER_TASKBAR_HOME_BUTTON_TAP);
         verify(mockStatsLogger, times(0)).log(LAUNCHER_TASKBAR_HOME_BUTTON_LONGPRESS);
     }
@@ -197,7 +200,7 @@
     @Test
     public void testLogOnTap() {
         mNavButtonController.init(mockTaskbarControllers);
-        mNavButtonController.onButtonClick(BUTTON_HOME);
+        mNavButtonController.onButtonClick(BUTTON_HOME, mockView);
         verify(mockStatsLogger, times(1)).log(LAUNCHER_TASKBAR_HOME_BUTTON_TAP);
         verify(mockStatsLogger, times(0)).log(LAUNCHER_TASKBAR_HOME_BUTTON_LONGPRESS);
     }
@@ -205,7 +208,7 @@
     @Test
     public void testLogOnLongpress() {
         mNavButtonController.init(mockTaskbarControllers);
-        mNavButtonController.onButtonLongClick(BUTTON_HOME);
+        mNavButtonController.onButtonLongClick(BUTTON_HOME, mockView);
         verify(mockStatsLogger, times(1)).log(LAUNCHER_TASKBAR_HOME_BUTTON_LONGPRESS);
         verify(mockStatsLogger, times(0)).log(LAUNCHER_TASKBAR_HOME_BUTTON_TAP);
     }
@@ -213,11 +216,11 @@
     @Test
     public void testBackOverviewLogOnLongpress() {
         mNavButtonController.init(mockTaskbarControllers);
-        mNavButtonController.onButtonLongClick(BUTTON_RECENTS);
+        mNavButtonController.onButtonLongClick(BUTTON_RECENTS, mockView);
         verify(mockStatsLogger, times(1)).log(LAUNCHER_TASKBAR_OVERVIEW_BUTTON_LONGPRESS);
         verify(mockStatsLogger, times(0)).log(LAUNCHER_TASKBAR_OVERVIEW_BUTTON_TAP);
 
-        mNavButtonController.onButtonLongClick(BUTTON_BACK);
+        mNavButtonController.onButtonLongClick(BUTTON_BACK, mockView);
         verify(mockStatsLogger, times(1)).log(LAUNCHER_TASKBAR_BACK_BUTTON_LONGPRESS);
         verify(mockStatsLogger, times(0)).log(LAUNCHER_TASKBAR_BACK_BUTTON_TAP);
     }
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfilePhone3ButtonTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfilePhone3ButtonTest.kt
new file mode 100644
index 0000000..3daf81d
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/DeviceProfilePhone3ButtonTest.kt
@@ -0,0 +1,486 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.DeviceProfileBaseTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Tests for DeviceProfile for phone with 3-Button navigation.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceProfilePhone3ButtonTest : DeviceProfileBaseTest() {
+
+    lateinit var dp: DeviceProfile
+
+    @Before
+    fun before() {
+        initializeVarsForPhone(isGestureMode = false)
+        dp = newDP()
+    }
+
+    @Test
+    fun isScalableGrid() {
+        assertThat(dp.isScalableGrid).isTrue()
+    }
+
+    @Test
+    fun cellWidthPx() {
+        assertThat(dp.cellWidthPx).isEqualTo(265)
+    }
+
+    @Test
+    fun cellHeightPx() {
+        assertThat(dp.cellHeightPx).isEqualTo(343)
+    }
+
+    @Test
+    fun getCellSizeX() {
+        assertThat(dp.getCellSize().x).isEqualTo(265)
+    }
+
+    @Test
+    fun getCellSizeY() {
+        assertThat(dp.getCellSize().y).isEqualTo(552)
+    }
+
+    @Test
+    fun cellLayoutBorderSpacePxX() {
+        assertThat(dp.cellLayoutBorderSpacePx.x).isEqualTo(66)
+    }
+
+    @Test
+    fun cellLayoutBorderSpacePxY() {
+        assertThat(dp.cellLayoutBorderSpacePx.y).isEqualTo(66)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxLeft() {
+        assertThat(dp.cellLayoutPaddingPx.left).isEqualTo(38)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxTop() {
+        assertThat(dp.cellLayoutPaddingPx.top).isEqualTo(0)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxRight() {
+        assertThat(dp.cellLayoutPaddingPx.right).isEqualTo(38)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxBottom() {
+        assertThat(dp.cellLayoutPaddingPx.bottom).isEqualTo(38)
+    }
+
+    @Test
+    fun iconSizePx() {
+        assertThat(dp.iconSizePx).isEqualTo(196)
+    }
+
+    @Test
+    fun iconTextSizePx() {
+        assertThat(dp.iconTextSizePx).isEqualTo(49)
+    }
+
+    @Test
+    fun iconDrawablePaddingPx() {
+        assertThat(dp.iconDrawablePaddingPx).isEqualTo(25)
+    }
+
+    @Test
+    fun folderCellWidthPx() {
+        assertThat(dp.folderCellWidthPx).isEqualTo(265)
+    }
+
+    @Test
+    fun folderCellHeightPx() {
+        assertThat(dp.folderCellHeightPx).isEqualTo(343)
+    }
+
+    @Test
+    fun folderChildIconSizePx() {
+        assertThat(dp.folderChildIconSizePx).isEqualTo(196)
+    }
+
+    @Test
+    fun folderChildTextSizePx() {
+        assertThat(dp.folderChildTextSizePx).isEqualTo(49)
+    }
+
+    @Test
+    fun folderChildDrawablePaddingPx() {
+        assertThat(dp.folderChildDrawablePaddingPx).isEqualTo(27)
+    }
+
+    @Test
+    fun folderCellLayoutBorderSpaceOriginalPx() {
+        assertThat(dp.folderCellLayoutBorderSpaceOriginalPx).isEqualTo(0)
+    }
+
+    @Test
+    fun folderCellLayoutBorderSpacePxX() {
+        assertThat(dp.folderCellLayoutBorderSpacePx.x).isEqualTo(0)
+    }
+
+    @Test
+    fun folderCellLayoutBorderSpacePxY() {
+        assertThat(dp.folderCellLayoutBorderSpacePx.y).isEqualTo(0)
+    }
+
+    @Test
+    fun bottomSheetTopPadding() {
+        assertThat(dp.bottomSheetTopPadding).isEqualTo(38)
+    }
+
+    @Test
+    fun allAppsShiftRange() {
+        assertThat(dp.allAppsShiftRange).isEqualTo(1050)
+    }
+
+    @Test
+    fun allAppsTopPadding() {
+        assertThat(dp.allAppsTopPadding).isEqualTo(0)
+    }
+
+    @Test
+    fun allAppsIconSizePx() {
+        assertThat(dp.allAppsIconSizePx).isEqualTo(232)
+    }
+
+    @Test
+    fun allAppsIconTextSizePx() {
+        assertThat(dp.allAppsIconTextSizePx).isEqualTo(58)
+    }
+
+    @Test
+    fun allAppsIconDrawablePaddingPx() {
+        assertThat(dp.allAppsIconDrawablePaddingPx).isEqualTo(25)
+    }
+
+    @Test
+    fun allAppsCellHeightPx() {
+        assertThat(dp.allAppsCellHeightPx).isEqualTo(409)
+    }
+
+    @Test
+    fun allAppsCellWidthPx() {
+        assertThat(dp.allAppsCellWidthPx).isEqualTo(265)
+    }
+
+    @Test
+    fun allAppsBorderSpacePxX() {
+        assertThat(dp.allAppsBorderSpacePx.x).isEqualTo(66)
+    }
+
+    @Test
+    fun allAppsBorderSpacePxY() {
+        assertThat(dp.allAppsBorderSpacePx.y).isEqualTo(66)
+    }
+
+    @Test
+    fun numShownAllAppsColumns() {
+        assertThat(dp.numShownAllAppsColumns).isEqualTo(0)
+    }
+
+    @Test
+    fun allAppsLeftRightPadding() {
+        assertThat(dp.allAppsLeftRightPadding).isEqualTo(91)
+    }
+
+    @Test
+    fun allAppsLeftRightMargin() {
+        assertThat(dp.allAppsLeftRightMargin).isEqualTo(0)
+    }
+
+    @Test
+    fun hotseatBarSizePx() {
+        assertThat(dp.hotseatBarSizePx).isEqualTo(669)
+    }
+
+    @Test
+    fun hotseatCellHeightPx() {
+        assertThat(dp.hotseatCellHeightPx).isEqualTo(221)
+    }
+
+    @Test
+    fun hotseatBarBottomPaddingPx() {
+        assertThat(dp.hotseatBarBottomSpacePx).isEqualTo(168)
+    }
+
+    @Test
+    fun hotseatBarSidePaddingStartPx() {
+        assertThat(dp.hotseatBarSidePaddingStartPx).isEqualTo(0)
+    }
+
+    @Test
+    fun hotseatBarSidePaddingEndPx() {
+        assertThat(dp.hotseatBarSidePaddingEndPx).isEqualTo(0)
+    }
+
+    @Test
+    fun hotseatQsbSpace() {
+        assertThat(dp.hotseatQsbSpace).isEqualTo(126)
+    }
+
+    @Test
+    fun hotseatQsbHeight() {
+        assertThat(dp.hotseatQsbHeight).isEqualTo(221)
+    }
+
+    @Test
+    fun springLoadedHotseatBarTopMarginPx() {
+        assertThat(dp.springLoadedHotseatBarTopMarginPx).isEqualTo(266)
+    }
+
+    @Test
+    fun numShownHotseatIcons() {
+        assertThat(dp.numShownHotseatIcons).isEqualTo(4)
+    }
+
+    @Test
+    fun hotseatBorderSpace() {
+        assertThat(dp.hotseatBorderSpace).isEqualTo(135)
+    }
+
+    @Test
+    fun isQsbInline() {
+        assertThat(dp.isQsbInline).isEqualTo(false)
+    }
+
+    @Test
+    fun qsbWidth() {
+        assertThat(dp.qsbWidth).isEqualTo(1189)
+    }
+
+    @Test
+    fun isTaskbarPresent() {
+        assertThat(dp.isTaskbarPresent).isEqualTo(false)
+    }
+
+    @Test
+    fun isTaskbarPresentInApps() {
+        assertThat(dp.isTaskbarPresentInApps).isEqualTo(false)
+    }
+
+    @Test
+    fun taskbarSize() {
+        assertThat(dp.taskbarSize).isEqualTo(0)
+    }
+
+    @Test
+    fun desiredWorkspaceHorizontalMarginPx() {
+        assertThat(dp.desiredWorkspaceHorizontalMarginPx).isEqualTo(91)
+    }
+
+    @Test
+    fun workspacePaddingLeft() {
+        assertThat(dp.workspacePadding.left).isEqualTo(53)
+    }
+
+    @Test
+    fun workspacePaddingTop() {
+        assertThat(dp.workspacePadding.top).isEqualTo(0)
+    }
+
+    @Test
+    fun workspacePaddingRight() {
+        assertThat(dp.workspacePadding.right).isEqualTo(53)
+    }
+
+    @Test
+    fun workspacePaddingBottom() {
+        assertThat(dp.workspacePadding.bottom).isEqualTo(573)
+    }
+
+    @Test
+    fun iconScale() {
+        assertThat(dp.iconScale).isEqualTo(1)
+    }
+
+    @Test
+    fun cellScaleToFit() {
+        assertThat(dp.cellScaleToFit).isEqualTo(1.182266f)
+    }
+
+    @Test
+    fun workspaceTopPadding() {
+        assertThat(dp.workspaceTopPadding).isEqualTo(0)
+    }
+
+    @Test
+    fun workspaceBottomPadding() {
+        assertThat(dp.workspaceBottomPadding).isEqualTo(0)
+    }
+
+    @Test
+    fun overviewTaskMarginPx() {
+        assertThat(dp.overviewTaskMarginPx).isEqualTo(56)
+    }
+
+    @Test
+    fun overviewTaskMarginGridPx() {
+        assertThat(dp.overviewTaskMarginGridPx).isEqualTo(0)
+    }
+
+    @Test
+    fun overviewTaskIconSizePx() {
+        assertThat(dp.overviewTaskIconSizePx).isEqualTo(168)
+    }
+
+    @Test
+    fun overviewTaskIconDrawableSizePx() {
+        assertThat(dp.overviewTaskIconDrawableSizePx).isEqualTo(154)
+    }
+
+    @Test
+    fun overviewTaskIconDrawableSizeGridPx() {
+        assertThat(dp.overviewTaskIconDrawableSizeGridPx).isEqualTo(0)
+    }
+
+    @Test
+    fun overviewTaskThumbnailTopMarginPx() {
+        assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(280)
+    }
+
+    @Test
+    fun overviewActionsTopMarginPx() {
+        assertThat(dp.overviewActionsTopMarginPx).isEqualTo(84)
+    }
+
+    @Test
+    fun overviewActionsHeight() {
+        assertThat(dp.overviewActionsHeight).isEqualTo(168)
+    }
+
+    @Test
+    fun overviewActionsButtonSpacing() {
+        assertThat(dp.overviewActionsButtonSpacing).isEqualTo(126)
+    }
+
+    @Test
+    fun overviewPageSpacing() {
+        assertThat(dp.overviewPageSpacing).isEqualTo(56)
+    }
+
+    @Test
+    fun overviewRowSpacing() {
+        assertThat(dp.overviewRowSpacing).isEqualTo(-112)
+    }
+
+    @Test
+    fun overviewGridSideMargin() {
+        assertThat(dp.overviewGridSideMargin).isEqualTo(0)
+    }
+
+    @Test
+    fun dropTargetBarTopMarginPx() {
+        assertThat(dp.dropTargetBarTopMarginPx).isEqualTo(112)
+    }
+
+    @Test
+    fun dropTargetBarSizePx() {
+        assertThat(dp.dropTargetBarSizePx).isEqualTo(196)
+    }
+
+    @Test
+    fun dropTargetBarBottomMarginPx() {
+        assertThat(dp.dropTargetBarBottomMarginPx).isEqualTo(56)
+    }
+
+    @Test
+    fun workspaceSpringLoadedMinNextPageVisiblePx() {
+        assertThat(dp.workspaceSpringLoadedMinNextPageVisiblePx).isEqualTo(84)
+    }
+
+    @Test
+    fun getWorkspaceSpringLoadScale() {
+        assertThat(dp.workspaceSpringLoadScale).isEqualTo(0.74417657f)
+    }
+
+    @Test
+    fun getCellLayoutHeight() {
+        assertThat(dp.cellLayoutHeight).isEqualTo(2447)
+    }
+
+    @Test
+    fun getCellLayoutWidth() {
+        assertThat(dp.cellLayoutWidth).isEqualTo(1334)
+    }
+
+    @Test
+    fun getPanelCount() {
+        assertThat(dp.panelCount).isEqualTo(1)
+    }
+
+    @Test
+    fun isVerticalBarLayout() {
+        assertThat(dp.isVerticalBarLayout).isEqualTo(false)
+    }
+
+    @Test
+    fun getCellLayoutSpringLoadShrunkTop() {
+        assertThat(dp.cellLayoutSpringLoadShrunkTop).isEqualTo(364)
+    }
+
+    @Test
+    fun getCellLayoutSpringLoadShrunkBottom() {
+        assertThat(dp.cellLayoutSpringLoadShrunkBottom).isEqualTo(2185)
+    }
+
+    @Test
+    fun getQsbOffsetY() {
+        assertThat(dp.qsbOffsetY).isEqualTo(147)
+    }
+
+    @Test
+    fun getTaskbarOffsetY() {
+        assertThat(dp.taskbarOffsetY).isEqualTo(558)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingLeft() {
+        assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(125)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingTop() {
+        assertThat(dp.getHotseatLayoutPadding(context).top).isEqualTo(0)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingRight() {
+        assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(125)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingBottom() {
+        assertThat(dp.getHotseatLayoutPadding(context).bottom).isEqualTo(448)
+    }
+
+    @Test
+    fun hotseatBarEndOffset() {
+        assertThat(dp.hotseatBarEndOffset).isEqualTo(0)
+    }
+}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfilePhoneTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfilePhoneTest.kt
new file mode 100644
index 0000000..e588c71
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/DeviceProfilePhoneTest.kt
@@ -0,0 +1,486 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.DeviceProfileBaseTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Tests for DeviceProfile for phone.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceProfilePhoneTest : DeviceProfileBaseTest() {
+
+    lateinit var dp: DeviceProfile
+
+    @Before
+    fun before() {
+        initializeVarsForPhone()
+        dp = newDP()
+    }
+
+    @Test
+    fun isScalableGrid() {
+        assertThat(dp.isScalableGrid).isTrue()
+    }
+
+    @Test
+    fun cellWidthPx() {
+        assertThat(dp.cellWidthPx).isEqualTo(265)
+    }
+
+    @Test
+    fun cellHeightPx() {
+        assertThat(dp.cellHeightPx).isEqualTo(343)
+    }
+
+    @Test
+    fun getCellSizeX() {
+        assertThat(dp.getCellSize().x).isEqualTo(265)
+    }
+
+    @Test
+    fun getCellSizeY() {
+        assertThat(dp.getCellSize().y).isEqualTo(552)
+    }
+
+    @Test
+    fun cellLayoutBorderSpacePxX() {
+        assertThat(dp.cellLayoutBorderSpacePx.x).isEqualTo(66)
+    }
+
+    @Test
+    fun cellLayoutBorderSpacePxY() {
+        assertThat(dp.cellLayoutBorderSpacePx.y).isEqualTo(66)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxLeft() {
+        assertThat(dp.cellLayoutPaddingPx.left).isEqualTo(38)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxTop() {
+        assertThat(dp.cellLayoutPaddingPx.top).isEqualTo(0)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxRight() {
+        assertThat(dp.cellLayoutPaddingPx.right).isEqualTo(38)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxBottom() {
+        assertThat(dp.cellLayoutPaddingPx.bottom).isEqualTo(38)
+    }
+
+    @Test
+    fun iconSizePx() {
+        assertThat(dp.iconSizePx).isEqualTo(196)
+    }
+
+    @Test
+    fun iconTextSizePx() {
+        assertThat(dp.iconTextSizePx).isEqualTo(49)
+    }
+
+    @Test
+    fun iconDrawablePaddingPx() {
+        assertThat(dp.iconDrawablePaddingPx).isEqualTo(25)
+    }
+
+    @Test
+    fun folderCellWidthPx() {
+        assertThat(dp.folderCellWidthPx).isEqualTo(265)
+    }
+
+    @Test
+    fun folderCellHeightPx() {
+        assertThat(dp.folderCellHeightPx).isEqualTo(343)
+    }
+
+    @Test
+    fun folderChildIconSizePx() {
+        assertThat(dp.folderChildIconSizePx).isEqualTo(196)
+    }
+
+    @Test
+    fun folderChildTextSizePx() {
+        assertThat(dp.folderChildTextSizePx).isEqualTo(49)
+    }
+
+    @Test
+    fun folderChildDrawablePaddingPx() {
+        assertThat(dp.folderChildDrawablePaddingPx).isEqualTo(27)
+    }
+
+    @Test
+    fun folderCellLayoutBorderSpaceOriginalPx() {
+        assertThat(dp.folderCellLayoutBorderSpaceOriginalPx).isEqualTo(0)
+    }
+
+    @Test
+    fun folderCellLayoutBorderSpacePxX() {
+        assertThat(dp.folderCellLayoutBorderSpacePx.x).isEqualTo(0)
+    }
+
+    @Test
+    fun folderCellLayoutBorderSpacePxY() {
+        assertThat(dp.folderCellLayoutBorderSpacePx.y).isEqualTo(0)
+    }
+
+    @Test
+    fun bottomSheetTopPadding() {
+        assertThat(dp.bottomSheetTopPadding).isEqualTo(38)
+    }
+
+    @Test
+    fun allAppsShiftRange() {
+        assertThat(dp.allAppsShiftRange).isEqualTo(1050)
+    }
+
+    @Test
+    fun allAppsTopPadding() {
+        assertThat(dp.allAppsTopPadding).isEqualTo(0)
+    }
+
+    @Test
+    fun allAppsIconSizePx() {
+        assertThat(dp.allAppsIconSizePx).isEqualTo(232)
+    }
+
+    @Test
+    fun allAppsIconTextSizePx() {
+        assertThat(dp.allAppsIconTextSizePx).isEqualTo(58)
+    }
+
+    @Test
+    fun allAppsIconDrawablePaddingPx() {
+        assertThat(dp.allAppsIconDrawablePaddingPx).isEqualTo(25)
+    }
+
+    @Test
+    fun allAppsCellHeightPx() {
+        assertThat(dp.allAppsCellHeightPx).isEqualTo(409)
+    }
+
+    @Test
+    fun allAppsCellWidthPx() {
+        assertThat(dp.allAppsCellWidthPx).isEqualTo(265)
+    }
+
+    @Test
+    fun allAppsBorderSpacePxX() {
+        assertThat(dp.allAppsBorderSpacePx.x).isEqualTo(66)
+    }
+
+    @Test
+    fun allAppsBorderSpacePxY() {
+        assertThat(dp.allAppsBorderSpacePx.y).isEqualTo(66)
+    }
+
+    @Test
+    fun numShownAllAppsColumns() {
+        assertThat(dp.numShownAllAppsColumns).isEqualTo(0)
+    }
+
+    @Test
+    fun allAppsLeftRightPadding() {
+        assertThat(dp.allAppsLeftRightPadding).isEqualTo(91)
+    }
+
+    @Test
+    fun allAppsLeftRightMargin() {
+        assertThat(dp.allAppsLeftRightMargin).isEqualTo(0)
+    }
+
+    @Test
+    fun hotseatBarSizePx() {
+        assertThat(dp.hotseatBarSizePx).isEqualTo(669)
+    }
+
+    @Test
+    fun hotseatCellHeightPx() {
+        assertThat(dp.hotseatCellHeightPx).isEqualTo(221)
+    }
+
+    @Test
+    fun hotseatBarBottomPaddingPx() {
+        assertThat(dp.hotseatBarBottomSpacePx).isEqualTo(168)
+    }
+
+    @Test
+    fun hotseatBarSidePaddingStartPx() {
+        assertThat(dp.hotseatBarSidePaddingStartPx).isEqualTo(0)
+    }
+
+    @Test
+    fun hotseatBarSidePaddingEndPx() {
+        assertThat(dp.hotseatBarSidePaddingEndPx).isEqualTo(0)
+    }
+
+    @Test
+    fun hotseatQsbSpace() {
+        assertThat(dp.hotseatQsbSpace).isEqualTo(126)
+    }
+
+    @Test
+    fun hotseatQsbHeight() {
+        assertThat(dp.hotseatQsbHeight).isEqualTo(221)
+    }
+
+    @Test
+    fun springLoadedHotseatBarTopMarginPx() {
+        assertThat(dp.springLoadedHotseatBarTopMarginPx).isEqualTo(266)
+    }
+
+    @Test
+    fun numShownHotseatIcons() {
+        assertThat(dp.numShownHotseatIcons).isEqualTo(4)
+    }
+
+    @Test
+    fun hotseatBorderSpace() {
+        assertThat(dp.hotseatBorderSpace).isEqualTo(135)
+    }
+
+    @Test
+    fun isQsbInline() {
+        assertThat(dp.isQsbInline).isEqualTo(false)
+    }
+
+    @Test
+    fun qsbWidth() {
+        assertThat(dp.qsbWidth).isEqualTo(1189)
+    }
+
+    @Test
+    fun isTaskbarPresent() {
+        assertThat(dp.isTaskbarPresent).isEqualTo(false)
+    }
+
+    @Test
+    fun isTaskbarPresentInApps() {
+        assertThat(dp.isTaskbarPresentInApps).isEqualTo(false)
+    }
+
+    @Test
+    fun taskbarSize() {
+        assertThat(dp.taskbarSize).isEqualTo(0)
+    }
+
+    @Test
+    fun desiredWorkspaceHorizontalMarginPx() {
+        assertThat(dp.desiredWorkspaceHorizontalMarginPx).isEqualTo(91)
+    }
+
+    @Test
+    fun workspacePaddingLeft() {
+        assertThat(dp.workspacePadding.left).isEqualTo(53)
+    }
+
+    @Test
+    fun workspacePaddingTop() {
+        assertThat(dp.workspacePadding.top).isEqualTo(0)
+    }
+
+    @Test
+    fun workspacePaddingRight() {
+        assertThat(dp.workspacePadding.right).isEqualTo(53)
+    }
+
+    @Test
+    fun workspacePaddingBottom() {
+        assertThat(dp.workspacePadding.bottom).isEqualTo(573)
+    }
+
+    @Test
+    fun iconScale() {
+        assertThat(dp.iconScale).isEqualTo(1)
+    }
+
+    @Test
+    fun cellScaleToFit() {
+        assertThat(dp.cellScaleToFit).isEqualTo(1.182266f)
+    }
+
+    @Test
+    fun workspaceTopPadding() {
+        assertThat(dp.workspaceTopPadding).isEqualTo(0)
+    }
+
+    @Test
+    fun workspaceBottomPadding() {
+        assertThat(dp.workspaceBottomPadding).isEqualTo(0)
+    }
+
+    @Test
+    fun overviewTaskMarginPx() {
+        assertThat(dp.overviewTaskMarginPx).isEqualTo(56)
+    }
+
+    @Test
+    fun overviewTaskMarginGridPx() {
+        assertThat(dp.overviewTaskMarginGridPx).isEqualTo(0)
+    }
+
+    @Test
+    fun overviewTaskIconSizePx() {
+        assertThat(dp.overviewTaskIconSizePx).isEqualTo(168)
+    }
+
+    @Test
+    fun overviewTaskIconDrawableSizePx() {
+        assertThat(dp.overviewTaskIconDrawableSizePx).isEqualTo(154)
+    }
+
+    @Test
+    fun overviewTaskIconDrawableSizeGridPx() {
+        assertThat(dp.overviewTaskIconDrawableSizeGridPx).isEqualTo(0)
+    }
+
+    @Test
+    fun overviewTaskThumbnailTopMarginPx() {
+        assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(280)
+    }
+
+    @Test
+    fun overviewActionsTopMarginPx() {
+        assertThat(dp.overviewActionsTopMarginPx).isEqualTo(84)
+    }
+
+    @Test
+    fun overviewActionsHeight() {
+        assertThat(dp.overviewActionsHeight).isEqualTo(168)
+    }
+
+    @Test
+    fun overviewActionsButtonSpacing() {
+        assertThat(dp.overviewActionsButtonSpacing).isEqualTo(126)
+    }
+
+    @Test
+    fun overviewPageSpacing() {
+        assertThat(dp.overviewPageSpacing).isEqualTo(56)
+    }
+
+    @Test
+    fun overviewRowSpacing() {
+        assertThat(dp.overviewRowSpacing).isEqualTo(-112)
+    }
+
+    @Test
+    fun overviewGridSideMargin() {
+        assertThat(dp.overviewGridSideMargin).isEqualTo(0)
+    }
+
+    @Test
+    fun dropTargetBarTopMarginPx() {
+        assertThat(dp.dropTargetBarTopMarginPx).isEqualTo(112)
+    }
+
+    @Test
+    fun dropTargetBarSizePx() {
+        assertThat(dp.dropTargetBarSizePx).isEqualTo(196)
+    }
+
+    @Test
+    fun dropTargetBarBottomMarginPx() {
+        assertThat(dp.dropTargetBarBottomMarginPx).isEqualTo(56)
+    }
+
+    @Test
+    fun workspaceSpringLoadedMinNextPageVisiblePx() {
+        assertThat(dp.workspaceSpringLoadedMinNextPageVisiblePx).isEqualTo(84)
+    }
+
+    @Test
+    fun getWorkspaceSpringLoadScale() {
+        assertThat(dp.workspaceSpringLoadScale).isEqualTo(0.74417657f)
+    }
+
+    @Test
+    fun getCellLayoutHeight() {
+        assertThat(dp.cellLayoutHeight).isEqualTo(2447)
+    }
+
+    @Test
+    fun getCellLayoutWidth() {
+        assertThat(dp.cellLayoutWidth).isEqualTo(1334)
+    }
+
+    @Test
+    fun getPanelCount() {
+        assertThat(dp.panelCount).isEqualTo(1)
+    }
+
+    @Test
+    fun isVerticalBarLayout() {
+        assertThat(dp.isVerticalBarLayout).isEqualTo(false)
+    }
+
+    @Test
+    fun getCellLayoutSpringLoadShrunkTop() {
+        assertThat(dp.cellLayoutSpringLoadShrunkTop).isEqualTo(364)
+    }
+
+    @Test
+    fun getCellLayoutSpringLoadShrunkBottom() {
+        assertThat(dp.cellLayoutSpringLoadShrunkBottom).isEqualTo(2185)
+    }
+
+    @Test
+    fun getQsbOffsetY() {
+        assertThat(dp.qsbOffsetY).isEqualTo(147)
+    }
+
+    @Test
+    fun getTaskbarOffsetY() {
+        assertThat(dp.taskbarOffsetY).isEqualTo(558)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingLeft() {
+        assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(125)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingTop() {
+        assertThat(dp.getHotseatLayoutPadding(context).top).isEqualTo(0)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingRight() {
+        assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(125)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingBottom() {
+        assertThat(dp.getHotseatLayoutPadding(context).bottom).isEqualTo(448)
+    }
+
+    @Test
+    fun hotseatBarEndOffset() {
+        assertThat(dp.hotseatBarEndOffset).isEqualTo(0)
+    }
+}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletLandscape3ButtonTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletLandscape3ButtonTest.kt
new file mode 100644
index 0000000..3f8d2fb
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletLandscape3ButtonTest.kt
@@ -0,0 +1,486 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.DeviceProfileBaseTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Tests for DeviceProfile for tablet in landscape with 3-Button navigation.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceProfileTabletLandscape3ButtonTest : DeviceProfileBaseTest() {
+
+    lateinit var dp: DeviceProfile
+
+    @Before
+    fun before() {
+        initializeVarsForTablet(isLandscape = true, isGestureMode = false)
+        dp = newDP()
+    }
+
+    @Test
+    fun isScalableGrid() {
+        assertThat(dp.isScalableGrid).isTrue()
+    }
+
+    @Test
+    fun cellWidthPx() {
+        assertThat(dp.cellWidthPx).isEqualTo(227)
+    }
+
+    @Test
+    fun cellHeightPx() {
+        assertThat(dp.cellHeightPx).isEqualTo(294)
+    }
+
+    @Test
+    fun getCellSizeX() {
+        assertThat(dp.cellSize.x).isEqualTo(558)
+    }
+
+    @Test
+    fun getCellSizeY() {
+        assertThat(dp.cellSize.y).isEqualTo(294)
+    }
+
+    @Test
+    fun cellLayoutBorderSpacePxX() {
+        assertThat(dp.cellLayoutBorderSpacePx.x).isEqualTo(57)
+    }
+
+    @Test
+    fun cellLayoutBorderSpacePxY() {
+        assertThat(dp.cellLayoutBorderSpacePx.y).isEqualTo(57)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxLeft() {
+        assertThat(dp.cellLayoutPaddingPx.left).isEqualTo(59)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxTop() {
+        assertThat(dp.cellLayoutPaddingPx.top).isEqualTo(0)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxRight() {
+        assertThat(dp.cellLayoutPaddingPx.right).isEqualTo(59)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxBottom() {
+        assertThat(dp.cellLayoutPaddingPx.bottom).isEqualTo(59)
+    }
+
+    @Test
+    fun iconSizePx() {
+        assertThat(dp.iconSizePx).isEqualTo(112)
+    }
+
+    @Test
+    fun iconTextSizePx() {
+        assertThat(dp.iconTextSizePx).isEqualTo(28)
+    }
+
+    @Test
+    fun iconDrawablePaddingPx() {
+        assertThat(dp.iconDrawablePaddingPx).isEqualTo(14)
+    }
+
+    @Test
+    fun folderCellWidthPx() {
+        assertThat(dp.folderCellWidthPx).isEqualTo(227)
+    }
+
+    @Test
+    fun folderCellHeightPx() {
+        assertThat(dp.folderCellHeightPx).isEqualTo(294)
+    }
+
+    @Test
+    fun folderChildIconSizePx() {
+        assertThat(dp.folderChildIconSizePx).isEqualTo(112)
+    }
+
+    @Test
+    fun folderChildTextSizePx() {
+        assertThat(dp.folderChildTextSizePx).isEqualTo(28)
+    }
+
+    @Test
+    fun folderChildDrawablePaddingPx() {
+        assertThat(dp.folderChildDrawablePaddingPx).isEqualTo(48)
+    }
+
+    @Test
+    fun folderCellLayoutBorderSpaceOriginalPx() {
+        assertThat(dp.folderCellLayoutBorderSpaceOriginalPx).isEqualTo(0)
+    }
+
+    @Test
+    fun folderCellLayoutBorderSpacePxX() {
+        assertThat(dp.folderCellLayoutBorderSpacePx.x).isEqualTo(0)
+    }
+
+    @Test
+    fun folderCellLayoutBorderSpacePxY() {
+        assertThat(dp.folderCellLayoutBorderSpacePx.y).isEqualTo(0)
+    }
+
+    @Test
+    fun bottomSheetTopPadding() {
+        assertThat(dp.bottomSheetTopPadding).isEqualTo(0)
+    }
+
+    @Test
+    fun allAppsShiftRange() {
+        assertThat(dp.allAppsShiftRange).isEqualTo(1600)
+    }
+
+    @Test
+    fun allAppsTopPadding() {
+        assertThat(dp.allAppsTopPadding).isEqualTo(0)
+    }
+
+    @Test
+    fun allAppsIconSizePx() {
+        assertThat(dp.allAppsIconSizePx).isEqualTo(199)
+    }
+
+    @Test
+    fun allAppsIconTextSizePx() {
+        assertThat(dp.allAppsIconTextSizePx).isEqualTo(50)
+    }
+
+    @Test
+    fun allAppsIconDrawablePaddingPx() {
+        assertThat(dp.allAppsIconDrawablePaddingPx).isEqualTo(14)
+    }
+
+    @Test
+    fun allAppsCellHeightPx() {
+        assertThat(dp.allAppsCellHeightPx).isEqualTo(351)
+    }
+
+    @Test
+    fun allAppsCellWidthPx() {
+        assertThat(dp.allAppsCellWidthPx).isEqualTo(227)
+    }
+
+    @Test
+    fun allAppsBorderSpacePxX() {
+        assertThat(dp.allAppsBorderSpacePx.x).isEqualTo(57)
+    }
+
+    @Test
+    fun allAppsBorderSpacePxY() {
+        assertThat(dp.allAppsBorderSpacePx.y).isEqualTo(57)
+    }
+
+    @Test
+    fun numShownAllAppsColumns() {
+        assertThat(dp.numShownAllAppsColumns).isEqualTo(0)
+    }
+
+    @Test
+    fun allAppsLeftRightPadding() {
+        assertThat(dp.allAppsLeftRightPadding).isEqualTo(64)
+    }
+
+    @Test
+    fun allAppsLeftRightMargin() {
+        assertThat(dp.allAppsLeftRightMargin).isEqualTo(1244)
+    }
+
+    @Test
+    fun hotseatBarSizePx() {
+        assertThat(dp.hotseatBarSizePx).isEqualTo(228)
+    }
+
+    @Test
+    fun hotseatCellHeightPx() {
+        assertThat(dp.hotseatCellHeightPx).isEqualTo(126)
+    }
+
+    @Test
+    fun hotseatBarBottomSpacePx() {
+        assertThat(dp.hotseatBarBottomSpacePx).isEqualTo(116)
+    }
+
+    @Test
+    fun hotseatBarSidePaddingStartPx() {
+        assertThat(dp.hotseatBarSidePaddingStartPx).isEqualTo(0)
+    }
+
+    @Test
+    fun hotseatBarSidePaddingEndPx() {
+        assertThat(dp.hotseatBarSidePaddingEndPx).isEqualTo(0)
+    }
+
+    @Test
+    fun hotseatQsbSpace() {
+        assertThat(dp.hotseatQsbSpace).isEqualTo(56)
+    }
+
+    @Test
+    fun hotseatQsbHeight() {
+        assertThat(dp.hotseatQsbHeight).isEqualTo(126)
+    }
+
+    @Test
+    fun springLoadedHotseatBarTopMarginPx() {
+        assertThat(dp.springLoadedHotseatBarTopMarginPx).isEqualTo(128)
+    }
+
+    @Test
+    fun numShownHotseatIcons() {
+        assertThat(dp.numShownHotseatIcons).isEqualTo(5)
+    }
+
+    @Test
+    fun hotseatBorderSpace() {
+        assertThat(dp.hotseatBorderSpace).isEqualTo(32)
+    }
+
+    @Test
+    fun isQsbInline() {
+        assertThat(dp.isQsbInline).isEqualTo(true)
+    }
+
+    @Test
+    fun qsbWidth() {
+        assertThat(dp.qsbWidth).isEqualTo(1237)
+    }
+
+    @Test
+    fun isTaskbarPresent() {
+        assertThat(dp.isTaskbarPresent).isEqualTo(true)
+    }
+
+    @Test
+    fun isTaskbarPresentInApps() {
+        assertThat(dp.isTaskbarPresentInApps).isEqualTo(false)
+    }
+
+    @Test
+    fun taskbarSize() {
+        assertThat(dp.taskbarSize).isEqualTo(120)
+    }
+
+    @Test
+    fun desiredWorkspaceHorizontalMarginPx() {
+        assertThat(dp.desiredWorkspaceHorizontalMarginPx).isEqualTo(78)
+    }
+
+    @Test
+    fun workspacePaddingLeft() {
+        assertThat(dp.workspacePadding.left).isEqualTo(19)
+    }
+
+    @Test
+    fun workspacePaddingTop() {
+        assertThat(dp.workspacePadding.top).isEqualTo(0)
+    }
+
+    @Test
+    fun workspacePaddingRight() {
+        assertThat(dp.workspacePadding.right).isEqualTo(19)
+    }
+
+    @Test
+    fun workspacePaddingBottom() {
+        assertThat(dp.workspacePadding.bottom).isEqualTo(93)
+    }
+
+    @Test
+    fun iconScale() {
+        assertThat(dp.iconScale).isEqualTo(1)
+    }
+
+    @Test
+    fun cellScaleToFit() {
+        assertThat(dp.cellScaleToFit).isEqualTo(1.7736843f)
+    }
+
+    @Test
+    fun workspaceTopPadding() {
+        assertThat(dp.workspaceTopPadding).isEqualTo(0)
+    }
+
+    @Test
+    fun workspaceBottomPadding() {
+        assertThat(dp.workspaceBottomPadding).isEqualTo(0)
+    }
+
+    @Test
+    fun overviewTaskMarginPx() {
+        assertThat(dp.overviewTaskMarginPx).isEqualTo(32)
+    }
+
+    @Test
+    fun overviewTaskMarginGridPx() {
+        assertThat(dp.overviewTaskMarginGridPx).isEqualTo(32)
+    }
+
+    @Test
+    fun overviewTaskIconSizePx() {
+        assertThat(dp.overviewTaskIconSizePx).isEqualTo(96)
+    }
+
+    @Test
+    fun overviewTaskIconDrawableSizePx() {
+        assertThat(dp.overviewTaskIconDrawableSizePx).isEqualTo(88)
+    }
+
+    @Test
+    fun overviewTaskIconDrawableSizeGridPx() {
+        assertThat(dp.overviewTaskIconDrawableSizeGridPx).isEqualTo(88)
+    }
+
+    @Test
+    fun overviewTaskThumbnailTopMarginPx() {
+        assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(160)
+    }
+
+    @Test
+    fun overviewActionsTopMarginPx() {
+        assertThat(dp.overviewActionsTopMarginPx).isEqualTo(40)
+    }
+
+    @Test
+    fun overviewActionsHeight() {
+        assertThat(dp.overviewActionsHeight).isEqualTo(96)
+    }
+
+    @Test
+    fun overviewActionsButtonSpacing() {
+        assertThat(dp.overviewActionsButtonSpacing).isEqualTo(72)
+    }
+
+    @Test
+    fun overviewPageSpacing() {
+        assertThat(dp.overviewPageSpacing).isEqualTo(88)
+    }
+
+    @Test
+    fun overviewRowSpacing() {
+        assertThat(dp.overviewRowSpacing).isEqualTo(40)
+    }
+
+    @Test
+    fun overviewGridSideMargin() {
+        assertThat(dp.overviewGridSideMargin).isEqualTo(128)
+    }
+
+    @Test
+    fun dropTargetBarTopMarginPx() {
+        assertThat(dp.dropTargetBarTopMarginPx).isEqualTo(0)
+    }
+
+    @Test
+    fun dropTargetBarSizePx() {
+        assertThat(dp.dropTargetBarSizePx).isEqualTo(144)
+    }
+
+    @Test
+    fun dropTargetBarBottomMarginPx() {
+        assertThat(dp.dropTargetBarBottomMarginPx).isEqualTo(64)
+    }
+
+    @Test
+    fun workspaceSpringLoadedMinNextPageVisiblePx() {
+        assertThat(dp.workspaceSpringLoadedMinNextPageVisiblePx).isEqualTo(48)
+    }
+
+    @Test
+    fun getWorkspaceSpringLoadScale() {
+        assertThat(dp.workspaceSpringLoadScale).isEqualTo(0.7363184f)
+    }
+
+    @Test
+    fun getCellLayoutHeight() {
+        assertThat(dp.cellLayoutHeight).isEqualTo(1407)
+    }
+
+    @Test
+    fun getCellLayoutWidth() {
+        assertThat(dp.cellLayoutWidth).isEqualTo(2522)
+    }
+
+    @Test
+    fun getPanelCount() {
+        assertThat(dp.panelCount).isEqualTo(1)
+    }
+
+    @Test
+    fun isVerticalBarLayout() {
+        assertThat(dp.isVerticalBarLayout).isEqualTo(false)
+    }
+
+    @Test
+    fun getCellLayoutSpringLoadShrunkTop() {
+        assertThat(dp.cellLayoutSpringLoadShrunkTop).isEqualTo(208)
+    }
+
+    @Test
+    fun getCellLayoutSpringLoadShrunkBottom() {
+        assertThat(dp.cellLayoutSpringLoadShrunkBottom).isEqualTo(1244)
+    }
+
+    @Test
+    fun getQsbOffsetY() {
+        assertThat(dp.qsbOffsetY).isEqualTo(109)
+    }
+
+    @Test
+    fun getTaskbarOffsetY() {
+        assertThat(dp.taskbarOffsetY).isEqualTo(112)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingLeft() {
+        assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(1443)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingTop() {
+        assertThat(dp.getHotseatLayoutPadding(context).top).isEqualTo(-7)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingRight() {
+        assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(428)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingBottom() {
+        assertThat(dp.getHotseatLayoutPadding(context).bottom).isEqualTo(109)
+    }
+
+    @Test
+    fun hotseatBarEndOffset() {
+        assertThat(dp.hotseatBarEndOffset).isEqualTo(428)
+    }
+}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletLandscapeTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletLandscapeTest.kt
new file mode 100644
index 0000000..456ed2c
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletLandscapeTest.kt
@@ -0,0 +1,486 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.DeviceProfileBaseTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Tests for DeviceProfile for tablet in landscape.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceProfileTabletLandscapeTest : DeviceProfileBaseTest() {
+
+    lateinit var dp: DeviceProfile
+
+    @Before
+    fun before() {
+        initializeVarsForTablet(isLandscape = true)
+        dp = newDP()
+    }
+
+    @Test
+    fun isScalableGrid() {
+        assertThat(dp.isScalableGrid).isTrue()
+    }
+
+    @Test
+    fun cellWidthPx() {
+        assertThat(dp.cellWidthPx).isEqualTo(227)
+    }
+
+    @Test
+    fun cellHeightPx() {
+        assertThat(dp.cellHeightPx).isEqualTo(294)
+    }
+
+    @Test
+    fun getCellSizeX() {
+        assertThat(dp.cellSize.x).isEqualTo(558)
+    }
+
+    @Test
+    fun getCellSizeY() {
+        assertThat(dp.cellSize.y).isEqualTo(294)
+    }
+
+    @Test
+    fun cellLayoutBorderSpacePxX() {
+        assertThat(dp.cellLayoutBorderSpacePx.x).isEqualTo(57)
+    }
+
+    @Test
+    fun cellLayoutBorderSpacePxY() {
+        assertThat(dp.cellLayoutBorderSpacePx.y).isEqualTo(57)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxLeft() {
+        assertThat(dp.cellLayoutPaddingPx.left).isEqualTo(59)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxTop() {
+        assertThat(dp.cellLayoutPaddingPx.top).isEqualTo(0)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxRight() {
+        assertThat(dp.cellLayoutPaddingPx.right).isEqualTo(59)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxBottom() {
+        assertThat(dp.cellLayoutPaddingPx.bottom).isEqualTo(59)
+    }
+
+    @Test
+    fun iconSizePx() {
+        assertThat(dp.iconSizePx).isEqualTo(112)
+    }
+
+    @Test
+    fun iconTextSizePx() {
+        assertThat(dp.iconTextSizePx).isEqualTo(28)
+    }
+
+    @Test
+    fun iconDrawablePaddingPx() {
+        assertThat(dp.iconDrawablePaddingPx).isEqualTo(14)
+    }
+
+    @Test
+    fun folderCellWidthPx() {
+        assertThat(dp.folderCellWidthPx).isEqualTo(227)
+    }
+
+    @Test
+    fun folderCellHeightPx() {
+        assertThat(dp.folderCellHeightPx).isEqualTo(294)
+    }
+
+    @Test
+    fun folderChildIconSizePx() {
+        assertThat(dp.folderChildIconSizePx).isEqualTo(112)
+    }
+
+    @Test
+    fun folderChildTextSizePx() {
+        assertThat(dp.folderChildTextSizePx).isEqualTo(28)
+    }
+
+    @Test
+    fun folderChildDrawablePaddingPx() {
+        assertThat(dp.folderChildDrawablePaddingPx).isEqualTo(48)
+    }
+
+    @Test
+    fun folderCellLayoutBorderSpaceOriginalPx() {
+        assertThat(dp.folderCellLayoutBorderSpaceOriginalPx).isEqualTo(0)
+    }
+
+    @Test
+    fun folderCellLayoutBorderSpacePxX() {
+        assertThat(dp.folderCellLayoutBorderSpacePx.x).isEqualTo(0)
+    }
+
+    @Test
+    fun folderCellLayoutBorderSpacePxY() {
+        assertThat(dp.folderCellLayoutBorderSpacePx.y).isEqualTo(0)
+    }
+
+    @Test
+    fun bottomSheetTopPadding() {
+        assertThat(dp.bottomSheetTopPadding).isEqualTo(0)
+    }
+
+    @Test
+    fun allAppsShiftRange() {
+        assertThat(dp.allAppsShiftRange).isEqualTo(1600)
+    }
+
+    @Test
+    fun allAppsTopPadding() {
+        assertThat(dp.allAppsTopPadding).isEqualTo(0)
+    }
+
+    @Test
+    fun allAppsIconSizePx() {
+        assertThat(dp.allAppsIconSizePx).isEqualTo(199)
+    }
+
+    @Test
+    fun allAppsIconTextSizePx() {
+        assertThat(dp.allAppsIconTextSizePx).isEqualTo(50)
+    }
+
+    @Test
+    fun allAppsIconDrawablePaddingPx() {
+        assertThat(dp.allAppsIconDrawablePaddingPx).isEqualTo(14)
+    }
+
+    @Test
+    fun allAppsCellHeightPx() {
+        assertThat(dp.allAppsCellHeightPx).isEqualTo(351)
+    }
+
+    @Test
+    fun allAppsCellWidthPx() {
+        assertThat(dp.allAppsCellWidthPx).isEqualTo(227)
+    }
+
+    @Test
+    fun allAppsBorderSpacePxX() {
+        assertThat(dp.allAppsBorderSpacePx.x).isEqualTo(57)
+    }
+
+    @Test
+    fun allAppsBorderSpacePxY() {
+        assertThat(dp.allAppsBorderSpacePx.y).isEqualTo(57)
+    }
+
+    @Test
+    fun numShownAllAppsColumns() {
+        assertThat(dp.numShownAllAppsColumns).isEqualTo(0)
+    }
+
+    @Test
+    fun allAppsLeftRightPadding() {
+        assertThat(dp.allAppsLeftRightPadding).isEqualTo(64)
+    }
+
+    @Test
+    fun allAppsLeftRightMargin() {
+        assertThat(dp.allAppsLeftRightMargin).isEqualTo(1244)
+    }
+
+    @Test
+    fun hotseatBarSizePx() {
+        assertThat(dp.hotseatBarSizePx).isEqualTo(228)
+    }
+
+    @Test
+    fun hotseatCellHeightPx() {
+        assertThat(dp.hotseatCellHeightPx).isEqualTo(126)
+    }
+
+    @Test
+    fun hotseatBarBottomSpacePx() {
+        assertThat(dp.hotseatBarBottomSpacePx).isEqualTo(116)
+    }
+
+    @Test
+    fun hotseatBarSidePaddingStartPx() {
+        assertThat(dp.hotseatBarSidePaddingStartPx).isEqualTo(0)
+    }
+
+    @Test
+    fun hotseatBarSidePaddingEndPx() {
+        assertThat(dp.hotseatBarSidePaddingEndPx).isEqualTo(0)
+    }
+
+    @Test
+    fun hotseatQsbSpace() {
+        assertThat(dp.hotseatQsbSpace).isEqualTo(56)
+    }
+
+    @Test
+    fun hotseatQsbHeight() {
+        assertThat(dp.hotseatQsbHeight).isEqualTo(126)
+    }
+
+    @Test
+    fun springLoadedHotseatBarTopMarginPx() {
+        assertThat(dp.springLoadedHotseatBarTopMarginPx).isEqualTo(128)
+    }
+
+    @Test
+    fun numShownHotseatIcons() {
+        assertThat(dp.numShownHotseatIcons).isEqualTo(4)
+    }
+
+    @Test
+    fun hotseatBorderSpace() {
+        assertThat(dp.hotseatBorderSpace).isEqualTo(503)
+    }
+
+    @Test
+    fun isQsbInline() {
+        assertThat(dp.isQsbInline).isEqualTo(true)
+    }
+
+    @Test
+    fun qsbWidth() {
+        assertThat(dp.qsbWidth).isEqualTo(-503)
+    }
+
+    @Test
+    fun isTaskbarPresent() {
+        assertThat(dp.isTaskbarPresent).isEqualTo(true)
+    }
+
+    @Test
+    fun isTaskbarPresentInApps() {
+        assertThat(dp.isTaskbarPresentInApps).isEqualTo(false)
+    }
+
+    @Test
+    fun taskbarSize() {
+        assertThat(dp.taskbarSize).isEqualTo(120)
+    }
+
+    @Test
+    fun desiredWorkspaceHorizontalMarginPx() {
+        assertThat(dp.desiredWorkspaceHorizontalMarginPx).isEqualTo(78)
+    }
+
+    @Test
+    fun workspacePaddingLeft() {
+        assertThat(dp.workspacePadding.left).isEqualTo(19)
+    }
+
+    @Test
+    fun workspacePaddingTop() {
+        assertThat(dp.workspacePadding.top).isEqualTo(0)
+    }
+
+    @Test
+    fun workspacePaddingRight() {
+        assertThat(dp.workspacePadding.right).isEqualTo(19)
+    }
+
+    @Test
+    fun workspacePaddingBottom() {
+        assertThat(dp.workspacePadding.bottom).isEqualTo(93)
+    }
+
+    @Test
+    fun iconScale() {
+        assertThat(dp.iconScale).isEqualTo(1)
+    }
+
+    @Test
+    fun cellScaleToFit() {
+        assertThat(dp.cellScaleToFit).isEqualTo(1.7736843f)
+    }
+
+    @Test
+    fun workspaceTopPadding() {
+        assertThat(dp.workspaceTopPadding).isEqualTo(0)
+    }
+
+    @Test
+    fun workspaceBottomPadding() {
+        assertThat(dp.workspaceBottomPadding).isEqualTo(0)
+    }
+
+    @Test
+    fun overviewTaskMarginPx() {
+        assertThat(dp.overviewTaskMarginPx).isEqualTo(32)
+    }
+
+    @Test
+    fun overviewTaskMarginGridPx() {
+        assertThat(dp.overviewTaskMarginGridPx).isEqualTo(32)
+    }
+
+    @Test
+    fun overviewTaskIconSizePx() {
+        assertThat(dp.overviewTaskIconSizePx).isEqualTo(96)
+    }
+
+    @Test
+    fun overviewTaskIconDrawableSizePx() {
+        assertThat(dp.overviewTaskIconDrawableSizePx).isEqualTo(88)
+    }
+
+    @Test
+    fun overviewTaskIconDrawableSizeGridPx() {
+        assertThat(dp.overviewTaskIconDrawableSizeGridPx).isEqualTo(88)
+    }
+
+    @Test
+    fun overviewTaskThumbnailTopMarginPx() {
+        assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(160)
+    }
+
+    @Test
+    fun overviewActionsTopMarginPx() {
+        assertThat(dp.overviewActionsTopMarginPx).isEqualTo(40)
+    }
+
+    @Test
+    fun overviewActionsHeight() {
+        assertThat(dp.overviewActionsHeight).isEqualTo(96)
+    }
+
+    @Test
+    fun overviewActionsButtonSpacing() {
+        assertThat(dp.overviewActionsButtonSpacing).isEqualTo(72)
+    }
+
+    @Test
+    fun overviewPageSpacing() {
+        assertThat(dp.overviewPageSpacing).isEqualTo(88)
+    }
+
+    @Test
+    fun overviewRowSpacing() {
+        assertThat(dp.overviewRowSpacing).isEqualTo(40)
+    }
+
+    @Test
+    fun overviewGridSideMargin() {
+        assertThat(dp.overviewGridSideMargin).isEqualTo(128)
+    }
+
+    @Test
+    fun dropTargetBarTopMarginPx() {
+        assertThat(dp.dropTargetBarTopMarginPx).isEqualTo(0)
+    }
+
+    @Test
+    fun dropTargetBarSizePx() {
+        assertThat(dp.dropTargetBarSizePx).isEqualTo(144)
+    }
+
+    @Test
+    fun dropTargetBarBottomMarginPx() {
+        assertThat(dp.dropTargetBarBottomMarginPx).isEqualTo(64)
+    }
+
+    @Test
+    fun workspaceSpringLoadedMinNextPageVisiblePx() {
+        assertThat(dp.workspaceSpringLoadedMinNextPageVisiblePx).isEqualTo(48)
+    }
+
+    @Test
+    fun getWorkspaceSpringLoadScale() {
+        assertThat(dp.workspaceSpringLoadScale).isEqualTo(0.7363184f)
+    }
+
+    @Test
+    fun getCellLayoutHeight() {
+        assertThat(dp.cellLayoutHeight).isEqualTo(1407)
+    }
+
+    @Test
+    fun getCellLayoutWidth() {
+        assertThat(dp.cellLayoutWidth).isEqualTo(2522)
+    }
+
+    @Test
+    fun getPanelCount() {
+        assertThat(dp.panelCount).isEqualTo(1)
+    }
+
+    @Test
+    fun isVerticalBarLayout() {
+        assertThat(dp.isVerticalBarLayout).isEqualTo(false)
+    }
+
+    @Test
+    fun getCellLayoutSpringLoadShrunkTop() {
+        assertThat(dp.cellLayoutSpringLoadShrunkTop).isEqualTo(208)
+    }
+
+    @Test
+    fun getCellLayoutSpringLoadShrunkBottom() {
+        assertThat(dp.cellLayoutSpringLoadShrunkBottom).isEqualTo(1244)
+    }
+
+    @Test
+    fun getQsbOffsetY() {
+        assertThat(dp.qsbOffsetY).isEqualTo(109)
+    }
+
+    @Test
+    fun getTaskbarOffsetY() {
+        assertThat(dp.taskbarOffsetY).isEqualTo(112)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingLeft() {
+        assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(301)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingTop() {
+        assertThat(dp.getHotseatLayoutPadding(context).top).isEqualTo(-7)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingRight() {
+        assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(301)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingBottom() {
+        assertThat(dp.getHotseatLayoutPadding(context).bottom).isEqualTo(109)
+    }
+
+    @Test
+    fun hotseatBarEndOffset() {
+        assertThat(dp.hotseatBarEndOffset).isEqualTo(0)
+    }
+}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletPortrait3ButtonTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletPortrait3ButtonTest.kt
new file mode 100644
index 0000000..b53b36a
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletPortrait3ButtonTest.kt
@@ -0,0 +1,486 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.DeviceProfileBaseTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Tests for DeviceProfile for tablet in portrait with 3-Button navigation.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceProfileTabletPortrait3ButtonTest : DeviceProfileBaseTest() {
+
+    lateinit var dp: DeviceProfile
+
+    @Before
+    fun before() {
+        initializeVarsForTablet(isGestureMode = false)
+        dp = newDP()
+    }
+
+    @Test
+    fun isScalableGrid() {
+        assertThat(dp.isScalableGrid).isTrue()
+    }
+
+    @Test
+    fun cellWidthPx() {
+        assertThat(dp.cellWidthPx).isEqualTo(294)
+    }
+
+    @Test
+    fun cellHeightPx() {
+        assertThat(dp.cellHeightPx).isEqualTo(382)
+    }
+
+    @Test
+    fun getCellSizeX() {
+        assertThat(dp.cellSize.x).isEqualTo(294)
+    }
+
+    @Test
+    fun getCellSizeY() {
+        assertThat(dp.cellSize.y).isEqualTo(482)
+    }
+
+    @Test
+    fun cellLayoutBorderSpacePxX() {
+        assertThat(dp.cellLayoutBorderSpacePx.x).isEqualTo(74)
+    }
+
+    @Test
+    fun cellLayoutBorderSpacePxY() {
+        assertThat(dp.cellLayoutBorderSpacePx.y).isEqualTo(74)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxLeft() {
+        assertThat(dp.cellLayoutPaddingPx.left).isEqualTo(72)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxTop() {
+        assertThat(dp.cellLayoutPaddingPx.top).isEqualTo(0)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxRight() {
+        assertThat(dp.cellLayoutPaddingPx.right).isEqualTo(72)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxBottom() {
+        assertThat(dp.cellLayoutPaddingPx.bottom).isEqualTo(72)
+    }
+
+    @Test
+    fun iconSizePx() {
+        assertThat(dp.iconSizePx).isEqualTo(112)
+    }
+
+    @Test
+    fun iconTextSizePx() {
+        assertThat(dp.iconTextSizePx).isEqualTo(28)
+    }
+
+    @Test
+    fun iconDrawablePaddingPx() {
+        assertThat(dp.iconDrawablePaddingPx).isEqualTo(14)
+    }
+
+    @Test
+    fun folderCellWidthPx() {
+        assertThat(dp.folderCellWidthPx).isEqualTo(294)
+    }
+
+    @Test
+    fun folderCellHeightPx() {
+        assertThat(dp.folderCellHeightPx).isEqualTo(382)
+    }
+
+    @Test
+    fun folderChildIconSizePx() {
+        assertThat(dp.folderChildIconSizePx).isEqualTo(112)
+    }
+
+    @Test
+    fun folderChildTextSizePx() {
+        assertThat(dp.folderChildTextSizePx).isEqualTo(28)
+    }
+
+    @Test
+    fun folderChildDrawablePaddingPx() {
+        assertThat(dp.folderChildDrawablePaddingPx).isEqualTo(77)
+    }
+
+    @Test
+    fun folderCellLayoutBorderSpaceOriginalPx() {
+        assertThat(dp.folderCellLayoutBorderSpaceOriginalPx).isEqualTo(0)
+    }
+
+    @Test
+    fun folderCellLayoutBorderSpacePxX() {
+        assertThat(dp.folderCellLayoutBorderSpacePx.x).isEqualTo(0)
+    }
+
+    @Test
+    fun folderCellLayoutBorderSpacePxY() {
+        assertThat(dp.folderCellLayoutBorderSpacePx.y).isEqualTo(0)
+    }
+
+    @Test
+    fun bottomSheetTopPadding() {
+        assertThat(dp.bottomSheetTopPadding).isEqualTo(600)
+    }
+
+    @Test
+    fun allAppsShiftRange() {
+        assertThat(dp.allAppsShiftRange).isEqualTo(1960)
+    }
+
+    @Test
+    fun allAppsTopPadding() {
+        assertThat(dp.allAppsTopPadding).isEqualTo(600)
+    }
+
+    @Test
+    fun allAppsIconSizePx() {
+        assertThat(dp.allAppsIconSizePx).isEqualTo(257)
+    }
+
+    @Test
+    fun allAppsIconTextSizePx() {
+        assertThat(dp.allAppsIconTextSizePx).isEqualTo(64)
+    }
+
+    @Test
+    fun allAppsIconDrawablePaddingPx() {
+        assertThat(dp.allAppsIconDrawablePaddingPx).isEqualTo(14)
+    }
+
+    @Test
+    fun allAppsCellHeightPx() {
+        assertThat(dp.allAppsCellHeightPx).isEqualTo(456)
+    }
+
+    @Test
+    fun allAppsCellWidthPx() {
+        assertThat(dp.allAppsCellWidthPx).isEqualTo(294)
+    }
+
+    @Test
+    fun allAppsBorderSpacePxX() {
+        assertThat(dp.allAppsBorderSpacePx.x).isEqualTo(74)
+    }
+
+    @Test
+    fun allAppsBorderSpacePxY() {
+        assertThat(dp.allAppsBorderSpacePx.y).isEqualTo(74)
+    }
+
+    @Test
+    fun numShownAllAppsColumns() {
+        assertThat(dp.numShownAllAppsColumns).isEqualTo(0)
+    }
+
+    @Test
+    fun allAppsLeftRightPadding() {
+        assertThat(dp.allAppsLeftRightPadding).isEqualTo(56)
+    }
+
+    @Test
+    fun allAppsLeftRightMargin() {
+        assertThat(dp.allAppsLeftRightMargin).isEqualTo(781)
+    }
+
+    @Test
+    fun hotseatBarSizePx() {
+        assertThat(dp.hotseatBarSizePx).isEqualTo(386)
+    }
+
+    @Test
+    fun hotseatCellHeightPx() {
+        assertThat(dp.hotseatCellHeightPx).isEqualTo(126)
+    }
+
+    @Test
+    fun hotseatBarBottomSpacePx() {
+        assertThat(dp.hotseatBarBottomSpacePx).isEqualTo(116)
+    }
+
+    @Test
+    fun hotseatBarSidePaddingStartPx() {
+        assertThat(dp.hotseatBarSidePaddingStartPx).isEqualTo(0)
+    }
+
+    @Test
+    fun hotseatBarSidePaddingEndPx() {
+        assertThat(dp.hotseatBarSidePaddingEndPx).isEqualTo(0)
+    }
+
+    @Test
+    fun hotseatQsbSpace() {
+        assertThat(dp.hotseatQsbSpace).isEqualTo(56)
+    }
+
+    @Test
+    fun hotseatQsbHeight() {
+        assertThat(dp.hotseatQsbHeight).isEqualTo(126)
+    }
+
+    @Test
+    fun springLoadedHotseatBarTopMarginPx() {
+        assertThat(dp.springLoadedHotseatBarTopMarginPx).isEqualTo(216)
+    }
+
+    @Test
+    fun numShownHotseatIcons() {
+        assertThat(dp.numShownHotseatIcons).isEqualTo(4)
+    }
+
+    @Test
+    fun hotseatBorderSpace() {
+        assertThat(dp.hotseatBorderSpace).isEqualTo(32)
+    }
+
+    @Test
+    fun isQsbInline() {
+        assertThat(dp.isQsbInline).isEqualTo(false)
+    }
+
+    @Test
+    fun qsbWidth() {
+        assertThat(dp.qsbWidth).isEqualTo(1216)
+    }
+
+    @Test
+    fun isTaskbarPresent() {
+        assertThat(dp.isTaskbarPresent).isEqualTo(true)
+    }
+
+    @Test
+    fun isTaskbarPresentInApps() {
+        assertThat(dp.isTaskbarPresentInApps).isEqualTo(false)
+    }
+
+    @Test
+    fun taskbarSize() {
+        assertThat(dp.taskbarSize).isEqualTo(120)
+    }
+
+    @Test
+    fun desiredWorkspaceHorizontalMarginPx() {
+        assertThat(dp.desiredWorkspaceHorizontalMarginPx).isEqualTo(101)
+    }
+
+    @Test
+    fun workspacePaddingLeft() {
+        assertThat(dp.workspacePadding.left).isEqualTo(29)
+    }
+
+    @Test
+    fun workspacePaddingTop() {
+        assertThat(dp.workspacePadding.top).isEqualTo(0)
+    }
+
+    @Test
+    fun workspacePaddingRight() {
+        assertThat(dp.workspacePadding.right).isEqualTo(29)
+    }
+
+    @Test
+    fun workspacePaddingBottom() {
+        assertThat(dp.workspacePadding.bottom).isEqualTo(238)
+    }
+
+    @Test
+    fun iconScale() {
+        assertThat(dp.iconScale).isEqualTo(1)
+    }
+
+    @Test
+    fun cellScaleToFit() {
+        assertThat(dp.cellScaleToFit).isEqualTo(2.2988505f)
+    }
+
+    @Test
+    fun workspaceTopPadding() {
+        assertThat(dp.workspaceTopPadding).isEqualTo(0)
+    }
+
+    @Test
+    fun workspaceBottomPadding() {
+        assertThat(dp.workspaceBottomPadding).isEqualTo(0)
+    }
+
+    @Test
+    fun overviewTaskMarginPx() {
+        assertThat(dp.overviewTaskMarginPx).isEqualTo(32)
+    }
+
+    @Test
+    fun overviewTaskMarginGridPx() {
+        assertThat(dp.overviewTaskMarginGridPx).isEqualTo(32)
+    }
+
+    @Test
+    fun overviewTaskIconSizePx() {
+        assertThat(dp.overviewTaskIconSizePx).isEqualTo(96)
+    }
+
+    @Test
+    fun overviewTaskIconDrawableSizePx() {
+        assertThat(dp.overviewTaskIconDrawableSizePx).isEqualTo(88)
+    }
+
+    @Test
+    fun overviewTaskIconDrawableSizeGridPx() {
+        assertThat(dp.overviewTaskIconDrawableSizeGridPx).isEqualTo(88)
+    }
+
+    @Test
+    fun overviewTaskThumbnailTopMarginPx() {
+        assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(160)
+    }
+
+    @Test
+    fun overviewActionsTopMarginPx() {
+        assertThat(dp.overviewActionsTopMarginPx).isEqualTo(48)
+    }
+
+    @Test
+    fun overviewActionsHeight() {
+        assertThat(dp.overviewActionsHeight).isEqualTo(96)
+    }
+
+    @Test
+    fun overviewActionsButtonSpacing() {
+        assertThat(dp.overviewActionsButtonSpacing).isEqualTo(72)
+    }
+
+    @Test
+    fun overviewPageSpacing() {
+        assertThat(dp.overviewPageSpacing).isEqualTo(88)
+    }
+
+    @Test
+    fun overviewRowSpacing() {
+        assertThat(dp.overviewRowSpacing).isEqualTo(40)
+    }
+
+    @Test
+    fun overviewGridSideMargin() {
+        assertThat(dp.overviewGridSideMargin).isEqualTo(128)
+    }
+
+    @Test
+    fun dropTargetBarTopMarginPx() {
+        assertThat(dp.dropTargetBarTopMarginPx).isEqualTo(220)
+    }
+
+    @Test
+    fun dropTargetBarSizePx() {
+        assertThat(dp.dropTargetBarSizePx).isEqualTo(144)
+    }
+
+    @Test
+    fun dropTargetBarBottomMarginPx() {
+        assertThat(dp.dropTargetBarBottomMarginPx).isEqualTo(96)
+    }
+
+    @Test
+    fun workspaceSpringLoadedMinNextPageVisiblePx() {
+        assertThat(dp.workspaceSpringLoadedMinNextPageVisiblePx).isEqualTo(48)
+    }
+
+    @Test
+    fun getWorkspaceSpringLoadScale() {
+        assertThat(dp.workspaceSpringLoadScale).isEqualTo(0.6741674f)
+    }
+
+    @Test
+    fun getCellLayoutHeight() {
+        assertThat(dp.cellLayoutHeight).isEqualTo(2222)
+    }
+
+    @Test
+    fun getCellLayoutWidth() {
+        assertThat(dp.cellLayoutWidth).isEqualTo(1542)
+    }
+
+    @Test
+    fun getPanelCount() {
+        assertThat(dp.panelCount).isEqualTo(1)
+    }
+
+    @Test
+    fun isVerticalBarLayout() {
+        assertThat(dp.isVerticalBarLayout).isEqualTo(false)
+    }
+
+    @Test
+    fun getCellLayoutSpringLoadShrunkTop() {
+        assertThat(dp.cellLayoutSpringLoadShrunkTop).isEqualTo(460)
+    }
+
+    @Test
+    fun getCellLayoutSpringLoadShrunkBottom() {
+        assertThat(dp.cellLayoutSpringLoadShrunkBottom).isEqualTo(1958)
+    }
+
+    @Test
+    fun getQsbOffsetY() {
+        assertThat(dp.qsbOffsetY).isEqualTo(272)
+    }
+
+    @Test
+    fun getTaskbarOffsetY() {
+        assertThat(dp.taskbarOffsetY).isEqualTo(112)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingLeft() {
+        assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(528)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingTop() {
+        assertThat(dp.getHotseatLayoutPadding(context).top).isEqualTo(151)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingRight() {
+        assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(528)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingBottom() {
+        assertThat(dp.getHotseatLayoutPadding(context).bottom).isEqualTo(109)
+    }
+
+    @Test
+    fun hotseatBarEndOffset() {
+        assertThat(dp.hotseatBarEndOffset).isEqualTo(428)
+    }
+}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletPortraitTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletPortraitTest.kt
new file mode 100644
index 0000000..4be3e45
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletPortraitTest.kt
@@ -0,0 +1,486 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.DeviceProfileBaseTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Tests for DeviceProfile for tablet in portrait.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceProfileTabletPortraitTest : DeviceProfileBaseTest() {
+
+    lateinit var dp: DeviceProfile
+
+    @Before
+    fun before() {
+        initializeVarsForTablet()
+        dp = newDP()
+    }
+
+    @Test
+    fun isScalableGrid() {
+        assertThat(dp.isScalableGrid).isTrue()
+    }
+
+    @Test
+    fun cellWidthPx() {
+        assertThat(dp.cellWidthPx).isEqualTo(294)
+    }
+
+    @Test
+    fun cellHeightPx() {
+        assertThat(dp.cellHeightPx).isEqualTo(382)
+    }
+
+    @Test
+    fun getCellSizeX() {
+        assertThat(dp.cellSize.x).isEqualTo(294)
+    }
+
+    @Test
+    fun getCellSizeY() {
+        assertThat(dp.cellSize.y).isEqualTo(482)
+    }
+
+    @Test
+    fun cellLayoutBorderSpacePxX() {
+        assertThat(dp.cellLayoutBorderSpacePx.x).isEqualTo(74)
+    }
+
+    @Test
+    fun cellLayoutBorderSpacePxY() {
+        assertThat(dp.cellLayoutBorderSpacePx.y).isEqualTo(74)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxLeft() {
+        assertThat(dp.cellLayoutPaddingPx.left).isEqualTo(72)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxTop() {
+        assertThat(dp.cellLayoutPaddingPx.top).isEqualTo(0)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxRight() {
+        assertThat(dp.cellLayoutPaddingPx.right).isEqualTo(72)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxBottom() {
+        assertThat(dp.cellLayoutPaddingPx.bottom).isEqualTo(72)
+    }
+
+    @Test
+    fun iconSizePx() {
+        assertThat(dp.iconSizePx).isEqualTo(112)
+    }
+
+    @Test
+    fun iconTextSizePx() {
+        assertThat(dp.iconTextSizePx).isEqualTo(28)
+    }
+
+    @Test
+    fun iconDrawablePaddingPx() {
+        assertThat(dp.iconDrawablePaddingPx).isEqualTo(14)
+    }
+
+    @Test
+    fun folderCellWidthPx() {
+        assertThat(dp.folderCellWidthPx).isEqualTo(294)
+    }
+
+    @Test
+    fun folderCellHeightPx() {
+        assertThat(dp.folderCellHeightPx).isEqualTo(382)
+    }
+
+    @Test
+    fun folderChildIconSizePx() {
+        assertThat(dp.folderChildIconSizePx).isEqualTo(112)
+    }
+
+    @Test
+    fun folderChildTextSizePx() {
+        assertThat(dp.folderChildTextSizePx).isEqualTo(28)
+    }
+
+    @Test
+    fun folderChildDrawablePaddingPx() {
+        assertThat(dp.folderChildDrawablePaddingPx).isEqualTo(77)
+    }
+
+    @Test
+    fun folderCellLayoutBorderSpaceOriginalPx() {
+        assertThat(dp.folderCellLayoutBorderSpaceOriginalPx).isEqualTo(0)
+    }
+
+    @Test
+    fun folderCellLayoutBorderSpacePxX() {
+        assertThat(dp.folderCellLayoutBorderSpacePx.x).isEqualTo(0)
+    }
+
+    @Test
+    fun folderCellLayoutBorderSpacePxY() {
+        assertThat(dp.folderCellLayoutBorderSpacePx.y).isEqualTo(0)
+    }
+
+    @Test
+    fun bottomSheetTopPadding() {
+        assertThat(dp.bottomSheetTopPadding).isEqualTo(600)
+    }
+
+    @Test
+    fun allAppsShiftRange() {
+        assertThat(dp.allAppsShiftRange).isEqualTo(1960)
+    }
+
+    @Test
+    fun allAppsTopPadding() {
+        assertThat(dp.allAppsTopPadding).isEqualTo(600)
+    }
+
+    @Test
+    fun allAppsIconSizePx() {
+        assertThat(dp.allAppsIconSizePx).isEqualTo(257)
+    }
+
+    @Test
+    fun allAppsIconTextSizePx() {
+        assertThat(dp.allAppsIconTextSizePx).isEqualTo(64)
+    }
+
+    @Test
+    fun allAppsIconDrawablePaddingPx() {
+        assertThat(dp.allAppsIconDrawablePaddingPx).isEqualTo(14)
+    }
+
+    @Test
+    fun allAppsCellHeightPx() {
+        assertThat(dp.allAppsCellHeightPx).isEqualTo(456)
+    }
+
+    @Test
+    fun allAppsCellWidthPx() {
+        assertThat(dp.allAppsCellWidthPx).isEqualTo(294)
+    }
+
+    @Test
+    fun allAppsBorderSpacePxX() {
+        assertThat(dp.allAppsBorderSpacePx.x).isEqualTo(74)
+    }
+
+    @Test
+    fun allAppsBorderSpacePxY() {
+        assertThat(dp.allAppsBorderSpacePx.y).isEqualTo(74)
+    }
+
+    @Test
+    fun numShownAllAppsColumns() {
+        assertThat(dp.numShownAllAppsColumns).isEqualTo(0)
+    }
+
+    @Test
+    fun allAppsLeftRightPadding() {
+        assertThat(dp.allAppsLeftRightPadding).isEqualTo(56)
+    }
+
+    @Test
+    fun allAppsLeftRightMargin() {
+        assertThat(dp.allAppsLeftRightMargin).isEqualTo(781)
+    }
+
+    @Test
+    fun hotseatBarSizePx() {
+        assertThat(dp.hotseatBarSizePx).isEqualTo(386)
+    }
+
+    @Test
+    fun hotseatCellHeightPx() {
+        assertThat(dp.hotseatCellHeightPx).isEqualTo(126)
+    }
+
+    @Test
+    fun hotseatBarBottomSpacePx() {
+        assertThat(dp.hotseatBarBottomSpacePx).isEqualTo(116)
+    }
+
+    @Test
+    fun hotseatBarSidePaddingStartPx() {
+        assertThat(dp.hotseatBarSidePaddingStartPx).isEqualTo(0)
+    }
+
+    @Test
+    fun hotseatBarSidePaddingEndPx() {
+        assertThat(dp.hotseatBarSidePaddingEndPx).isEqualTo(0)
+    }
+
+    @Test
+    fun hotseatQsbSpace() {
+        assertThat(dp.hotseatQsbSpace).isEqualTo(56)
+    }
+
+    @Test
+    fun hotseatQsbHeight() {
+        assertThat(dp.hotseatQsbHeight).isEqualTo(126)
+    }
+
+    @Test
+    fun springLoadedHotseatBarTopMarginPx() {
+        assertThat(dp.springLoadedHotseatBarTopMarginPx).isEqualTo(216)
+    }
+
+    @Test
+    fun numShownHotseatIcons() {
+        assertThat(dp.numShownHotseatIcons).isEqualTo(4)
+    }
+
+    @Test
+    fun hotseatBorderSpace() {
+        assertThat(dp.hotseatBorderSpace).isEqualTo(256)
+    }
+
+    @Test
+    fun isQsbInline() {
+        assertThat(dp.isQsbInline).isEqualTo(false)
+    }
+
+    @Test
+    fun qsbWidth() {
+        assertThat(dp.qsbWidth).isEqualTo(1216)
+    }
+
+    @Test
+    fun isTaskbarPresent() {
+        assertThat(dp.isTaskbarPresent).isEqualTo(true)
+    }
+
+    @Test
+    fun isTaskbarPresentInApps() {
+        assertThat(dp.isTaskbarPresentInApps).isEqualTo(false)
+    }
+
+    @Test
+    fun taskbarSize() {
+        assertThat(dp.taskbarSize).isEqualTo(120)
+    }
+
+    @Test
+    fun desiredWorkspaceHorizontalMarginPx() {
+        assertThat(dp.desiredWorkspaceHorizontalMarginPx).isEqualTo(101)
+    }
+
+    @Test
+    fun workspacePaddingLeft() {
+        assertThat(dp.workspacePadding.left).isEqualTo(29)
+    }
+
+    @Test
+    fun workspacePaddingTop() {
+        assertThat(dp.workspacePadding.top).isEqualTo(0)
+    }
+
+    @Test
+    fun workspacePaddingRight() {
+        assertThat(dp.workspacePadding.right).isEqualTo(29)
+    }
+
+    @Test
+    fun workspacePaddingBottom() {
+        assertThat(dp.workspacePadding.bottom).isEqualTo(238)
+    }
+
+    @Test
+    fun iconScale() {
+        assertThat(dp.iconScale).isEqualTo(1)
+    }
+
+    @Test
+    fun cellScaleToFit() {
+        assertThat(dp.cellScaleToFit).isEqualTo(2.2988505f)
+    }
+
+    @Test
+    fun workspaceTopPadding() {
+        assertThat(dp.workspaceTopPadding).isEqualTo(0)
+    }
+
+    @Test
+    fun workspaceBottomPadding() {
+        assertThat(dp.workspaceBottomPadding).isEqualTo(0)
+    }
+
+    @Test
+    fun overviewTaskMarginPx() {
+        assertThat(dp.overviewTaskMarginPx).isEqualTo(32)
+    }
+
+    @Test
+    fun overviewTaskMarginGridPx() {
+        assertThat(dp.overviewTaskMarginGridPx).isEqualTo(32)
+    }
+
+    @Test
+    fun overviewTaskIconSizePx() {
+        assertThat(dp.overviewTaskIconSizePx).isEqualTo(96)
+    }
+
+    @Test
+    fun overviewTaskIconDrawableSizePx() {
+        assertThat(dp.overviewTaskIconDrawableSizePx).isEqualTo(88)
+    }
+
+    @Test
+    fun overviewTaskIconDrawableSizeGridPx() {
+        assertThat(dp.overviewTaskIconDrawableSizeGridPx).isEqualTo(88)
+    }
+
+    @Test
+    fun overviewTaskThumbnailTopMarginPx() {
+        assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(160)
+    }
+
+    @Test
+    fun overviewActionsTopMarginPx() {
+        assertThat(dp.overviewActionsTopMarginPx).isEqualTo(48)
+    }
+
+    @Test
+    fun overviewActionsHeight() {
+        assertThat(dp.overviewActionsHeight).isEqualTo(96)
+    }
+
+    @Test
+    fun overviewActionsButtonSpacing() {
+        assertThat(dp.overviewActionsButtonSpacing).isEqualTo(72)
+    }
+
+    @Test
+    fun overviewPageSpacing() {
+        assertThat(dp.overviewPageSpacing).isEqualTo(88)
+    }
+
+    @Test
+    fun overviewRowSpacing() {
+        assertThat(dp.overviewRowSpacing).isEqualTo(40)
+    }
+
+    @Test
+    fun overviewGridSideMargin() {
+        assertThat(dp.overviewGridSideMargin).isEqualTo(128)
+    }
+
+    @Test
+    fun dropTargetBarTopMarginPx() {
+        assertThat(dp.dropTargetBarTopMarginPx).isEqualTo(220)
+    }
+
+    @Test
+    fun dropTargetBarSizePx() {
+        assertThat(dp.dropTargetBarSizePx).isEqualTo(144)
+    }
+
+    @Test
+    fun dropTargetBarBottomMarginPx() {
+        assertThat(dp.dropTargetBarBottomMarginPx).isEqualTo(96)
+    }
+
+    @Test
+    fun workspaceSpringLoadedMinNextPageVisiblePx() {
+        assertThat(dp.workspaceSpringLoadedMinNextPageVisiblePx).isEqualTo(48)
+    }
+
+    @Test
+    fun getWorkspaceSpringLoadScale() {
+        assertThat(dp.workspaceSpringLoadScale).isEqualTo(0.6741674f)
+    }
+
+    @Test
+    fun getCellLayoutHeight() {
+        assertThat(dp.cellLayoutHeight).isEqualTo(2222)
+    }
+
+    @Test
+    fun getCellLayoutWidth() {
+        assertThat(dp.cellLayoutWidth).isEqualTo(1542)
+    }
+
+    @Test
+    fun getPanelCount() {
+        assertThat(dp.panelCount).isEqualTo(1)
+    }
+
+    @Test
+    fun isVerticalBarLayout() {
+        assertThat(dp.isVerticalBarLayout).isEqualTo(false)
+    }
+
+    @Test
+    fun getCellLayoutSpringLoadShrunkTop() {
+        assertThat(dp.cellLayoutSpringLoadShrunkTop).isEqualTo(460)
+    }
+
+    @Test
+    fun getCellLayoutSpringLoadShrunkBottom() {
+        assertThat(dp.cellLayoutSpringLoadShrunkBottom).isEqualTo(1958)
+    }
+
+    @Test
+    fun getQsbOffsetY() {
+        assertThat(dp.qsbOffsetY).isEqualTo(272)
+    }
+
+    @Test
+    fun getTaskbarOffsetY() {
+        assertThat(dp.taskbarOffsetY).isEqualTo(112)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingLeft() {
+        assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(192)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingTop() {
+        assertThat(dp.getHotseatLayoutPadding(context).top).isEqualTo(151)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingRight() {
+        assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(192)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingBottom() {
+        assertThat(dp.getHotseatLayoutPadding(context).bottom).isEqualTo(109)
+    }
+
+    @Test
+    fun hotseatBarEndOffset() {
+        assertThat(dp.hotseatBarEndOffset).isEqualTo(0)
+    }
+}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelLandscape3ButtonTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelLandscape3ButtonTest.kt
new file mode 100644
index 0000000..274ca95
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelLandscape3ButtonTest.kt
@@ -0,0 +1,486 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.DeviceProfileBaseTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Tests for DeviceProfile for two panel in landscape with 3-Button navigation.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceProfileTwoPanelLandscape3ButtonTest : DeviceProfileBaseTest() {
+
+    lateinit var dp: DeviceProfile
+
+    @Before
+    fun before() {
+        initializeVarsForTablet(isLandscape = true, isTwoPanel = true, isGestureMode = false)
+        dp = newDP()
+    }
+
+    @Test
+    fun isScalableGrid() {
+        assertThat(dp.isScalableGrid).isTrue()
+    }
+
+    @Test
+    fun cellWidthPx() {
+        assertThat(dp.cellWidthPx).isEqualTo(200)
+    }
+
+    @Test
+    fun cellHeightPx() {
+        assertThat(dp.cellHeightPx).isEqualTo(260)
+    }
+
+    @Test
+    fun getCellSizeX() {
+        assertThat(dp.cellSize.x).isEqualTo(259)
+    }
+
+    @Test
+    fun getCellSizeY() {
+        assertThat(dp.cellSize.y).isEqualTo(260)
+    }
+
+    @Test
+    fun cellLayoutBorderSpacePxX() {
+        assertThat(dp.cellLayoutBorderSpacePx.x).isEqualTo(50)
+    }
+
+    @Test
+    fun cellLayoutBorderSpacePxY() {
+        assertThat(dp.cellLayoutBorderSpacePx.y).isEqualTo(50)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxLeft() {
+        assertThat(dp.cellLayoutPaddingPx.left).isEqualTo(25)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxTop() {
+        assertThat(dp.cellLayoutPaddingPx.top).isEqualTo(0)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxRight() {
+        assertThat(dp.cellLayoutPaddingPx.right).isEqualTo(25)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxBottom() {
+        assertThat(dp.cellLayoutPaddingPx.bottom).isEqualTo(25)
+    }
+
+    @Test
+    fun iconSizePx() {
+        assertThat(dp.iconSizePx).isEqualTo(112)
+    }
+
+    @Test
+    fun iconTextSizePx() {
+        assertThat(dp.iconTextSizePx).isEqualTo(28)
+    }
+
+    @Test
+    fun iconDrawablePaddingPx() {
+        assertThat(dp.iconDrawablePaddingPx).isEqualTo(14)
+    }
+
+    @Test
+    fun folderCellWidthPx() {
+        assertThat(dp.folderCellWidthPx).isEqualTo(200)
+    }
+
+    @Test
+    fun folderCellHeightPx() {
+        assertThat(dp.folderCellHeightPx).isEqualTo(260)
+    }
+
+    @Test
+    fun folderChildIconSizePx() {
+        assertThat(dp.folderChildIconSizePx).isEqualTo(112)
+    }
+
+    @Test
+    fun folderChildTextSizePx() {
+        assertThat(dp.folderChildTextSizePx).isEqualTo(28)
+    }
+
+    @Test
+    fun folderChildDrawablePaddingPx() {
+        assertThat(dp.folderChildDrawablePaddingPx).isEqualTo(36)
+    }
+
+    @Test
+    fun folderCellLayoutBorderSpaceOriginalPx() {
+        assertThat(dp.folderCellLayoutBorderSpaceOriginalPx).isEqualTo(0)
+    }
+
+    @Test
+    fun folderCellLayoutBorderSpacePxX() {
+        assertThat(dp.folderCellLayoutBorderSpacePx.x).isEqualTo(0)
+    }
+
+    @Test
+    fun folderCellLayoutBorderSpacePxY() {
+        assertThat(dp.folderCellLayoutBorderSpacePx.y).isEqualTo(0)
+    }
+
+    @Test
+    fun bottomSheetTopPadding() {
+        assertThat(dp.bottomSheetTopPadding).isEqualTo(0)
+    }
+
+    @Test
+    fun allAppsShiftRange() {
+        assertThat(dp.allAppsShiftRange).isEqualTo(1600)
+    }
+
+    @Test
+    fun allAppsTopPadding() {
+        assertThat(dp.allAppsTopPadding).isEqualTo(0)
+    }
+
+    @Test
+    fun allAppsIconSizePx() {
+        assertThat(dp.allAppsIconSizePx).isEqualTo(175)
+    }
+
+    @Test
+    fun allAppsIconTextSizePx() {
+        assertThat(dp.allAppsIconTextSizePx).isEqualTo(44)
+    }
+
+    @Test
+    fun allAppsIconDrawablePaddingPx() {
+        assertThat(dp.allAppsIconDrawablePaddingPx).isEqualTo(14)
+    }
+
+    @Test
+    fun allAppsCellHeightPx() {
+        assertThat(dp.allAppsCellHeightPx).isEqualTo(310)
+    }
+
+    @Test
+    fun allAppsCellWidthPx() {
+        assertThat(dp.allAppsCellWidthPx).isEqualTo(200)
+    }
+
+    @Test
+    fun allAppsBorderSpacePxX() {
+        assertThat(dp.allAppsBorderSpacePx.x).isEqualTo(50)
+    }
+
+    @Test
+    fun allAppsBorderSpacePxY() {
+        assertThat(dp.allAppsBorderSpacePx.y).isEqualTo(50)
+    }
+
+    @Test
+    fun numShownAllAppsColumns() {
+        assertThat(dp.numShownAllAppsColumns).isEqualTo(0)
+    }
+
+    @Test
+    fun allAppsLeftRightPadding() {
+        assertThat(dp.allAppsLeftRightPadding).isEqualTo(64)
+    }
+
+    @Test
+    fun allAppsLeftRightMargin() {
+        assertThat(dp.allAppsLeftRightMargin).isEqualTo(1241)
+    }
+
+    @Test
+    fun hotseatBarSizePx() {
+        assertThat(dp.hotseatBarSizePx).isEqualTo(386)
+    }
+
+    @Test
+    fun hotseatCellHeightPx() {
+        assertThat(dp.hotseatCellHeightPx).isEqualTo(126)
+    }
+
+    @Test
+    fun hotseatBarBottomSpacePx() {
+        assertThat(dp.hotseatBarBottomSpacePx).isEqualTo(116)
+    }
+
+    @Test
+    fun hotseatBarSidePaddingStartPx() {
+        assertThat(dp.hotseatBarSidePaddingStartPx).isEqualTo(0)
+    }
+
+    @Test
+    fun hotseatBarSidePaddingEndPx() {
+        assertThat(dp.hotseatBarSidePaddingEndPx).isEqualTo(0)
+    }
+
+    @Test
+    fun hotseatQsbSpace() {
+        assertThat(dp.hotseatQsbSpace).isEqualTo(56)
+    }
+
+    @Test
+    fun hotseatQsbHeight() {
+        assertThat(dp.hotseatQsbHeight).isEqualTo(126)
+    }
+
+    @Test
+    fun springLoadedHotseatBarTopMarginPx() {
+        assertThat(dp.springLoadedHotseatBarTopMarginPx).isEqualTo(128)
+    }
+
+    @Test
+    fun numShownHotseatIcons() {
+        assertThat(dp.numShownHotseatIcons).isEqualTo(6)
+    }
+
+    @Test
+    fun hotseatBorderSpace() {
+        assertThat(dp.hotseatBorderSpace).isEqualTo(32)
+    }
+
+    @Test
+    fun isQsbInline() {
+        assertThat(dp.isQsbInline).isEqualTo(false)
+    }
+
+    @Test
+    fun qsbWidth() {
+        assertThat(dp.qsbWidth).isEqualTo(1039)
+    }
+
+    @Test
+    fun isTaskbarPresent() {
+        assertThat(dp.isTaskbarPresent).isEqualTo(true)
+    }
+
+    @Test
+    fun isTaskbarPresentInApps() {
+        assertThat(dp.isTaskbarPresentInApps).isEqualTo(false)
+    }
+
+    @Test
+    fun taskbarSize() {
+        assertThat(dp.taskbarSize).isEqualTo(120)
+    }
+
+    @Test
+    fun desiredWorkspaceHorizontalMarginPx() {
+        assertThat(dp.desiredWorkspaceHorizontalMarginPx).isEqualTo(68)
+    }
+
+    @Test
+    fun workspacePaddingLeft() {
+        assertThat(dp.workspacePadding.left).isEqualTo(43)
+    }
+
+    @Test
+    fun workspacePaddingTop() {
+        assertThat(dp.workspacePadding.top).isEqualTo(0)
+    }
+
+    @Test
+    fun workspacePaddingRight() {
+        assertThat(dp.workspacePadding.right).isEqualTo(43)
+    }
+
+    @Test
+    fun workspacePaddingBottom() {
+        assertThat(dp.workspacePadding.bottom).isEqualTo(285)
+    }
+
+    @Test
+    fun iconScale() {
+        assertThat(dp.iconScale).isEqualTo(1)
+    }
+
+    @Test
+    fun cellScaleToFit() {
+        assertThat(dp.cellScaleToFit).isEqualTo(1.5657895f)
+    }
+
+    @Test
+    fun workspaceTopPadding() {
+        assertThat(dp.workspaceTopPadding).isEqualTo(0)
+    }
+
+    @Test
+    fun workspaceBottomPadding() {
+        assertThat(dp.workspaceBottomPadding).isEqualTo(0)
+    }
+
+    @Test
+    fun overviewTaskMarginPx() {
+        assertThat(dp.overviewTaskMarginPx).isEqualTo(32)
+    }
+
+    @Test
+    fun overviewTaskMarginGridPx() {
+        assertThat(dp.overviewTaskMarginGridPx).isEqualTo(32)
+    }
+
+    @Test
+    fun overviewTaskIconSizePx() {
+        assertThat(dp.overviewTaskIconSizePx).isEqualTo(96)
+    }
+
+    @Test
+    fun overviewTaskIconDrawableSizePx() {
+        assertThat(dp.overviewTaskIconDrawableSizePx).isEqualTo(88)
+    }
+
+    @Test
+    fun overviewTaskIconDrawableSizeGridPx() {
+        assertThat(dp.overviewTaskIconDrawableSizeGridPx).isEqualTo(88)
+    }
+
+    @Test
+    fun overviewTaskThumbnailTopMarginPx() {
+        assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(160)
+    }
+
+    @Test
+    fun overviewActionsTopMarginPx() {
+        assertThat(dp.overviewActionsTopMarginPx).isEqualTo(40)
+    }
+
+    @Test
+    fun overviewActionsHeight() {
+        assertThat(dp.overviewActionsHeight).isEqualTo(96)
+    }
+
+    @Test
+    fun overviewActionsButtonSpacing() {
+        assertThat(dp.overviewActionsButtonSpacing).isEqualTo(72)
+    }
+
+    @Test
+    fun overviewPageSpacing() {
+        assertThat(dp.overviewPageSpacing).isEqualTo(88)
+    }
+
+    @Test
+    fun overviewRowSpacing() {
+        assertThat(dp.overviewRowSpacing).isEqualTo(40)
+    }
+
+    @Test
+    fun overviewGridSideMargin() {
+        assertThat(dp.overviewGridSideMargin).isEqualTo(128)
+    }
+
+    @Test
+    fun dropTargetBarTopMarginPx() {
+        assertThat(dp.dropTargetBarTopMarginPx).isEqualTo(0)
+    }
+
+    @Test
+    fun dropTargetBarSizePx() {
+        assertThat(dp.dropTargetBarSizePx).isEqualTo(144)
+    }
+
+    @Test
+    fun dropTargetBarBottomMarginPx() {
+        assertThat(dp.dropTargetBarBottomMarginPx).isEqualTo(64)
+    }
+
+    @Test
+    fun workspaceSpringLoadedMinNextPageVisiblePx() {
+        assertThat(dp.workspaceSpringLoadedMinNextPageVisiblePx).isEqualTo(48)
+    }
+
+    @Test
+    fun getWorkspaceSpringLoadScale() {
+        assertThat(dp.workspaceSpringLoadScale).isEqualTo(0.7226337f)
+    }
+
+    @Test
+    fun getCellLayoutHeight() {
+        assertThat(dp.cellLayoutHeight).isEqualTo(1215)
+    }
+
+    @Test
+    fun getCellLayoutWidth() {
+        assertThat(dp.cellLayoutWidth).isEqualTo(1237)
+    }
+
+    @Test
+    fun getPanelCount() {
+        assertThat(dp.panelCount).isEqualTo(2)
+    }
+
+    @Test
+    fun isVerticalBarLayout() {
+        assertThat(dp.isVerticalBarLayout).isEqualTo(false)
+    }
+
+    @Test
+    fun getCellLayoutSpringLoadShrunkTop() {
+        assertThat(dp.cellLayoutSpringLoadShrunkTop).isEqualTo(208)
+    }
+
+    @Test
+    fun getCellLayoutSpringLoadShrunkBottom() {
+        assertThat(dp.cellLayoutSpringLoadShrunkBottom).isEqualTo(1086)
+    }
+
+    @Test
+    fun getQsbOffsetY() {
+        assertThat(dp.qsbOffsetY).isEqualTo(272)
+    }
+
+    @Test
+    fun getTaskbarOffsetY() {
+        assertThat(dp.taskbarOffsetY).isEqualTo(112)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingLeft() {
+        assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(864)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingTop() {
+        assertThat(dp.getHotseatLayoutPadding(context).top).isEqualTo(151)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingRight() {
+        assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(864)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingBottom() {
+        assertThat(dp.getHotseatLayoutPadding(context).bottom).isEqualTo(109)
+    }
+
+    @Test
+    fun hotseatBarEndOffset() {
+        assertThat(dp.hotseatBarEndOffset).isEqualTo(428)
+    }
+}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelLandscapeTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelLandscapeTest.kt
new file mode 100644
index 0000000..ba3ef55
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelLandscapeTest.kt
@@ -0,0 +1,486 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.DeviceProfileBaseTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Tests for DeviceProfile for two panel in landscape.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceProfileTwoPanelLandscapeTest : DeviceProfileBaseTest() {
+
+    lateinit var dp: DeviceProfile
+
+    @Before
+    fun before() {
+        initializeVarsForTablet(isLandscape = true, isTwoPanel = true)
+        dp = newDP()
+    }
+
+    @Test
+    fun isScalableGrid() {
+        assertThat(dp.isScalableGrid).isTrue()
+    }
+
+    @Test
+    fun cellWidthPx() {
+        assertThat(dp.cellWidthPx).isEqualTo(200)
+    }
+
+    @Test
+    fun cellHeightPx() {
+        assertThat(dp.cellHeightPx).isEqualTo(260)
+    }
+
+    @Test
+    fun getCellSizeX() {
+        assertThat(dp.cellSize.x).isEqualTo(259)
+    }
+
+    @Test
+    fun getCellSizeY() {
+        assertThat(dp.cellSize.y).isEqualTo(260)
+    }
+
+    @Test
+    fun cellLayoutBorderSpacePxX() {
+        assertThat(dp.cellLayoutBorderSpacePx.x).isEqualTo(50)
+    }
+
+    @Test
+    fun cellLayoutBorderSpacePxY() {
+        assertThat(dp.cellLayoutBorderSpacePx.y).isEqualTo(50)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxLeft() {
+        assertThat(dp.cellLayoutPaddingPx.left).isEqualTo(25)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxTop() {
+        assertThat(dp.cellLayoutPaddingPx.top).isEqualTo(0)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxRight() {
+        assertThat(dp.cellLayoutPaddingPx.right).isEqualTo(25)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxBottom() {
+        assertThat(dp.cellLayoutPaddingPx.bottom).isEqualTo(25)
+    }
+
+    @Test
+    fun iconSizePx() {
+        assertThat(dp.iconSizePx).isEqualTo(112)
+    }
+
+    @Test
+    fun iconTextSizePx() {
+        assertThat(dp.iconTextSizePx).isEqualTo(28)
+    }
+
+    @Test
+    fun iconDrawablePaddingPx() {
+        assertThat(dp.iconDrawablePaddingPx).isEqualTo(14)
+    }
+
+    @Test
+    fun folderCellWidthPx() {
+        assertThat(dp.folderCellWidthPx).isEqualTo(200)
+    }
+
+    @Test
+    fun folderCellHeightPx() {
+        assertThat(dp.folderCellHeightPx).isEqualTo(260)
+    }
+
+    @Test
+    fun folderChildIconSizePx() {
+        assertThat(dp.folderChildIconSizePx).isEqualTo(112)
+    }
+
+    @Test
+    fun folderChildTextSizePx() {
+        assertThat(dp.folderChildTextSizePx).isEqualTo(28)
+    }
+
+    @Test
+    fun folderChildDrawablePaddingPx() {
+        assertThat(dp.folderChildDrawablePaddingPx).isEqualTo(36)
+    }
+
+    @Test
+    fun folderCellLayoutBorderSpaceOriginalPx() {
+        assertThat(dp.folderCellLayoutBorderSpaceOriginalPx).isEqualTo(0)
+    }
+
+    @Test
+    fun folderCellLayoutBorderSpacePxX() {
+        assertThat(dp.folderCellLayoutBorderSpacePx.x).isEqualTo(0)
+    }
+
+    @Test
+    fun folderCellLayoutBorderSpacePxY() {
+        assertThat(dp.folderCellLayoutBorderSpacePx.y).isEqualTo(0)
+    }
+
+    @Test
+    fun bottomSheetTopPadding() {
+        assertThat(dp.bottomSheetTopPadding).isEqualTo(0)
+    }
+
+    @Test
+    fun allAppsShiftRange() {
+        assertThat(dp.allAppsShiftRange).isEqualTo(1600)
+    }
+
+    @Test
+    fun allAppsTopPadding() {
+        assertThat(dp.allAppsTopPadding).isEqualTo(0)
+    }
+
+    @Test
+    fun allAppsIconSizePx() {
+        assertThat(dp.allAppsIconSizePx).isEqualTo(175)
+    }
+
+    @Test
+    fun allAppsIconTextSizePx() {
+        assertThat(dp.allAppsIconTextSizePx).isEqualTo(44)
+    }
+
+    @Test
+    fun allAppsIconDrawablePaddingPx() {
+        assertThat(dp.allAppsIconDrawablePaddingPx).isEqualTo(14)
+    }
+
+    @Test
+    fun allAppsCellHeightPx() {
+        assertThat(dp.allAppsCellHeightPx).isEqualTo(310)
+    }
+
+    @Test
+    fun allAppsCellWidthPx() {
+        assertThat(dp.allAppsCellWidthPx).isEqualTo(200)
+    }
+
+    @Test
+    fun allAppsBorderSpacePxX() {
+        assertThat(dp.allAppsBorderSpacePx.x).isEqualTo(50)
+    }
+
+    @Test
+    fun allAppsBorderSpacePxY() {
+        assertThat(dp.allAppsBorderSpacePx.y).isEqualTo(50)
+    }
+
+    @Test
+    fun numShownAllAppsColumns() {
+        assertThat(dp.numShownAllAppsColumns).isEqualTo(0)
+    }
+
+    @Test
+    fun allAppsLeftRightPadding() {
+        assertThat(dp.allAppsLeftRightPadding).isEqualTo(64)
+    }
+
+    @Test
+    fun allAppsLeftRightMargin() {
+        assertThat(dp.allAppsLeftRightMargin).isEqualTo(1241)
+    }
+
+    @Test
+    fun hotseatBarSizePx() {
+        assertThat(dp.hotseatBarSizePx).isEqualTo(386)
+    }
+
+    @Test
+    fun hotseatCellHeightPx() {
+        assertThat(dp.hotseatCellHeightPx).isEqualTo(126)
+    }
+
+    @Test
+    fun hotseatBarBottomSpacePx() {
+        assertThat(dp.hotseatBarBottomSpacePx).isEqualTo(116)
+    }
+
+    @Test
+    fun hotseatBarSidePaddingStartPx() {
+        assertThat(dp.hotseatBarSidePaddingStartPx).isEqualTo(0)
+    }
+
+    @Test
+    fun hotseatBarSidePaddingEndPx() {
+        assertThat(dp.hotseatBarSidePaddingEndPx).isEqualTo(0)
+    }
+
+    @Test
+    fun hotseatQsbSpace() {
+        assertThat(dp.hotseatQsbSpace).isEqualTo(56)
+    }
+
+    @Test
+    fun hotseatQsbHeight() {
+        assertThat(dp.hotseatQsbHeight).isEqualTo(126)
+    }
+
+    @Test
+    fun springLoadedHotseatBarTopMarginPx() {
+        assertThat(dp.springLoadedHotseatBarTopMarginPx).isEqualTo(128)
+    }
+
+    @Test
+    fun numShownHotseatIcons() {
+        assertThat(dp.numShownHotseatIcons).isEqualTo(6)
+    }
+
+    @Test
+    fun hotseatBorderSpace() {
+        assertThat(dp.hotseatBorderSpace).isEqualTo(73)
+    }
+
+    @Test
+    fun isQsbInline() {
+        assertThat(dp.isQsbInline).isEqualTo(false)
+    }
+
+    @Test
+    fun qsbWidth() {
+        assertThat(dp.qsbWidth).isEqualTo(1039)
+    }
+
+    @Test
+    fun isTaskbarPresent() {
+        assertThat(dp.isTaskbarPresent).isEqualTo(true)
+    }
+
+    @Test
+    fun isTaskbarPresentInApps() {
+        assertThat(dp.isTaskbarPresentInApps).isEqualTo(false)
+    }
+
+    @Test
+    fun taskbarSize() {
+        assertThat(dp.taskbarSize).isEqualTo(120)
+    }
+
+    @Test
+    fun desiredWorkspaceHorizontalMarginPx() {
+        assertThat(dp.desiredWorkspaceHorizontalMarginPx).isEqualTo(68)
+    }
+
+    @Test
+    fun workspacePaddingLeft() {
+        assertThat(dp.workspacePadding.left).isEqualTo(43)
+    }
+
+    @Test
+    fun workspacePaddingTop() {
+        assertThat(dp.workspacePadding.top).isEqualTo(0)
+    }
+
+    @Test
+    fun workspacePaddingRight() {
+        assertThat(dp.workspacePadding.right).isEqualTo(43)
+    }
+
+    @Test
+    fun workspacePaddingBottom() {
+        assertThat(dp.workspacePadding.bottom).isEqualTo(285)
+    }
+
+    @Test
+    fun iconScale() {
+        assertThat(dp.iconScale).isEqualTo(1)
+    }
+
+    @Test
+    fun cellScaleToFit() {
+        assertThat(dp.cellScaleToFit).isEqualTo(1.5657895f)
+    }
+
+    @Test
+    fun workspaceTopPadding() {
+        assertThat(dp.workspaceTopPadding).isEqualTo(0)
+    }
+
+    @Test
+    fun workspaceBottomPadding() {
+        assertThat(dp.workspaceBottomPadding).isEqualTo(0)
+    }
+
+    @Test
+    fun overviewTaskMarginPx() {
+        assertThat(dp.overviewTaskMarginPx).isEqualTo(32)
+    }
+
+    @Test
+    fun overviewTaskMarginGridPx() {
+        assertThat(dp.overviewTaskMarginGridPx).isEqualTo(32)
+    }
+
+    @Test
+    fun overviewTaskIconSizePx() {
+        assertThat(dp.overviewTaskIconSizePx).isEqualTo(96)
+    }
+
+    @Test
+    fun overviewTaskIconDrawableSizePx() {
+        assertThat(dp.overviewTaskIconDrawableSizePx).isEqualTo(88)
+    }
+
+    @Test
+    fun overviewTaskIconDrawableSizeGridPx() {
+        assertThat(dp.overviewTaskIconDrawableSizeGridPx).isEqualTo(88)
+    }
+
+    @Test
+    fun overviewTaskThumbnailTopMarginPx() {
+        assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(160)
+    }
+
+    @Test
+    fun overviewActionsTopMarginPx() {
+        assertThat(dp.overviewActionsTopMarginPx).isEqualTo(40)
+    }
+
+    @Test
+    fun overviewActionsHeight() {
+        assertThat(dp.overviewActionsHeight).isEqualTo(96)
+    }
+
+    @Test
+    fun overviewActionsButtonSpacing() {
+        assertThat(dp.overviewActionsButtonSpacing).isEqualTo(72)
+    }
+
+    @Test
+    fun overviewPageSpacing() {
+        assertThat(dp.overviewPageSpacing).isEqualTo(88)
+    }
+
+    @Test
+    fun overviewRowSpacing() {
+        assertThat(dp.overviewRowSpacing).isEqualTo(40)
+    }
+
+    @Test
+    fun overviewGridSideMargin() {
+        assertThat(dp.overviewGridSideMargin).isEqualTo(128)
+    }
+
+    @Test
+    fun dropTargetBarTopMarginPx() {
+        assertThat(dp.dropTargetBarTopMarginPx).isEqualTo(0)
+    }
+
+    @Test
+    fun dropTargetBarSizePx() {
+        assertThat(dp.dropTargetBarSizePx).isEqualTo(144)
+    }
+
+    @Test
+    fun dropTargetBarBottomMarginPx() {
+        assertThat(dp.dropTargetBarBottomMarginPx).isEqualTo(64)
+    }
+
+    @Test
+    fun workspaceSpringLoadedMinNextPageVisiblePx() {
+        assertThat(dp.workspaceSpringLoadedMinNextPageVisiblePx).isEqualTo(48)
+    }
+
+    @Test
+    fun getWorkspaceSpringLoadScale() {
+        assertThat(dp.workspaceSpringLoadScale).isEqualTo(0.7226337f)
+    }
+
+    @Test
+    fun getCellLayoutHeight() {
+        assertThat(dp.cellLayoutHeight).isEqualTo(1215)
+    }
+
+    @Test
+    fun getCellLayoutWidth() {
+        assertThat(dp.cellLayoutWidth).isEqualTo(1237)
+    }
+
+    @Test
+    fun getPanelCount() {
+        assertThat(dp.panelCount).isEqualTo(2)
+    }
+
+    @Test
+    fun isVerticalBarLayout() {
+        assertThat(dp.isVerticalBarLayout).isEqualTo(false)
+    }
+
+    @Test
+    fun getCellLayoutSpringLoadShrunkTop() {
+        assertThat(dp.cellLayoutSpringLoadShrunkTop).isEqualTo(208)
+    }
+
+    @Test
+    fun getCellLayoutSpringLoadShrunkBottom() {
+        assertThat(dp.cellLayoutSpringLoadShrunkBottom).isEqualTo(1086)
+    }
+
+    @Test
+    fun getQsbOffsetY() {
+        assertThat(dp.qsbOffsetY).isEqualTo(272)
+    }
+
+    @Test
+    fun getTaskbarOffsetY() {
+        assertThat(dp.taskbarOffsetY).isEqualTo(112)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingLeft() {
+        assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(761)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingTop() {
+        assertThat(dp.getHotseatLayoutPadding(context).top).isEqualTo(151)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingRight() {
+        assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(761)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingBottom() {
+        assertThat(dp.getHotseatLayoutPadding(context).bottom).isEqualTo(109)
+    }
+
+    @Test
+    fun hotseatBarEndOffset() {
+        assertThat(dp.hotseatBarEndOffset).isEqualTo(0)
+    }
+}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelPortrait3ButtonTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelPortrait3ButtonTest.kt
new file mode 100644
index 0000000..7dd95f9
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelPortrait3ButtonTest.kt
@@ -0,0 +1,486 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.DeviceProfileBaseTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Tests for DeviceProfile for two panel in portrait with 3-Button navigation.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceProfileTwoPanelPortrait3ButtonTest : DeviceProfileBaseTest() {
+
+    lateinit var dp: DeviceProfile
+
+    @Before
+    fun before() {
+        initializeVarsForTablet(isTwoPanel = true, isGestureMode = false)
+        dp = newDP()
+    }
+
+    @Test
+    fun isScalableGrid() {
+        assertThat(dp.isScalableGrid).isTrue()
+    }
+
+    @Test
+    fun cellWidthPx() {
+        assertThat(dp.cellWidthPx).isEqualTo(153)
+    }
+
+    @Test
+    fun cellHeightPx() {
+        assertThat(dp.cellHeightPx).isEqualTo(199)
+    }
+
+    @Test
+    fun getCellSizeX() {
+        assertThat(dp.cellSize.x).isEqualTo(153)
+    }
+
+    @Test
+    fun getCellSizeY() {
+        assertThat(dp.cellSize.y).isEqualTo(509)
+    }
+
+    @Test
+    fun cellLayoutBorderSpacePxX() {
+        assertThat(dp.cellLayoutBorderSpacePx.x).isEqualTo(38)
+    }
+
+    @Test
+    fun cellLayoutBorderSpacePxY() {
+        assertThat(dp.cellLayoutBorderSpacePx.y).isEqualTo(38)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxLeft() {
+        assertThat(dp.cellLayoutPaddingPx.left).isEqualTo(19)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxTop() {
+        assertThat(dp.cellLayoutPaddingPx.top).isEqualTo(0)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxRight() {
+        assertThat(dp.cellLayoutPaddingPx.right).isEqualTo(19)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxBottom() {
+        assertThat(dp.cellLayoutPaddingPx.bottom).isEqualTo(19)
+    }
+
+    @Test
+    fun iconSizePx() {
+        assertThat(dp.iconSizePx).isEqualTo(112)
+    }
+
+    @Test
+    fun iconTextSizePx() {
+        assertThat(dp.iconTextSizePx).isEqualTo(28)
+    }
+
+    @Test
+    fun iconDrawablePaddingPx() {
+        assertThat(dp.iconDrawablePaddingPx).isEqualTo(14)
+    }
+
+    @Test
+    fun folderCellWidthPx() {
+        assertThat(dp.folderCellWidthPx).isEqualTo(153)
+    }
+
+    @Test
+    fun folderCellHeightPx() {
+        assertThat(dp.folderCellHeightPx).isEqualTo(199)
+    }
+
+    @Test
+    fun folderChildIconSizePx() {
+        assertThat(dp.folderChildIconSizePx).isEqualTo(112)
+    }
+
+    @Test
+    fun folderChildTextSizePx() {
+        assertThat(dp.folderChildTextSizePx).isEqualTo(28)
+    }
+
+    @Test
+    fun folderChildDrawablePaddingPx() {
+        assertThat(dp.folderChildDrawablePaddingPx).isEqualTo(16)
+    }
+
+    @Test
+    fun folderCellLayoutBorderSpaceOriginalPx() {
+        assertThat(dp.folderCellLayoutBorderSpaceOriginalPx).isEqualTo(0)
+    }
+
+    @Test
+    fun folderCellLayoutBorderSpacePxX() {
+        assertThat(dp.folderCellLayoutBorderSpacePx.x).isEqualTo(0)
+    }
+
+    @Test
+    fun folderCellLayoutBorderSpacePxY() {
+        assertThat(dp.folderCellLayoutBorderSpacePx.y).isEqualTo(0)
+    }
+
+    @Test
+    fun bottomSheetTopPadding() {
+        assertThat(dp.bottomSheetTopPadding).isEqualTo(600)
+    }
+
+    @Test
+    fun allAppsShiftRange() {
+        assertThat(dp.allAppsShiftRange).isEqualTo(1960)
+    }
+
+    @Test
+    fun allAppsTopPadding() {
+        assertThat(dp.allAppsTopPadding).isEqualTo(600)
+    }
+
+    @Test
+    fun allAppsIconSizePx() {
+        assertThat(dp.allAppsIconSizePx).isEqualTo(134)
+    }
+
+    @Test
+    fun allAppsIconTextSizePx() {
+        assertThat(dp.allAppsIconTextSizePx).isEqualTo(34)
+    }
+
+    @Test
+    fun allAppsIconDrawablePaddingPx() {
+        assertThat(dp.allAppsIconDrawablePaddingPx).isEqualTo(14)
+    }
+
+    @Test
+    fun allAppsCellHeightPx() {
+        assertThat(dp.allAppsCellHeightPx).isEqualTo(237)
+    }
+
+    @Test
+    fun allAppsCellWidthPx() {
+        assertThat(dp.allAppsCellWidthPx).isEqualTo(153)
+    }
+
+    @Test
+    fun allAppsBorderSpacePxX() {
+        assertThat(dp.allAppsBorderSpacePx.x).isEqualTo(38)
+    }
+
+    @Test
+    fun allAppsBorderSpacePxY() {
+        assertThat(dp.allAppsBorderSpacePx.y).isEqualTo(38)
+    }
+
+    @Test
+    fun numShownAllAppsColumns() {
+        assertThat(dp.numShownAllAppsColumns).isEqualTo(0)
+    }
+
+    @Test
+    fun allAppsLeftRightPadding() {
+        assertThat(dp.allAppsLeftRightPadding).isEqualTo(56)
+    }
+
+    @Test
+    fun allAppsLeftRightMargin() {
+        assertThat(dp.allAppsLeftRightMargin).isEqualTo(763)
+    }
+
+    @Test
+    fun hotseatBarSizePx() {
+        assertThat(dp.hotseatBarSizePx).isEqualTo(386)
+    }
+
+    @Test
+    fun hotseatCellHeightPx() {
+        assertThat(dp.hotseatCellHeightPx).isEqualTo(126)
+    }
+
+    @Test
+    fun hotseatBarBottomSpacePx() {
+        assertThat(dp.hotseatBarBottomSpacePx).isEqualTo(116)
+    }
+
+    @Test
+    fun hotseatBarSidePaddingStartPx() {
+        assertThat(dp.hotseatBarSidePaddingStartPx).isEqualTo(0)
+    }
+
+    @Test
+    fun hotseatBarSidePaddingEndPx() {
+        assertThat(dp.hotseatBarSidePaddingEndPx).isEqualTo(0)
+    }
+
+    @Test
+    fun hotseatQsbSpace() {
+        assertThat(dp.hotseatQsbSpace).isEqualTo(56)
+    }
+
+    @Test
+    fun hotseatQsbHeight() {
+        assertThat(dp.hotseatQsbHeight).isEqualTo(126)
+    }
+
+    @Test
+    fun springLoadedHotseatBarTopMarginPx() {
+        assertThat(dp.springLoadedHotseatBarTopMarginPx).isEqualTo(216)
+    }
+
+    @Test
+    fun numShownHotseatIcons() {
+        assertThat(dp.numShownHotseatIcons).isEqualTo(6)
+    }
+
+    @Test
+    fun hotseatBorderSpace() {
+        assertThat(dp.hotseatBorderSpace).isEqualTo(32)
+    }
+
+    @Test
+    fun isQsbInline() {
+        assertThat(dp.isQsbInline).isEqualTo(false)
+    }
+
+    @Test
+    fun qsbWidth() {
+        assertThat(dp.qsbWidth).isEqualTo(685)
+    }
+
+    @Test
+    fun isTaskbarPresent() {
+        assertThat(dp.isTaskbarPresent).isEqualTo(true)
+    }
+
+    @Test
+    fun isTaskbarPresentInApps() {
+        assertThat(dp.isTaskbarPresentInApps).isEqualTo(false)
+    }
+
+    @Test
+    fun taskbarSize() {
+        assertThat(dp.taskbarSize).isEqualTo(120)
+    }
+
+    @Test
+    fun desiredWorkspaceHorizontalMarginPx() {
+        assertThat(dp.desiredWorkspaceHorizontalMarginPx).isEqualTo(52)
+    }
+
+    @Test
+    fun workspacePaddingLeft() {
+        assertThat(dp.workspacePadding.left).isEqualTo(33)
+    }
+
+    @Test
+    fun workspacePaddingTop() {
+        assertThat(dp.workspacePadding.top).isEqualTo(0)
+    }
+
+    @Test
+    fun workspacePaddingRight() {
+        assertThat(dp.workspacePadding.right).isEqualTo(33)
+    }
+
+    @Test
+    fun workspacePaddingBottom() {
+        assertThat(dp.workspacePadding.bottom).isEqualTo(291)
+    }
+
+    @Test
+    fun iconScale() {
+        assertThat(dp.iconScale).isEqualTo(1)
+    }
+
+    @Test
+    fun cellScaleToFit() {
+        assertThat(dp.cellScaleToFit).isEqualTo(1.1976048f)
+    }
+
+    @Test
+    fun workspaceTopPadding() {
+        assertThat(dp.workspaceTopPadding).isEqualTo(0)
+    }
+
+    @Test
+    fun workspaceBottomPadding() {
+        assertThat(dp.workspaceBottomPadding).isEqualTo(0)
+    }
+
+    @Test
+    fun overviewTaskMarginPx() {
+        assertThat(dp.overviewTaskMarginPx).isEqualTo(32)
+    }
+
+    @Test
+    fun overviewTaskMarginGridPx() {
+        assertThat(dp.overviewTaskMarginGridPx).isEqualTo(32)
+    }
+
+    @Test
+    fun overviewTaskIconSizePx() {
+        assertThat(dp.overviewTaskIconSizePx).isEqualTo(96)
+    }
+
+    @Test
+    fun overviewTaskIconDrawableSizePx() {
+        assertThat(dp.overviewTaskIconDrawableSizePx).isEqualTo(88)
+    }
+
+    @Test
+    fun overviewTaskIconDrawableSizeGridPx() {
+        assertThat(dp.overviewTaskIconDrawableSizeGridPx).isEqualTo(88)
+    }
+
+    @Test
+    fun overviewTaskThumbnailTopMarginPx() {
+        assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(160)
+    }
+
+    @Test
+    fun overviewActionsTopMarginPx() {
+        assertThat(dp.overviewActionsTopMarginPx).isEqualTo(48)
+    }
+
+    @Test
+    fun overviewActionsHeight() {
+        assertThat(dp.overviewActionsHeight).isEqualTo(96)
+    }
+
+    @Test
+    fun overviewActionsButtonSpacing() {
+        assertThat(dp.overviewActionsButtonSpacing).isEqualTo(72)
+    }
+
+    @Test
+    fun overviewPageSpacing() {
+        assertThat(dp.overviewPageSpacing).isEqualTo(88)
+    }
+
+    @Test
+    fun overviewRowSpacing() {
+        assertThat(dp.overviewRowSpacing).isEqualTo(40)
+    }
+
+    @Test
+    fun overviewGridSideMargin() {
+        assertThat(dp.overviewGridSideMargin).isEqualTo(128)
+    }
+
+    @Test
+    fun dropTargetBarTopMarginPx() {
+        assertThat(dp.dropTargetBarTopMarginPx).isEqualTo(220)
+    }
+
+    @Test
+    fun dropTargetBarSizePx() {
+        assertThat(dp.dropTargetBarSizePx).isEqualTo(144)
+    }
+
+    @Test
+    fun dropTargetBarBottomMarginPx() {
+        assertThat(dp.dropTargetBarBottomMarginPx).isEqualTo(96)
+    }
+
+    @Test
+    fun workspaceSpringLoadedMinNextPageVisiblePx() {
+        assertThat(dp.workspaceSpringLoadedMinNextPageVisiblePx).isEqualTo(48)
+    }
+
+    @Test
+    fun getWorkspaceSpringLoadScale() {
+        assertThat(dp.workspaceSpringLoadScale).isEqualTo(0.69064087f)
+    }
+
+    @Test
+    fun getCellLayoutHeight() {
+        assertThat(dp.cellLayoutHeight).isEqualTo(2169)
+    }
+
+    @Test
+    fun getCellLayoutWidth() {
+        assertThat(dp.cellLayoutWidth).isEqualTo(767)
+    }
+
+    @Test
+    fun getPanelCount() {
+        assertThat(dp.panelCount).isEqualTo(2)
+    }
+
+    @Test
+    fun isVerticalBarLayout() {
+        assertThat(dp.isVerticalBarLayout).isEqualTo(false)
+    }
+
+    @Test
+    fun getCellLayoutSpringLoadShrunkTop() {
+        assertThat(dp.cellLayoutSpringLoadShrunkTop).isEqualTo(460)
+    }
+
+    @Test
+    fun getCellLayoutSpringLoadShrunkBottom() {
+        assertThat(dp.cellLayoutSpringLoadShrunkBottom).isEqualTo(1958)
+    }
+
+    @Test
+    fun getQsbOffsetY() {
+        assertThat(dp.qsbOffsetY).isEqualTo(272)
+    }
+
+    @Test
+    fun getTaskbarOffsetY() {
+        assertThat(dp.taskbarOffsetY).isEqualTo(112)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingLeft() {
+        assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(340)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingTop() {
+        assertThat(dp.getHotseatLayoutPadding(context).top).isEqualTo(151)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingRight() {
+        assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(428)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingBottom() {
+        assertThat(dp.getHotseatLayoutPadding(context).bottom).isEqualTo(109)
+    }
+
+    @Test
+    fun hotseatBarEndOffset() {
+        assertThat(dp.hotseatBarEndOffset).isEqualTo(428)
+    }
+}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelPortraitTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelPortraitTest.kt
new file mode 100644
index 0000000..0c5968e
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelPortraitTest.kt
@@ -0,0 +1,486 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.DeviceProfileBaseTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Tests for DeviceProfile for two panel in portrait.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceProfileTwoPanelPortraitTest : DeviceProfileBaseTest() {
+
+    lateinit var dp: DeviceProfile
+
+    @Before
+    fun before() {
+        initializeVarsForTablet(isTwoPanel = true)
+        dp = newDP()
+    }
+
+    @Test
+    fun isScalableGrid() {
+        assertThat(dp.isScalableGrid).isTrue()
+    }
+
+    @Test
+    fun cellWidthPx() {
+        assertThat(dp.cellWidthPx).isEqualTo(153)
+    }
+
+    @Test
+    fun cellHeightPx() {
+        assertThat(dp.cellHeightPx).isEqualTo(199)
+    }
+
+    @Test
+    fun getCellSizeX() {
+        assertThat(dp.cellSize.x).isEqualTo(153)
+    }
+
+    @Test
+    fun getCellSizeY() {
+        assertThat(dp.cellSize.y).isEqualTo(509)
+    }
+
+    @Test
+    fun cellLayoutBorderSpacePxX() {
+        assertThat(dp.cellLayoutBorderSpacePx.x).isEqualTo(38)
+    }
+
+    @Test
+    fun cellLayoutBorderSpacePxY() {
+        assertThat(dp.cellLayoutBorderSpacePx.y).isEqualTo(38)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxLeft() {
+        assertThat(dp.cellLayoutPaddingPx.left).isEqualTo(19)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxTop() {
+        assertThat(dp.cellLayoutPaddingPx.top).isEqualTo(0)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxRight() {
+        assertThat(dp.cellLayoutPaddingPx.right).isEqualTo(19)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxBottom() {
+        assertThat(dp.cellLayoutPaddingPx.bottom).isEqualTo(19)
+    }
+
+    @Test
+    fun iconSizePx() {
+        assertThat(dp.iconSizePx).isEqualTo(112)
+    }
+
+    @Test
+    fun iconTextSizePx() {
+        assertThat(dp.iconTextSizePx).isEqualTo(28)
+    }
+
+    @Test
+    fun iconDrawablePaddingPx() {
+        assertThat(dp.iconDrawablePaddingPx).isEqualTo(14)
+    }
+
+    @Test
+    fun folderCellWidthPx() {
+        assertThat(dp.folderCellWidthPx).isEqualTo(153)
+    }
+
+    @Test
+    fun folderCellHeightPx() {
+        assertThat(dp.folderCellHeightPx).isEqualTo(199)
+    }
+
+    @Test
+    fun folderChildIconSizePx() {
+        assertThat(dp.folderChildIconSizePx).isEqualTo(112)
+    }
+
+    @Test
+    fun folderChildTextSizePx() {
+        assertThat(dp.folderChildTextSizePx).isEqualTo(28)
+    }
+
+    @Test
+    fun folderChildDrawablePaddingPx() {
+        assertThat(dp.folderChildDrawablePaddingPx).isEqualTo(16)
+    }
+
+    @Test
+    fun folderCellLayoutBorderSpaceOriginalPx() {
+        assertThat(dp.folderCellLayoutBorderSpaceOriginalPx).isEqualTo(0)
+    }
+
+    @Test
+    fun folderCellLayoutBorderSpacePxX() {
+        assertThat(dp.folderCellLayoutBorderSpacePx.x).isEqualTo(0)
+    }
+
+    @Test
+    fun folderCellLayoutBorderSpacePxY() {
+        assertThat(dp.folderCellLayoutBorderSpacePx.y).isEqualTo(0)
+    }
+
+    @Test
+    fun bottomSheetTopPadding() {
+        assertThat(dp.bottomSheetTopPadding).isEqualTo(600)
+    }
+
+    @Test
+    fun allAppsShiftRange() {
+        assertThat(dp.allAppsShiftRange).isEqualTo(1960)
+    }
+
+    @Test
+    fun allAppsTopPadding() {
+        assertThat(dp.allAppsTopPadding).isEqualTo(600)
+    }
+
+    @Test
+    fun allAppsIconSizePx() {
+        assertThat(dp.allAppsIconSizePx).isEqualTo(134)
+    }
+
+    @Test
+    fun allAppsIconTextSizePx() {
+        assertThat(dp.allAppsIconTextSizePx).isEqualTo(34)
+    }
+
+    @Test
+    fun allAppsIconDrawablePaddingPx() {
+        assertThat(dp.allAppsIconDrawablePaddingPx).isEqualTo(14)
+    }
+
+    @Test
+    fun allAppsCellHeightPx() {
+        assertThat(dp.allAppsCellHeightPx).isEqualTo(237)
+    }
+
+    @Test
+    fun allAppsCellWidthPx() {
+        assertThat(dp.allAppsCellWidthPx).isEqualTo(153)
+    }
+
+    @Test
+    fun allAppsBorderSpacePxX() {
+        assertThat(dp.allAppsBorderSpacePx.x).isEqualTo(38)
+    }
+
+    @Test
+    fun allAppsBorderSpacePxY() {
+        assertThat(dp.allAppsBorderSpacePx.y).isEqualTo(38)
+    }
+
+    @Test
+    fun numShownAllAppsColumns() {
+        assertThat(dp.numShownAllAppsColumns).isEqualTo(0)
+    }
+
+    @Test
+    fun allAppsLeftRightPadding() {
+        assertThat(dp.allAppsLeftRightPadding).isEqualTo(56)
+    }
+
+    @Test
+    fun allAppsLeftRightMargin() {
+        assertThat(dp.allAppsLeftRightMargin).isEqualTo(763)
+    }
+
+    @Test
+    fun hotseatBarSizePx() {
+        assertThat(dp.hotseatBarSizePx).isEqualTo(386)
+    }
+
+    @Test
+    fun hotseatCellHeightPx() {
+        assertThat(dp.hotseatCellHeightPx).isEqualTo(126)
+    }
+
+    @Test
+    fun hotseatBarBottomSpacePx() {
+        assertThat(dp.hotseatBarBottomSpacePx).isEqualTo(116)
+    }
+
+    @Test
+    fun hotseatBarSidePaddingStartPx() {
+        assertThat(dp.hotseatBarSidePaddingStartPx).isEqualTo(0)
+    }
+
+    @Test
+    fun hotseatBarSidePaddingEndPx() {
+        assertThat(dp.hotseatBarSidePaddingEndPx).isEqualTo(0)
+    }
+
+    @Test
+    fun hotseatQsbSpace() {
+        assertThat(dp.hotseatQsbSpace).isEqualTo(56)
+    }
+
+    @Test
+    fun hotseatQsbHeight() {
+        assertThat(dp.hotseatQsbHeight).isEqualTo(126)
+    }
+
+    @Test
+    fun springLoadedHotseatBarTopMarginPx() {
+        assertThat(dp.springLoadedHotseatBarTopMarginPx).isEqualTo(216)
+    }
+
+    @Test
+    fun numShownHotseatIcons() {
+        assertThat(dp.numShownHotseatIcons).isEqualTo(6)
+    }
+
+    @Test
+    fun hotseatBorderSpace() {
+        assertThat(dp.hotseatBorderSpace).isEqualTo(2)
+    }
+
+    @Test
+    fun isQsbInline() {
+        assertThat(dp.isQsbInline).isEqualTo(false)
+    }
+
+    @Test
+    fun qsbWidth() {
+        assertThat(dp.qsbWidth).isEqualTo(685)
+    }
+
+    @Test
+    fun isTaskbarPresent() {
+        assertThat(dp.isTaskbarPresent).isEqualTo(true)
+    }
+
+    @Test
+    fun isTaskbarPresentInApps() {
+        assertThat(dp.isTaskbarPresentInApps).isEqualTo(false)
+    }
+
+    @Test
+    fun taskbarSize() {
+        assertThat(dp.taskbarSize).isEqualTo(120)
+    }
+
+    @Test
+    fun desiredWorkspaceHorizontalMarginPx() {
+        assertThat(dp.desiredWorkspaceHorizontalMarginPx).isEqualTo(52)
+    }
+
+    @Test
+    fun workspacePaddingLeft() {
+        assertThat(dp.workspacePadding.left).isEqualTo(33)
+    }
+
+    @Test
+    fun workspacePaddingTop() {
+        assertThat(dp.workspacePadding.top).isEqualTo(0)
+    }
+
+    @Test
+    fun workspacePaddingRight() {
+        assertThat(dp.workspacePadding.right).isEqualTo(33)
+    }
+
+    @Test
+    fun workspacePaddingBottom() {
+        assertThat(dp.workspacePadding.bottom).isEqualTo(291)
+    }
+
+    @Test
+    fun iconScale() {
+        assertThat(dp.iconScale).isEqualTo(1)
+    }
+
+    @Test
+    fun cellScaleToFit() {
+        assertThat(dp.cellScaleToFit).isEqualTo(1.1976048f)
+    }
+
+    @Test
+    fun workspaceTopPadding() {
+        assertThat(dp.workspaceTopPadding).isEqualTo(0)
+    }
+
+    @Test
+    fun workspaceBottomPadding() {
+        assertThat(dp.workspaceBottomPadding).isEqualTo(0)
+    }
+
+    @Test
+    fun overviewTaskMarginPx() {
+        assertThat(dp.overviewTaskMarginPx).isEqualTo(32)
+    }
+
+    @Test
+    fun overviewTaskMarginGridPx() {
+        assertThat(dp.overviewTaskMarginGridPx).isEqualTo(32)
+    }
+
+    @Test
+    fun overviewTaskIconSizePx() {
+        assertThat(dp.overviewTaskIconSizePx).isEqualTo(96)
+    }
+
+    @Test
+    fun overviewTaskIconDrawableSizePx() {
+        assertThat(dp.overviewTaskIconDrawableSizePx).isEqualTo(88)
+    }
+
+    @Test
+    fun overviewTaskIconDrawableSizeGridPx() {
+        assertThat(dp.overviewTaskIconDrawableSizeGridPx).isEqualTo(88)
+    }
+
+    @Test
+    fun overviewTaskThumbnailTopMarginPx() {
+        assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(160)
+    }
+
+    @Test
+    fun overviewActionsTopMarginPx() {
+        assertThat(dp.overviewActionsTopMarginPx).isEqualTo(48)
+    }
+
+    @Test
+    fun overviewActionsHeight() {
+        assertThat(dp.overviewActionsHeight).isEqualTo(96)
+    }
+
+    @Test
+    fun overviewActionsButtonSpacing() {
+        assertThat(dp.overviewActionsButtonSpacing).isEqualTo(72)
+    }
+
+    @Test
+    fun overviewPageSpacing() {
+        assertThat(dp.overviewPageSpacing).isEqualTo(88)
+    }
+
+    @Test
+    fun overviewRowSpacing() {
+        assertThat(dp.overviewRowSpacing).isEqualTo(40)
+    }
+
+    @Test
+    fun overviewGridSideMargin() {
+        assertThat(dp.overviewGridSideMargin).isEqualTo(128)
+    }
+
+    @Test
+    fun dropTargetBarTopMarginPx() {
+        assertThat(dp.dropTargetBarTopMarginPx).isEqualTo(220)
+    }
+
+    @Test
+    fun dropTargetBarSizePx() {
+        assertThat(dp.dropTargetBarSizePx).isEqualTo(144)
+    }
+
+    @Test
+    fun dropTargetBarBottomMarginPx() {
+        assertThat(dp.dropTargetBarBottomMarginPx).isEqualTo(96)
+    }
+
+    @Test
+    fun workspaceSpringLoadedMinNextPageVisiblePx() {
+        assertThat(dp.workspaceSpringLoadedMinNextPageVisiblePx).isEqualTo(48)
+    }
+
+    @Test
+    fun getWorkspaceSpringLoadScale() {
+        assertThat(dp.workspaceSpringLoadScale).isEqualTo(0.69064087f)
+    }
+
+    @Test
+    fun getCellLayoutHeight() {
+        assertThat(dp.cellLayoutHeight).isEqualTo(2169)
+    }
+
+    @Test
+    fun getCellLayoutWidth() {
+        assertThat(dp.cellLayoutWidth).isEqualTo(767)
+    }
+
+    @Test
+    fun getPanelCount() {
+        assertThat(dp.panelCount).isEqualTo(2)
+    }
+
+    @Test
+    fun isVerticalBarLayout() {
+        assertThat(dp.isVerticalBarLayout).isEqualTo(false)
+    }
+
+    @Test
+    fun getCellLayoutSpringLoadShrunkTop() {
+        assertThat(dp.cellLayoutSpringLoadShrunkTop).isEqualTo(460)
+    }
+
+    @Test
+    fun getCellLayoutSpringLoadShrunkBottom() {
+        assertThat(dp.cellLayoutSpringLoadShrunkBottom).isEqualTo(1958)
+    }
+
+    @Test
+    fun getQsbOffsetY() {
+        assertThat(dp.qsbOffsetY).isEqualTo(272)
+    }
+
+    @Test
+    fun getTaskbarOffsetY() {
+        assertThat(dp.taskbarOffsetY).isEqualTo(112)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingLeft() {
+        assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(459)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingTop() {
+        assertThat(dp.getHotseatLayoutPadding(context).top).isEqualTo(151)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingRight() {
+        assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(459)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingBottom() {
+        assertThat(dp.getHotseatLayoutPadding(context).bottom).isEqualTo(109)
+    }
+
+    @Test
+    fun hotseatBarEndOffset() {
+        assertThat(dp.hotseatBarEndOffset).isEqualTo(0)
+    }
+}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfileVerticalBar3ButtonTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfileVerticalBar3ButtonTest.kt
new file mode 100644
index 0000000..2bad6bb
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/DeviceProfileVerticalBar3ButtonTest.kt
@@ -0,0 +1,486 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.DeviceProfileBaseTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Tests for DeviceProfile for landscape phone with vertical bar and 3-Button navigation.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceProfileVerticalBar3ButtonTest : DeviceProfileBaseTest() {
+
+    lateinit var dp: DeviceProfile
+
+    @Before
+    fun before() {
+        initializeVarsForPhone(isVerticalBar = true, isGestureMode = false)
+        dp = newDP()
+    }
+
+    @Test
+    fun isScalableGrid() {
+        assertThat(dp.isScalableGrid).isFalse()
+    }
+
+    @Test
+    fun cellWidthPx() {
+        assertThat(dp.cellWidthPx).isEqualTo(210)
+    }
+
+    @Test
+    fun cellHeightPx() {
+        assertThat(dp.cellHeightPx).isEqualTo(221)
+    }
+
+    @Test
+    fun getCellSizeX() {
+        assertThat(dp.cellSize.x).isEqualTo(675)
+    }
+
+    @Test
+    fun getCellSizeY() {
+        assertThat(dp.cellSize.y).isEqualTo(321)
+    }
+
+    @Test
+    fun cellLayoutBorderSpacePxX() {
+        assertThat(dp.cellLayoutBorderSpacePx.x).isEqualTo(0)
+    }
+
+    @Test
+    fun cellLayoutBorderSpacePxY() {
+        assertThat(dp.cellLayoutBorderSpacePx.y).isEqualTo(0)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxLeft() {
+        assertThat(dp.cellLayoutPaddingPx.left).isEqualTo(70)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxTop() {
+        assertThat(dp.cellLayoutPaddingPx.top).isEqualTo(0)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxRight() {
+        assertThat(dp.cellLayoutPaddingPx.right).isEqualTo(70)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxBottom() {
+        assertThat(dp.cellLayoutPaddingPx.bottom).isEqualTo(53)
+    }
+
+    @Test
+    fun iconSizePx() {
+        assertThat(dp.iconSizePx).isEqualTo(196)
+    }
+
+    @Test
+    fun iconTextSizePx() {
+        assertThat(dp.iconTextSizePx).isEqualTo(0)
+    }
+
+    @Test
+    fun iconDrawablePaddingPx() {
+        assertThat(dp.iconDrawablePaddingPx).isEqualTo(0)
+    }
+
+    @Test
+    fun folderCellWidthPx() {
+        assertThat(dp.folderCellWidthPx).isEqualTo(260)
+    }
+
+    @Test
+    fun folderCellHeightPx() {
+        assertThat(dp.folderCellHeightPx).isEqualTo(304)
+    }
+
+    @Test
+    fun folderChildIconSizePx() {
+        assertThat(dp.folderChildIconSizePx).isEqualTo(196)
+    }
+
+    @Test
+    fun folderChildTextSizePx() {
+        assertThat(dp.folderChildTextSizePx).isEqualTo(49)
+    }
+
+    @Test
+    fun folderChildDrawablePaddingPx() {
+        assertThat(dp.folderChildDrawablePaddingPx).isEqualTo(14)
+    }
+
+    @Test
+    fun folderCellLayoutBorderSpaceOriginalPx() {
+        assertThat(dp.folderCellLayoutBorderSpaceOriginalPx).isEqualTo(0)
+    }
+
+    @Test
+    fun folderCellLayoutBorderSpacePxX() {
+        assertThat(dp.folderCellLayoutBorderSpacePx.x).isEqualTo(0)
+    }
+
+    @Test
+    fun folderCellLayoutBorderSpacePxY() {
+        assertThat(dp.folderCellLayoutBorderSpacePx.y).isEqualTo(0)
+    }
+
+    @Test
+    fun bottomSheetTopPadding() {
+        assertThat(dp.bottomSheetTopPadding).isEqualTo(53)
+    }
+
+    @Test
+    fun allAppsShiftRange() {
+        assertThat(dp.allAppsShiftRange).isEqualTo(1050)
+    }
+
+    @Test
+    fun allAppsTopPadding() {
+        assertThat(dp.allAppsTopPadding).isEqualTo(0)
+    }
+
+    @Test
+    fun allAppsIconSizePx() {
+        assertThat(dp.allAppsIconSizePx).isEqualTo(196)
+    }
+
+    @Test
+    fun allAppsIconTextSizePx() {
+        assertThat(dp.allAppsIconTextSizePx).isEqualTo(49)
+    }
+
+    @Test
+    fun allAppsIconDrawablePaddingPx() {
+        assertThat(dp.allAppsIconDrawablePaddingPx).isEqualTo(28)
+    }
+
+    @Test
+    fun allAppsCellHeightPx() {
+        assertThat(dp.allAppsCellHeightPx).isEqualTo(422)
+    }
+
+    @Test
+    fun allAppsCellWidthPx() {
+        assertThat(dp.allAppsCellWidthPx).isEqualTo(252)
+    }
+
+    @Test
+    fun allAppsBorderSpacePxX() {
+        assertThat(dp.allAppsBorderSpacePx.x).isEqualTo(56)
+    }
+
+    @Test
+    fun allAppsBorderSpacePxY() {
+        assertThat(dp.allAppsBorderSpacePx.y).isEqualTo(56)
+    }
+
+    @Test
+    fun numShownAllAppsColumns() {
+        assertThat(dp.numShownAllAppsColumns).isEqualTo(0)
+    }
+
+    @Test
+    fun allAppsLeftRightPadding() {
+        assertThat(dp.allAppsLeftRightPadding).isEqualTo(0)
+    }
+
+    @Test
+    fun allAppsLeftRightMargin() {
+        assertThat(dp.allAppsLeftRightMargin).isEqualTo(0)
+    }
+
+    @Test
+    fun hotseatBarSizePx() {
+        assertThat(dp.hotseatBarSizePx).isEqualTo(336)
+    }
+
+    @Test
+    fun hotseatCellHeightPx() {
+        assertThat(dp.hotseatCellHeightPx).isEqualTo(221)
+    }
+
+    @Test
+    fun hotseatBarBottomSpacePx() {
+        assertThat(dp.hotseatBarBottomSpacePx).isEqualTo(168)
+    }
+
+    @Test
+    fun hotseatBarSidePaddingStartPx() {
+        assertThat(dp.hotseatBarSidePaddingStartPx).isEqualTo(84)
+    }
+
+    @Test
+    fun hotseatBarSidePaddingEndPx() {
+        assertThat(dp.hotseatBarSidePaddingEndPx).isEqualTo(56)
+    }
+
+    @Test
+    fun hotseatQsbSpace() {
+        assertThat(dp.hotseatQsbSpace).isEqualTo(126)
+    }
+
+    @Test
+    fun hotseatQsbHeight() {
+        assertThat(dp.hotseatQsbHeight).isEqualTo(221)
+    }
+
+    @Test
+    fun springLoadedHotseatBarTopMarginPx() {
+        assertThat(dp.springLoadedHotseatBarTopMarginPx).isEqualTo(158)
+    }
+
+    @Test
+    fun numShownHotseatIcons() {
+        assertThat(dp.numShownHotseatIcons).isEqualTo(4)
+    }
+
+    @Test
+    fun hotseatBorderSpace() {
+        assertThat(dp.hotseatBorderSpace).isEqualTo(0)
+    }
+
+    @Test
+    fun isQsbInline() {
+        assertThat(dp.isQsbInline).isEqualTo(false)
+    }
+
+    @Test
+    fun qsbWidth() {
+        assertThat(dp.qsbWidth).isEqualTo(2221)
+    }
+
+    @Test
+    fun isTaskbarPresent() {
+        assertThat(dp.isTaskbarPresent).isEqualTo(false)
+    }
+
+    @Test
+    fun isTaskbarPresentInApps() {
+        assertThat(dp.isTaskbarPresentInApps).isEqualTo(false)
+    }
+
+    @Test
+    fun taskbarSize() {
+        assertThat(dp.taskbarSize).isEqualTo(0)
+    }
+
+    @Test
+    fun desiredWorkspaceHorizontalMarginPx() {
+        assertThat(dp.desiredWorkspaceHorizontalMarginPx).isEqualTo(0)
+    }
+
+    @Test
+    fun workspacePaddingLeft() {
+        assertThat(dp.workspacePadding.left).isEqualTo(14)
+    }
+
+    @Test
+    fun workspacePaddingTop() {
+        assertThat(dp.workspacePadding.top).isEqualTo(0)
+    }
+
+    @Test
+    fun workspacePaddingRight() {
+        assertThat(dp.workspacePadding.right).isEqualTo(266)
+    }
+
+    @Test
+    fun workspacePaddingBottom() {
+        assertThat(dp.workspacePadding.bottom).isEqualTo(0)
+    }
+
+    @Test
+    fun iconScale() {
+        assertThat(dp.iconScale).isEqualTo(1)
+    }
+
+    @Test
+    fun cellScaleToFit() {
+        assertThat(dp.cellScaleToFit).isEqualTo(1.0f)
+    }
+
+    @Test
+    fun workspaceTopPadding() {
+        assertThat(dp.workspaceTopPadding).isEqualTo(0)
+    }
+
+    @Test
+    fun workspaceBottomPadding() {
+        assertThat(dp.workspaceBottomPadding).isEqualTo(0)
+    }
+
+    @Test
+    fun overviewTaskMarginPx() {
+        assertThat(dp.overviewTaskMarginPx).isEqualTo(56)
+    }
+
+    @Test
+    fun overviewTaskMarginGridPx() {
+        assertThat(dp.overviewTaskMarginGridPx).isEqualTo(0)
+    }
+
+    @Test
+    fun overviewTaskIconSizePx() {
+        assertThat(dp.overviewTaskIconSizePx).isEqualTo(168)
+    }
+
+    @Test
+    fun overviewTaskIconDrawableSizePx() {
+        assertThat(dp.overviewTaskIconDrawableSizePx).isEqualTo(154)
+    }
+
+    @Test
+    fun overviewTaskIconDrawableSizeGridPx() {
+        assertThat(dp.overviewTaskIconDrawableSizeGridPx).isEqualTo(0)
+    }
+
+    @Test
+    fun overviewTaskThumbnailTopMarginPx() {
+        assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(280)
+    }
+
+    @Test
+    fun overviewActionsTopMarginPx() {
+        assertThat(dp.overviewActionsTopMarginPx).isEqualTo(42)
+    }
+
+    @Test
+    fun overviewActionsHeight() {
+        assertThat(dp.overviewActionsHeight).isEqualTo(168)
+    }
+
+    @Test
+    fun overviewActionsButtonSpacing() {
+        assertThat(dp.overviewActionsButtonSpacing).isEqualTo(126)
+    }
+
+    @Test
+    fun overviewPageSpacing() {
+        assertThat(dp.overviewPageSpacing).isEqualTo(56)
+    }
+
+    @Test
+    fun overviewRowSpacing() {
+        assertThat(dp.overviewRowSpacing).isEqualTo(-112)
+    }
+
+    @Test
+    fun overviewGridSideMargin() {
+        assertThat(dp.overviewGridSideMargin).isEqualTo(0)
+    }
+
+    @Test
+    fun dropTargetBarTopMarginPx() {
+        assertThat(dp.dropTargetBarTopMarginPx).isEqualTo(21)
+    }
+
+    @Test
+    fun dropTargetBarSizePx() {
+        assertThat(dp.dropTargetBarSizePx).isEqualTo(126)
+    }
+
+    @Test
+    fun dropTargetBarBottomMarginPx() {
+        assertThat(dp.dropTargetBarBottomMarginPx).isEqualTo(21)
+    }
+
+    @Test
+    fun workspaceSpringLoadedMinNextPageVisiblePx() {
+        assertThat(dp.workspaceSpringLoadedMinNextPageVisiblePx).isEqualTo(84)
+    }
+
+    @Test
+    fun getWorkspaceSpringLoadScale() {
+        assertThat(dp.workspaceSpringLoadScale).isEqualTo(0.8880597f)
+    }
+
+    @Test
+    fun getCellLayoutHeight() {
+        assertThat(dp.cellLayoutHeight).isEqualTo(1340)
+    }
+
+    @Test
+    fun getCellLayoutWidth() {
+        assertThat(dp.cellLayoutWidth).isEqualTo(2840)
+    }
+
+    @Test
+    fun getPanelCount() {
+        assertThat(dp.panelCount).isEqualTo(1)
+    }
+
+    @Test
+    fun isVerticalBarLayout() {
+        assertThat(dp.isVerticalBarLayout).isEqualTo(true)
+    }
+
+    @Test
+    fun getCellLayoutSpringLoadShrunkTop() {
+        assertThat(dp.cellLayoutSpringLoadShrunkTop).isEqualTo(168)
+    }
+
+    @Test
+    fun getCellLayoutSpringLoadShrunkBottom() {
+        assertThat(dp.cellLayoutSpringLoadShrunkBottom).isEqualTo(1358)
+    }
+
+    @Test
+    fun getQsbOffsetY() {
+        assertThat(dp.qsbOffsetY).isEqualTo(147)
+    }
+
+    @Test
+    fun getTaskbarOffsetY() {
+        assertThat(dp.taskbarOffsetY).isEqualTo(225)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingLeft() {
+        assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(56)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingTop() {
+        assertThat(dp.getHotseatLayoutPadding(context).top).isEqualTo(0)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingRight() {
+        assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(84)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingBottom() {
+        assertThat(dp.getHotseatLayoutPadding(context).bottom).isEqualTo(165)
+    }
+
+    @Test
+    fun hotseatBarEndOffset() {
+        assertThat(dp.hotseatBarEndOffset).isEqualTo(0)
+    }
+}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/DeviceProfileVerticalBarTest.kt b/quickstep/tests/src/com/android/quickstep/DeviceProfileVerticalBarTest.kt
new file mode 100644
index 0000000..6256a43
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/DeviceProfileVerticalBarTest.kt
@@ -0,0 +1,486 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.DeviceProfile
+import com.android.launcher3.DeviceProfileBaseTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Tests for DeviceProfile for landscape phone with vertical bar.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceProfileVerticalBarTest : DeviceProfileBaseTest() {
+
+    lateinit var dp: DeviceProfile
+
+    @Before
+    fun before() {
+        initializeVarsForPhone(isVerticalBar = true)
+        dp = newDP()
+    }
+
+    @Test
+    fun isScalableGrid() {
+        assertThat(dp.isScalableGrid).isFalse()
+    }
+
+    @Test
+    fun cellWidthPx() {
+        assertThat(dp.cellWidthPx).isEqualTo(210)
+    }
+
+    @Test
+    fun cellHeightPx() {
+        assertThat(dp.cellHeightPx).isEqualTo(221)
+    }
+
+    @Test
+    fun getCellSizeX() {
+        assertThat(dp.cellSize.x).isEqualTo(675)
+    }
+
+    @Test
+    fun getCellSizeY() {
+        assertThat(dp.cellSize.y).isEqualTo(321)
+    }
+
+    @Test
+    fun cellLayoutBorderSpacePxX() {
+        assertThat(dp.cellLayoutBorderSpacePx.x).isEqualTo(0)
+    }
+
+    @Test
+    fun cellLayoutBorderSpacePxY() {
+        assertThat(dp.cellLayoutBorderSpacePx.y).isEqualTo(0)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxLeft() {
+        assertThat(dp.cellLayoutPaddingPx.left).isEqualTo(70)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxTop() {
+        assertThat(dp.cellLayoutPaddingPx.top).isEqualTo(0)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxRight() {
+        assertThat(dp.cellLayoutPaddingPx.right).isEqualTo(70)
+    }
+
+    @Test
+    fun cellLayoutPaddingPxBottom() {
+        assertThat(dp.cellLayoutPaddingPx.bottom).isEqualTo(53)
+    }
+
+    @Test
+    fun iconSizePx() {
+        assertThat(dp.iconSizePx).isEqualTo(196)
+    }
+
+    @Test
+    fun iconTextSizePx() {
+        assertThat(dp.iconTextSizePx).isEqualTo(0)
+    }
+
+    @Test
+    fun iconDrawablePaddingPx() {
+        assertThat(dp.iconDrawablePaddingPx).isEqualTo(0)
+    }
+
+    @Test
+    fun folderCellWidthPx() {
+        assertThat(dp.folderCellWidthPx).isEqualTo(260)
+    }
+
+    @Test
+    fun folderCellHeightPx() {
+        assertThat(dp.folderCellHeightPx).isEqualTo(304)
+    }
+
+    @Test
+    fun folderChildIconSizePx() {
+        assertThat(dp.folderChildIconSizePx).isEqualTo(196)
+    }
+
+    @Test
+    fun folderChildTextSizePx() {
+        assertThat(dp.folderChildTextSizePx).isEqualTo(49)
+    }
+
+    @Test
+    fun folderChildDrawablePaddingPx() {
+        assertThat(dp.folderChildDrawablePaddingPx).isEqualTo(14)
+    }
+
+    @Test
+    fun folderCellLayoutBorderSpaceOriginalPx() {
+        assertThat(dp.folderCellLayoutBorderSpaceOriginalPx).isEqualTo(0)
+    }
+
+    @Test
+    fun folderCellLayoutBorderSpacePxX() {
+        assertThat(dp.folderCellLayoutBorderSpacePx.x).isEqualTo(0)
+    }
+
+    @Test
+    fun folderCellLayoutBorderSpacePxY() {
+        assertThat(dp.folderCellLayoutBorderSpacePx.y).isEqualTo(0)
+    }
+
+    @Test
+    fun bottomSheetTopPadding() {
+        assertThat(dp.bottomSheetTopPadding).isEqualTo(53)
+    }
+
+    @Test
+    fun allAppsShiftRange() {
+        assertThat(dp.allAppsShiftRange).isEqualTo(1050)
+    }
+
+    @Test
+    fun allAppsTopPadding() {
+        assertThat(dp.allAppsTopPadding).isEqualTo(0)
+    }
+
+    @Test
+    fun allAppsIconSizePx() {
+        assertThat(dp.allAppsIconSizePx).isEqualTo(196)
+    }
+
+    @Test
+    fun allAppsIconTextSizePx() {
+        assertThat(dp.allAppsIconTextSizePx).isEqualTo(49)
+    }
+
+    @Test
+    fun allAppsIconDrawablePaddingPx() {
+        assertThat(dp.allAppsIconDrawablePaddingPx).isEqualTo(28)
+    }
+
+    @Test
+    fun allAppsCellHeightPx() {
+        assertThat(dp.allAppsCellHeightPx).isEqualTo(422)
+    }
+
+    @Test
+    fun allAppsCellWidthPx() {
+        assertThat(dp.allAppsCellWidthPx).isEqualTo(252)
+    }
+
+    @Test
+    fun allAppsBorderSpacePxX() {
+        assertThat(dp.allAppsBorderSpacePx.x).isEqualTo(56)
+    }
+
+    @Test
+    fun allAppsBorderSpacePxY() {
+        assertThat(dp.allAppsBorderSpacePx.y).isEqualTo(56)
+    }
+
+    @Test
+    fun numShownAllAppsColumns() {
+        assertThat(dp.numShownAllAppsColumns).isEqualTo(0)
+    }
+
+    @Test
+    fun allAppsLeftRightPadding() {
+        assertThat(dp.allAppsLeftRightPadding).isEqualTo(0)
+    }
+
+    @Test
+    fun allAppsLeftRightMargin() {
+        assertThat(dp.allAppsLeftRightMargin).isEqualTo(0)
+    }
+
+    @Test
+    fun hotseatBarSizePx() {
+        assertThat(dp.hotseatBarSizePx).isEqualTo(336)
+    }
+
+    @Test
+    fun hotseatCellHeightPx() {
+        assertThat(dp.hotseatCellHeightPx).isEqualTo(221)
+    }
+
+    @Test
+    fun hotseatBarBottomSpacePx() {
+        assertThat(dp.hotseatBarBottomSpacePx).isEqualTo(168)
+    }
+
+    @Test
+    fun hotseatBarSidePaddingStartPx() {
+        assertThat(dp.hotseatBarSidePaddingStartPx).isEqualTo(84)
+    }
+
+    @Test
+    fun hotseatBarSidePaddingEndPx() {
+        assertThat(dp.hotseatBarSidePaddingEndPx).isEqualTo(56)
+    }
+
+    @Test
+    fun hotseatQsbSpace() {
+        assertThat(dp.hotseatQsbSpace).isEqualTo(126)
+    }
+
+    @Test
+    fun hotseatQsbHeight() {
+        assertThat(dp.hotseatQsbHeight).isEqualTo(221)
+    }
+
+    @Test
+    fun springLoadedHotseatBarTopMarginPx() {
+        assertThat(dp.springLoadedHotseatBarTopMarginPx).isEqualTo(158)
+    }
+
+    @Test
+    fun numShownHotseatIcons() {
+        assertThat(dp.numShownHotseatIcons).isEqualTo(4)
+    }
+
+    @Test
+    fun hotseatBorderSpace() {
+        assertThat(dp.hotseatBorderSpace).isEqualTo(0)
+    }
+
+    @Test
+    fun isQsbInline() {
+        assertThat(dp.isQsbInline).isEqualTo(false)
+    }
+
+    @Test
+    fun qsbWidth() {
+        assertThat(dp.qsbWidth).isEqualTo(2221)
+    }
+
+    @Test
+    fun isTaskbarPresent() {
+        assertThat(dp.isTaskbarPresent).isEqualTo(false)
+    }
+
+    @Test
+    fun isTaskbarPresentInApps() {
+        assertThat(dp.isTaskbarPresentInApps).isEqualTo(false)
+    }
+
+    @Test
+    fun taskbarSize() {
+        assertThat(dp.taskbarSize).isEqualTo(0)
+    }
+
+    @Test
+    fun desiredWorkspaceHorizontalMarginPx() {
+        assertThat(dp.desiredWorkspaceHorizontalMarginPx).isEqualTo(0)
+    }
+
+    @Test
+    fun workspacePaddingLeft() {
+        assertThat(dp.workspacePadding.left).isEqualTo(14)
+    }
+
+    @Test
+    fun workspacePaddingTop() {
+        assertThat(dp.workspacePadding.top).isEqualTo(0)
+    }
+
+    @Test
+    fun workspacePaddingRight() {
+        assertThat(dp.workspacePadding.right).isEqualTo(266)
+    }
+
+    @Test
+    fun workspacePaddingBottom() {
+        assertThat(dp.workspacePadding.bottom).isEqualTo(0)
+    }
+
+    @Test
+    fun iconScale() {
+        assertThat(dp.iconScale).isEqualTo(1)
+    }
+
+    @Test
+    fun cellScaleToFit() {
+        assertThat(dp.cellScaleToFit).isEqualTo(1.0f)
+    }
+
+    @Test
+    fun workspaceTopPadding() {
+        assertThat(dp.workspaceTopPadding).isEqualTo(0)
+    }
+
+    @Test
+    fun workspaceBottomPadding() {
+        assertThat(dp.workspaceBottomPadding).isEqualTo(0)
+    }
+
+    @Test
+    fun overviewTaskMarginPx() {
+        assertThat(dp.overviewTaskMarginPx).isEqualTo(56)
+    }
+
+    @Test
+    fun overviewTaskMarginGridPx() {
+        assertThat(dp.overviewTaskMarginGridPx).isEqualTo(0)
+    }
+
+    @Test
+    fun overviewTaskIconSizePx() {
+        assertThat(dp.overviewTaskIconSizePx).isEqualTo(168)
+    }
+
+    @Test
+    fun overviewTaskIconDrawableSizePx() {
+        assertThat(dp.overviewTaskIconDrawableSizePx).isEqualTo(154)
+    }
+
+    @Test
+    fun overviewTaskIconDrawableSizeGridPx() {
+        assertThat(dp.overviewTaskIconDrawableSizeGridPx).isEqualTo(0)
+    }
+
+    @Test
+    fun overviewTaskThumbnailTopMarginPx() {
+        assertThat(dp.overviewTaskThumbnailTopMarginPx).isEqualTo(280)
+    }
+
+    @Test
+    fun overviewActionsTopMarginPx() {
+        assertThat(dp.overviewActionsTopMarginPx).isEqualTo(42)
+    }
+
+    @Test
+    fun overviewActionsHeight() {
+        assertThat(dp.overviewActionsHeight).isEqualTo(168)
+    }
+
+    @Test
+    fun overviewActionsButtonSpacing() {
+        assertThat(dp.overviewActionsButtonSpacing).isEqualTo(126)
+    }
+
+    @Test
+    fun overviewPageSpacing() {
+        assertThat(dp.overviewPageSpacing).isEqualTo(56)
+    }
+
+    @Test
+    fun overviewRowSpacing() {
+        assertThat(dp.overviewRowSpacing).isEqualTo(-112)
+    }
+
+    @Test
+    fun overviewGridSideMargin() {
+        assertThat(dp.overviewGridSideMargin).isEqualTo(0)
+    }
+
+    @Test
+    fun dropTargetBarTopMarginPx() {
+        assertThat(dp.dropTargetBarTopMarginPx).isEqualTo(21)
+    }
+
+    @Test
+    fun dropTargetBarSizePx() {
+        assertThat(dp.dropTargetBarSizePx).isEqualTo(126)
+    }
+
+    @Test
+    fun dropTargetBarBottomMarginPx() {
+        assertThat(dp.dropTargetBarBottomMarginPx).isEqualTo(21)
+    }
+
+    @Test
+    fun workspaceSpringLoadedMinNextPageVisiblePx() {
+        assertThat(dp.workspaceSpringLoadedMinNextPageVisiblePx).isEqualTo(84)
+    }
+
+    @Test
+    fun getWorkspaceSpringLoadScale() {
+        assertThat(dp.workspaceSpringLoadScale).isEqualTo(0.8880597f)
+    }
+
+    @Test
+    fun getCellLayoutHeight() {
+        assertThat(dp.cellLayoutHeight).isEqualTo(1340)
+    }
+
+    @Test
+    fun getCellLayoutWidth() {
+        assertThat(dp.cellLayoutWidth).isEqualTo(2840)
+    }
+
+    @Test
+    fun getPanelCount() {
+        assertThat(dp.panelCount).isEqualTo(1)
+    }
+
+    @Test
+    fun isVerticalBarLayout() {
+        assertThat(dp.isVerticalBarLayout).isEqualTo(true)
+    }
+
+    @Test
+    fun getCellLayoutSpringLoadShrunkTop() {
+        assertThat(dp.cellLayoutSpringLoadShrunkTop).isEqualTo(168)
+    }
+
+    @Test
+    fun getCellLayoutSpringLoadShrunkBottom() {
+        assertThat(dp.cellLayoutSpringLoadShrunkBottom).isEqualTo(1358)
+    }
+
+    @Test
+    fun getQsbOffsetY() {
+        assertThat(dp.qsbOffsetY).isEqualTo(147)
+    }
+
+    @Test
+    fun getTaskbarOffsetY() {
+        assertThat(dp.taskbarOffsetY).isEqualTo(225)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingLeft() {
+        assertThat(dp.getHotseatLayoutPadding(context).left).isEqualTo(56)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingTop() {
+        assertThat(dp.getHotseatLayoutPadding(context).top).isEqualTo(0)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingRight() {
+        assertThat(dp.getHotseatLayoutPadding(context).right).isEqualTo(84)
+    }
+
+    @Test
+    fun getHotseatLayoutPaddingBottom() {
+        assertThat(dp.getHotseatLayoutPadding(context).bottom).isEqualTo(165)
+    }
+
+    @Test
+    fun hotseatBarEndOffset() {
+        assertThat(dp.hotseatBarEndOffset).isEqualTo(0)
+    }
+}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/TaskThumbnailViewTest.kt b/quickstep/tests/src/com/android/quickstep/TaskThumbnailViewTest.kt
new file mode 100644
index 0000000..cf3c8c9
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/TaskThumbnailViewTest.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep
+
+import android.graphics.Rect
+import android.graphics.RectF
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.launcher3.DeviceProfileBaseTest
+import com.android.quickstep.views.TaskThumbnailView.PreviewPositionHelper
+import com.android.systemui.shared.recents.model.ThumbnailData
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+
+/**
+ * Test for TaskThumbnailView class.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class TaskThumbnailViewTest : DeviceProfileBaseTest() {
+
+    private var mThumbnailData: ThumbnailData = mock(ThumbnailData::class.java)
+
+    private val mPreviewPositionHelper = PreviewPositionHelper()
+
+    @Test
+    fun getInsetsToDrawInFullscreen_clipTaskbarSizeFromBottomForTablets() {
+        initializeVarsForTablet()
+        val dp = newDP()
+        val previewRect = Rect(0, 0, 100, 100)
+        val canvasWidth = dp.widthPx / 2
+        val canvasHeight = dp.heightPx / 2
+        val currentRotation = 0
+        val isRtl = false
+
+        mPreviewPositionHelper.updateThumbnailMatrix(previewRect, mThumbnailData, canvasWidth,
+                canvasHeight, dp, currentRotation, isRtl)
+
+        val expectedClippedInsets = RectF(0f, 0f, 0f, dp.taskbarSize / 2f)
+        assertThat(mPreviewPositionHelper.getInsetsToDrawInFullscreen(dp))
+                .isEqualTo(expectedClippedInsets)
+    }
+
+    @Test
+    fun getInsetsToDrawInFullscreen_doNotClipTaskbarSizeFromBottomForPhones() {
+        initializeVarsForPhone()
+        val dp = newDP()
+        val previewRect = Rect(0, 0, 100, 100)
+        val canvasWidth = dp.widthPx / 2
+        val canvasHeight = dp.heightPx / 2
+        val currentRotation = 0
+        val isRtl = false
+
+        mPreviewPositionHelper.updateThumbnailMatrix(previewRect, mThumbnailData, canvasWidth,
+                canvasHeight, dp, currentRotation, isRtl)
+
+        val expectedClippedInsets = RectF(0f, 0f, 0f, 0f)
+        assertThat(mPreviewPositionHelper.getInsetsToDrawInFullscreen(dp))
+                .isEqualTo(expectedClippedInsets)
+    }
+}
\ No newline at end of file
diff --git a/res/drawable/bg_work_apps_paused_action_button.xml b/res/drawable/bg_work_apps_paused_action_button.xml
new file mode 100644
index 0000000..74d4693
--- /dev/null
+++ b/res/drawable/bg_work_apps_paused_action_button.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="@color/accent_ripple_color">
+    <item android:id="@android:id/mask">
+        <shape android:shape="rectangle">
+            <corners android:radius="@dimen/rounded_button_radius" />
+            <solid android:color="@android:color/white" />
+        </shape>
+    </item>
+
+    <item android:id="@android:id/background">
+        <shape android:shape="rectangle">
+            <solid android:color="?android:attr/colorControlHighlight" />
+            <corners android:radius="@dimen/rounded_button_radius" />
+        </shape>
+    </item>
+</ripple>
\ No newline at end of file
diff --git a/res/drawable/work_apps_toggle_background.xml b/res/drawable/work_apps_toggle_background.xml
index a47c8fe..6ad6c82 100644
--- a/res/drawable/work_apps_toggle_background.xml
+++ b/res/drawable/work_apps_toggle_background.xml
@@ -13,16 +13,8 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_enabled="false">
-        <shape android:shape="rectangle">
-            <corners android:radius="@dimen/work_fab_radius" />
-            <solid android:color="?android:attr/colorControlHighlight" />
-            <padding
-                android:left="@dimen/work_profile_footer_padding"
-                android:right="@dimen/work_profile_footer_padding" />
-        </shape>
-    </item>
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="@color/accent_ripple_color">
     <item>
         <shape android:shape="rectangle">
             <corners android:radius="@dimen/work_fab_radius" />
@@ -32,4 +24,4 @@
                 android:right="@dimen/work_profile_footer_padding" />
         </shape>
     </item>
-</selector>
+</ripple>
diff --git a/res/layout/widgets_full_sheet_paged_view.xml b/res/layout/widgets_full_sheet_paged_view.xml
index dfe226a..098c9b0 100644
--- a/res/layout/widgets_full_sheet_paged_view.xml
+++ b/res/layout/widgets_full_sheet_paged_view.xml
@@ -16,37 +16,35 @@
 <merge xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:launcher="http://schemas.android.com/apk/res-auto">
 
-    <com.android.launcher3.workprofile.PersonalWorkPagedView
+    <com.android.launcher3.widget.picker.WidgetPagedView
         android:id="@+id/widgets_view_pager"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:clipToPadding="false"
         android:layout_below="@id/collapse_handle"
         android:descendantFocusability="afterDescendants"
-        launcher:pageIndicator="@+id/tabs">
+        android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
+        launcher:pageIndicator="@+id/tabs" >
 
         <com.android.launcher3.widget.picker.WidgetsRecyclerView
             android:id="@+id/primary_widgets_list_view"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
-            android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
             android:clipToPadding="false" />
 
         <com.android.launcher3.widget.picker.WidgetsRecyclerView
             android:id="@+id/work_widgets_list_view"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
-            android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
             android:clipToPadding="false" />
 
-    </com.android.launcher3.workprofile.PersonalWorkPagedView>
+    </com.android.launcher3.widget.picker.WidgetPagedView>
 
     <!-- SearchAndRecommendationsView contains the tab layout as well -->
-    <com.android.launcher3.widget.picker.SearchAndRecommendationsView
+    <com.android.launcher3.views.StickyHeaderLayout
         android:id="@+id/search_and_recommendations_container"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
         android:layout_below="@id/collapse_handle"
         android:paddingBottom="0dp"
         android:orientation="vertical">
@@ -58,6 +56,7 @@
             android:gravity="center_horizontal"
             android:textSize="24sp"
             android:layout_marginTop="24dp"
+            android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
             android:textColor="?android:attr/textColorSecondary"
             android:text="@string/widget_button_text"/>
 
@@ -68,7 +67,8 @@
             android:elevation="0.1dp"
             android:background="?android:attr/colorBackground"
             android:paddingBottom="8dp"
-            android:clipToPadding="false">
+            android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
+            launcher:layout_sticky="true">
             <include layout="@layout/widgets_search_bar" />
         </FrameLayout>
 
@@ -79,6 +79,7 @@
             android:layout_marginTop="8dp"
             android:background="@drawable/widgets_recommendation_background"
             android:paddingVertical="@dimen/recommended_widgets_table_vertical_padding"
+            android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
             android:visibility="gone" />
 
         <com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip
@@ -91,7 +92,8 @@
             android:paddingLeft="@dimen/widget_tabs_horizontal_padding"
             android:paddingRight="@dimen/widget_tabs_horizontal_padding"
             android:background="?android:attr/colorBackground"
-            style="@style/TextHeadline">
+            style="@style/TextHeadline"
+            launcher:layout_sticky="true">
 
             <Button
                 android:id="@+id/tab_personal"
@@ -120,5 +122,5 @@
                 style="?android:attr/borderlessButtonStyle" />
         </com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip>
 
-    </com.android.launcher3.widget.picker.SearchAndRecommendationsView>
+    </com.android.launcher3.views.StickyHeaderLayout>
 </merge>
\ No newline at end of file
diff --git a/res/layout/widgets_full_sheet_recyclerview.xml b/res/layout/widgets_full_sheet_recyclerview.xml
index 6a5d6cb..9da3e87 100644
--- a/res/layout/widgets_full_sheet_recyclerview.xml
+++ b/res/layout/widgets_full_sheet_recyclerview.xml
@@ -13,21 +13,21 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<merge xmlns:android="http://schemas.android.com/apk/res/android">
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:launcher="http://schemas.android.com/apk/res-auto" >
     <com.android.launcher3.widget.picker.WidgetsRecyclerView
         android:id="@+id/primary_widgets_list_view"
         android:layout_below="@id/collapse_handle"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
+        android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
         android:clipToPadding="false" />
 
     <!-- SearchAndRecommendationsView without the tab layout as well -->
-    <com.android.launcher3.widget.picker.SearchAndRecommendationsView
+    <com.android.launcher3.views.StickyHeaderLayout
         android:id="@+id/search_and_recommendations_container"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
         android:layout_below="@id/collapse_handle"
         android:paddingBottom="16dp"
         android:orientation="vertical">
@@ -40,6 +40,7 @@
             android:textSize="24sp"
             android:layout_marginTop="24dp"
             android:textColor="?android:attr/textColorSecondary"
+            android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
             android:text="@string/widget_button_text"/>
 
         <FrameLayout
@@ -48,8 +49,10 @@
             android:layout_height="wrap_content"
             android:elevation="0.1dp"
             android:background="?android:attr/colorBackground"
+            android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
             android:paddingBottom="8dp"
-            android:clipToPadding="false">
+            android:clipToPadding="false"
+            launcher:layout_sticky="true" >
             <include layout="@layout/widgets_search_bar" />
         </FrameLayout>
 
@@ -60,7 +63,8 @@
             android:layout_marginTop="8dp"
             android:background="@drawable/widgets_recommendation_background"
             android:paddingVertical="@dimen/recommended_widgets_table_vertical_padding"
+            android:paddingHorizontal="@dimen/widget_list_horizontal_margin"
             android:visibility="gone" />
-    </com.android.launcher3.widget.picker.SearchAndRecommendationsView>
+    </com.android.launcher3.views.StickyHeaderLayout>
 
 </merge>
\ No newline at end of file
diff --git a/res/layout/work_apps_paused.xml b/res/layout/work_apps_paused.xml
index 79bce70..f614d9b 100644
--- a/res/layout/work_apps_paused.xml
+++ b/res/layout/work_apps_paused.xml
@@ -48,7 +48,7 @@
         android:textColor="?attr/workProfileOverlayTextColor"
         android:text="@string/work_apps_enable_btn_text"
         android:textAlignment="center"
-        android:background="@drawable/rounded_action_button"
+        android:background="@drawable/bg_work_apps_paused_action_button"
         android:paddingStart="16dp"
         android:paddingEnd="16dp"
         android:textSize="14sp" />
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index 55139b3..445bfda 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Item is verwyder"</string>
     <string name="undo" msgid="4151576204245173321">"Ontdoen"</string>
     <string name="action_move" msgid="4339390619886385032">"Skuif item"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Skuif na ry <xliff:g id="NUMBER_0">%1$s</xliff:g> kolom <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Skuif na ry <xliff:g id="NUMBER_0">%1$s</xliff:g> kolom <xliff:g id="NUMBER_1">%2$s</xliff:g> in <xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Skuif na posisie <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Skuif na gunstelingposisie <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="item_moved" msgid="4606538322571412879">"Item geskuif"</string>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 6b6147b..e5a7356 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"ንጥል ነገር ተንቀሳቅሷል"</string>
     <string name="undo" msgid="4151576204245173321">"ቀልብስ"</string>
     <string name="action_move" msgid="4339390619886385032">"ንጥልን አንቀሳቅስ"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"ወደ ረድፍ <xliff:g id="NUMBER_0">%1$s</xliff:g> ዓምድ <xliff:g id="NUMBER_1">%2$s</xliff:g> አንቀሳቅስ"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"በ<xliff:g id="STRING">%3$s</xliff:g> ውስጥ ወደ ረድፍ <xliff:g id="NUMBER_0">%1$s</xliff:g> ዓምድ <xliff:g id="NUMBER_1">%2$s</xliff:g> ይውሰዱ"</string>
     <string name="move_to_position" msgid="6750008980455459790">"ወደ አቀማመጥ <xliff:g id="NUMBER">%1$s</xliff:g> አንቀሳቅስ"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"ወደ ተወዳጆች አቀማመጥ <xliff:g id="NUMBER">%1$s</xliff:g> አንቀሳቅስ"</string>
     <string name="item_moved" msgid="4606538322571412879">"ንጥል ተንቀሳቅሷል"</string>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 0e48179..a4f7c03 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"تمّت إزالة العنصر."</string>
     <string name="undo" msgid="4151576204245173321">"تراجع"</string>
     <string name="action_move" msgid="4339390619886385032">"نقل العنصر"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"نقل إلى الصف <xliff:g id="NUMBER_0">%1$s</xliff:g> العمود <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"انتقل إلى الصف <xliff:g id="NUMBER_0">%1$s</xliff:g> العمود <xliff:g id="NUMBER_1">%2$s</xliff:g> في <xliff:g id="STRING">%3$s</xliff:g>."</string>
     <string name="move_to_position" msgid="6750008980455459790">"نقل إلى الموضع <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"نقل إلى الموضع المفضل <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="item_moved" msgid="4606538322571412879">"تم نقل العنصر"</string>
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index 5fc8ca6..beb9619 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"বস্তুটো আঁতৰোৱা হ’ল"</string>
     <string name="undo" msgid="4151576204245173321">"আনডু কৰক"</string>
     <string name="action_move" msgid="4339390619886385032">"বস্তু স্থানান্তৰ কৰক"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"শাৰী <xliff:g id="NUMBER_0">%1$s</xliff:g> স্তম্ভ <xliff:g id="NUMBER_1">%2$s</xliff:g>লৈ স্থানান্তৰিত কৰক"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g>ত <xliff:g id="NUMBER_0">%1$s</xliff:g> নম্বৰ শাৰী <xliff:g id="NUMBER_1">%2$s</xliff:g> নম্বৰ স্তম্ভলৈ লৈ যাওক"</string>
     <string name="move_to_position" msgid="6750008980455459790">"পছন্দৰ অৱস্থান <xliff:g id="NUMBER">%1$s</xliff:g>লৈ স্থানান্তৰিত কৰক"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"পছন্দৰ অৱস্থান <xliff:g id="NUMBER">%1$s</xliff:g>লৈ স্থানান্তৰিত কৰক"</string>
     <string name="item_moved" msgid="4606538322571412879">"বস্তুটো স্থানান্তৰ কৰা হ’ল"</string>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index ad72424..93e6f08 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Element silindi"</string>
     <string name="undo" msgid="4151576204245173321">"Ləğv edin"</string>
     <string name="action_move" msgid="4339390619886385032">"Elementi köçürün"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Sıra <xliff:g id="NUMBER_0">%1$s</xliff:g> sütun <xliff:g id="NUMBER_1">%2$s</xliff:g> köçürün"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="NUMBER_0">%1$s</xliff:g> saylı sətir, <xliff:g id="NUMBER_1">%2$s</xliff:g> saylı sütuna (<xliff:g id="STRING">%3$s</xliff:g>) köçürün"</string>
     <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g> mövqeyinə köçürün"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"<xliff:g id="NUMBER">%1$s</xliff:g> sevimlilər mövqeyinə köçürün"</string>
     <string name="item_moved" msgid="4606538322571412879">"Elementin yeri dəyişildi"</string>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index d7e85d9..ded4dc9 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Stavka je uklonjena"</string>
     <string name="undo" msgid="4151576204245173321">"Opozovi"</string>
     <string name="action_move" msgid="4339390619886385032">"Premesti stavku"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Premesti u red <xliff:g id="NUMBER_0">%1$s</xliff:g> i kolonu <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Premestite u red <xliff:g id="NUMBER_0">%1$s</xliff:g> kolonu <xliff:g id="NUMBER_1">%2$s</xliff:g> na <xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Premesti na <xliff:g id="NUMBER">%1$s</xliff:g>. poziciju"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Premesti na <xliff:g id="NUMBER">%1$s</xliff:g>. poziciju u omiljenim"</string>
     <string name="item_moved" msgid="4606538322571412879">"Stavka je premeštena"</string>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index a845f9a..7fa8341 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Элемент выдалены"</string>
     <string name="undo" msgid="4151576204245173321">"Адрабіць"</string>
     <string name="action_move" msgid="4339390619886385032">"Перамясціць элемент"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Перамясціць у радок <xliff:g id="NUMBER_0">%1$s</xliff:g> слупок <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Перайсці да радка <xliff:g id="NUMBER_0">%1$s</xliff:g> у слупку <xliff:g id="NUMBER_1">%2$s</xliff:g> на старонцы <xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Перамясціць у пазіцыю <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Перамясціць у абранае, у пазіцыю <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="item_moved" msgid="4606538322571412879">"Элемент перамешчаны"</string>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 0c7fcd3..62b7796 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Елементът е премахнат"</string>
     <string name="undo" msgid="4151576204245173321">"Отмяна"</string>
     <string name="action_move" msgid="4339390619886385032">"Преместване на елемента"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Преместване към ред <xliff:g id="NUMBER_0">%1$s</xliff:g>, колона <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Преместване на <xliff:g id="STRING">%3$s</xliff:g> – ред <xliff:g id="NUMBER_0">%1$s</xliff:g>, колона <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Преместване към позиция <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Преместване към позиция <xliff:g id="NUMBER">%1$s</xliff:g> в любимите"</string>
     <string name="item_moved" msgid="4606538322571412879">"Елементът е преместен"</string>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index 5c8c342..1be272b 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"আইটেম সরানো হয়েছে"</string>
     <string name="undo" msgid="4151576204245173321">"ফিরিয়ে আনুন"</string>
     <string name="action_move" msgid="4339390619886385032">"আইটেম সরান"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"সারি <xliff:g id="NUMBER_0">%1$s</xliff:g> কলাম <xliff:g id="NUMBER_1">%2$s</xliff:g> এ সরান"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g>-এ সারি <xliff:g id="NUMBER_0">%1$s</xliff:g> কলাম <xliff:g id="NUMBER_1">%2$s</xliff:g> সরান"</string>
     <string name="move_to_position" msgid="6750008980455459790">"অবস্থানে সরান <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"পছন্দসই অবস্থানে সরান <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="item_moved" msgid="4606538322571412879">"আইটেম সরানো হয়েছে"</string>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index 173518f..639b6b3 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Stavka je uklonjena"</string>
     <string name="undo" msgid="4151576204245173321">"Poništi"</string>
     <string name="action_move" msgid="4339390619886385032">"Premjesti stavku"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Pomjeri stavku u red <xliff:g id="NUMBER_0">%1$s</xliff:g> kolonu <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Premještanje u red <xliff:g id="NUMBER_0">%1$s</xliff:g>, kolonu <xliff:g id="NUMBER_1">%2$s</xliff:g> na <xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Pomjeri stavku na poziciju <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Pomjeri stavku na poziciju <xliff:g id="NUMBER">%1$s</xliff:g> među omiljenim"</string>
     <string name="item_moved" msgid="4606538322571412879">"Stavka je premještena"</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index e6a4af2..ee35fd3 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"S\'ha suprimit l\'element"</string>
     <string name="undo" msgid="4151576204245173321">"Desfés"</string>
     <string name="action_move" msgid="4339390619886385032">"Desplaça l\'element"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Desplaça l\'element a la fila <xliff:g id="NUMBER_0">%1$s</xliff:g> i la columna <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Mou a la fila <xliff:g id="NUMBER_0">%1$s</xliff:g>, columna <xliff:g id="NUMBER_1">%2$s</xliff:g> a <xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Desplaça l\'element a la posició <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Desplaça l\'element a la posició de preferits <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="item_moved" msgid="4606538322571412879">"Element desplaçat"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 8b3ef2d..1b418c6 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Položka byla odstraněna"</string>
     <string name="undo" msgid="4151576204245173321">"Zpět"</string>
     <string name="action_move" msgid="4339390619886385032">"Přesunout položku"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Přesunout na řádek <xliff:g id="NUMBER_0">%1$s</xliff:g> do sloupce <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Přesunout na řádek <xliff:g id="NUMBER_0">%1$s</xliff:g>, sloupec <xliff:g id="NUMBER_1">%2$s</xliff:g> na ploše <xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Přesunout na pozici <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Přesunout do oblíbených položek na pozici <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="item_moved" msgid="4606538322571412879">"Položka byla přesunuta"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index 8160465..5cdadcd 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Elementet er fjernet"</string>
     <string name="undo" msgid="4151576204245173321">"Fortryd"</string>
     <string name="action_move" msgid="4339390619886385032">"Flyt element"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Flyt til række <xliff:g id="NUMBER_0">%1$s</xliff:g>, kolonne <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Flyt til række <xliff:g id="NUMBER_0">%1$s</xliff:g> kolonne <xliff:g id="NUMBER_1">%2$s</xliff:g> i <xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Flyt til position <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Flyt til foretrukne position <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="item_moved" msgid="4606538322571412879">"Elementet blev flyttet"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 912e7fa..b75ef9d 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Element entfernt"</string>
     <string name="undo" msgid="4151576204245173321">"Rückgängig"</string>
     <string name="action_move" msgid="4339390619886385032">"Element verschieben"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"In Zeile <xliff:g id="NUMBER_0">%1$s</xliff:g>, Spalte <xliff:g id="NUMBER_1">%2$s</xliff:g> verschoben"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"In <xliff:g id="STRING">%3$s</xliff:g> zu Zeile <xliff:g id="NUMBER_0">%1$s</xliff:g> Spalte <xliff:g id="NUMBER_1">%2$s</xliff:g> bewegen"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Auf Position <xliff:g id="NUMBER">%1$s</xliff:g> verschoben"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Auf Favoritenposition <xliff:g id="NUMBER">%1$s</xliff:g> verschoben"</string>
     <string name="item_moved" msgid="4606538322571412879">"Element verschoben"</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 0f613de..99c9e23 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Το στοιχείο καταργήθηκε"</string>
     <string name="undo" msgid="4151576204245173321">"Αναίρεση"</string>
     <string name="action_move" msgid="4339390619886385032">"Μετακίνηση στοιχείου"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Μετακίνηση στη σειρά <xliff:g id="NUMBER_0">%1$s</xliff:g>, στήλη <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Μετακίνηση στη σειρά <xliff:g id="NUMBER_0">%1$s</xliff:g> στήλη <xliff:g id="NUMBER_1">%2$s</xliff:g> στην <xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Μετακίνηση στη θέση <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Μετακίνηση στη θέση <xliff:g id="NUMBER">%1$s</xliff:g> στα αγαπημένα"</string>
     <string name="item_moved" msgid="4606538322571412879">"Το στοιχείο καταργήθηκε"</string>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index a78c7f8..97f0528 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Item removed"</string>
     <string name="undo" msgid="4151576204245173321">"Undo"</string>
     <string name="action_move" msgid="4339390619886385032">"Move item"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Move to row <xliff:g id="NUMBER_0">%1$s</xliff:g> column <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Move to row <xliff:g id="NUMBER_0">%1$s</xliff:g> column <xliff:g id="NUMBER_1">%2$s</xliff:g> in <xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Move to position <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Move to favourites position <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="item_moved" msgid="4606538322571412879">"Item moved"</string>
diff --git a/res/values-en-rCA/strings.xml b/res/values-en-rCA/strings.xml
index a78c7f8..97f0528 100644
--- a/res/values-en-rCA/strings.xml
+++ b/res/values-en-rCA/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Item removed"</string>
     <string name="undo" msgid="4151576204245173321">"Undo"</string>
     <string name="action_move" msgid="4339390619886385032">"Move item"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Move to row <xliff:g id="NUMBER_0">%1$s</xliff:g> column <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Move to row <xliff:g id="NUMBER_0">%1$s</xliff:g> column <xliff:g id="NUMBER_1">%2$s</xliff:g> in <xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Move to position <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Move to favourites position <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="item_moved" msgid="4606538322571412879">"Item moved"</string>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index a78c7f8..97f0528 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Item removed"</string>
     <string name="undo" msgid="4151576204245173321">"Undo"</string>
     <string name="action_move" msgid="4339390619886385032">"Move item"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Move to row <xliff:g id="NUMBER_0">%1$s</xliff:g> column <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Move to row <xliff:g id="NUMBER_0">%1$s</xliff:g> column <xliff:g id="NUMBER_1">%2$s</xliff:g> in <xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Move to position <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Move to favourites position <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="item_moved" msgid="4606538322571412879">"Item moved"</string>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index a78c7f8..97f0528 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Item removed"</string>
     <string name="undo" msgid="4151576204245173321">"Undo"</string>
     <string name="action_move" msgid="4339390619886385032">"Move item"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Move to row <xliff:g id="NUMBER_0">%1$s</xliff:g> column <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Move to row <xliff:g id="NUMBER_0">%1$s</xliff:g> column <xliff:g id="NUMBER_1">%2$s</xliff:g> in <xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Move to position <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Move to favourites position <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="item_moved" msgid="4606538322571412879">"Item moved"</string>
diff --git a/res/values-en-rXC/strings.xml b/res/values-en-rXC/strings.xml
index d6951ad..4977beb 100644
--- a/res/values-en-rXC/strings.xml
+++ b/res/values-en-rXC/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‎‏‎‏‏‏‏‎‎‏‏‏‏‏‏‎‎‏‎‎‏‎‎‎‏‏‎‎‏‎‎‎‎‎‏‎‎‎‎‏‎‏‏‏‎‏‎‎‏‏‏‎‏‏‎‎‏‎‏‏‏‎Item removed‎‏‎‎‏‎"</string>
     <string name="undo" msgid="4151576204245173321">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‏‏‎‎‏‏‎‎‏‏‏‎‏‎‏‎‏‏‏‎‎‏‎‎‎‏‏‏‏‏‏‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‏‎‎‎‏‎‎‏‎‎‏‎Undo‎‏‎‎‏‎"</string>
     <string name="action_move" msgid="4339390619886385032">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‏‏‏‎‎‎‎‏‏‏‎‎‎‏‎‎‏‏‏‎‎‏‏‎‎‎‏‏‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‎‏‏‏‏‎‎‎‏‎‎‎‎Move item‎‏‎‎‏‎"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‎‎‏‏‏‎‏‎‏‎‎‏‏‎‏‎‏‏‏‎‏‏‎‎‏‎‏‎‎‏‎‎‎‎‏‎‏‎‎‎‏‏‎‎‎‎‏‏‎‎‏‎‏‏‏‏‏‎‎‏‏‎Move to row ‎‏‎‎‏‏‎<xliff:g id="NUMBER_0">%1$s</xliff:g>‎‏‎‎‏‏‏‎ column ‎‏‎‎‏‏‎<xliff:g id="NUMBER_1">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‏‏‎‏‏‎‎‏‏‏‏‏‏‎‎‏‎‏‎‏‎‏‏‎‏‏‎‎‎‎‎‎‎‏‏‎‏‎‎‏‏‎‎‏‏‏‎‏‎‏‎‏‎‎‏‎Move to row ‎‏‎‎‏‏‎<xliff:g id="NUMBER_0">%1$s</xliff:g>‎‏‎‎‏‏‏‎ column ‎‏‎‎‏‏‎<xliff:g id="NUMBER_1">%2$s</xliff:g>‎‏‎‎‏‏‏‎ in ‎‏‎‎‏‏‎<xliff:g id="STRING">%3$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="move_to_position" msgid="6750008980455459790">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‏‎‏‎‏‏‎‎‏‏‎‏‏‎‎‏‎‏‏‎‎‏‏‏‏‎‎‏‎‏‏‎‏‎‏‏‏‏‏‎‏‎‎‎‏‎‏‏‏‏‎‎‏‏‏‎‎Move to position ‎‏‎‎‏‏‎<xliff:g id="NUMBER">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‎‏‎‏‏‏‎‏‏‏‎‎‏‏‎‎‏‎‏‎‏‏‎‎‏‏‏‎‎‏‎‎‏‎‎‏‎‎‏‏‏‎‏‏‎‏‎‎‎‎‏‎‎‎‎‎‎Move to favorites position ‎‏‎‎‏‏‎<xliff:g id="NUMBER">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="item_moved" msgid="4606538322571412879">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‎‏‏‎‏‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‏‏‎‏‏‎‎‎‏‏‎‎‏‎‏‎‏‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‎‎‏‎‏‏‎‎‎‏‏‏‏‎Item moved‎‏‎‎‏‎"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 3897974..3ed312b 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Se eliminó el elemento."</string>
     <string name="undo" msgid="4151576204245173321">"Deshacer"</string>
     <string name="action_move" msgid="4339390619886385032">"Mover elemento"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Mover a fila <xliff:g id="NUMBER_0">%1$s</xliff:g>, columna <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Mover a la fila <xliff:g id="NUMBER_0">%1$s</xliff:g>, columna <xliff:g id="NUMBER_1">%2$s</xliff:g> en <xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Mover a la posición número <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Mover a la posición de favoritos número <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="item_moved" msgid="4606538322571412879">"Elemento movido"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 823e5a7..567ff0d 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Elemento quitado"</string>
     <string name="undo" msgid="4151576204245173321">"Deshacer"</string>
     <string name="action_move" msgid="4339390619886385032">"Mover elemento"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Mover a la fila <xliff:g id="NUMBER_0">%1$s</xliff:g>, columna <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Mover a la fila <xliff:g id="NUMBER_0">%1$s</xliff:g>, columna <xliff:g id="NUMBER_1">%2$s</xliff:g> en <xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Mover a la posición número <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Mover a la posición número <xliff:g id="NUMBER">%1$s</xliff:g> de favoritos"</string>
     <string name="item_moved" msgid="4606538322571412879">"Elemento movido"</string>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index bd2190a..c34a003 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Üksus eemaldati"</string>
     <string name="undo" msgid="4151576204245173321">"Võta tagasi"</string>
     <string name="action_move" msgid="4339390619886385032">"Teisalda üksus"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Teisaldamine <xliff:g id="NUMBER_0">%1$s</xliff:g>. rea <xliff:g id="NUMBER_1">%2$s</xliff:g>. veergu"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Teisalda reale <xliff:g id="NUMBER_0">%1$s</xliff:g> veerus <xliff:g id="NUMBER_1">%2$s</xliff:g> kohas <xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Teisaldamine <xliff:g id="NUMBER">%1$s</xliff:g>. positsioonile"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Teisaldamine lemmikute <xliff:g id="NUMBER">%1$s</xliff:g>. positsioonile"</string>
     <string name="item_moved" msgid="4606538322571412879">"Üksus teisaldati"</string>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index c1eaea8..55e0c3c 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Kendu da elementua"</string>
     <string name="undo" msgid="4151576204245173321">"Desegin"</string>
     <string name="action_move" msgid="4339390619886385032">"Mugitu elementua"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Eraman <xliff:g id="NUMBER_0">%1$s</xliff:g>. errenkadara, <xliff:g id="NUMBER_1">%2$s</xliff:g>. zutabera"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Joan <xliff:g id="NUMBER_0">%1$s</xliff:g>garren errenkadako <xliff:g id="NUMBER_1">%2$s</xliff:g>garren zutabera, <xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Eraman <xliff:g id="NUMBER">%1$s</xliff:g>. postura"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Eraman gogokoen <xliff:g id="NUMBER">%1$s</xliff:g>. postura"</string>
     <string name="item_moved" msgid="4606538322571412879">"Elementua mugitu da"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index d23eac2..12db5bd 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"مورد حذف شد"</string>
     <string name="undo" msgid="4151576204245173321">"واگرد"</string>
     <string name="action_move" msgid="4339390619886385032">"انتقال مورد"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"انتقال به سطر <xliff:g id="NUMBER_0">%1$s</xliff:g> ستون <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"انتقال به ردیف <xliff:g id="NUMBER_0">%1$s</xliff:g> ستون <xliff:g id="NUMBER_1">%2$s</xliff:g> در <xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"انتقال به موقعیت <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"انتقال به موقعیت دلخواه <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="item_moved" msgid="4606538322571412879">"مورد منتقل شد"</string>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index ccc22f0..135c452 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Kohde poistettiin"</string>
     <string name="undo" msgid="4151576204245173321">"Kumoa"</string>
     <string name="action_move" msgid="4339390619886385032">"Siirrä kohde"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Siirrä rivin <xliff:g id="NUMBER_0">%1$s</xliff:g> sarakkeeseen <xliff:g id="NUMBER_1">%2$s</xliff:g>."</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Siirrä riviin <xliff:g id="NUMBER_0">%1$s</xliff:g> sarakkeeseen <xliff:g id="NUMBER_1">%2$s</xliff:g> näytöllä <xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Siirrä kohtaan <xliff:g id="NUMBER">%1$s</xliff:g>."</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Siirrä suosikkien kohtaan <xliff:g id="NUMBER">%1$s</xliff:g>."</string>
     <string name="item_moved" msgid="4606538322571412879">"Kohde on siirretty."</string>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index 11bcae1..446a507 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Élément retiré"</string>
     <string name="undo" msgid="4151576204245173321">"Annuler"</string>
     <string name="action_move" msgid="4339390619886385032">"Déplacer l\'élément"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Déplacer vers rangée <xliff:g id="NUMBER_0">%1$s</xliff:g> colonne <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Déplacer à la ligne <xliff:g id="NUMBER_0">%1$s</xliff:g> colonne <xliff:g id="NUMBER_1">%2$s</xliff:g> dans <xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Déplacer vers la position <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Déplacer vers la position <xliff:g id="NUMBER">%1$s</xliff:g> dans les favoris"</string>
     <string name="item_moved" msgid="4606538322571412879">"Élément déplacé"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 9b30b1e..1d623b1 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Élément supprimé"</string>
     <string name="undo" msgid="4151576204245173321">"Annuler"</string>
     <string name="action_move" msgid="4339390619886385032">"Déplacer l\'élément"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Déplacer vers la ligne <xliff:g id="NUMBER_0">%1$s</xliff:g>, colonne <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Déplacer vers la ligne <xliff:g id="NUMBER_0">%1$s</xliff:g>, colonne <xliff:g id="NUMBER_1">%2$s</xliff:g> dans <xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Déplacer vers la position <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Déplacer vers la position <xliff:g id="NUMBER">%1$s</xliff:g> dans les favoris"</string>
     <string name="item_moved" msgid="4606538322571412879">"Élément déplacé"</string>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index 013011a..256c4d3 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Quitouse o elemento"</string>
     <string name="undo" msgid="4151576204245173321">"Desfacer"</string>
     <string name="action_move" msgid="4339390619886385032">"Mover elemento"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Mover á fila <xliff:g id="NUMBER_0">%1$s</xliff:g> columna <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Mover á fila <xliff:g id="NUMBER_0">%1$s</xliff:g>, columna <xliff:g id="NUMBER_1">%2$s</xliff:g> de <xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Mover á posición <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Mover á posición dos favoritos <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="item_moved" msgid="4606538322571412879">"Moveuse o elemento"</string>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index 2d504e3..f1e1996 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"આઇટમ કાઢી નાખી"</string>
     <string name="undo" msgid="4151576204245173321">"રદ કરો"</string>
     <string name="action_move" msgid="4339390619886385032">"આઇટમ ખસેડો"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"<xliff:g id="NUMBER_0">%1$s</xliff:g> પંક્તિ <xliff:g id="NUMBER_1">%2$s</xliff:g> કૉલમ પર ખસેડો"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g>માં પંક્તિ <xliff:g id="NUMBER_0">%1$s</xliff:g> કૉલમ <xliff:g id="NUMBER_1">%2$s</xliff:g> પર આ આઇટમને ખસેડો"</string>
     <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g> સ્થિતિ પર ખસેડો"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"મનપસંદ સ્થિતિ <xliff:g id="NUMBER">%1$s</xliff:g> પર ખસેડો"</string>
     <string name="item_moved" msgid="4606538322571412879">"આઇટમ ખસેડી"</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index de6ad1c..1b05040 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"आइटम हटाया गया"</string>
     <string name="undo" msgid="4151576204245173321">"पहले जैसा करें"</string>
     <string name="action_move" msgid="4339390619886385032">"आइटम ले जाएं"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"पंक्ति <xliff:g id="NUMBER_0">%1$s</xliff:g> स्तंभ <xliff:g id="NUMBER_1">%2$s</xliff:g> पर ले जाएं"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> में, पंक्ति <xliff:g id="NUMBER_0">%1$s</xliff:g> कॉलम <xliff:g id="NUMBER_1">%2$s</xliff:g> पर जाएं"</string>
     <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g> स्थिति पर ले जाएं"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"<xliff:g id="NUMBER">%1$s</xliff:g> की पसंदीदा स्थिति पर ले जाएं"</string>
     <string name="item_moved" msgid="4606538322571412879">"आइटम ले जाया गया"</string>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 0e58e53..4a0fbe8 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Stavka je uklonjena"</string>
     <string name="undo" msgid="4151576204245173321">"Poništi"</string>
     <string name="action_move" msgid="4339390619886385032">"Premještanje stavke"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Premještanje u redak <xliff:g id="NUMBER_0">%1$s</xliff:g>, stupac <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Premjesti u redak <xliff:g id="NUMBER_0">%1$s</xliff:g> stupac <xliff:g id="NUMBER_1">%2$s</xliff:g> na <xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Premještanje na položaj <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Premještanje na položaj favorita <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="item_moved" msgid="4606538322571412879">"Stavka premještena"</string>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index 30095d7..c1e527d 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Elem eltávolítva"</string>
     <string name="undo" msgid="4151576204245173321">"Mégse"</string>
     <string name="action_move" msgid="4339390619886385032">"Elem mozgatása"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Áthelyezés ide: <xliff:g id="NUMBER_0">%1$s</xliff:g>. sor, <xliff:g id="NUMBER_1">%2$s</xliff:g>. oszlop"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Áthelyezés a(z) <xliff:g id="NUMBER_0">%1$s</xliff:g>. sorba és a(z) <xliff:g id="NUMBER_1">%2$s</xliff:g>. oszlopba itt: <xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Áthelyezés a(z) <xliff:g id="NUMBER">%1$s</xliff:g>. pozícióba"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Áthelyezés a kedvencek <xliff:g id="NUMBER">%1$s</xliff:g>. pozíciójába"</string>
     <string name="item_moved" msgid="4606538322571412879">"Elem áthelyezve"</string>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index 3b9761f..f0d01f1 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Տարրը հեռացվեց"</string>
     <string name="undo" msgid="4151576204245173321">"Հետարկել"</string>
     <string name="action_move" msgid="4339390619886385032">"Տեղափոխել տարրը"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Տեղափոխել տող <xliff:g id="NUMBER_0">%1$s</xliff:g> սյունակ <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Տեղափոխել շարք <xliff:g id="NUMBER_0">%1$s</xliff:g>, սյունակ <xliff:g id="NUMBER_1">%2$s</xliff:g> (<xliff:g id="STRING">%3$s</xliff:g>)"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Տեղափոխել դիրք <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Տեղափոխել նախընտրած դիրք՝ <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="item_moved" msgid="4606538322571412879">"Տարրը տեղափոխվեց"</string>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 02f4860..1a97a45 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Item dihapus"</string>
     <string name="undo" msgid="4151576204245173321">"Urungkan"</string>
     <string name="action_move" msgid="4339390619886385032">"Pindahkan item"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Pindahkan ke baris <xliff:g id="NUMBER_0">%1$s</xliff:g> kolom <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Pindahkan ke baris <xliff:g id="NUMBER_0">%1$s</xliff:g> kolom <xliff:g id="NUMBER_1">%2$s</xliff:g> di <xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"PIndahkan ke posisi <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Pindahkan ke posisi favorit <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="item_moved" msgid="4606538322571412879">"Item dipindahkan"</string>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index 17cf2e4..a1959f8 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Atriði fjarlægt"</string>
     <string name="undo" msgid="4151576204245173321">"Afturkalla"</string>
     <string name="action_move" msgid="4339390619886385032">"Færa atriði"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Færa í línu <xliff:g id="NUMBER_0">%1$s</xliff:g>, dálk <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Færðu þig í línu <xliff:g id="NUMBER_0">%1$s</xliff:g>, dálk <xliff:g id="NUMBER_1">%2$s</xliff:g> í <xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Færa í stöðu <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Færa í stöðu <xliff:g id="NUMBER">%1$s</xliff:g> á festisvæði"</string>
     <string name="item_moved" msgid="4606538322571412879">"Atriði fært"</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index edff814..ff4fe4f 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -137,7 +137,8 @@
     <string name="item_removed" msgid="851119963877842327">"Elemento rimosso"</string>
     <string name="undo" msgid="4151576204245173321">"Annulla"</string>
     <string name="action_move" msgid="4339390619886385032">"Sposta elemento"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Sposta a riga <xliff:g id="NUMBER_0">%1$s</xliff:g>, colonna <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <!-- no translation found for move_to_empty_cell_description (5254852678218206889) -->
+    <skip />
     <string name="move_to_position" msgid="6750008980455459790">"Sposta nella posizione <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Sposta nella posizione <xliff:g id="NUMBER">%1$s</xliff:g> dei preferiti"</string>
     <string name="item_moved" msgid="4606538322571412879">"Elemento spostato"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 7de8f08..15e200f 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"הפריט הוסר"</string>
     <string name="undo" msgid="4151576204245173321">"ביטול"</string>
     <string name="action_move" msgid="4339390619886385032">"העברת הפריט"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"העברה אל שורה <xliff:g id="NUMBER_0">%1$s</xliff:g> עמודה <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"צריך לעבור לשורה <xliff:g id="NUMBER_0">%1$s</xliff:g> ולטור <xliff:g id="NUMBER_1">%2$s</xliff:g> ב-<xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"העברה אל מיקום <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"העברה אל מיקום <xliff:g id="NUMBER">%1$s</xliff:g> במועדפים"</string>
     <string name="item_moved" msgid="4606538322571412879">"הפריט הועבר"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index d7cbb07..2fd3ab5 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"アイテムを削除しました"</string>
     <string name="undo" msgid="4151576204245173321">"元に戻す"</string>
     <string name="action_move" msgid="4339390619886385032">"アイテムを移動"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"行<xliff:g id="NUMBER_0">%1$s</xliff:g>、列<xliff:g id="NUMBER_1">%2$s</xliff:g>に移動"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> の行 <xliff:g id="NUMBER_0">%1$s</xliff:g>、列 <xliff:g id="NUMBER_1">%2$s</xliff:g> に移動します"</string>
     <string name="move_to_position" msgid="6750008980455459790">"位置<xliff:g id="NUMBER">%1$s</xliff:g>に移動"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"お気に入りの位置<xliff:g id="NUMBER">%1$s</xliff:g>に移動"</string>
     <string name="item_moved" msgid="4606538322571412879">"アイテムを移動しました"</string>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index ba5be87..24f95dd 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"ერთეული წაიშალა"</string>
     <string name="undo" msgid="4151576204245173321">"მოქმედების გაუქმება"</string>
     <string name="action_move" msgid="4339390619886385032">"ერთეულის გადაადგილება"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"გადატანა რიგში <xliff:g id="NUMBER_0">%1$s</xliff:g> სვეტში <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"გადაიტანეთ მწკრივი #<xliff:g id="NUMBER_0">%1$s</xliff:g> სვეტი #<xliff:g id="NUMBER_1">%2$s</xliff:g> <xliff:g id="STRING">%3$s</xliff:g>-ში"</string>
     <string name="move_to_position" msgid="6750008980455459790">"გადატანა <xliff:g id="NUMBER">%1$s</xliff:g> პოზიციაზე"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"გადატანა რჩეულთა პოზიციაზე <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="item_moved" msgid="4606538322571412879">"ერთეული გადაადგილდა"</string>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index 83b5a66..8ed7ec5 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Элемент жойылды"</string>
     <string name="undo" msgid="4151576204245173321">"Қайтару"</string>
     <string name="action_move" msgid="4339390619886385032">"Элементті жылжыту"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"<xliff:g id="NUMBER_0">%1$s</xliff:g>-жол, <xliff:g id="NUMBER_1">%2$s</xliff:g>-бағанға жылжыту"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> экранында <xliff:g id="NUMBER_0">%1$s</xliff:g> жолын, <xliff:g id="NUMBER_1">%2$s</xliff:g> бағанын жылжытыңыз."</string>
     <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g>-орынға жылжыту"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"<xliff:g id="NUMBER">%1$s</xliff:g> нөмірлі таңдаулы орынға жылжыту"</string>
     <string name="item_moved" msgid="4606538322571412879">"Элемент жылжытылды"</string>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index 73ec4df..306449c 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"បានដកធាតុចេញ"</string>
     <string name="undo" msgid="4151576204245173321">"ត្រឡប់វិញ"</string>
     <string name="action_move" msgid="4339390619886385032">"ផ្លាស់ទីធាតុ"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"ផ្លាស់ទីទៅជួរដេកទី <xliff:g id="NUMBER_0">%1$s</xliff:g> ជួរឈរទី <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"ផ្លាស់ទៅជួរដេក <xliff:g id="NUMBER_0">%1$s</xliff:g> ជួរឈរ <xliff:g id="NUMBER_1">%2$s</xliff:g> ក្នុង <xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"ផ្លាស់ទីទៅទីតាំង <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"ផ្លាស់ទីទៅការចូលចិត្តទីតាំងទី <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="item_moved" msgid="4606538322571412879">"បានផ្លាស់ទីធាតុ"</string>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index 1e6f39e..a9b89b5 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"ಐಟಂ ತೆಗೆದುಹಾಕಲಾಗಿದೆ"</string>
     <string name="undo" msgid="4151576204245173321">"ರದ್ದುಮಾಡಿ"</string>
     <string name="action_move" msgid="4339390619886385032">"ಐಟಂ ಸರಿಸಿ"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"<xliff:g id="NUMBER_0">%1$s</xliff:g> ಸಾಲು <xliff:g id="NUMBER_1">%2$s</xliff:g> ಕಾಲಮ್‌ಗೆ ಸರಿಸಿ"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> ನಲ್ಲಿ ಸಾಲು <xliff:g id="NUMBER_0">%1$s</xliff:g> ಅನ್ನು ಕಾಲಮ್ <xliff:g id="NUMBER_1">%2$s</xliff:g> ಗೆ ಸರಿಸಿ"</string>
     <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g> ಸ್ಥಾನಕ್ಕೆ ಸರಿಸಿ"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"ಮೆಚ್ಚಿನ <xliff:g id="NUMBER">%1$s</xliff:g> ಸ್ಥಾನಕ್ಕೆ ಸರಿಸಿ"</string>
     <string name="item_moved" msgid="4606538322571412879">"ಐಟಂ ಸರಿಸಲಾಗಿದೆ"</string>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index d5ec664..0935aa1 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"항목 삭제됨"</string>
     <string name="undo" msgid="4151576204245173321">"실행취소"</string>
     <string name="action_move" msgid="4339390619886385032">"항목 이동"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"<xliff:g id="NUMBER_0">%1$s</xliff:g>행 <xliff:g id="NUMBER_1">%2$s</xliff:g>열로 이동"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g>의 <xliff:g id="NUMBER_0">%1$s</xliff:g>행 <xliff:g id="NUMBER_1">%2$s</xliff:g>열로 이동"</string>
     <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g>번 위치로 이동"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"즐겨찾는 <xliff:g id="NUMBER">%1$s</xliff:g>번 위치로 이동"</string>
     <string name="item_moved" msgid="4606538322571412879">"항목을 이동했습니다."</string>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index ea4a7d2..755e274 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Жоюлду"</string>
     <string name="undo" msgid="4151576204245173321">"Кайтаруу"</string>
     <string name="action_move" msgid="4339390619886385032">"Муну жылдыруу"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"<xliff:g id="NUMBER_0">%1$s</xliff:g> катарга <xliff:g id="NUMBER_1">%2$s</xliff:g> тилкеге жылдыруу"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> ичиндеги <xliff:g id="NUMBER_1">%2$s</xliff:g>-тилкенин <xliff:g id="NUMBER_0">%1$s</xliff:g>-cабына жылдыруу"</string>
     <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g> орунга жылдыруу"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Тандалмаларга <xliff:g id="NUMBER">%1$s</xliff:g> жылдыруу"</string>
     <string name="item_moved" msgid="4606538322571412879">"Нерсе жылдырылды"</string>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index 7291cf2..9e75492 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"ເອົາ​ລາຍ​ການ​ອອກ​ໄປ​ແລ້ວ"</string>
     <string name="undo" msgid="4151576204245173321">"ຍົກເລີກ"</string>
     <string name="action_move" msgid="4339390619886385032">"ຍ້າຍ​ລາຍ​ການ"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"ຍ້າຍ​ໄປ​ໃສ່​ແຖວ <xliff:g id="NUMBER_0">%1$s</xliff:g> ຖັນ <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"ຍ້າຍໄປແຖວ <xliff:g id="NUMBER_0">%1$s</xliff:g> ຖັນ <xliff:g id="NUMBER_1">%2$s</xliff:g> ໃນ <xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"ຍ້າຍ​ໄປ​ໃສ່​ຕຳ​ແໜ່ງ <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"ຍ້າຍ​ໄປ​ໃສ່​ຕຳ​ແໜ່ງ​ທີ່​ມັກ <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="item_moved" msgid="4606538322571412879">"ຍ້າຍ​ລາຍ​ການ​ແລ້ວ"</string>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index ddff9d4..bbe0cfa 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Elementas perkeltas"</string>
     <string name="undo" msgid="4151576204245173321">"Anuliuoti"</string>
     <string name="action_move" msgid="4339390619886385032">"Perkelti elementą"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Perkelti į <xliff:g id="NUMBER_0">%1$s</xliff:g> eilutę, <xliff:g id="NUMBER_1">%2$s</xliff:g> stulpelį"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Perkelti į <xliff:g id="NUMBER_0">%1$s</xliff:g> eilutės <xliff:g id="NUMBER_1">%2$s</xliff:g> stulpelį, esantį <xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Perkelti į <xliff:g id="NUMBER">%1$s</xliff:g> poziciją"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Perkelti į <xliff:g id="NUMBER">%1$s</xliff:g> mėgstamiausių poziciją"</string>
     <string name="item_moved" msgid="4606538322571412879">"Elementas perkeltas"</string>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index a5b7c66..b5836ef 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Vienums noņemts"</string>
     <string name="undo" msgid="4151576204245173321">"Atsaukt"</string>
     <string name="action_move" msgid="4339390619886385032">"Pārvietot vienumu"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Pārvietot uz <xliff:g id="NUMBER_0">%1$s</xliff:g>. rindu, <xliff:g id="NUMBER_1">%2$s</xliff:g>. kolonnu"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Pārvietot uz rindu numur <xliff:g id="NUMBER_0">%1$s</xliff:g>, kolonnu numur <xliff:g id="NUMBER_1">%2$s</xliff:g> šajā ekrānā: <xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Pārvietot uz <xliff:g id="NUMBER">%1$s</xliff:g>. pozīciju"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Pārvietot uz <xliff:g id="NUMBER">%1$s</xliff:g>. izlases pozīciju"</string>
     <string name="item_moved" msgid="4606538322571412879">"Vienums pārvietots"</string>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index 32c3fc0..d452eba 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Ставката е отстранета"</string>
     <string name="undo" msgid="4151576204245173321">"Врати"</string>
     <string name="action_move" msgid="4339390619886385032">"Премести ја ставката"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Премести во ред <xliff:g id="NUMBER_0">%1$s</xliff:g> колона <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Преместете во редица <xliff:g id="NUMBER_0">%1$s</xliff:g>, колона <xliff:g id="NUMBER_1">%2$s</xliff:g> во <xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Премести на место <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Премести на место <xliff:g id="NUMBER">%1$s</xliff:g> во омилени"</string>
     <string name="item_moved" msgid="4606538322571412879">"Ставката е преместена"</string>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index d25f9dd..df532c0 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"ഇനം നീക്കംചെയ്‌തു"</string>
     <string name="undo" msgid="4151576204245173321">"പഴയപടിയാക്കുക"</string>
     <string name="action_move" msgid="4339390619886385032">"ഇനം നീക്കുക"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"വരി <xliff:g id="NUMBER_0">%1$s</xliff:g> നിര <xliff:g id="NUMBER_1">%2$s</xliff:g>-ലേക്ക് നീക്കുക"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> എന്നതിലെ <xliff:g id="NUMBER_1">%2$s</xliff:g>-ാം കോളത്തിലെ <xliff:g id="NUMBER_0">%1$s</xliff:g>-ാം വരിയിലേക്ക് നീക്കുക"</string>
     <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g>-ലേക്ക് നീക്കുക"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"ഇഷ്‌ടമുള്ള <xliff:g id="NUMBER">%1$s</xliff:g> സ്ഥാനത്തേക്ക് നീക്കുക"</string>
     <string name="item_moved" msgid="4606538322571412879">"ഇനം നീക്കി"</string>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index 0723c25..c71aaff 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Зүйлийг устгалаа"</string>
     <string name="undo" msgid="4151576204245173321">"Болих"</string>
     <string name="action_move" msgid="4339390619886385032">"Зөөх"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"<xliff:g id="NUMBER_0">%1$s</xliff:g> мөр <xliff:g id="NUMBER_1">%2$s</xliff:g> баганад зөөх"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> дахь <xliff:g id="NUMBER_0">%1$s</xliff:g>-р мөр, <xliff:g id="NUMBER_1">%2$s</xliff:g>-р багана руу зөөх"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Байршил <xliff:g id="NUMBER">%1$s</xliff:g>-д зөөх"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Дуртай байршил болох <xliff:g id="NUMBER">%1$s</xliff:g>-д зөөх"</string>
     <string name="item_moved" msgid="4606538322571412879">"Зөөвөрлөсөн зүйл"</string>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index 06c1da7..e871073 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"आयटम काढला"</string>
     <string name="undo" msgid="4151576204245173321">"पूर्ववत करा"</string>
     <string name="action_move" msgid="4339390619886385032">"आयटम हलवा"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"पंक्ति <xliff:g id="NUMBER_0">%1$s</xliff:g> स्तंभ <xliff:g id="NUMBER_1">%2$s</xliff:g> मध्ये हलवा"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> मधील <xliff:g id="NUMBER_0">%1$s</xliff:g> पंक्ती <xliff:g id="NUMBER_1">%2$s</xliff:g> स्तंभ यावर हलवा"</string>
     <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g> स्थानावर हलवा"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"आवडत्या <xliff:g id="NUMBER">%1$s</xliff:g> स्थानावर हलवा"</string>
     <string name="item_moved" msgid="4606538322571412879">"आयटम हलविला"</string>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index d123d14..cbfb551 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Item dialih keluar"</string>
     <string name="undo" msgid="4151576204245173321">"Buat asal"</string>
     <string name="action_move" msgid="4339390619886385032">"Alihkan Item"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Alihkan ke baris <xliff:g id="NUMBER_0">%1$s</xliff:g> lajur <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Alihkan ke baris <xliff:g id="NUMBER_0">%1$s</xliff:g> lajur <xliff:g id="NUMBER_1">%2$s</xliff:g> dalam <xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Alihkan ke kedudukan <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Alihkan ke kedudukan kegemaran <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="item_moved" msgid="4606538322571412879">"Item dialihkan"</string>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index aef6169..984ec4c 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"ဖယ်ရှားပြီးပြီ"</string>
     <string name="undo" msgid="4151576204245173321">"နောက်ပြန်ရန်"</string>
     <string name="action_move" msgid="4339390619886385032">"၎င်းအား ရွှေ့ပါ"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"အတန်း <xliff:g id="NUMBER_0">%1$s</xliff:g> အတိုင် <xliff:g id="NUMBER_1">%2$s</xliff:g> သို့ ရွှေ့ပါ"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> တွင် အတန်း <xliff:g id="NUMBER_0">%1$s</xliff:g> ကော်လံ <xliff:g id="NUMBER_1">%2$s</xliff:g> သို့ ရွှေ့နိုင်သည်"</string>
     <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g> သို့ နေရာရွှေ့ပါ"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"စိတ်ကြိုက်နေရာ <xliff:g id="NUMBER">%1$s</xliff:g> သို့ ရွှေ့ပါ"</string>
     <string name="item_moved" msgid="4606538322571412879">"၎င်းအားရွှေ့ပြီး"</string>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index eba195c..b29ee77 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Elementet er fjernet"</string>
     <string name="undo" msgid="4151576204245173321">"Angre"</string>
     <string name="action_move" msgid="4339390619886385032">"Flytt elementet"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Flytt til rad <xliff:g id="NUMBER_0">%1$s</xliff:g>, kolonne <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Flytt til rad <xliff:g id="NUMBER_0">%1$s</xliff:g> kolonne <xliff:g id="NUMBER_1">%2$s</xliff:g> i <xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Flytt til posisjon <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Flytt til posisjonen for favoritter <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="item_moved" msgid="4606538322571412879">"Elementet er flyttet"</string>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index 49d92ac..fcc1ece 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -92,8 +92,8 @@
     <string name="disabled_app_label" msgid="6673129024321402780">"असक्षम पारिएको <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="dotted_app_label" msgid="1865617679843363410">"{count,plural, =1{{app_name} सँग सम्बन्धित # सूचना छ}other{{app_name} सँग सम्बन्धित # वटा सूचना छन्}}"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"पृष्ठ %2$d को %1$d"</string>
-    <string name="workspace_scroll_format" msgid="8458889198184077399">"गृह स्क्रिन %1$d को %2$d"</string>
-    <string name="workspace_new_page" msgid="257366611030256142">"नयाँ गृह स्क्रिन पृष्ठ"</string>
+    <string name="workspace_scroll_format" msgid="8458889198184077399">"होम स्क्रिन %1$d को %2$d"</string>
+    <string name="workspace_new_page" msgid="257366611030256142">"नयाँ होम स्क्रिन पृष्ठ"</string>
     <string name="folder_opened" msgid="94695026776264709">"फोल्डर खुल्यो <xliff:g id="WIDTH">%1$d</xliff:g> बाट <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
     <string name="folder_tap_to_close" msgid="4625795376335528256">"फोल्डरलाई बन्द गर्न ट्याप गर्नुहोस्"</string>
     <string name="folder_tap_to_rename" msgid="4017685068016979677">"पुनःनामाकरणलाई सुरक्षित गर्न ट्याप गर्नुहोस्"</string>
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"वस्तु हटाइयो"</string>
     <string name="undo" msgid="4151576204245173321">"अन्डू गर्नुहोस्"</string>
     <string name="action_move" msgid="4339390619886385032">"वस्तु सार्नुहोस्"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"पङ्क्ति <xliff:g id="NUMBER_0">%1$s</xliff:g> स्तम्भ <xliff:g id="NUMBER_1">%2$s</xliff:g> मा सार्नुहोस्"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"यो वस्तु सारेर <xliff:g id="STRING">%3$s</xliff:g> मा रहेको रो <xliff:g id="NUMBER_0">%1$s</xliff:g> कोलम <xliff:g id="NUMBER_1">%2$s</xliff:g> मा लैजानुहोस्"</string>
     <string name="move_to_position" msgid="6750008980455459790">"स्थिति <xliff:g id="NUMBER">%1$s</xliff:g> मा सार्नुहोस्"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"मन पर्ने स्थिति <xliff:g id="NUMBER">%1$s</xliff:g> मा सार्नुहोस्"</string>
     <string name="item_moved" msgid="4606538322571412879">"वस्तु सारियो"</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index e19b361..bd41952 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Item verwijderd"</string>
     <string name="undo" msgid="4151576204245173321">"Ongedaan maken"</string>
     <string name="action_move" msgid="4339390619886385032">"Item verplaatsen"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Verplaatsen naar rij <xliff:g id="NUMBER_0">%1$s</xliff:g>, kolom <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Verplaatsen naar rij <xliff:g id="NUMBER_0">%1$s</xliff:g> kolom <xliff:g id="NUMBER_1">%2$s</xliff:g> in <xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Verplaatsen naar positie <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Verplaatsen naar positie <xliff:g id="NUMBER">%1$s</xliff:g> voor favorieten"</string>
     <string name="item_moved" msgid="4606538322571412879">"Item verplaatst"</string>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index 2506200..5a6cdd3 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"ଆଇଟମକୁ କାଢ଼ି ଦିଆଯାଇଛି"</string>
     <string name="undo" msgid="4151576204245173321">"ପୂର୍ବବତ୍‍"</string>
     <string name="action_move" msgid="4339390619886385032">"ଆଇଟମ୍‌ ଘୁଞ୍ଚାନ୍ତୁ"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"ଧାଡ଼ି <xliff:g id="NUMBER_0">%1$s</xliff:g> ସ୍ତମ୍ଭ <xliff:g id="NUMBER_1">%2$s</xliff:g>କୁ ନିଅନ୍ତୁ"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g>ରେ ଧାଡି <xliff:g id="NUMBER_0">%1$s</xliff:g> ସ୍ତମ୍ଭ <xliff:g id="NUMBER_1">%2$s</xliff:g>କୁ ମୁଭ କରନ୍ତୁ"</string>
     <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g> ସ୍ଥିତିକୁ ନିଅନ୍ତୁ"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"ପସନ୍ଦର ସ୍ଥିତି <xliff:g id="NUMBER">%1$s</xliff:g>କୁ ନିଅନ୍ତୁ"</string>
     <string name="item_moved" msgid="4606538322571412879">"ଆଇଟମ୍‌ ଘୁଞ୍ଚେଇ ଦିଆଗଲା"</string>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index 3df6886..01fbee0 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"ਆਈਟਮ ਹਟਾਈ ਗਈ"</string>
     <string name="undo" msgid="4151576204245173321">"ਅਣਕੀਤਾ ਕਰੋ"</string>
     <string name="action_move" msgid="4339390619886385032">"ਆਈਟਮ ਨੂੰ ਮੂਵ ਕਰੋ"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"ਕਤਾਰ <xliff:g id="NUMBER_0">%1$s</xliff:g> ਕਾਲਮ <xliff:g id="NUMBER_1">%2$s</xliff:g> ਵਿੱਚ ਮੂਵ ਕਰੋ"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> ਵਿੱਚ ਕਤਾਰ <xliff:g id="NUMBER_0">%1$s</xliff:g> ਦੇ ਕਾਲਮ <xliff:g id="NUMBER_1">%2$s</xliff:g> \'ਤੇ ਜਾਓ"</string>
     <string name="move_to_position" msgid="6750008980455459790">"ਸਥਿਤੀ <xliff:g id="NUMBER">%1$s</xliff:g> ਵਿੱਚ ਮੂਵ ਕਰੋ"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"ਮਨਪਸੰਦ ਸਥਿਤੀ <xliff:g id="NUMBER">%1$s</xliff:g> ਵਿੱਚ ਮੂਵ ਕਰੋ"</string>
     <string name="item_moved" msgid="4606538322571412879">"ਆਈਟਮ ਮੂਵ ਕੀਤੀ ਗਈ"</string>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 869e91b..4b46f0b 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Element został usunięty"</string>
     <string name="undo" msgid="4151576204245173321">"Cofnij"</string>
     <string name="action_move" msgid="4339390619886385032">"Przenieś element"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Przenieś do wiersza <xliff:g id="NUMBER_0">%1$s</xliff:g> w kolumnie <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Przenieś do wiersza <xliff:g id="NUMBER_0">%1$s</xliff:g> w kolumnie <xliff:g id="NUMBER_1">%2$s</xliff:g>, komórka: <xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Przenieś do pozycji <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Przenieś do pozycji ulubionych: <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="item_moved" msgid="4606538322571412879">"Element został przeniesiony"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 1512754..dbfa0cb 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Item removido"</string>
     <string name="undo" msgid="4151576204245173321">"Anular"</string>
     <string name="action_move" msgid="4339390619886385032">"Mover item"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Mover para a linha <xliff:g id="NUMBER_0">%1$s</xliff:g>, coluna <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Mova para a linha <xliff:g id="NUMBER_0">%1$s</xliff:g>, coluna <xliff:g id="NUMBER_1">%2$s</xliff:g> em <xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Mover para a posição <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Mover para a posição <xliff:g id="NUMBER">%1$s</xliff:g> dos favoritos"</string>
     <string name="item_moved" msgid="4606538322571412879">"Item movido"</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 2dcb98f..3a5ed5b 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Item removido"</string>
     <string name="undo" msgid="4151576204245173321">"Desfazer"</string>
     <string name="action_move" msgid="4339390619886385032">"Mover item"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Mover para a linha <xliff:g id="NUMBER_0">%1$s</xliff:g>, coluna <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Mover para a linha <xliff:g id="NUMBER_0">%1$s</xliff:g>, coluna <xliff:g id="NUMBER_1">%2$s</xliff:g> na <xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Mover para a posição <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Mover para a posição <xliff:g id="NUMBER">%1$s</xliff:g> dos favoritos"</string>
     <string name="item_moved" msgid="4606538322571412879">"Item movido"</string>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index ee9f492..1b3d2b1 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Element eliminat"</string>
     <string name="undo" msgid="4151576204245173321">"Anulați"</string>
     <string name="action_move" msgid="4339390619886385032">"Mutați elementul"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Mutați pe rândul <xliff:g id="NUMBER_0">%1$s</xliff:g>, coloana <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Mutați în rândul <xliff:g id="NUMBER_0">%1$s</xliff:g> coloana <xliff:g id="NUMBER_1">%2$s</xliff:g> din <xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Mutați pe poziția <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Mutați în preferate, pe poziția <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="item_moved" msgid="4606538322571412879">"Element mutat"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 9733ac6..0ad12bd 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Объект убран."</string>
     <string name="undo" msgid="4151576204245173321">"Отменить"</string>
     <string name="action_move" msgid="4339390619886385032">"Переместить элемент"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Переместить в ячейку <xliff:g id="NUMBER_0">%1$s</xliff:g> <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Переместите в строку <xliff:g id="NUMBER_0">%1$s</xliff:g> столбца <xliff:g id="NUMBER_1">%2$s</xliff:g> на экране \"<xliff:g id="STRING">%3$s</xliff:g>\""</string>
     <string name="move_to_position" msgid="6750008980455459790">"Переместить в позицию <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Переместить в Избранное (<xliff:g id="NUMBER">%1$s</xliff:g>)"</string>
     <string name="item_moved" msgid="4606538322571412879">"Элемент перемещен."</string>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index e160e81..cf3dc64 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"අයිතමය ඉවත් කරන ලදි"</string>
     <string name="undo" msgid="4151576204245173321">"අස් කරන්න"</string>
     <string name="action_move" msgid="4339390619886385032">"අයිතමය ගෙනයන්න"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"පේළිය <xliff:g id="NUMBER_0">%1$s</xliff:g> තීරුව <xliff:g id="NUMBER_1">%2$s</xliff:g> වෙත ගෙන යන්න"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> තුළ <xliff:g id="NUMBER_0">%1$s</xliff:g> තීරුවේ<xliff:g id="NUMBER_1">%2$s</xliff:g> පේළියට යන්න"</string>
     <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g> ස්ථානය වෙත ගෙන යන්න"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"ප්‍රියතම ස්ථානය <xliff:g id="NUMBER">%1$s</xliff:g> වෙත ගෙන යන්න"</string>
     <string name="item_moved" msgid="4606538322571412879">"අයිතමය ගෙන යන ලදි"</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 8582baf..e3bcc52 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Položka bola odstránená"</string>
     <string name="undo" msgid="4151576204245173321">"Späť"</string>
     <string name="action_move" msgid="4339390619886385032">"Presunúť položku"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Presunúť do stĺpca <xliff:g id="NUMBER_1">%2$s</xliff:g> v riadku <xliff:g id="NUMBER_0">%1$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Premiestnite položku do <xliff:g id="NUMBER_0">%1$s</xliff:g>. riadka <xliff:g id="NUMBER_1">%2$s</xliff:g>. stĺpca na obrazovke <xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Presunúť na <xliff:g id="NUMBER">%1$s</xliff:g>. miesto"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Presunúť na <xliff:g id="NUMBER">%1$s</xliff:g>. miesto v obľúbených položkách"</string>
     <string name="item_moved" msgid="4606538322571412879">"Položka bola presunutá"</string>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index 0762cf7..5f952cf 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Element je bil odstranjen."</string>
     <string name="undo" msgid="4151576204245173321">"Razveljavi"</string>
     <string name="action_move" msgid="4339390619886385032">"Premik elementa"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Premik v <xliff:g id="NUMBER_0">%1$s</xliff:g>. vrstico <xliff:g id="NUMBER_1">%2$s</xliff:g>. stolpca"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Premik v vrstico <xliff:g id="NUMBER_0">%1$s</xliff:g> v stolpcu <xliff:g id="NUMBER_1">%2$s</xliff:g> na »<xliff:g id="STRING">%3$s</xliff:g>«"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Premk na mesto št. <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Premik na mesto priljubljenih št. <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="item_moved" msgid="4606538322571412879">"Element je premaknjen"</string>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index 821bd50..9f29a87 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Artikulli u hoq"</string>
     <string name="undo" msgid="4151576204245173321">"Zhbëj"</string>
     <string name="action_move" msgid="4339390619886385032">"Zhvendose artikullin"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Zhvendos te rreshti <xliff:g id="NUMBER_0">%1$s</xliff:g>, kolona <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Kalo te rreshti <xliff:g id="NUMBER_0">%1$s</xliff:g> kolona <xliff:g id="NUMBER_1">%2$s</xliff:g> në <xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Zhvendos te pozicioni <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Zhvendos te pozicioni <xliff:g id="NUMBER">%1$s</xliff:g> i preferencave"</string>
     <string name="item_moved" msgid="4606538322571412879">"Artikulli u zhvendos"</string>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index 85cc281..948d9b6 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Ставка је уклоњена"</string>
     <string name="undo" msgid="4151576204245173321">"Опозови"</string>
     <string name="action_move" msgid="4339390619886385032">"Премести ставку"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Премести у ред <xliff:g id="NUMBER_0">%1$s</xliff:g> и колону <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Преместите у ред <xliff:g id="NUMBER_0">%1$s</xliff:g> колону <xliff:g id="NUMBER_1">%2$s</xliff:g> на <xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Премести на <xliff:g id="NUMBER">%1$s</xliff:g>. позицију"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Премести на <xliff:g id="NUMBER">%1$s</xliff:g>. позицију у омиљеним"</string>
     <string name="item_moved" msgid="4606538322571412879">"Ставка је премештена"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 7525eec..c35adb0 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Objektet har tagits bort"</string>
     <string name="undo" msgid="4151576204245173321">"Ångra"</string>
     <string name="action_move" msgid="4339390619886385032">"Flytta objekt"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Flytta till rad <xliff:g id="NUMBER_0">%1$s</xliff:g>, kolumn <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Flytta till rad <xliff:g id="NUMBER_0">%1$s</xliff:g> kolumn <xliff:g id="NUMBER_1">%2$s</xliff:g> i <xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Flytta till plats <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Flytta till favoritplats <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="item_moved" msgid="4606538322571412879">"Objektet har flyttats"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 3ae5dcf..c233cc4 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Kipengee kimeondolewa"</string>
     <string name="undo" msgid="4151576204245173321">"Tendua"</string>
     <string name="action_move" msgid="4339390619886385032">"Hamisha kipengee"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Hamishia safu mlalo <xliff:g id="NUMBER_0">%1$s</xliff:g> safu wima <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Hamishia safu mlalo <xliff:g id="NUMBER_0">%1$s</xliff:g> safu wima <xliff:g id="NUMBER_1">%2$s</xliff:g> kwenye <xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Hamishia nafasi ya <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Hamishia nafasi inayopendwa ya <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="item_moved" msgid="4606538322571412879">"Kipengee kimesogezwa"</string>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index fa2b789..bfec460 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -75,7 +75,7 @@
     <string name="uninstall_drop_target_label" msgid="4722034217958379417">"நிறுவல் நீக்கு"</string>
     <string name="app_info_drop_target_label" msgid="692894985365717661">"ஆப்ஸ் தகவல்"</string>
     <string name="install_drop_target_label" msgid="2539096853673231757">"நிறுவு"</string>
-    <string name="dismiss_prediction_label" msgid="3357562989568808658">"ஆப்ஸ் பரிந்துரைக்காதே"</string>
+    <string name="dismiss_prediction_label" msgid="3357562989568808658">"பரிந்துரைக்காதே"</string>
     <string name="pin_prediction" msgid="4196423321649756498">"கணிக்கப்பட்ட ஆப்ஸைப் பின் செய்தல்"</string>
     <string name="permlab_install_shortcut" msgid="5632423390354674437">"குறுக்குவழிகளை நிறுவுதல்"</string>
     <string name="permdesc_install_shortcut" msgid="923466509822011139">"பயனரின் அனுமதி இல்லாமல் குறுக்குவழிகளைச் சேர்க்கப் ஆப்ஸை அனுமதிக்கிறது."</string>
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"அகற்றப்பட்டது"</string>
     <string name="undo" msgid="4151576204245173321">"செயல்தவிர்"</string>
     <string name="action_move" msgid="4339390619886385032">"நகர்த்து"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"<xliff:g id="NUMBER_0">%1$s</xliff:g> வரிசை, <xliff:g id="NUMBER_1">%2$s</xliff:g> நெடுவரிசைக்கு நகர்த்து"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> என்பதில் <xliff:g id="NUMBER_0">%1$s</xliff:g>வது வரிசை <xliff:g id="NUMBER_1">%2$s</xliff:g>வது நெடுவரிசைக்கு நகர்த்தப்படும்"</string>
     <string name="move_to_position" msgid="6750008980455459790">"நிலை <xliff:g id="NUMBER">%1$s</xliff:g>க்கு நகர்த்து"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"விரும்பும் நிலை <xliff:g id="NUMBER">%1$s</xliff:g>க்கு நகர்த்து"</string>
     <string name="item_moved" msgid="4606538322571412879">"உருப்படி நகர்த்தப்பட்டது"</string>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 663e44f..8b42a8f 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"ఐటెమ్ తీసివేయబడింది"</string>
     <string name="undo" msgid="4151576204245173321">"చర్య రద్దు"</string>
     <string name="action_move" msgid="4339390619886385032">"అంశాన్ని తరలించు"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"అడ్డు వరుస <xliff:g id="NUMBER_0">%1$s</xliff:g> నిలువు వరుస <xliff:g id="NUMBER_1">%2$s</xliff:g>కి తరలించు"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> లో <xliff:g id="NUMBER_0">%1$s</xliff:g> అడ్డు వరుస <xliff:g id="NUMBER_1">%2$s</xliff:g>నిలువు వరుసకు తరలించండి"</string>
     <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g>వ స్థానానికి తరలించు"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"ఇష్టమైనవిలో <xliff:g id="NUMBER">%1$s</xliff:g>వ స్థానానికి తరలించు"</string>
     <string name="item_moved" msgid="4606538322571412879">"అంశం తరలించబడింది"</string>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 07e5796..f633b5f 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"นำรายการออกแล้ว"</string>
     <string name="undo" msgid="4151576204245173321">"เลิกทำ"</string>
     <string name="action_move" msgid="4339390619886385032">"ย้ายรายการ"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"ย้ายไปที่แถว <xliff:g id="NUMBER_0">%1$s</xliff:g> คอลัมน์ <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"ย้ายไปยังแถว <xliff:g id="NUMBER_0">%1$s</xliff:g> คอลัมน์ <xliff:g id="NUMBER_1">%2$s</xliff:g> ใน <xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"ย้ายไปยังตำแหน่ง <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"ย้ายไปยังตำแหน่งที่ <xliff:g id="NUMBER">%1$s</xliff:g> ของรายการโปรด"</string>
     <string name="item_moved" msgid="4606538322571412879">"ย้ายรายการแล้ว"</string>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index f7158b4..de4f952 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Naalis na ang item"</string>
     <string name="undo" msgid="4151576204245173321">"I-undo"</string>
     <string name="action_move" msgid="4339390619886385032">"Ilipat ang item"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Ilipat sa row <xliff:g id="NUMBER_0">%1$s</xliff:g> column <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Pumunta sa row <xliff:g id="NUMBER_0">%1$s</xliff:g> column <xliff:g id="NUMBER_1">%2$s</xliff:g> sa <xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Ilipat sa posisyon <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Ilipat sa posisyon <xliff:g id="NUMBER">%1$s</xliff:g> sa mga paborito"</string>
     <string name="item_moved" msgid="4606538322571412879">"Nalipat ang item"</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index b12b9b1..f41284b 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Öğe silindi"</string>
     <string name="undo" msgid="4151576204245173321">"Geri al"</string>
     <string name="action_move" msgid="4339390619886385032">"Öğeyi taşı"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"<xliff:g id="NUMBER_0">%1$s</xliff:g>. satır <xliff:g id="NUMBER_1">%2$s</xliff:g>. sütuna taşı"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> sayfasında <xliff:g id="NUMBER_0">%1$s</xliff:g>. satır <xliff:g id="NUMBER_1">%2$s</xliff:g>. sütuna taşı"</string>
     <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g>. sıraya taşı"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Favorilerde <xliff:g id="NUMBER">%1$s</xliff:g>. sıraya taşı"</string>
     <string name="item_moved" msgid="4606538322571412879">"Öğe taşındı"</string>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 2b43a4d..360950e 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Елемент вилучено"</string>
     <string name="undo" msgid="4151576204245173321">"Відмінити"</string>
     <string name="action_move" msgid="4339390619886385032">"Перемістити елемент"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Перемістити в рядок <xliff:g id="NUMBER_0">%1$s</xliff:g>, стовпець <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Перенести: <xliff:g id="STRING">%3$s</xliff:g>, рядок <xliff:g id="NUMBER_0">%1$s</xliff:g>, стовпець <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Перемістити на <xliff:g id="NUMBER">%1$s</xliff:g> місце"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Перемістити у вибране на <xliff:g id="NUMBER">%1$s</xliff:g> місце"</string>
     <string name="item_moved" msgid="4606538322571412879">"Елемент переміщено"</string>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index 70a853f..e68e43e 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"آئٹم ہٹا دیا گیا"</string>
     <string name="undo" msgid="4151576204245173321">"کالعدم کریں"</string>
     <string name="action_move" msgid="4339390619886385032">"آئٹم منتقل کریں"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"قطار <xliff:g id="NUMBER_0">%1$s</xliff:g> کالم <xliff:g id="NUMBER_1">%2$s</xliff:g> میں منتقل کریں"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> میں قطار <xliff:g id="NUMBER_0">%1$s</xliff:g> کالم <xliff:g id="NUMBER_1">%2$s</xliff:g> میں منتقل کریں"</string>
     <string name="move_to_position" msgid="6750008980455459790">"پوزیشن <xliff:g id="NUMBER">%1$s</xliff:g> میں منتقل کریں"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"پسندیدہ پوزیشن <xliff:g id="NUMBER">%1$s</xliff:g> میں منتقل کریں"</string>
     <string name="item_moved" msgid="4606538322571412879">"آئٹم منتقل کر دیا گیا"</string>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index 57a446b..9c7ed56 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Element olib tashlandi"</string>
     <string name="undo" msgid="4151576204245173321">"Qaytarish"</string>
     <string name="action_move" msgid="4339390619886385032">"Obyektni ko‘chirib o‘tkazish"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"<xliff:g id="NUMBER_0">%1$s</xliff:g> <xliff:g id="NUMBER_1">%2$s</xliff:g> katakka olish"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"<xliff:g id="STRING">%3$s</xliff:g> ichidagi <xliff:g id="NUMBER_0">%1$s</xliff:g>-qator <xliff:g id="NUMBER_1">%2$s</xliff:g>-ustuniga oʻting"</string>
     <string name="move_to_position" msgid="6750008980455459790">"<xliff:g id="NUMBER">%1$s</xliff:g>-joyga olish"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Sevimlilarga olish (<xliff:g id="NUMBER">%1$s</xliff:g>)"</string>
     <string name="item_moved" msgid="4606538322571412879">"Element ko‘chirib o‘tkazildi"</string>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 2c81ae7..30167fa 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Đã xóa mục"</string>
     <string name="undo" msgid="4151576204245173321">"Hủy"</string>
     <string name="action_move" msgid="4339390619886385032">"Di chuyển mục"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Di chuyển đến hàng <xliff:g id="NUMBER_0">%1$s</xliff:g> cột <xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Di chuyển đến hàng <xliff:g id="NUMBER_0">%1$s</xliff:g> cột <xliff:g id="NUMBER_1">%2$s</xliff:g> tại <xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Di chuyển tới vị trí <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Di chuyển tới vị trí mục yêu thích <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="item_moved" msgid="4606538322571412879">"Đã di chuyển mục"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 7c104e2..141d165 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"项目已移除"</string>
     <string name="undo" msgid="4151576204245173321">"撤消"</string>
     <string name="action_move" msgid="4339390619886385032">"移动项目"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"移至第 <xliff:g id="NUMBER_0">%1$s</xliff:g> 行第 <xliff:g id="NUMBER_1">%2$s</xliff:g> 列"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"移至<xliff:g id="STRING">%3$s</xliff:g>中的第 <xliff:g id="NUMBER_0">%1$s</xliff:g> 行第 <xliff:g id="NUMBER_1">%2$s</xliff:g> 列"</string>
     <string name="move_to_position" msgid="6750008980455459790">"移至第 <xliff:g id="NUMBER">%1$s</xliff:g> 个位置"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"移至收藏夹第 <xliff:g id="NUMBER">%1$s</xliff:g> 个位置"</string>
     <string name="item_moved" msgid="4606538322571412879">"已移动项目"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index ed288d6..2ff233b 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"項目已移除"</string>
     <string name="undo" msgid="4151576204245173321">"復原"</string>
     <string name="action_move" msgid="4339390619886385032">"移動項目"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"移動至第 <xliff:g id="NUMBER_0">%1$s</xliff:g> 行第 <xliff:g id="NUMBER_1">%2$s</xliff:g> 列"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"移去 <xliff:g id="STRING">%3$s</xliff:g> 嘅第 <xliff:g id="NUMBER_0">%1$s</xliff:g> 列,第 <xliff:g id="NUMBER_1">%2$s</xliff:g> 欄"</string>
     <string name="move_to_position" msgid="6750008980455459790">"移動至位置 <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"移動至喜愛的位置 <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="item_moved" msgid="4606538322571412879">"已移動項目"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 3690c3a..91cfd82 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"已移除項目"</string>
     <string name="undo" msgid="4151576204245173321">"復原"</string>
     <string name="action_move" msgid="4339390619886385032">"移動項目"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"移至第 <xliff:g id="NUMBER_0">%1$s</xliff:g> 列第 <xliff:g id="NUMBER_1">%2$s</xliff:g> 欄"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"移動至 <xliff:g id="STRING">%3$s</xliff:g> 的第 <xliff:g id="NUMBER_0">%1$s</xliff:g> 列,第 <xliff:g id="NUMBER_1">%2$s</xliff:g> 欄"</string>
     <string name="move_to_position" msgid="6750008980455459790">"移至位置 <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"移至收藏位置 <xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="item_moved" msgid="4606538322571412879">"已移動項目"</string>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 26e7dcf..12e8568 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -137,7 +137,7 @@
     <string name="item_removed" msgid="851119963877842327">"Into isusiwe"</string>
     <string name="undo" msgid="4151576204245173321">"Susa"</string>
     <string name="action_move" msgid="4339390619886385032">"Hambisa into"</string>
-    <string name="move_to_empty_cell" msgid="2833711483015685619">"Hambisa kurowu engu-<xliff:g id="NUMBER_0">%1$s</xliff:g> ikholomu engu-<xliff:g id="NUMBER_1">%2$s</xliff:g>"</string>
+    <string name="move_to_empty_cell_description" msgid="5254852678218206889">"Hamba emugqeni <xliff:g id="NUMBER_0">%1$s</xliff:g> ikholomu <xliff:g id="NUMBER_1">%2$s</xliff:g> ku-<xliff:g id="STRING">%3$s</xliff:g>"</string>
     <string name="move_to_position" msgid="6750008980455459790">"Hambisa kusimo esingu-<xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="move_to_hotseat_position" msgid="6295412897075147808">"Hambisa kusimo sezintandokazi esingu-<xliff:g id="NUMBER">%1$s</xliff:g>"</string>
     <string name="item_moved" msgid="4606538322571412879">"Into ihanjisiwe"</string>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 9f25905..e5b588c 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -136,6 +136,10 @@
         <attr name="layout_ignoreInsets" format="boolean" />
     </declare-styleable>
 
+    <declare-styleable name="StickyScroller_Layout">
+        <attr name="layout_sticky" format="boolean" />
+    </declare-styleable>
+
     <declare-styleable name="GridDisplayOption">
         <attr name="name" format="string" />
 
@@ -329,6 +333,24 @@
         <!-- defaults to hotseatBorderSpace, if not specified -->
         <attr name="hotseatBorderSpaceTwoPanelPortrait" format="float" />
 
+        <!-- defaults to res.hotseat_bar_bottom_space_default, if not specified -->
+        <attr name="hotseatBarBottomSpace" format="float" />
+        <!-- defaults to hotseatBarBottomSpace, if not specified -->
+        <attr name="hotseatBarBottomSpaceLandscape" format="float" />
+        <!-- defaults to hotseatBarBottomSpace, if not specified -->
+        <attr name="hotseatBarBottomSpaceTwoPanelLandscape" format="float" />
+        <!-- defaults to hotseatBarBottomSpace, if not specified -->
+        <attr name="hotseatBarBottomSpaceTwoPanelPortrait" format="float" />
+
+        <!-- defaults to res.hotseat_qsb_space_default, if not specified -->
+        <attr name="hotseatQsbSpace" format="float" />
+        <!-- defaults to hotseatQsbSpace, if not specified -->
+        <attr name="hotseatQsbSpaceLandscape" format="float" />
+        <!-- defaults to hotseatQsbSpace, if not specified -->
+        <attr name="hotseatQsbSpaceTwoPanelLandscape" format="float" />
+        <!-- defaults to hotseatQsbSpace, if not specified -->
+        <attr name="hotseatQsbSpaceTwoPanelPortrait" format="float" />
+
         <attr name="iconImageSize" format="float" />
         <!-- defaults to iconImageSize, if not specified -->
         <attr name="iconSizeLandscape" format="float" />
diff --git a/res/values/config.xml b/res/values/config.xml
index 9aa1f03..3f94c34 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -130,6 +130,11 @@
     <item type="id" name="search_container_all_apps" />
     <item type="id" name="search_container_hotseat" />
 
+    <!-- Scalable Grid configuration -->
+    <!-- This is a float because it is converted to dp later in DeviceProfile -->
+    <dimen name="hotseat_bar_bottom_space_default">48</dimen>
+    <dimen name="hotseat_qsb_space_default">0</dimen>
+
     <!-- Recents -->
     <item type="id" name="overview_panel"/>
 
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 39f0a2b..d095ae3 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -22,8 +22,6 @@
     <dimen name="dynamic_grid_edge_margin">10.77dp</dimen>
     <dimen name="dynamic_grid_left_right_margin">8dp</dimen>
     <dimen name="dynamic_grid_icon_drawable_padding">7dp</dimen>
-    <!-- Minimum space between workspace and hotseat in spring loaded mode -->
-    <dimen name="dynamic_grid_min_spring_loaded_space">8dp</dimen>
     <!-- Minimum amount of next page visible in spring loaded mode -->
     <dimen name="dynamic_grid_spring_loaded_min_next_space_visible">24dp</dimen>
 
@@ -32,10 +30,7 @@
     <dimen name="dynamic_grid_cell_padding_x">8dp</dimen>
 
     <!-- Hotseat -->
-    <dimen name="dynamic_grid_hotseat_top_padding">8dp</dimen>
-    <dimen name="dynamic_grid_hotseat_bottom_padding">2dp</dimen>
     <dimen name="dynamic_grid_hotseat_bottom_tall_padding">0dp</dimen>
-    <dimen name="inline_qsb_bottom_margin">0dp</dimen>
     <dimen name="spring_loaded_hotseat_top_margin">76dp</dimen>
 
     <!-- Qsb -->
@@ -44,13 +39,10 @@
       it is close to the bottom of the screen -->
     <item name="qsb_center_factor" format="float" type="dimen">0.325</item>
 
-    <!-- Extra bottom padding for non-tall devices. -->
-    <dimen name="dynamic_grid_hotseat_bottom_non_tall_padding">0dp</dimen>
-    <dimen name="dynamic_grid_hotseat_extra_vertical_size">34dp</dimen>
     <dimen name="dynamic_grid_hotseat_side_padding">0dp</dimen>
 
     <!-- Scalable Grid -->
-    <dimen name="scalable_grid_qsb_bottom_margin">42dp</dimen>
+    <dimen name="min_qsb_margin">8dp</dimen>
 
     <!-- Workspace page indicator -->
     <dimen name="workspace_page_indicator_height">24dp</dimen>
@@ -364,9 +356,13 @@
     <dimen name="taskbar_size">0dp</dimen>
     <dimen name="taskbar_stashed_size">0dp</dimen>
     <dimen name="qsb_widget_height">0dp</dimen>
+    <dimen name="qsb_shadow_height">0dp</dimen>
     <dimen name="taskbar_icon_size">44dp</dimen>
     <!-- Note that this applies to both sides of all icons, so visible space is double this. -->
     <dimen name="taskbar_icon_spacing">8dp</dimen>
+    <dimen name="taskbar_nav_buttons_size">0dp</dimen>
+    <dimen name="taskbar_contextual_button_margin">0dp</dimen>
+    <dimen name="taskbar_hotseat_nav_spacing">0dp</dimen>
 
     <!-- Size of the maximum radius for the enforced rounded rectangles. -->
     <dimen name="enforced_rounded_corner_max_radius">16dp</dimen>
@@ -418,4 +414,7 @@
     <dimen name="bottom_sheet_handle_height">4dp</dimen>
     <dimen name="bottom_sheet_handle_margin">16dp</dimen>
     <dimen name="bottom_sheet_handle_corner_radius">2dp</dimen>
+
+    <!-- State transition -->
+    <item name="workspace_content_scale" format="float" type="dimen">0.97</item>
 </resources>
diff --git a/res/xml/device_profiles.xml b/res/xml/device_profiles.xml
index 0802552..5ee291b 100644
--- a/res/xml/device_profiles.xml
+++ b/res/xml/device_profiles.xml
@@ -199,6 +199,8 @@
             launcher:allAppsBorderSpaceLandscape="16"
             launcher:hotseatBorderSpace="58"
             launcher:hotseatBorderSpaceLandscape="50.4"
+            launcher:hotseatBarBottomSpace="76"
+            launcher:hotseatBarBottomSpaceLandscape="40"
             launcher:canBeDefault="true" />
 
     </grid-option>
diff --git a/res/xml/paddings_6x5.xml b/res/xml/paddings_6x5.xml
index a72f554..2f421b7 100644
--- a/res/xml/paddings_6x5.xml
+++ b/res/xml/paddings_6x5.xml
@@ -17,60 +17,29 @@
 
 <device-paddings xmlns:launcher="http://schemas.android.com/apk/res-auto" >
 
-    <!--  Some non default screen sizes  -->
     <device-padding
-        launcher:maxEmptySpace="30dp">
+        launcher:maxEmptySpace="100dp">
         <workspaceTopPadding
-            launcher:a="0.34"
+            launcher:a="0.31"
             launcher:b="0"/>
         <workspaceBottomPadding
-            launcher:a="0.26"
+            launcher:a="0.69"
             launcher:b="0"/>
         <hotseatBottomPadding
-            launcher:a="0.4"
-            launcher:b="0"/>
-    </device-padding>
-
-    <device-padding
-        launcher:maxEmptySpace="170dp">
-        <workspaceTopPadding
             launcher:a="0"
-            launcher:b="20dp"/>
-        <workspaceBottomPadding
-            launcher:a="0.4"
-            launcher:b="0"
-            launcher:c="20dp"/>
-        <hotseatBottomPadding
-            launcher:a="0.6"
-            launcher:b="0"
-            launcher:c="20dp"/>
-    </device-padding>
-
-    <device-padding
-        launcher:maxEmptySpace="410dp">
-        <workspaceTopPadding
-            launcher:a="0"
-            launcher:b="112dp"/>
-        <workspaceBottomPadding
-            launcher:a="0.4"
-            launcher:b="0"
-            launcher:c="112dp"/>
-        <hotseatBottomPadding
-            launcher:a="0.6"
-            launcher:b="0"
-            launcher:c="112dp"/>
+            launcher:b="0"/>
     </device-padding>
 
     <device-padding
         launcher:maxEmptySpace="9999dp">
         <workspaceTopPadding
-            launcher:a="0.40"
-            launcher:c="36dp"/>
+            launcher:a="0.48"
+            launcher:b="0"/>
         <workspaceBottomPadding
-            launcher:a="0.60"
-            launcher:c="36dp"/>
+            launcher:a="0.52"
+            launcher:b="0"/>
         <hotseatBottomPadding
             launcher:a="0"
-            launcher:b="36dp"/>
+            launcher:b="0"/>
     </device-padding>
 </device-paddings>
\ No newline at end of file
diff --git a/src/com/android/launcher3/Alarm.java b/src/com/android/launcher3/Alarm.java
index d5b434c..e4aebf6 100644
--- a/src/com/android/launcher3/Alarm.java
+++ b/src/com/android/launcher3/Alarm.java
@@ -30,6 +30,7 @@
     private Handler mHandler;
     private OnAlarmListener mAlarmListener;
     private boolean mAlarmPending = false;
+    private long mLastSetTimeout;
 
     public Alarm() {
         mHandler = new Handler();
@@ -46,6 +47,7 @@
         mAlarmPending = true;
         long oldTriggerTime = mAlarmTriggerTime;
         mAlarmTriggerTime = currentTime + millisecondsInFuture;
+        mLastSetTimeout = millisecondsInFuture;
 
         // If the previous alarm was set for a longer duration, cancel it.
         if (mWaitingForCallback && oldTriggerTime > mAlarmTriggerTime) {
@@ -84,4 +86,9 @@
     public boolean alarmPending() {
         return mAlarmPending;
     }
+
+    /** Returns the last value passed to {@link #setAlarm(long)} */
+    public long getLastSetTimeout() {
+        return mLastSetTimeout;
+    }
 }
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 73d3e33..d34f535 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -77,8 +77,8 @@
             new ArrayList<>();
 
     protected DeviceProfile mDeviceProfile;
-    protected StatsLogManager mStatsLogManager;
     protected SystemUiController mSystemUiController;
+    private StatsLogManager mStatsLogManager;
 
 
     public static final int ACTIVITY_STATE_STARTED = 1 << 0;
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 8c1f505..adf6216 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -107,15 +107,14 @@
     public Rect cellLayoutPaddingPx = new Rect();
 
     public final int edgeMarginPx;
-    public float workspaceSpringLoadShrunkTop;
-    public float workspaceSpringLoadShrunkBottom;
-    public final int workspaceSpringLoadedBottomSpace;
+    public final float workspaceContentScale;
+    private float mWorkspaceSpringLoadShrunkTop;
+    private float mWorkspaceSpringLoadShrunkBottom;
     public final int workspaceSpringLoadedMinNextPageVisiblePx;
 
     private final int extraSpace;
     public int workspaceTopPadding;
     public int workspaceBottomPadding;
-    public int extraHotseatBottomPadding;
 
     // Workspace page indicator
     public final int workspacePageIndicatorHeight;
@@ -157,24 +156,23 @@
     public int folderChildDrawablePaddingPx;
 
     // Hotseat
-    public int hotseatBarSizeExtraSpacePx;
     public final int numShownHotseatIcons;
     public int hotseatCellHeightPx;
-    private final int hotseatExtraVerticalSize;
-    private final boolean areNavButtonsInline;
+    public final boolean areNavButtonsInline;
     // In portrait: size = height, in landscape: size = width
     public int hotseatBarSizePx;
-    public int hotseatBarTopPaddingPx;
-    public final int hotseatBarBottomPaddingPx;
+    public int hotseatBarBottomSpacePx;
+    public int hotseatBarEndOffset;
+    public int hotseatQsbSpace;
     public int springLoadedHotseatBarTopMarginPx;
     // Start is the side next to the nav bar, end is the side next to the workspace
     public final int hotseatBarSidePaddingStartPx;
     public final int hotseatBarSidePaddingEndPx;
     public final int hotseatQsbHeight;
+    public final int hotseatQsbVisualHeight;
+    private final int hotseatQsbShadowHeight;
     public int hotseatBorderSpace;
 
-    public final float qsbBottomMarginOriginalPx;
-    public int qsbBottomMarginPx;
     public int qsbWidth; // only used when isQsbInline
 
     // All apps
@@ -222,7 +220,7 @@
     // Insets
     private final Rect mInsets = new Rect();
     public final Rect workspacePadding = new Rect();
-    private final Rect mHotseatPadding = new Rect();
+    private final Rect mHotseatBarPadding = new Rect();
     // When true, nav bar is on the left side of the screen.
     private boolean mIsSeascape;
 
@@ -275,7 +273,7 @@
         widthPx = windowBounds.bounds.width();
         heightPx = windowBounds.bounds.height();
         availableWidthPx = windowBounds.availableSize.x;
-        availableHeightPx =  windowBounds.availableSize.y;
+        availableHeightPx = windowBounds.availableSize.y;
 
         aspectRatio = ((float) Math.max(widthPx, heightPx)) / Math.min(widthPx, heightPx);
         boolean isTallDevice = Float.compare(aspectRatio, TALL_DEVICE_ASPECT_RATIO_THRESHOLD) >= 0;
@@ -301,6 +299,7 @@
         }
 
         edgeMarginPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin);
+        workspaceContentScale = res.getFloat(R.dimen.workspace_content_scale);
 
         desiredWorkspaceHorizontalMarginPx = getHorizontalMarginPx(inv, res);
         desiredWorkspaceHorizontalMarginOriginalPx = desiredWorkspaceHorizontalMarginPx;
@@ -352,14 +351,15 @@
         dropTargetButtonWorkspaceEdgeGapPx = res.getDimensionPixelSize(
                 R.dimen.drop_target_button_workspace_edge_gap);
 
-        workspaceSpringLoadedBottomSpace =
-                res.getDimensionPixelSize(R.dimen.dynamic_grid_min_spring_loaded_space);
         workspaceSpringLoadedMinNextPageVisiblePx = res.getDimensionPixelSize(
                 R.dimen.dynamic_grid_spring_loaded_min_next_space_visible);
 
         workspaceCellPaddingXPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_padding_x);
 
         hotseatQsbHeight = res.getDimensionPixelSize(R.dimen.qsb_widget_height);
+        hotseatQsbShadowHeight = res.getDimensionPixelSize(R.dimen.qsb_shadow_height);
+        hotseatQsbVisualHeight = hotseatQsbHeight - 2 * hotseatQsbShadowHeight;
+
         // Whether QSB might be inline in appropriate orientation (e.g. landscape).
         boolean canQsbInline = (isTwoPanels ? inv.inlineQsb[INDEX_TWO_PANEL_PORTRAIT]
                 || inv.inlineQsb[INDEX_TWO_PANEL_LANDSCAPE]
@@ -379,17 +379,28 @@
 
         numShownAllAppsColumns =
                 isTwoPanels ? inv.numDatabaseAllAppsColumns : inv.numAllAppsColumns;
-        hotseatBarSizeExtraSpacePx = 0;
-        hotseatBarTopPaddingPx =
-                res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_top_padding);
-        if (isQsbInline) {
-            hotseatBarBottomPaddingPx = res.getDimensionPixelSize(R.dimen.inline_qsb_bottom_margin);
+
+        int hotseatBarBottomSpace = pxFromDp(inv.hotseatBarBottomSpace[mTypeIndex], mMetrics);
+        int minQsbMargin = res.getDimensionPixelSize(R.dimen.min_qsb_margin);
+        hotseatQsbSpace = pxFromDp(inv.hotseatQsbSpace[mTypeIndex], mMetrics);
+        // Have a little space between the inset and the QSB
+        if (mInsets.bottom + minQsbMargin > hotseatBarBottomSpace) {
+            int availableSpace = hotseatQsbSpace - (mInsets.bottom - hotseatBarBottomSpace);
+
+            // Only change the spaces if there is space
+            if (availableSpace > 0) {
+                // Make sure there is enough space between hotseat/QSB and QSB/navBar
+                if (availableSpace < minQsbMargin * 2) {
+                    minQsbMargin = availableSpace / 2;
+                    hotseatQsbSpace = minQsbMargin;
+                } else {
+                    hotseatQsbSpace -= minQsbMargin;
+                }
+            }
+            hotseatBarBottomSpacePx = mInsets.bottom + minQsbMargin;
+
         } else {
-            hotseatBarBottomPaddingPx = (isTallDevice ? res.getDimensionPixelSize(
-                    R.dimen.dynamic_grid_hotseat_bottom_tall_padding)
-                    : res.getDimensionPixelSize(
-                            R.dimen.dynamic_grid_hotseat_bottom_non_tall_padding))
-                    + res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_bottom_padding);
+            hotseatBarBottomSpacePx = hotseatBarBottomSpace;
         }
 
         springLoadedHotseatBarTopMarginPx = res.getDimensionPixelSize(
@@ -398,13 +409,19 @@
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_side_padding);
         // Add a bit of space between nav bar and hotseat in vertical bar layout.
         hotseatBarSidePaddingStartPx = isVerticalBarLayout() ? workspacePageIndicatorHeight : 0;
-        hotseatExtraVerticalSize =
-                res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_extra_vertical_size);
-        updateHotseatIconSize(pxFromDp(inv.iconSize[INDEX_DEFAULT], mMetrics));
-
-        qsbBottomMarginOriginalPx = isScalableGrid
-                ? res.getDimensionPixelSize(R.dimen.scalable_grid_qsb_bottom_margin)
-                : 0;
+        updateHotseatSizes(pxFromDp(inv.iconSize[INDEX_DEFAULT], mMetrics));
+        if (areNavButtonsInline) {
+            /*
+             * 3 nav buttons +
+             * Little space at the end for contextual buttons +
+             * Little space between icons and nav buttons
+             */
+            hotseatBarEndOffset = 3 * res.getDimensionPixelSize(R.dimen.taskbar_nav_buttons_size)
+                    + res.getDimensionPixelSize(R.dimen.taskbar_contextual_button_margin)
+                    + res.getDimensionPixelSize(R.dimen.taskbar_hotseat_nav_spacing);
+        } else {
+            hotseatBarEndOffset = 0;
+        }
 
         overviewTaskMarginPx = res.getDimensionPixelSize(R.dimen.overview_task_margin);
         overviewTaskMarginGridPx = res.getDimensionPixelSize(R.dimen.overview_task_margin_grid);
@@ -443,42 +460,6 @@
 
             workspaceTopPadding = Math.round(paddingWorkspaceTop * cellScaleToFit);
             workspaceBottomPadding = Math.round(paddingWorkspaceBottom * cellScaleToFit);
-            extraHotseatBottomPadding = Math.round(paddingHotseatBottom * cellScaleToFit);
-
-            hotseatBarSizePx += extraHotseatBottomPadding;
-
-            qsbBottomMarginPx = Math.round(qsbBottomMarginOriginalPx * cellScaleToFit);
-        } else if (!isVerticalBarLayout() && isPhone && isTallDevice) {
-            // We increase the hotseat size when there is extra space.
-
-            if (Float.compare(aspectRatio, TALLER_DEVICE_ASPECT_RATIO_THRESHOLD) >= 0
-                    && extraSpace >= Utilities.dpToPx(TALL_DEVICE_EXTRA_SPACE_THRESHOLD_DP)) {
-                // For taller devices, we will take a piece of the extra space from each row,
-                // and add it to the space above and below the hotseat.
-
-                // For devices with more extra space, we take a larger piece from each cell.
-                int piece = extraSpace < Utilities.dpToPx(TALL_DEVICE_MORE_EXTRA_SPACE_THRESHOLD_DP)
-                        ? 7 : 5;
-
-                int extraSpace = ((getCellSize().y - iconSizePx - iconDrawablePaddingPx * 2)
-                        * inv.numRows) / piece;
-
-                workspaceTopPadding = extraSpace / 8;
-                int halfLeftOver = (extraSpace - workspaceTopPadding) / 2;
-                hotseatBarTopPaddingPx += halfLeftOver;
-                hotseatBarSizeExtraSpacePx = halfLeftOver;
-            } else {
-                // ie. For a display with a large aspect ratio, we can keep the icons on the
-                // workspace in portrait mode closer together by adding more height to the hotseat.
-                // Note: This calculation was created after noticing a pattern in the design spec.
-                hotseatBarSizeExtraSpacePx = getCellSize().y - iconSizePx
-                        - iconDrawablePaddingPx * 2 - workspacePageIndicatorHeight;
-            }
-
-            updateHotseatIconSize(iconSizePx);
-
-            // Recalculate the available dimensions using the new hotseat size.
-            updateAvailableDimensions(res);
         }
 
         int cellLayoutPadding =
@@ -534,22 +515,27 @@
                 : res.getDimensionPixelSize(R.dimen.dynamic_grid_left_right_margin);
     }
 
-    private void updateHotseatIconSize(int hotseatIconSizePx) {
+    /** Updates hotseatCellHeightPx and hotseatBarSizePx */
+    private void updateHotseatSizes(int hotseatIconSizePx) {
         // Ensure there is enough space for folder icons, which have a slightly larger radius.
         hotseatCellHeightPx = (int) Math.ceil(hotseatIconSizePx * ICON_OVERLAP_FACTOR);
+
         if (isVerticalBarLayout()) {
             hotseatBarSizePx = hotseatIconSizePx + hotseatBarSidePaddingStartPx
                     + hotseatBarSidePaddingEndPx;
+        } else if (isQsbInline) {
+            hotseatBarSizePx = Math.max(hotseatIconSizePx, hotseatQsbVisualHeight)
+                    + hotseatBarBottomSpacePx;
         } else {
-            hotseatBarSizePx = hotseatIconSizePx + hotseatBarTopPaddingPx
-                    + hotseatBarBottomPaddingPx + (isScalableGrid ? 0 : hotseatExtraVerticalSize)
-                    + hotseatBarSizeExtraSpacePx;
+            hotseatBarSizePx = hotseatIconSizePx
+                    + hotseatQsbSpace
+                    + hotseatQsbVisualHeight
+                    + hotseatBarBottomSpacePx;
         }
     }
 
     private Point getCellLayoutBorderSpace(InvariantDeviceProfile idp) {
         return getCellLayoutBorderSpace(idp, 1f);
-
     }
 
     private Point getCellLayoutBorderSpace(InvariantDeviceProfile idp, float scale) {
@@ -761,7 +747,7 @@
         // All apps
         updateAllAppsIconSize(scale, res);
 
-        updateHotseatIconSize(iconSizePx);
+        updateHotseatSizes(iconSizePx);
 
         // Folder icon
         folderIconSizePx = IconNormalizer.getNormalizedCircleSize(iconSizePx);
@@ -930,35 +916,32 @@
      * Gets the space in px from the bottom of last item in the vertical-bar hotseat to the
      * bottom of the screen.
      */
-    public int getVerticalHotseatLastItemBottomOffset() {
+    private int getVerticalHotseatLastItemBottomOffset() {
         int cellHeight = calculateCellHeight(
-                heightPx - mHotseatPadding.top - mHotseatPadding.bottom, hotseatBorderSpace,
+                heightPx - mHotseatBarPadding.top - mHotseatBarPadding.bottom, hotseatBorderSpace,
                 numShownHotseatIcons);
-        int hotseatSize = (cellHeight * numShownHotseatIcons)
-                + (hotseatBorderSpace * (numShownHotseatIcons - 1));
-        int extraHotseatEndSpacing = (heightPx - hotseatSize) / 2;
         int extraIconEndSpacing = (cellHeight - iconSizePx) / 2;
-        return extraHotseatEndSpacing + extraIconEndSpacing + mHotseatPadding.bottom;
+        return extraIconEndSpacing + mHotseatBarPadding.bottom;
     }
 
     /**
      * Gets the scaled top of the workspace in px for the spring-loaded edit state.
      */
     public float getCellLayoutSpringLoadShrunkTop() {
-        workspaceSpringLoadShrunkTop = mInsets.top + dropTargetBarTopMarginPx + dropTargetBarSizePx
+        mWorkspaceSpringLoadShrunkTop = mInsets.top + dropTargetBarTopMarginPx + dropTargetBarSizePx
                 + dropTargetBarBottomMarginPx;
-        return workspaceSpringLoadShrunkTop;
+        return mWorkspaceSpringLoadShrunkTop;
     }
 
     /**
      * Gets the scaled bottom of the workspace in px for the spring-loaded edit state.
      */
-    private float getCellLayoutSpringLoadShrunkBottom() {
+    public float getCellLayoutSpringLoadShrunkBottom() {
         int topOfHotseat = hotseatBarSizePx + springLoadedHotseatBarTopMarginPx;
-        workspaceSpringLoadShrunkBottom =
+        mWorkspaceSpringLoadShrunkBottom =
                 heightPx - (isVerticalBarLayout() ? getVerticalHotseatLastItemBottomOffset()
                         : topOfHotseat);
-        return workspaceSpringLoadShrunkBottom;
+        return mWorkspaceSpringLoadShrunkBottom;
     }
 
     /**
@@ -1020,10 +1003,11 @@
                 padding.right = hotseatBarSizePx;
             }
         } else {
-            // Pad the bottom of the workspace with search/hotseat bar sizes
-            int hotseatTop = hotseatBarSizePx;
-            int paddingBottom = hotseatTop + workspacePageIndicatorHeight
-                    + workspaceBottomPadding - mWorkspacePageIndicatorOverlapWorkspace;
+            // Pad the bottom of the workspace with hotseat bar
+            // and leave a bit of space in case a widget go all the way down
+            int paddingBottom = hotseatBarSizePx + workspaceBottomPadding
+                    + workspacePageIndicatorHeight - mWorkspacePageIndicatorOverlapWorkspace
+                    - mInsets.bottom;
             int paddingTop = workspaceTopPadding + (isScalableGrid ? 0 : edgeMarginPx);
             int paddingSide = desiredWorkspaceHorizontalMarginPx;
 
@@ -1063,50 +1047,49 @@
                     + diffOverlapFactor), 0);
 
             if (isSeascape()) {
-                mHotseatPadding.set(mInsets.left + hotseatBarSidePaddingStartPx, paddingTop,
+                mHotseatBarPadding.set(mInsets.left + hotseatBarSidePaddingStartPx, paddingTop,
                         hotseatBarSidePaddingEndPx, paddingBottom);
             } else {
-                mHotseatPadding.set(hotseatBarSidePaddingEndPx, paddingTop,
+                mHotseatBarPadding.set(hotseatBarSidePaddingEndPx, paddingTop,
                         mInsets.right + hotseatBarSidePaddingStartPx, paddingBottom);
             }
         } else if (isTaskbarPresent) {
             // Center the QSB vertically with hotseat
-            int hotseatBottomPadding = getHotseatBottomPadding();
-            int hotseatTopPadding =
-                    workspacePadding.bottom - hotseatBottomPadding - hotseatCellHeightPx;
+            int hotseatBarBottomPadding = getHotseatBarBottomPadding();
+            int hotseatBarTopPadding =
+                    hotseatBarSizePx - hotseatBarBottomPadding - hotseatCellHeightPx;
 
             // Push icons to the side
             int additionalQsbSpace = isQsbInline ? qsbWidth + hotseatBorderSpace : 0;
             int requiredWidth = iconSizePx * numShownHotseatIcons
                     + hotseatBorderSpace * (numShownHotseatIcons - 1)
                     + additionalQsbSpace;
-            int endOffset = ApiWrapper.getHotseatEndOffset(context);
-            int hotseatWidth = Math.min(requiredWidth, availableWidthPx - endOffset);
+            int hotseatWidth = Math.min(requiredWidth, availableWidthPx - hotseatBarEndOffset);
             int sideSpacing = (availableWidthPx - hotseatWidth) / 2;
 
-            mHotseatPadding.set(sideSpacing, hotseatTopPadding, sideSpacing, hotseatBottomPadding);
+            mHotseatBarPadding.set(sideSpacing, hotseatBarTopPadding, sideSpacing,
+                    hotseatBarBottomPadding);
 
             boolean isRtl = Utilities.isRtl(context.getResources());
             if (isRtl) {
-                mHotseatPadding.right += additionalQsbSpace;
+                mHotseatBarPadding.right += additionalQsbSpace;
             } else {
-                mHotseatPadding.left += additionalQsbSpace;
+                mHotseatBarPadding.left += additionalQsbSpace;
             }
 
-            if (endOffset > sideSpacing) {
+            if (hotseatBarEndOffset > sideSpacing) {
                 int diff = isRtl
-                        ? sideSpacing - endOffset
-                        : endOffset - sideSpacing;
-                mHotseatPadding.left -= diff;
-                mHotseatPadding.right += diff;
+                        ? sideSpacing - hotseatBarEndOffset
+                        : hotseatBarEndOffset - sideSpacing;
+                mHotseatBarPadding.left -= diff;
+                mHotseatBarPadding.right += diff;
             }
         } else if (isScalableGrid) {
             int sideSpacing = (availableWidthPx - qsbWidth) / 2;
-            mHotseatPadding.set(sideSpacing,
-                    hotseatBarTopPaddingPx,
+            mHotseatBarPadding.set(sideSpacing,
+                    0,
                     sideSpacing,
-                    hotseatBarSizePx - hotseatCellHeightPx - hotseatBarTopPaddingPx
-                            + mInsets.bottom);
+                    getHotseatBarBottomPadding());
         } else {
             // We want the edges of the hotseat to line up with the edges of the workspace, but the
             // icons in the hotseat are a different size, and so don't line up perfectly. To account
@@ -1115,14 +1098,15 @@
             float workspaceCellWidth = (float) widthPx / inv.numColumns;
             float hotseatCellWidth = (float) widthPx / numShownHotseatIcons;
             int hotseatAdjustment = Math.round((workspaceCellWidth - hotseatCellWidth) / 2);
-            mHotseatPadding.set(hotseatAdjustment + workspacePadding.left + cellLayoutPaddingPx.left
-                            + mInsets.left, hotseatBarTopPaddingPx,
+            mHotseatBarPadding.set(
+                    hotseatAdjustment + workspacePadding.left + cellLayoutPaddingPx.left
+                            + mInsets.left,
+                    0,
                     hotseatAdjustment + workspacePadding.right + cellLayoutPaddingPx.right
                             + mInsets.right,
-                    hotseatBarSizePx - hotseatCellHeightPx - hotseatBarTopPaddingPx
-                            + mInsets.bottom);
+                    getHotseatBarBottomPadding());
         }
-        return mHotseatPadding;
+        return mHotseatBarPadding;
     }
 
     /**
@@ -1130,27 +1114,22 @@
      */
     public int getQsbOffsetY() {
         if (isQsbInline) {
-            return hotseatBarBottomPaddingPx;
-        }
-
-        int freeSpace = isTaskbarPresent
-                ? workspacePadding.bottom
-                : hotseatBarSizePx - hotseatCellHeightPx - hotseatQsbHeight;
-
-        if (isScalableGrid && qsbBottomMarginPx > mInsets.bottom) {
-            // Note that taskbarSize = 0 unless isTaskbarPresent.
-            return Math.min(qsbBottomMarginPx + taskbarSize, freeSpace);
+            return getHotseatBarBottomPadding() - ((hotseatQsbHeight - hotseatCellHeightPx) / 2);
+        } else if (isTaskbarPresent) { // QSB on top
+            return hotseatBarSizePx - hotseatQsbHeight + hotseatQsbShadowHeight;
         } else {
-            return (int) (freeSpace * mQsbCenterFactor)
-                    + (isTaskbarPresent ? taskbarSize : mInsets.bottom);
+            return hotseatBarBottomSpacePx - hotseatQsbShadowHeight;
         }
     }
 
-    private int getHotseatBottomPadding() {
-        if (isQsbInline) {
-            return getQsbOffsetY() + ((hotseatQsbHeight - hotseatCellHeightPx) / 2);
+    /**
+     * Returns the number of pixels the hotseat is translated from the bottom of the screen.
+     */
+    private int getHotseatBarBottomPadding() {
+        if (isTaskbarPresent) { // QSB on top or inline
+            return hotseatBarBottomSpacePx - (Math.abs(hotseatCellHeightPx - iconSizePx) / 2);
         } else {
-            return (getQsbOffsetY() - taskbarSize) / 2;
+            return hotseatBarSizePx - hotseatCellHeightPx;
         }
     }
 
@@ -1161,7 +1140,7 @@
         int taskbarIconBottomSpace = (taskbarSize - iconSizePx) / 2;
         int launcherIconBottomSpace =
                 Math.min((hotseatCellHeightPx - iconSizePx) / 2, gridVisualizationPaddingY);
-        return getHotseatBottomPadding() + launcherIconBottomSpace - taskbarIconBottomSpace;
+        return getHotseatBarBottomPadding() + launcherIconBottomSpace - taskbarIconBottomSpace;
     }
 
     /**
@@ -1170,7 +1149,7 @@
     public int getOverviewActionsClaimedSpaceBelow() {
         if (isTaskbarPresent && !isGestureMode) {
             // Align vertically to where nav buttons are.
-            return  ((taskbarSize - overviewActionsHeight) / 2) + getTaskbarOffsetY();
+            return ((taskbarSize - overviewActionsHeight) / 2) + getTaskbarOffsetY();
         }
 
         return isTaskbarPresent ? stashedTaskbarSize : mInsets.bottom;
@@ -1265,6 +1244,7 @@
         return "\t" + name + ": " + value + "px (" + dpiFromPx(value, mMetrics.densityDpi) + "dp)";
     }
 
+    // LINT.IfChange
     public void dump(String prefix, PrintWriter writer) {
         writer.println(prefix + "DeviceProfile:");
         writer.println(prefix + "\t1 dp = " + mMetrics.density + " px");
@@ -1344,7 +1324,8 @@
                 allAppsIconDrawablePaddingPx));
         writer.println(prefix + pxToDpStr("allAppsCellHeightPx", allAppsCellHeightPx));
         writer.println(prefix + pxToDpStr("allAppsCellWidthPx", allAppsCellWidthPx));
-        writer.println(prefix + pxToDpStr("allAppsBorderSpacePx", allAppsBorderSpacePx.x));
+        writer.println(prefix + pxToDpStr("allAppsBorderSpacePxX", allAppsBorderSpacePx.x));
+        writer.println(prefix + pxToDpStr("allAppsBorderSpacePxY", allAppsBorderSpacePx.y));
         writer.println(prefix + "\tnumShownAllAppsColumns: " + numShownAllAppsColumns);
         writer.println(prefix + pxToDpStr("allAppsLeftRightPadding", allAppsLeftRightPadding));
         writer.println(prefix + pxToDpStr("allAppsLeftRightMargin", allAppsLeftRightMargin));
@@ -1352,18 +1333,20 @@
         writer.println(prefix + pxToDpStr("hotseatBarSizePx", hotseatBarSizePx));
         writer.println(prefix + "\tinv.hotseatColumnSpan: " + inv.hotseatColumnSpan[mTypeIndex]);
         writer.println(prefix + pxToDpStr("hotseatCellHeightPx", hotseatCellHeightPx));
-        writer.println(prefix + pxToDpStr("hotseatBarTopPaddingPx", hotseatBarTopPaddingPx));
-        writer.println(prefix + pxToDpStr("hotseatBarBottomPaddingPx", hotseatBarBottomPaddingPx));
+        writer.println(prefix + pxToDpStr("hotseatBarBottomSpacePx", hotseatBarBottomSpacePx));
         writer.println(prefix + pxToDpStr("hotseatBarSidePaddingStartPx",
                 hotseatBarSidePaddingStartPx));
         writer.println(prefix + pxToDpStr("hotseatBarSidePaddingEndPx",
                 hotseatBarSidePaddingEndPx));
+        writer.println(prefix + pxToDpStr("hotseatBarEndOffset", hotseatBarEndOffset));
+        writer.println(prefix + pxToDpStr("hotseatQsbSpace", hotseatQsbSpace));
+        writer.println(prefix + pxToDpStr("hotseatQsbHeight", hotseatQsbHeight));
         writer.println(prefix + pxToDpStr("springLoadedHotseatBarTopMarginPx",
                 springLoadedHotseatBarTopMarginPx));
-        writer.println(prefix + pxToDpStr("mHotseatPadding.top", mHotseatPadding.top));
-        writer.println(prefix + pxToDpStr("mHotseatPadding.bottom", mHotseatPadding.bottom));
-        writer.println(prefix + pxToDpStr("mHotseatPadding.left", mHotseatPadding.left));
-        writer.println(prefix + pxToDpStr("mHotseatPadding.right", mHotseatPadding.right));
+        writer.println(prefix + pxToDpStr("mHotseatBarPadding.top", mHotseatBarPadding.top));
+        writer.println(prefix + pxToDpStr("mHotseatBarPadding.bottom", mHotseatBarPadding.bottom));
+        writer.println(prefix + pxToDpStr("mHotseatBarPadding.left", mHotseatBarPadding.left));
+        writer.println(prefix + pxToDpStr("mHotseatBarPadding.right", mHotseatBarPadding.right));
         writer.println(prefix + "\tnumShownHotseatIcons: " + numShownHotseatIcons);
         writer.println(prefix + pxToDpStr("hotseatBorderSpace", hotseatBorderSpace));
         writer.println(prefix + "\tisQsbInline: " + isQsbInline);
@@ -1392,7 +1375,6 @@
         }
         writer.println(prefix + pxToDpStr("workspaceTopPadding", workspaceTopPadding));
         writer.println(prefix + pxToDpStr("workspaceBottomPadding", workspaceBottomPadding));
-        writer.println(prefix + pxToDpStr("extraHotseatBottomPadding", extraHotseatBottomPadding));
 
         writer.println(prefix + pxToDpStr("overviewTaskMarginPx", overviewTaskMarginPx));
         writer.println(prefix + pxToDpStr("overviewTaskMarginGridPx", overviewTaskMarginGridPx));
@@ -1419,16 +1401,29 @@
                 prefix + pxToDpStr("dropTargetBarBottomMarginPx", dropTargetBarBottomMarginPx));
 
         writer.println(
-                prefix + pxToDpStr("workspaceSpringLoadShrunkTop", workspaceSpringLoadShrunkTop));
+                prefix + pxToDpStr("workspaceSpringLoadShrunkTop", mWorkspaceSpringLoadShrunkTop));
         writer.println(prefix + pxToDpStr("workspaceSpringLoadShrunkBottom",
-                workspaceSpringLoadShrunkBottom));
-        writer.println(prefix + pxToDpStr("workspaceSpringLoadedBottomSpace",
-                workspaceSpringLoadedBottomSpace));
+                mWorkspaceSpringLoadShrunkBottom));
         writer.println(prefix + pxToDpStr("workspaceSpringLoadedMinNextPageVisiblePx",
                 workspaceSpringLoadedMinNextPageVisiblePx));
         writer.println(
                 prefix + pxToDpStr("getWorkspaceSpringLoadScale()", getWorkspaceSpringLoadScale()));
+        writer.println(prefix + pxToDpStr("getCellLayoutHeight()", getCellLayoutHeight()));
+        writer.println(prefix + pxToDpStr("getCellLayoutWidth()", getCellLayoutWidth()));
     }
+    // LINT.ThenChange(
+    //     packages/apps/Launcher3/quickstep/tests/src/com/android/quickstep/DeviceProfilePhoneTest.kt,
+    //     packages/apps/Launcher3/quickstep/tests/src/com/android/quickstep/DeviceProfileVerticalBarTest.kt,
+    //     packages/apps/Launcher3/quickstep/tests/src/com/android/quickstep/DeviceProfilePhone3ButtonTest.kt,
+    //     packages/apps/Launcher3/quickstep/tests/src/com/android/quickstep/DeviceProfileVerticalBar3ButtonTest.kt,
+    //     packages/apps/Launcher3/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletLandscapeTest.kt,
+    //     packages/apps/Launcher3/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletPortraitTest.kt,
+    //     packages/apps/Launcher3/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletLandscape3ButtonTest.kt,
+    //     packages/apps/Launcher3/quickstep/tests/src/com/android/quickstep/DeviceProfileTabletPortrait3ButtonTest.kt,
+    //     packages/apps/Launcher3/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelLandscapeTest.kt,
+    //     packages/apps/Launcher3/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelPortraitTest.kt,
+    //     packages/apps/Launcher3/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelLandscape3ButtonTest.kt,
+    //     packages/apps/Launcher3/quickstep/tests/src/com/android/quickstep/DeviceProfileTwoPanelPortrait3ButtonTest.kt)
 
     private static Context getContext(Context c, Info info, int orientation, WindowBounds bounds) {
         Configuration config = new Configuration(c.getResources().getConfiguration());
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 8c4c662..05ed319 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -111,9 +111,7 @@
             mQsb.setVisibility(View.VISIBLE);
             lp.gravity = Gravity.BOTTOM;
             lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
-            lp.height = grid.isTaskbarPresent
-                    ? grid.workspacePadding.bottom
-                    : grid.hotseatBarSizePx + insets.bottom;
+            lp.height = grid.hotseatBarSizePx;
         }
 
         Rect padding = grid.getHotseatLayoutPadding(getContext());
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index dacbe92..2085b84 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -46,6 +46,7 @@
 import androidx.annotation.IntDef;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
+import androidx.core.content.res.ResourcesCompat;
 
 import com.android.launcher3.model.DeviceGridState;
 import com.android.launcher3.provider.RestoreDbTask;
@@ -77,7 +78,8 @@
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({TYPE_PHONE, TYPE_MULTI_DISPLAY, TYPE_TABLET})
-    public @interface DeviceType{}
+    public @interface DeviceType {}
+
     public static final int TYPE_PHONE = 0;
     public static final int TYPE_MULTI_DISPLAY = 1;
     public static final int TYPE_TABLET = 2;
@@ -153,6 +155,8 @@
     public int numDatabaseHotseatIcons;
 
     public int[] hotseatColumnSpan;
+    public float[] hotseatBarBottomSpace;
+    public float[] hotseatQsbSpace;
 
     /**
      * Number of columns in the all apps list.
@@ -360,6 +364,8 @@
                 ? closestProfile.numDatabaseHotseatIcons : closestProfile.numHotseatIcons;
         hotseatColumnSpan = closestProfile.hotseatColumnSpan;
         hotseatBorderSpaces = displayOption.hotseatBorderSpaces;
+        hotseatBarBottomSpace = displayOption.hotseatBarBottomSpace;
+        hotseatQsbSpace = displayOption.hotseatQsbSpace;
 
         numAllAppsColumns = closestProfile.numAllAppsColumns;
         numDatabaseAllAppsColumns = deviceType == TYPE_MULTI_DISPLAY
@@ -726,6 +732,7 @@
         private final int numHotseatIcons;
         private final int numShrunkenHotseatIcons;
         private final int numDatabaseHotseatIcons;
+
         private final int[] hotseatColumnSpan = new int[COUNT_SIZES];
 
         private final String dbFile;
@@ -766,6 +773,7 @@
                     R.styleable.GridDisplayOption_numShrunkenHotseatIcons, numHotseatIcons / 2);
             numDatabaseHotseatIcons = a.getInt(
                     R.styleable.GridDisplayOption_numExtendedHotseatIcons, 2 * numHotseatIcons);
+
             hotseatColumnSpan[INDEX_DEFAULT] = a.getInt(
                     R.styleable.GridDisplayOption_hotseatColumnSpan, numColumns);
             hotseatColumnSpan[INDEX_LANDSCAPE] = a.getInt(
@@ -825,6 +833,8 @@
         private final float[] horizontalMargin = new float[COUNT_SIZES];
         //TODO(http://b/228998082) remove this when 3 button spaces are fixed
         private final float[] hotseatBorderSpaces = new float[COUNT_SIZES];
+        private final float[] hotseatBarBottomSpace = new float[COUNT_SIZES];
+        private final float[] hotseatQsbSpace = new float[COUNT_SIZES];
 
         private final float[] iconSizes = new float[COUNT_SIZES];
         private final float[] textSizes = new float[COUNT_SIZES];
@@ -1050,6 +1060,34 @@
                     R.styleable.ProfileDisplayOption_hotseatBorderSpaceTwoPanelPortrait,
                     hotseatBorderSpaces[INDEX_DEFAULT]);
 
+            hotseatBarBottomSpace[INDEX_DEFAULT] = a.getFloat(
+                    R.styleable.ProfileDisplayOption_hotseatBarBottomSpace,
+                    ResourcesCompat.getFloat(context.getResources(),
+                            R.dimen.hotseat_bar_bottom_space_default));
+            hotseatBarBottomSpace[INDEX_LANDSCAPE] = a.getFloat(
+                    R.styleable.ProfileDisplayOption_hotseatBarBottomSpaceLandscape,
+                    hotseatBarBottomSpace[INDEX_DEFAULT]);
+            hotseatBarBottomSpace[INDEX_TWO_PANEL_LANDSCAPE] = a.getFloat(
+                    R.styleable.ProfileDisplayOption_hotseatBarBottomSpaceTwoPanelLandscape,
+                    hotseatBarBottomSpace[INDEX_DEFAULT]);
+            hotseatBarBottomSpace[INDEX_TWO_PANEL_PORTRAIT] = a.getFloat(
+                    R.styleable.ProfileDisplayOption_hotseatBarBottomSpaceTwoPanelPortrait,
+                    hotseatBarBottomSpace[INDEX_DEFAULT]);
+
+            hotseatQsbSpace[INDEX_DEFAULT] = a.getFloat(
+                    R.styleable.ProfileDisplayOption_hotseatQsbSpace,
+                    ResourcesCompat.getFloat(context.getResources(),
+                            R.dimen.hotseat_qsb_space_default));
+            hotseatQsbSpace[INDEX_LANDSCAPE] = a.getFloat(
+                    R.styleable.ProfileDisplayOption_hotseatQsbSpaceLandscape,
+                    hotseatQsbSpace[INDEX_DEFAULT]);
+            hotseatQsbSpace[INDEX_TWO_PANEL_LANDSCAPE] = a.getFloat(
+                    R.styleable.ProfileDisplayOption_hotseatQsbSpaceTwoPanelLandscape,
+                    hotseatQsbSpace[INDEX_DEFAULT]);
+            hotseatQsbSpace[INDEX_TWO_PANEL_PORTRAIT] = a.getFloat(
+                    R.styleable.ProfileDisplayOption_hotseatQsbSpaceTwoPanelPortrait,
+                    hotseatQsbSpace[INDEX_DEFAULT]);
+
             a.recycle();
         }
 
@@ -1085,6 +1123,8 @@
                 minCellSize[i].y *= w;
                 horizontalMargin[i] *= w;
                 hotseatBorderSpaces[i] *= w;
+                hotseatBarBottomSpace[i] *= w;
+                hotseatQsbSpace[i] *= w;
                 allAppsCellSize[i].x *= w;
                 allAppsCellSize[i].y *= w;
                 allAppsIconSizes[i] *= w;
@@ -1108,6 +1148,8 @@
                 minCellSize[i].y += p.minCellSize[i].y;
                 horizontalMargin[i] += p.horizontalMargin[i];
                 hotseatBorderSpaces[i] += p.hotseatBorderSpaces[i];
+                hotseatBarBottomSpace[i] += p.hotseatBarBottomSpace[i];
+                hotseatQsbSpace[i] += p.hotseatQsbSpace[i];
                 allAppsCellSize[i].x += p.allAppsCellSize[i].x;
                 allAppsCellSize[i].y += p.allAppsCellSize[i].y;
                 allAppsIconSizes[i] += p.allAppsIconSizes[i];
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index ebed31b..9c62251 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -30,10 +30,12 @@
 import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
 import static com.android.launcher3.AbstractFloatingView.TYPE_SNACKBAR;
 import static com.android.launcher3.AbstractFloatingView.getTopOpenViewWithType;
+import static com.android.launcher3.LauncherAnimUtils.HOTSEAT_SCALE_PROPERTY_FACTORY;
+import static com.android.launcher3.LauncherAnimUtils.SCALE_INDEX_WIDGET_TRANSITION;
 import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
+import static com.android.launcher3.LauncherAnimUtils.WORKSPACE_SCALE_PROPERTY_FACTORY;
 import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
 import static com.android.launcher3.LauncherState.ALL_APPS;
-import static com.android.launcher3.LauncherState.FLAG_CLOSE_POPUPS;
 import static com.android.launcher3.LauncherState.FLAG_MULTI_PAGE;
 import static com.android.launcher3.LauncherState.FLAG_NON_INTERACTIVE;
 import static com.android.launcher3.LauncherState.NORMAL;
@@ -97,6 +99,7 @@
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.text.method.TextKeyListener;
+import android.util.FloatProperty;
 import android.util.Log;
 import android.util.SparseArray;
 import android.view.KeyEvent;
@@ -195,6 +198,7 @@
 import com.android.launcher3.util.TouchController;
 import com.android.launcher3.util.TraceHelper;
 import com.android.launcher3.util.UiThreadHelper;
+import com.android.launcher3.util.ViewCapture;
 import com.android.launcher3.util.ViewOnDrawExecutor;
 import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.views.FloatingIconView;
@@ -266,7 +270,7 @@
     protected static final int REQUEST_LAST = 100;
 
     // Type: int
-    private static final String RUNTIME_STATE = "launcher.state";
+    protected static final String RUNTIME_STATE = "launcher.state";
     // Type: PendingRequestArgs
     private static final String RUNTIME_STATE_PENDING_REQUEST_ARGS = "launcher.request_args";
     // Type: int
@@ -278,6 +282,9 @@
     // Type int[]
     private static final String RUNTIME_STATE_CURRENT_SCREEN_IDS = "launcher.current_screen_ids";
 
+    // Type PendingSplitSelectInfo<Parcelable>
+    protected static final String PENDING_SPLIT_SELECT_INFO = "launcher.pending_split_select_info";
+
     public static final String ON_CREATE_EVT = "Launcher.onCreate";
     public static final String ON_START_EVT = "Launcher.onStart";
     public static final String ON_RESUME_EVT = "Launcher.onResume";
@@ -299,13 +306,17 @@
     public static final int DISPLAY_WORKSPACE_TRACE_COOKIE = 0;
     public static final int DISPLAY_ALL_APPS_TRACE_COOKIE = 1;
 
+    private static final FloatProperty<Workspace<?>> WORKSPACE_WIDGET_SCALE =
+            WORKSPACE_SCALE_PROPERTY_FACTORY.get(SCALE_INDEX_WIDGET_TRANSITION);
+    private static final FloatProperty<Hotseat> HOTSEAT_WIDGET_SCALE =
+            HOTSEAT_SCALE_PROPERTY_FACTORY.get(SCALE_INDEX_WIDGET_TRANSITION);
+
     private Configuration mOldConfig;
 
     @Thunk
     Workspace<?> mWorkspace;
     @Thunk
     DragLayer mDragLayer;
-    private DragController mDragController;
 
     private WidgetManagerHelper mAppWidgetManager;
     private LauncherAppWidgetHost mAppWidgetHost;
@@ -369,6 +380,7 @@
     private RotationHelper mRotationHelper;
 
     protected LauncherOverlayManager mOverlayManager;
+    protected DragController mDragController;
     // If true, overlay callbacks are deferred
     private boolean mDeferOverlayCallbacks;
     private final Runnable mDeferredOverlayCallbacks = this::checkIfOverlayStillDeferred;
@@ -386,6 +398,7 @@
     private LauncherState mPrevLauncherState;
 
     private StringCache mStringCache;
+    private ViewCapture mViewCapture;
 
     @Override
     @TargetApi(Build.VERSION_CODES.S)
@@ -465,7 +478,7 @@
         mIconCache = app.getIconCache();
         mAccessibilityDelegate = createAccessibilityDelegate();
 
-        mDragController = new LauncherDragController(this);
+        initDragController();
         mAllAppsController = new AllAppsTransitionController(this);
         mStateManager = new StateManager<>(this, NORMAL);
 
@@ -614,6 +627,13 @@
         super.onConfigurationChanged(newConfig);
     }
 
+    /**
+     * Initializes the drag controller.
+     */
+    protected void initDragController() {
+        mDragController = new LauncherDragController(this);
+    }
+
     @Override
     public void onIdpChanged(boolean modelPropertiesChanged) {
         initDeviceProfile(mDeviceProfile.inv);
@@ -740,7 +760,7 @@
                 completeAddAppWidget(appWidgetId, info, null, null);
                 break;
             case REQUEST_RECONFIGURE_APPWIDGET:
-                mStatsLogManager.logger().withItemInfo(info).log(LAUNCHER_WIDGET_RECONFIGURED);
+                getStatsLogManager().logger().withItemInfo(info).log(LAUNCHER_WIDGET_RECONFIGURED);
                 completeRestoreAppWidget(appWidgetId, LauncherAppWidgetInfo.RESTORE_COMPLETED);
                 break;
             case REQUEST_BIND_PENDING_APPWIDGET: {
@@ -1080,10 +1100,6 @@
         }
         addActivityFlags(ACTIVITY_STATE_TRANSITION_ACTIVE);
 
-        if (state.hasFlag(FLAG_CLOSE_POPUPS)) {
-            AbstractFloatingView.closeAllOpenViews(this, !state.hasFlag(FLAG_NON_INTERACTIVE));
-        }
-
         if (state == SPRING_LOADED) {
             // Prevent any Un/InstallShortcutReceivers from updating the db while we are
             // not on homescreen
@@ -1473,6 +1489,14 @@
     public void onAttachedToWindow() {
         super.onAttachedToWindow();
         mOverlayManager.onAttachedToWindow();
+        if (FeatureFlags.CONTINUOUS_VIEW_TREE_CAPTURE.get()) {
+            View root = getDragLayer().getRootView();
+            if (mViewCapture != null) {
+                root.getViewTreeObserver().removeOnDrawListener(mViewCapture);
+            }
+            mViewCapture = new ViewCapture(root);
+            root.getViewTreeObserver().addOnDrawListener(mViewCapture);
+        }
     }
 
     @Override
@@ -2992,6 +3016,10 @@
         writer.println(prefix + "\tmRotationHelper: " + mRotationHelper);
         writer.println(prefix + "\tmAppWidgetHost.isListening: " + mAppWidgetHost.isListening());
 
+        if (mViewCapture != null) {
+            writer.println(prefix + "\tmViewCapture: " + mViewCapture.dumpToString());
+        }
+
         // Extra logging for general debugging
         mDragLayer.dump(prefix, writer);
         mStateManager.dump(prefix, writer);
@@ -3220,7 +3248,12 @@
      * @param progress Transition progress from 0 to 1; where 0 => home and 1 => widgets.
      */
     public void onWidgetsTransition(float progress) {
-        // No-Op
+        if (mDeviceProfile.isTablet) {
+            float scale =
+                    Utilities.comp(Utilities.comp(mDeviceProfile.workspaceContentScale) * progress);
+            WORKSPACE_WIDGET_SCALE.set(getWorkspace(), scale);
+            HOTSEAT_WIDGET_SCALE.set(getHotseat(), scale);
+        }
     }
 
     private static class NonConfigInstance {
diff --git a/src/com/android/launcher3/LauncherAnimUtils.java b/src/com/android/launcher3/LauncherAnimUtils.java
index 0c7c311..b858d1a 100644
--- a/src/com/android/launcher3/LauncherAnimUtils.java
+++ b/src/com/android/launcher3/LauncherAnimUtils.java
@@ -81,9 +81,9 @@
             new MultiScalePropertyFactory<Hotseat>("hotseat_scale_property");
 
     public static final int SCALE_INDEX_UNFOLD_ANIMATION = 1;
-    public static final int SCALE_INDEX_UNLOCK_ANIMATION = 2;
-    public static final int SCALE_INDEX_WORKSPACE_STATE = 3;
-    public static final int SCALE_INDEX_REVEAL_ANIM = 4;
+    public static final int SCALE_INDEX_WORKSPACE_STATE = 2;
+    public static final int SCALE_INDEX_REVEAL_ANIM = 3;
+    public static final int SCALE_INDEX_WIDGET_TRANSITION = 4;
 
     /** Increase the duration if we prevented the fling, as we are going against a high velocity. */
     public static int blockedFlingDurationFactor(float velocity) {
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 5aa8a46..a20ff8c 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -162,7 +162,7 @@
     private synchronized boolean prepForMigration(String dbFile, String targetTableName,
             Supplier<DatabaseHelper> src, Supplier<DatabaseHelper> dst) {
         if (TextUtils.equals(dbFile, mOpenHelper.getDatabaseName())) {
-            Log.e("b/198965093", "prepForMigration - target db is same as current: " + dbFile);
+            Log.e(TAG, "prepForMigration - target db is same as current: " + dbFile);
             return false;
         }
 
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index cba0b7d..73be5be 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -1187,9 +1187,7 @@
     }
 
     public int getScrollForPage(int index) {
-        // TODO(b/233112195): Use !pageScrollsInitialized() instead of mPageScrolls == null, once we
-        // root cause where we should be using runOnPageScrollsInitialized().
-        if (mPageScrolls == null || index >= mPageScrolls.length || index < 0) {
+        if (!pageScrollsInitialized() || index >= mPageScrolls.length || index < 0) {
             return 0;
         } else {
             return mPageScrolls[index];
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index c482ed5..e6dea8f 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -159,7 +159,7 @@
     private LayoutTransition mLayoutTransition;
     @Thunk final WallpaperManager mWallpaperManager;
 
-    private ShortcutAndWidgetContainer mDragSourceInternal;
+    protected ShortcutAndWidgetContainer mDragSourceInternal;
 
     @Thunk final IntSparseArrayMap<CellLayout> mWorkspaceScreens = new IntSparseArrayMap<>();
     @Thunk final IntArray mScreenOrder = new IntArray();
@@ -195,7 +195,7 @@
     @Thunk final Launcher mLauncher;
     @Thunk DragController mDragController;
 
-    private final int[] mTempXY = new int[2];
+    protected final int[] mTempXY = new int[2];
     private final float[] mTempFXY = new float[2];
     private final Rect mTempRect = new Rect();
     @Thunk float[] mDragViewVisualCenter = new float[2];
diff --git a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
index 499e749..72a9b14 100644
--- a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java
@@ -304,7 +304,8 @@
             mTouchHandler.handleTouchEvent(ev, mFastScrollerOffset);
             return true;
         }
-        if (isSearching()) {
+        if (isSearching()
+                && mActivityContext.getDragLayer().isEventOverView(getVisibleContainerView(), ev)) {
             // if in search state, consume touch event.
             return true;
         }
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderRow.java b/src/com/android/launcher3/allapps/FloatingHeaderRow.java
index 6ff2132..75e527a 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderRow.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderRow.java
@@ -15,11 +15,8 @@
  */
 package com.android.launcher3.allapps;
 
-import android.graphics.Rect;
 import android.view.View;
 
-import com.android.launcher3.DeviceProfile;
-
 /**
  * A abstract representation of a row in all-apps view
  */
@@ -29,8 +26,6 @@
 
     void setup(FloatingHeaderView parent, FloatingHeaderRow[] allRows, boolean tabsHidden);
 
-    void setInsets(Rect insets, DeviceProfile grid);
-
     int getExpectedHeight();
 
     /**
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java
index 02655b7..c5bdb69 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderView.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java
@@ -31,7 +31,6 @@
 import androidx.annotation.Nullable;
 import androidx.recyclerview.widget.RecyclerView;
 
-import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Insettable;
 import com.android.launcher3.R;
 import com.android.launcher3.allapps.BaseAllAppsContainerView.AdapterHolder;
@@ -164,22 +163,6 @@
         PluginManagerWrapper.INSTANCE.get(getContext()).removePluginListener(this);
     }
 
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        mTabLayout.getLayoutParams().width = getTabWidth();
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-    }
-
-    /**
-     * Returns distance between left and right app icons
-     */
-    public int getTabWidth() {
-        DeviceProfile grid = ActivityContext.lookupContext(getContext()).getDeviceProfile();
-        int totalWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
-        int iconPadding = totalWidth / grid.numShownAllAppsColumns - grid.allAppsIconSizePx;
-        return totalWidth - iconPadding - grid.allAppsIconDrawablePaddingPx;
-    }
-
     private void recreateAllRowsArray() {
         int pluginCount = mPluginRows.size();
         if (pluginCount == 0) {
@@ -429,15 +412,6 @@
         p.y = getTop() - mCurrentRV.getTop() - ((ViewGroup) mCurrentRV.getParent()).getTop();
     }
 
-    public boolean hasVisibleContent() {
-        for (FloatingHeaderRow row : mAllRows) {
-            if (row.hasVisibleContent()) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     public boolean isHeaderProtectionSupported() {
         return mHeaderProtectionSupported;
     }
@@ -449,10 +423,9 @@
 
     @Override
     public void setInsets(Rect insets) {
-        DeviceProfile grid = ActivityContext.lookupContext(getContext()).getDeviceProfile();
-        for (FloatingHeaderRow row : mAllRows) {
-            row.setInsets(insets, grid);
-        }
+        int leftRightPadding = ActivityContext.lookupContext(getContext())
+                .getDeviceProfile().allAppsLeftRightPadding;
+        setPadding(leftRightPadding, getPaddingTop(), leftRightPadding, getPaddingBottom());
     }
 
     public <T extends FloatingHeaderRow> T findFixedRowByType(Class<T> type) {
diff --git a/src/com/android/launcher3/allapps/PluginHeaderRow.java b/src/com/android/launcher3/allapps/PluginHeaderRow.java
index 5b5fbb7..a9d36d1 100644
--- a/src/com/android/launcher3/allapps/PluginHeaderRow.java
+++ b/src/com/android/launcher3/allapps/PluginHeaderRow.java
@@ -18,10 +18,8 @@
 import static android.view.View.INVISIBLE;
 import static android.view.View.VISIBLE;
 
-import android.graphics.Rect;
 import android.view.View;
 
-import com.android.launcher3.DeviceProfile;
 import com.android.systemui.plugins.AllAppsRow;
 
 /**
@@ -43,9 +41,6 @@
             boolean tabsHidden) { }
 
     @Override
-    public void setInsets(Rect insets, DeviceProfile grid) { }
-
-    @Override
     public int getExpectedHeight() {
         return mPlugin.getExpectedHeight();
     }
diff --git a/src/com/android/launcher3/allapps/WorkEduCard.java b/src/com/android/launcher3/allapps/WorkEduCard.java
index 836cd5a..539cff1 100644
--- a/src/com/android/launcher3/allapps/WorkEduCard.java
+++ b/src/com/android/launcher3/allapps/WorkEduCard.java
@@ -15,6 +15,8 @@
  */
 package com.android.launcher3.allapps;
 
+import static com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip.getTabWidth;
+
 import android.content.Context;
 import android.util.AttributeSet;
 import android.view.View;
@@ -70,8 +72,6 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
         findViewById(R.id.action_btn).setOnClickListener(this);
-        MarginLayoutParams lp = ((MarginLayoutParams) findViewById(R.id.wrapper).getLayoutParams());
-        lp.width = mActivityContext.getAppsView().getFloatingHeaderView().getTabWidth();
     }
 
     @Override
@@ -87,14 +87,10 @@
     }
 
     @Override
-    public void onAnimationRepeat(Animation animation) {
-
-    }
+    public void onAnimationRepeat(Animation animation) { }
 
     @Override
-    public void onAnimationStart(Animation animation) {
-
-    }
+    public void onAnimationStart(Animation animation) { }
 
     private void removeCard() {
         if (mPosition == -1) {
@@ -107,8 +103,14 @@
         }
     }
 
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        int size = MeasureSpec.getSize(widthMeasureSpec);
+        findViewById(R.id.wrapper).getLayoutParams().width = getTabWidth(getContext(), size);
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+    }
+
     public void setPosition(int position) {
         mPosition = position;
     }
-
 }
diff --git a/src/com/android/launcher3/allapps/WorkModeSwitch.java b/src/com/android/launcher3/allapps/WorkModeSwitch.java
index 733577e..aee7c4c 100644
--- a/src/com/android/launcher3/allapps/WorkModeSwitch.java
+++ b/src/com/android/launcher3/allapps/WorkModeSwitch.java
@@ -16,20 +16,23 @@
 package com.android.launcher3.allapps;
 
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TURN_OFF_WORK_APPS_TAP;
+import static com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip.getTabWidth;
 
 import android.content.Context;
 import android.graphics.Insets;
 import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.view.View;
-import android.view.ViewGroup;
+import android.view.ViewGroup.MarginLayoutParams;
 import android.view.WindowInsets;
 import android.widget.Button;
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Insettable;
+import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.KeyboardInsetAnimationCallback;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.model.StringCache;
 import com.android.launcher3.views.ActivityContext;
 import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip;
@@ -50,7 +53,6 @@
     private boolean mWorkEnabled;
     private boolean mOnWorkTab;
 
-
     public WorkModeSwitch(Context context) {
         this(context, null, 0);
     }
@@ -85,15 +87,39 @@
 
     @Override
     public void setInsets(Rect insets) {
-        int bottomInset = insets.bottom - mInsets.bottom;
         mInsets.set(insets);
-        ViewGroup.MarginLayoutParams marginLayoutParams =
-                (ViewGroup.MarginLayoutParams) getLayoutParams();
-        if (marginLayoutParams != null) {
-            marginLayoutParams.bottomMargin = bottomInset + marginLayoutParams.bottomMargin;
+        MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams();
+        if (lp != null) {
+            int bottomMargin = getResources().getDimensionPixelSize(R.dimen.work_fab_margin_bottom);
+            if (FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get()) {
+                bottomMargin <<= 1;  // Double margin to add space above search bar.
+                bottomMargin += getResources().getDimensionPixelSize(R.dimen.qsb_widget_height);
+            }
+
+            DeviceProfile dp = ActivityContext.lookupContext(getContext()).getDeviceProfile();
+            if (!dp.isGestureMode) {
+                if (dp.isTaskbarPresent) {
+                    bottomMargin += dp.taskbarSize;
+                } else {
+                    bottomMargin += insets.bottom;
+                }
+            }
+
+            lp.bottomMargin = bottomMargin;
         }
     }
 
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        DeviceProfile dp = ActivityContext.lookupContext(getContext()).getDeviceProfile();
+        View parent = (View) getParent();
+        int size = parent.getWidth() - parent.getPaddingLeft() - parent.getPaddingRight()
+                - 2 * dp.allAppsLeftRightPadding;
+        int tabWidth = getTabWidth(getContext(), size);
+        int shift = (size - tabWidth) / 2 + dp.allAppsLeftRightPadding;
+        setTranslationX(Utilities.isRtl(getResources()) ? shift : -shift);
+    }
 
     @Override
     public void onActivePageChanged(int page) {
diff --git a/src/com/android/launcher3/allapps/WorkProfileManager.java b/src/com/android/launcher3/allapps/WorkProfileManager.java
index 5edd431..2f5b7a2 100644
--- a/src/com/android/launcher3/allapps/WorkProfileManager.java
+++ b/src/com/android/launcher3/allapps/WorkProfileManager.java
@@ -26,7 +26,6 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.Log;
-import android.view.ViewGroup;
 
 import androidx.annotation.IntDef;
 import androidx.annotation.Nullable;
@@ -34,7 +33,6 @@
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip;
 
@@ -144,29 +142,6 @@
             mWorkModeSwitch = (WorkModeSwitch) mAllApps.getLayoutInflater().inflate(
                     R.layout.work_mode_fab, mAllApps, false);
         }
-        ViewGroup.MarginLayoutParams lp =
-                (ViewGroup.MarginLayoutParams) mWorkModeSwitch.getLayoutParams();
-        int workFabMarginBottom =
-                mWorkModeSwitch.getResources().getDimensionPixelSize(
-                        R.dimen.work_fab_margin_bottom);
-        if (FeatureFlags.ENABLE_FLOATING_SEARCH_BAR.get()) {
-            workFabMarginBottom <<= 1;  // Double margin to add space above search bar.
-            workFabMarginBottom +=
-                    mWorkModeSwitch.getResources().getDimensionPixelSize(R.dimen.qsb_widget_height);
-        }
-        if (!mAllApps.mActivityContext.getDeviceProfile().isGestureMode){
-            if (mDeviceProfile.isTaskbarPresent){
-                workFabMarginBottom += mDeviceProfile.taskbarSize;
-            } else {
-                workFabMarginBottom +=
-                        mAllApps.mActivityContext.getDeviceProfile().getInsets().bottom;
-            }
-        }
-        lp.bottomMargin = workFabMarginBottom;
-        int allAppsContainerWidth = mAllApps.getVisibleContainerView().getWidth();
-        int personalWorkTabWidth =
-                mAllApps.mActivityContext.getAppsView().getFloatingHeaderView().getTabWidth();
-        lp.rightMargin = lp.leftMargin = (allAppsContainerWidth - personalWorkTabWidth) / 2;
         if (mWorkModeSwitch.getParent() != mAllApps) {
             mAllApps.addView(mWorkModeSwitch);
         }
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 38b5c65..4fd13b2 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -277,6 +277,17 @@
             "ENABLE_DISMISS_PREDICTION_UNDO", false,
             "Show an 'Undo' snackbar when users dismiss a predicted hotseat item");
 
+    public static final BooleanFlag ENABLE_CACHED_WIDGET = getDebugFlag(
+            "ENABLE_CACHED_WIDGET", true,
+            "Show previously cached widgets as opposed to deferred widget where available");
+
+    public static final BooleanFlag USE_SEARCH_REQUEST_TIMEOUT_OVERRIDES = getDebugFlag(
+            "USE_SEARCH_REQUEST_TIMEOUT_OVERRIDES", false,
+            "Use local overrides for search request timeout");
+
+    public static final BooleanFlag CONTINUOUS_VIEW_TREE_CAPTURE = getDebugFlag(
+            "CONTINUOUS_VIEW_TREE_CAPTURE", false, "Capture View tree every frame");
+
     public static void initialize(Context context) {
         synchronized (sDebugFlags) {
             for (DebugFlag flag : sDebugFlags) {
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index 0264ae2..09fe740 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -363,7 +363,10 @@
             // If the content is already removed, ignore
             return;
         }
-        View newContent = getViewFromDrawable(getContext(), crossFadeDrawable);
+        ImageView newContent = getViewFromDrawable(getContext(), crossFadeDrawable);
+        // We need to fill the ImageView with the content, otherwise the shapes of the final view
+        // and the drag view might not match exactly
+        newContent.setScaleType(ImageView.ScaleType.FIT_XY);
         newContent.measure(makeMeasureSpec(mWidth, EXACTLY), makeMeasureSpec(mHeight, EXACTLY));
         newContent.layout(0, 0, mWidth, mHeight);
         addViewInLayout(newContent, 0, new LayoutParams(mWidth, mHeight));
@@ -573,7 +576,7 @@
         }
     }
 
-    private static View getViewFromDrawable(Context context, Drawable drawable) {
+    private static ImageView getViewFromDrawable(Context context, Drawable drawable) {
         ImageView iv = new ImageView(context);
         iv.setImageDrawable(drawable);
         return iv;
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
index a7b0b9d..c25929a 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
@@ -110,9 +110,8 @@
     private static boolean needsToMigrate(
             DeviceGridState srcDeviceState, DeviceGridState destDeviceState) {
         boolean needsToMigrate = !destDeviceState.isCompatible(srcDeviceState);
-        // TODO(b/198965093): Revert this change after bug is fixed
         if (needsToMigrate) {
-            Log.d("b/198965093", "Migration is needed. destDeviceState: " + destDeviceState
+            Log.i(TAG, "Migration is needed. destDeviceState: " + destDeviceState
                     + ", srcDeviceState: " + srcDeviceState);
         }
         return needsToMigrate;
diff --git a/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java b/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
index 1681ea5..87ae890 100644
--- a/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
+++ b/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
@@ -271,7 +271,7 @@
         } else {
             lp.leftMargin = lp.rightMargin = 0;
             lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
-            lp.bottomMargin = grid.hotseatBarSizePx + insets.bottom;
+            lp.bottomMargin = grid.hotseatBarSizePx;
         }
         setLayoutParams(lp);
     }
diff --git a/src/com/android/launcher3/statemanager/StatefulActivity.java b/src/com/android/launcher3/statemanager/StatefulActivity.java
index c554d06..2158dea 100644
--- a/src/com/android/launcher3/statemanager/StatefulActivity.java
+++ b/src/com/android/launcher3/statemanager/StatefulActivity.java
@@ -15,6 +15,7 @@
  */
 package com.android.launcher3.statemanager;
 
+import static com.android.launcher3.LauncherState.FLAG_CLOSE_POPUPS;
 import static com.android.launcher3.LauncherState.FLAG_NON_INTERACTIVE;
 
 import android.os.Handler;
@@ -23,6 +24,7 @@
 
 import androidx.annotation.CallSuper;
 
+import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.LauncherRootView;
 import com.android.launcher3.Utilities;
@@ -87,6 +89,10 @@
         if (mDeferredResumePending) {
             handleDeferredResume();
         }
+
+        if (state.hasFlag(FLAG_CLOSE_POPUPS)) {
+            AbstractFloatingView.closeAllOpenViews(this, !state.hasFlag(FLAG_NON_INTERACTIVE));
+        }
     }
 
     /**
diff --git a/src/com/android/launcher3/testing/HotseatCellCenterRequest.java b/src/com/android/launcher3/testing/HotseatCellCenterRequest.java
new file mode 100644
index 0000000..cbb847e
--- /dev/null
+++ b/src/com/android/launcher3/testing/HotseatCellCenterRequest.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.testing;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Request object for querying a hotseat cell region in Rect.
+ */
+public class HotseatCellCenterRequest implements TestInformationRequest {
+    public final int cellInd;
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(cellInd);
+    }
+
+    @Override
+    public String getRequestName() {
+        return TestProtocol.REQUEST_HOTSEAT_CELL_CENTER;
+    }
+
+    public static final Parcelable.Creator<HotseatCellCenterRequest> CREATOR =
+            new Parcelable.Creator<HotseatCellCenterRequest>() {
+
+                @Override
+                public HotseatCellCenterRequest createFromParcel(Parcel source) {
+                    return new HotseatCellCenterRequest(source);
+                }
+
+                @Override
+                public HotseatCellCenterRequest[] newArray(int size) {
+                    return new HotseatCellCenterRequest[size];
+                }
+            };
+
+    private HotseatCellCenterRequest(int cellInd) {
+        this.cellInd = cellInd;
+    }
+
+    private HotseatCellCenterRequest(Parcel in) {
+        this(in.readInt());
+    }
+
+    /**
+     * Create a builder for HotseatCellCenterRequest.
+     *
+     * @return HotseatCellCenterRequest builder.
+     */
+    public static HotseatCellCenterRequest.Builder builder() {
+        return new HotseatCellCenterRequest.Builder();
+    }
+
+    /**
+     * HotseatCellCenterRequest Builder.
+     */
+    public static final class Builder {
+        private int mCellInd;
+
+        private Builder() {
+            mCellInd = 0;
+        }
+
+        /**
+         * Set the index of hotseat cells.
+         */
+        public HotseatCellCenterRequest.Builder setCellInd(int i) {
+            this.mCellInd = i;
+            return this;
+        }
+
+        /**
+         * build the HotseatCellCenterRequest.
+         */
+        public HotseatCellCenterRequest build() {
+            return new HotseatCellCenterRequest(mCellInd);
+        }
+    }
+}
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index 242d2d4..0334b96 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -33,6 +33,7 @@
 
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Hotseat;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
@@ -153,10 +154,6 @@
                         mDeviceProfile.isTwoPanels);
                 return response;
 
-            case TestProtocol.REQUEST_SET_FORCE_PAUSE_TIMEOUT:
-                TestProtocol.sForcePauseTimeout = Long.parseLong(arg);
-                return response;
-
             case TestProtocol.REQUEST_GET_HAD_NONTEST_EVENTS:
                 response.putBoolean(
                         TestProtocol.TEST_INFO_RESPONSE_FIELD, TestLogging.sHadEventsNotFromTest);
@@ -185,7 +182,7 @@
                     return new int[]{cellLayout.getCountX(), cellLayout.getCountY()};
                 });
 
-            case TestProtocol.REQUEST_WORKSPACE_CELL_CENTER:
+            case TestProtocol.REQUEST_WORKSPACE_CELL_CENTER: {
                 final WorkspaceCellCenterRequest request = extra.getParcelable(
                         TestProtocol.TEST_INFO_REQUEST_FIELD);
                 return getLauncherUIProperty(Bundle::putParcelable, launcher -> {
@@ -197,6 +194,21 @@
                             cellLayout, request.cellX, request.cellY, request.spanX, request.spanY);
                     return new Point(cellRect.centerX(), cellRect.centerY());
                 });
+            }
+
+            case TestProtocol.REQUEST_HOTSEAT_CELL_CENTER: {
+                final HotseatCellCenterRequest request = extra.getParcelable(
+                        TestProtocol.TEST_INFO_REQUEST_FIELD);
+                return getLauncherUIProperty(Bundle::putParcelable, launcher -> {
+                    final Hotseat hotseat = launcher.getHotseat();
+                    final Rect cellRect = getDescendantRectRelativeToDragLayerForCell(launcher,
+                            hotseat, request.cellInd, /* cellY= */ 0,
+                            /* spanX= */ 1, /* spanY= */ 1);
+                    // TODO(b/234322284): return the real center point.
+                    return new Point(cellRect.left + (cellRect.right - cellRect.left) / 3,
+                            cellRect.top + (cellRect.bottom - cellRect.top) / 3);
+                });
+            }
 
             case TestProtocol.REQUEST_HAS_TIS: {
                 response.putBoolean(
diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java
index b76e9d5..9bc9067 100644
--- a/src/com/android/launcher3/testing/TestProtocol.java
+++ b/src/com/android/launcher3/testing/TestProtocol.java
@@ -115,6 +115,8 @@
     public static final String REQUEST_WORKSPACE_CELL_LAYOUT_SIZE = "workspace-cell-layout-size";
     public static final String REQUEST_WORKSPACE_CELL_CENTER = "workspace-cell-center";
 
+    public static final String REQUEST_HOTSEAT_CELL_CENTER = "hotseat-cell-center";
+
     public static final String REQUEST_GET_FOCUSED_TASK_HEIGHT_FOR_TABLET =
             "get-focused-task-height-for-tablet";
     public static final String REQUEST_GET_GRID_TASK_SIZE_RECT_FOR_TABLET =
@@ -122,9 +124,6 @@
     public static final String REQUEST_GET_OVERVIEW_PAGE_SPACING = "get-overview-page-spacing";
     public static final String REQUEST_ENABLE_ROTATION = "enable_rotation";
 
-    public static Long sForcePauseTimeout;
-    public static final String REQUEST_SET_FORCE_PAUSE_TIMEOUT = "set-force-pause-timeout";
-
     public static boolean sDebugTracing = false;
     public static final String REQUEST_ENABLE_DEBUG_TRACING = "enable-debug-tracing";
     public static final String REQUEST_DISABLE_DEBUG_TRACING = "disable-debug-tracing";
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index 612be8e..fa4eb70 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -51,9 +51,9 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.util.SplitConfigurationOptions;
+import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
 import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
 import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
-import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
 import com.android.launcher3.views.BaseDragLayer;
 
 import java.util.Collections;
@@ -538,7 +538,8 @@
         // We calculate the "midpoint" of the thumbnail area, and place the icons there.
         // This is the place where the thumbnail area splits by default, in a near-50/50 split.
         // It is usually not exactly 50/50, due to insets/screen cutouts.
-        int fullscreenInsetThickness = deviceProfile.getInsets().top;
+        int fullscreenInsetThickness = deviceProfile.getInsets().top
+                - deviceProfile.getInsets().bottom;
         int fullscreenMidpointFromBottom = ((deviceProfile.heightPx - fullscreenInsetThickness)
                 / 2);
         float midpointFromBottomPct = (float) fullscreenMidpointFromBottom / deviceProfile.heightPx;
diff --git a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
index dff2c5d..339f910 100644
--- a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
@@ -38,8 +38,9 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
+import com.android.launcher3.util.SplitConfigurationOptions;
 import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
+import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
 import com.android.launcher3.views.BaseDragLayer;
 
 import java.util.Collections;
@@ -106,7 +107,25 @@
         return new PointF(-margin, margin);
     }
 
+    @Override
+    public void setSplitTaskSwipeRect(DeviceProfile dp, Rect outRect, SplitBounds splitInfo,
+            int desiredStagePosition) {
+        float topLeftTaskPercent = splitInfo.appsStackedVertically
+                ? splitInfo.topTaskPercent
+                : splitInfo.leftTaskPercent;
+        float dividerBarPercent = splitInfo.appsStackedVertically
+                ? splitInfo.dividerHeightPercent
+                : splitInfo.dividerWidthPercent;
 
+        // In seascape, the primary thumbnail is counterintuitively placed at the physical bottom of
+        // the screen. This is to preserve consistency when the user rotates: From the user's POV,
+        // the primary should always be on the left.
+        if (desiredStagePosition == SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT) {
+            outRect.top += (int) (outRect.height() * (topLeftTaskPercent + dividerBarPercent));
+        } else {
+            outRect.bottom = outRect.top + (int) (outRect.height() * topLeftTaskPercent);
+        }
+    }
 
     @Override
     public Pair<Float, Float> getDwbLayoutTranslations(int taskViewWidth,
@@ -215,7 +234,8 @@
         // We calculate the "midpoint" of the thumbnail area, and place the icons there.
         // This is the place where the thumbnail area splits by default, in a near-50/50 split.
         // It is usually not exactly 50/50, due to insets/screen cutouts.
-        int fullscreenInsetThickness = deviceProfile.getInsets().top;
+        int fullscreenInsetThickness = deviceProfile.getInsets().top
+                - deviceProfile.getInsets().bottom;
         int fullscreenMidpointFromBottom = ((deviceProfile.heightPx
                 - fullscreenInsetThickness) / 2);
         float midpointFromBottomPct = (float) fullscreenMidpointFromBottom / deviceProfile.heightPx;
@@ -232,14 +252,14 @@
         if (splitConfig.initiatedFromSeascape) {
             // if the split was initiated from seascape,
             // the task on the right (secondary) is slightly larger
-            primaryIconView.setTranslationY(-bottomToMidpointOffset - insetOffset);
-            secondaryIconView.setTranslationY(-bottomToMidpointOffset - insetOffset
+            primaryIconView.setTranslationY(-bottomToMidpointOffset - insetOffset
                     + taskIconHeight);
+            secondaryIconView.setTranslationY(-bottomToMidpointOffset - insetOffset);
         } else {
             // if not,
             // the task on the left (primary) is slightly larger
-            primaryIconView.setTranslationY(-bottomToMidpointOffset);
-            secondaryIconView.setTranslationY(-bottomToMidpointOffset + taskIconHeight);
+            primaryIconView.setTranslationY(-bottomToMidpointOffset + taskIconHeight);
+            secondaryIconView.setTranslationY(-bottomToMidpointOffset);
         }
 
         primaryIconView.setLayoutParams(primaryIconParams);
diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java
index 1a77674..15fe1d9 100644
--- a/src/com/android/launcher3/util/DisplayController.java
+++ b/src/com/android/launcher3/util/DisplayController.java
@@ -66,6 +66,7 @@
 public class DisplayController implements ComponentCallbacks, SafeCloseable {
 
     private static final String TAG = "DisplayController";
+    private static final boolean DEBUG = false;
 
     public static final MainThreadInitializedObject<DisplayController> INSTANCE =
             new MainThreadInitializedObject<>(DisplayController::new);
@@ -243,10 +244,9 @@
                 || !newInfo.mPerDisplayBounds.equals(oldInfo.mPerDisplayBounds)) {
             change |= CHANGE_SUPPORTED_BOUNDS;
         }
-        Log.d("b/198965093", "handleInfoChange"
-                + "\n\tchange: 0b" + Integer.toBinaryString(change)
-                + "\n\tConfiguration diff: 0x" + Integer.toHexString(
-                        newInfo.mConfiguration.diff(oldInfo.mConfiguration)));
+        if (DEBUG) {
+            Log.d(TAG, "handleInfoChange - change: 0b" + Integer.toBinaryString(change));
+        }
 
         if (change != 0) {
             mInfo = newInfo;
@@ -286,9 +286,6 @@
         private final ArrayMap<CachedDisplayInfo, WindowBounds[]> mPerDisplayBounds =
                 new ArrayMap<>();
 
-        // TODO(b/198965093): Remove after investigation
-        private Configuration mConfiguration;
-
         public Info(Context displayInfoContext) {
             /* don't need system overrides for external displays */
             this(displayInfoContext, new WindowManagerProxy(), new ArrayMap<>());
@@ -310,21 +307,18 @@
             mScreenSizeDp = new PortraitSize(config.screenHeightDp, config.screenWidthDp);
             navigationMode = parseNavigationMode(displayInfoContext);
 
-            // TODO(b/198965093): Remove after investigation
-            mConfiguration = config;
-
             mPerDisplayBounds.putAll(perDisplayBoundsCache);
             WindowBounds[] cachedValue = mPerDisplayBounds.get(normalizedDisplayInfo);
 
             WindowBounds realBounds = wmProxy.getRealBounds(displayInfoContext, displayInfo);
             if (cachedValue == null) {
                 // Unexpected normalizedDisplayInfo is found, recreate the cache
-                Log.e("b/198965093", "Unexpected normalizedDisplayInfo found, invalidating cache");
+                Log.e(TAG, "Unexpected normalizedDisplayInfo found, invalidating cache");
                 mPerDisplayBounds.clear();
                 mPerDisplayBounds.putAll(wmProxy.estimateInternalDisplayBounds(displayInfoContext));
                 cachedValue = mPerDisplayBounds.get(normalizedDisplayInfo);
                 if (cachedValue == null) {
-                    Log.e("b/198965093", "normalizedDisplayInfo not found in estimation: "
+                    Log.e(TAG, "normalizedDisplayInfo not found in estimation: "
                             + normalizedDisplayInfo);
                     supportedBounds.add(realBounds);
                 }
@@ -342,12 +336,13 @@
             }
             mPerDisplayBounds.values().forEach(
                     windowBounds -> Collections.addAll(supportedBounds, windowBounds));
-            Log.e("b/198965093", "mConfiguration: " + mConfiguration);
-            Log.d("b/198965093", "displayInfo: " + displayInfo);
-            Log.d("b/198965093", "realBounds: " + realBounds);
-            Log.d("b/198965093", "normalizedDisplayInfo: " + normalizedDisplayInfo);
-            mPerDisplayBounds.forEach((key, value) -> Log.d("b/198965093",
-                    "perDisplayBounds - " + key + ": " + Arrays.deepToString(value)));
+            if (DEBUG) {
+                Log.d(TAG, "displayInfo: " + displayInfo);
+                Log.d(TAG, "realBounds: " + realBounds);
+                Log.d(TAG, "normalizedDisplayInfo: " + normalizedDisplayInfo);
+                mPerDisplayBounds.forEach((key, value) -> Log.d(TAG,
+                        "perDisplayBounds - " + key + ": " + Arrays.deepToString(value)));
+            }
         }
 
         /**
diff --git a/src/com/android/launcher3/util/PendingSplitSelectInfo.java b/src/com/android/launcher3/util/PendingSplitSelectInfo.java
new file mode 100644
index 0000000..ed02465
--- /dev/null
+++ b/src/com/android/launcher3/util/PendingSplitSelectInfo.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util;
+
+import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
+
+/**
+ * Utility class to store information regarding a split select request. This includes the taskId of
+ * the originating task, plus the stage position.
+ * This information is intended to be saved across launcher instances, e.g. when Launcher needs to
+ * recover straight into a split select state.
+ */
+public class PendingSplitSelectInfo {
+
+    private final int mStagedTaskId;
+    private final int mStagePosition;
+
+    public PendingSplitSelectInfo(int stagedTaskId, int stagePosition) {
+        this.mStagedTaskId = stagedTaskId;
+        this.mStagePosition = stagePosition;
+    }
+
+    public int getStagedTaskId() {
+        return mStagedTaskId;
+    }
+
+    public @StagePosition int getStagePosition() {
+        return mStagePosition;
+    }
+}
diff --git a/src/com/android/launcher3/util/ViewCapture.java b/src/com/android/launcher3/util/ViewCapture.java
new file mode 100644
index 0000000..140971b
--- /dev/null
+++ b/src/com/android/launcher3/util/ViewCapture.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.util;
+
+import android.content.res.Resources;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.SystemClock;
+import android.os.Trace;
+import android.util.Base64;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver.OnDrawListener;
+
+import androidx.annotation.UiThread;
+
+import com.android.launcher3.view.ViewCaptureData.ExportedData;
+import com.android.launcher3.view.ViewCaptureData.FrameData;
+import com.android.launcher3.view.ViewCaptureData.ViewNode;
+
+import java.util.concurrent.FutureTask;
+
+/**
+ * Utility class for capturing view data every frame
+ */
+public class ViewCapture implements OnDrawListener {
+
+    private static final String TAG = "ViewCapture";
+
+    private static final int MEMORY_SIZE = 2000;
+
+    private final View mRoot;
+    private final long[] mFrameTimes = new long[MEMORY_SIZE];
+    private final Node[] mNodes = new Node[MEMORY_SIZE];
+
+    private int mFrameIndex = -1;
+
+    /**
+     * @param root the root view for the capture data
+     */
+    public ViewCapture(View root) {
+        mRoot = root;
+    }
+
+    @Override
+    public void onDraw() {
+        Trace.beginSection("view_capture");
+        long now = SystemClock.elapsedRealtimeNanos();
+
+        mFrameIndex++;
+        if (mFrameIndex >= MEMORY_SIZE) {
+            mFrameIndex = 0;
+        }
+        mFrameTimes[mFrameIndex] = now;
+        mNodes[mFrameIndex] = captureView(mRoot, mNodes[mFrameIndex]);
+        Trace.endSection();
+    }
+
+    /**
+     * Creates a proto of all the data captured so far.
+     */
+    public String dumpToString() {
+        Handler handler = mRoot.getHandler();
+        if (handler == null) {
+            handler = Executors.MAIN_EXECUTOR.getHandler();
+        }
+        FutureTask<ExportedData> task = new FutureTask<>(this::dumpToProtoUI);
+        if (Looper.myLooper() == handler.getLooper()) {
+            task.run();
+        } else {
+            handler.post(task);
+        }
+        try {
+            return Base64.encodeToString(task.get().toByteArray(),
+                    Base64.NO_CLOSE | Base64.NO_PADDING | Base64.NO_WRAP);
+        } catch (Exception e) {
+            Log.e(TAG, "Error capturing proto", e);
+            return "--error--";
+        }
+    }
+
+    @UiThread
+    private ExportedData dumpToProtoUI() {
+        ExportedData.Builder dataBuilder = ExportedData.newBuilder();
+        Resources res = mRoot.getResources();
+
+        int size = (mNodes[MEMORY_SIZE - 1] == null) ? mFrameIndex + 1 : MEMORY_SIZE;
+        for (int i = size - 1; i >= 0; i--) {
+            int index = (MEMORY_SIZE + mFrameIndex - i) % MEMORY_SIZE;
+            dataBuilder.addFrameData(FrameData.newBuilder()
+                    .setNode(mNodes[index].toProto(res))
+                    .setTimestamp(mFrameTimes[index]));
+        }
+        return dataBuilder.build();
+    }
+
+    private Node captureView(View view, Node recycle) {
+        Node result = recycle == null ? new Node() : recycle;
+
+        result.clazz = view.getClass();
+        result.hashCode = view.hashCode();
+        result.id = view.getId();
+        result.left = view.getLeft();
+        result.top = view.getTop();
+        result.right = view.getRight();
+        result.bottom = view.getBottom();
+        result.scrollX = view.getScrollX();
+        result.scrollY = view.getScrollY();
+
+        result.translateX = view.getTranslationX();
+        result.translateY = view.getTranslationY();
+        result.scaleX = view.getScaleX();
+        result.scaleY = view.getScaleY();
+        result.alpha = view.getAlpha();
+
+        result.visibility = view.getVisibility();
+        result.willNotDraw = view.willNotDraw();
+
+        if (view instanceof ViewGroup) {
+            ViewGroup parent = (ViewGroup) view;
+            result.clipChildren = parent.getClipChildren();
+            int childCount = parent.getChildCount();
+            if (childCount == 0) {
+                result.children = null;
+            } else {
+                result.children = captureView(parent.getChildAt(0), result.children);
+                Node lastChild = result.children;
+                for (int i = 1; i < childCount; i++) {
+                    lastChild.sibling = captureView(parent.getChildAt(i), lastChild.sibling);
+                    lastChild = lastChild.sibling;
+                }
+                lastChild.sibling = null;
+            }
+        } else {
+            result.clipChildren = false;
+            result.children = null;
+        }
+        return result;
+    }
+
+    private static class Node {
+
+        // We store reference in memory to avoid generating and storing too many strings
+        public Class clazz;
+        public int hashCode;
+
+        public int id;
+        public int left, top, right, bottom;
+        public int scrollX, scrollY;
+
+        public float translateX, translateY;
+        public float scaleX, scaleY;
+        public float alpha;
+
+        public int visibility;
+        public boolean willNotDraw;
+        public boolean clipChildren;
+
+        public Node sibling;
+        public Node children;
+
+        public ViewNode toProto(Resources res) {
+            String resolvedId;
+            if (id >= 0) {
+                try {
+                    resolvedId = res.getResourceTypeName(id) + '/' + res.getResourceEntryName(id);
+                } catch (Resources.NotFoundException e) {
+                    resolvedId = "id/" + "0x" + Integer.toHexString(id).toUpperCase();
+                }
+            } else {
+                resolvedId = "NO_ID";
+            }
+
+            ViewNode.Builder result = ViewNode.newBuilder()
+                    .setClassname(clazz.getName() + "@" + hashCode)
+                    .setId(resolvedId)
+                    .setLeft(left)
+                    .setTop(top)
+                    .setWidth(right - left)
+                    .setHeight(bottom - top)
+                    .setTranslationX(translateX)
+                    .setTranslationY(translateY)
+                    .setScaleX(scaleX)
+                    .setScaleY(scaleY)
+                    .setAlpha(alpha)
+                    .setVisibility(visibility)
+                    .setWillNotDraw(willNotDraw)
+                    .setClipChildren(clipChildren);
+            Node child = children;
+            while (child != null) {
+                result.addChildren(child.toProto(res));
+                child = child.sibling;
+            }
+            return result.build();
+        }
+
+    }
+}
diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java
index f553fb4..800b1f6 100644
--- a/src/com/android/launcher3/views/BaseDragLayer.java
+++ b/src/com/android/launcher3/views/BaseDragLayer.java
@@ -22,13 +22,11 @@
 
 import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs;
 
-import android.annotation.TargetApi;
 import android.app.WallpaperManager;
 import android.content.Context;
 import android.graphics.Insets;
 import android.graphics.Rect;
 import android.graphics.RectF;
-import android.os.Build;
 import android.util.AttributeSet;
 import android.util.Property;
 import android.view.MotionEvent;
@@ -550,18 +548,24 @@
     }
 
     @Override
-    @TargetApi(Build.VERSION_CODES.Q)
     public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) {
         if (Utilities.ATLEAST_Q) {
             Insets gestureInsets = insets.getMandatorySystemGestureInsets();
             int gestureInsetBottom = gestureInsets.bottom;
+            Insets imeInset = Utilities.ATLEAST_R
+                    ? insets.getInsets(WindowInsets.Type.ime())
+                    : Insets.NONE;
             DeviceProfile dp = mActivity.getDeviceProfile();
             if (dp.isTaskbarPresent) {
                 // Ignore taskbar gesture insets to avoid interfering with TouchControllers.
                 gestureInsetBottom = Math.max(0, gestureInsetBottom - dp.taskbarSize);
             }
-            mSystemGestureRegion.set(gestureInsets.left, gestureInsets.top,
-                    gestureInsets.right, gestureInsetBottom);
+            mSystemGestureRegion.set(
+                    Math.max(gestureInsets.left, imeInset.left),
+                    Math.max(gestureInsets.top, imeInset.top),
+                    Math.max(gestureInsets.right, imeInset.right),
+                    Math.max(gestureInsetBottom, imeInset.bottom)
+            );
         }
         return super.dispatchApplyWindowInsets(insets);
     }
diff --git a/src/com/android/launcher3/views/StickyHeaderLayout.java b/src/com/android/launcher3/views/StickyHeaderLayout.java
new file mode 100644
index 0000000..d6481a9
--- /dev/null
+++ b/src/com/android/launcher3/views/StickyHeaderLayout.java
@@ -0,0 +1,327 @@
+/*
+ * 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.views;
+
+import static android.view.View.MeasureSpec.EXACTLY;
+import static android.view.View.MeasureSpec.makeMeasureSpec;
+
+import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.util.FloatProperty;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.launcher3.R;
+
+/**
+ * A {@link LinearLayout} container which allows scrolling parts of its content based on the
+ * scroll of a different view. Views which are marked as sticky are not scrolled, giving the
+ * illusion of a sticky header.
+ */
+public class StickyHeaderLayout extends LinearLayout implements
+        RecyclerView.OnChildAttachStateChangeListener {
+
+    private static final FloatProperty<StickyHeaderLayout> SCROLL_OFFSET =
+            new FloatProperty<StickyHeaderLayout>("scrollAnimOffset") {
+                @Override
+                public void setValue(StickyHeaderLayout view, float offset) {
+                    view.mScrollOffset = offset;
+                    view.updateHeaderScroll();
+                }
+
+                @Override
+                public Float get(StickyHeaderLayout view) {
+                    return view.mScrollOffset;
+                }
+            };
+
+    private static final MotionEventProxyMethod INTERCEPT_PROXY = ViewGroup::onInterceptTouchEvent;
+    private static final MotionEventProxyMethod TOUCH_PROXY = ViewGroup::onTouchEvent;
+
+    private RecyclerView mCurrentRecyclerView;
+    private EmptySpaceView mCurrentEmptySpaceView;
+
+    private float mLastScroll = 0;
+    private float mScrollOffset = 0;
+    private Animator mOffsetAnimator;
+
+    private boolean mShouldForwardToRecyclerView = false;
+    private int mHeaderHeight;
+
+    public StickyHeaderLayout(Context context) {
+        this(context, /* attrs= */ null);
+    }
+
+    public StickyHeaderLayout(Context context, AttributeSet attrs) {
+        this(context, attrs, /* defStyleAttr= */ 0);
+    }
+
+    public StickyHeaderLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, /* defStyleRes= */ 0);
+    }
+
+    public StickyHeaderLayout(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    /**
+     * Sets the recycler view, this sticky header should track
+     */
+    public void setCurrentRecyclerView(RecyclerView currentRecyclerView) {
+        boolean animateReset = mCurrentRecyclerView != null;
+        if (mCurrentRecyclerView != null) {
+            mCurrentRecyclerView.removeOnChildAttachStateChangeListener(this);
+        }
+        mCurrentRecyclerView = currentRecyclerView;
+        mCurrentRecyclerView.addOnChildAttachStateChangeListener(this);
+        findCurrentEmptyView();
+        reset(animateReset);
+    }
+
+    public int getHeaderHeight() {
+        return mHeaderHeight;
+    }
+
+    private void updateHeaderScroll() {
+        mLastScroll = getCurrentScroll();
+        int count = getChildCount();
+        for (int i = 0; i < count; i++) {
+            View child = getChildAt(i);
+            MyLayoutParams lp = (MyLayoutParams) child.getLayoutParams();
+            child.setTranslationY(Math.max(mLastScroll, lp.scrollLimit));
+        }
+    }
+
+    private float getCurrentScroll() {
+        return mScrollOffset + (mCurrentEmptySpaceView == null ? 0 : mCurrentEmptySpaceView.getY());
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+        mHeaderHeight = getMeasuredHeight();
+        if (mCurrentEmptySpaceView != null) {
+            mCurrentEmptySpaceView.setFixedHeight(mHeaderHeight);
+        }
+    }
+
+    /** Resets any previous view translation. */
+    public void reset(boolean animate) {
+        if (mOffsetAnimator != null) {
+            mOffsetAnimator.cancel();
+            mOffsetAnimator = null;
+        }
+
+        mScrollOffset = 0;
+        if (!animate) {
+            updateHeaderScroll();
+        } else {
+            float startValue = mLastScroll - getCurrentScroll();
+            mOffsetAnimator = ObjectAnimator.ofFloat(this, SCROLL_OFFSET, startValue, 0);
+            mOffsetAnimator.addListener(forEndCallback(() -> mOffsetAnimator = null));
+            mOffsetAnimator.start();
+        }
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent event) {
+        return (mShouldForwardToRecyclerView = proxyMotionEvent(event, INTERCEPT_PROXY))
+                || super.onInterceptTouchEvent(event);
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        return mShouldForwardToRecyclerView && proxyMotionEvent(event, TOUCH_PROXY)
+                || super.onTouchEvent(event);
+    }
+
+    private boolean proxyMotionEvent(MotionEvent event, MotionEventProxyMethod method) {
+        float dx = mCurrentRecyclerView.getLeft() - getLeft();
+        float dy = mCurrentRecyclerView.getTop() - getTop();
+        event.offsetLocation(dx, dy);
+        try {
+            return method.proxyEvent(mCurrentRecyclerView, event);
+        } finally {
+            event.offsetLocation(-dx, -dy);
+        }
+    }
+
+    @Override
+    public void onChildViewAttachedToWindow(@NonNull View view) {
+        if (view instanceof EmptySpaceView) {
+            findCurrentEmptyView();
+        }
+    }
+
+    @Override
+    public void onChildViewDetachedFromWindow(@NonNull View view) {
+        if (view == mCurrentEmptySpaceView) {
+            findCurrentEmptyView();
+        }
+    }
+
+    private void findCurrentEmptyView() {
+        if (mCurrentEmptySpaceView != null) {
+            mCurrentEmptySpaceView.setOnYChangeCallback(null);
+            mCurrentEmptySpaceView = null;
+        }
+        int childCount = mCurrentRecyclerView.getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            View view = mCurrentRecyclerView.getChildAt(i);
+            if (view instanceof EmptySpaceView) {
+                mCurrentEmptySpaceView = (EmptySpaceView) view;
+                mCurrentEmptySpaceView.setFixedHeight(getHeaderHeight());
+                mCurrentEmptySpaceView.setOnYChangeCallback(this::updateHeaderScroll);
+                return;
+            }
+        }
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        super.onLayout(changed, l, t, r, b);
+
+        // Update various stick parameters
+        int count = getChildCount();
+        int stickyHeaderHeight = 0;
+        for (int i = 0; i < count; i++) {
+            View v = getChildAt(i);
+            MyLayoutParams lp = (MyLayoutParams) v.getLayoutParams();
+            if (lp.sticky) {
+                lp.scrollLimit = -v.getTop() + stickyHeaderHeight;
+                stickyHeaderHeight += v.getHeight();
+            } else {
+                lp.scrollLimit = Integer.MIN_VALUE;
+            }
+        }
+        updateHeaderScroll();
+    }
+
+    @Override
+    protected LayoutParams generateDefaultLayoutParams() {
+        return new MyLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
+    }
+
+    @Override
+    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
+        return new MyLayoutParams(lp.width, lp.height);
+    }
+
+    @Override
+    public LayoutParams generateLayoutParams(AttributeSet attrs) {
+        return new MyLayoutParams(getContext(), attrs);
+    }
+
+    @Override
+    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
+        return p instanceof MyLayoutParams;
+    }
+
+    private static class MyLayoutParams extends LayoutParams {
+
+        public final boolean sticky;
+        public int scrollLimit;
+
+        MyLayoutParams(int width, int height) {
+            super(width, height);
+            sticky = false;
+        }
+
+        MyLayoutParams(Context c, AttributeSet attrs) {
+            super(c, attrs);
+            TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.StickyScroller_Layout);
+            sticky = a.getBoolean(R.styleable.StickyScroller_Layout_layout_sticky, false);
+            a.recycle();
+        }
+    }
+
+    private interface MotionEventProxyMethod {
+
+        boolean proxyEvent(ViewGroup view, MotionEvent event);
+    }
+
+    /**
+     * Empty view which allows listening for 'Y' changes
+     */
+    public static class EmptySpaceView extends View {
+
+        private Runnable mOnYChangeCallback;
+        private int mHeight = 0;
+
+        public EmptySpaceView(Context context) {
+            super(context);
+            animate().setUpdateListener(v -> notifyYChanged());
+        }
+
+        /**
+         * Sets the height for the empty view
+         * @return true if the height changed, false otherwise
+         */
+        public boolean setFixedHeight(int height) {
+            if (mHeight != height) {
+                mHeight = height;
+                requestLayout();
+                return true;
+            }
+            return false;
+        }
+
+        @Override
+        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+            super.onMeasure(widthMeasureSpec, makeMeasureSpec(mHeight, EXACTLY));
+        }
+
+        public void setOnYChangeCallback(Runnable callback) {
+            mOnYChangeCallback = callback;
+        }
+
+        @Override
+        protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+            super.onLayout(changed, left, top, right, bottom);
+            notifyYChanged();
+        }
+
+        @Override
+        public void offsetTopAndBottom(int offset) {
+            super.offsetTopAndBottom(offset);
+            notifyYChanged();
+        }
+
+        @Override
+        public void setTranslationY(float translationY) {
+            super.setTranslationY(translationY);
+            notifyYChanged();
+        }
+
+        private void notifyYChanged() {
+            if (mOnYChangeCallback != null) {
+                mOnYChangeCallback.run();
+            }
+        }
+    }
+}
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHost.java b/src/com/android/launcher3/widget/LauncherAppWidgetHost.java
index fe83f3f..98a960c 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHost.java
@@ -28,6 +28,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.util.SparseArray;
+import android.widget.RemoteViews;
 import android.widget.Toast;
 
 import androidx.annotation.Nullable;
@@ -37,6 +38,7 @@
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.model.WidgetsModel;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.testing.TestLogging;
@@ -70,13 +72,14 @@
     private final ArrayList<ProviderChangedListener> mProviderChangeListeners = new ArrayList<>();
     private final SparseArray<LauncherAppWidgetHostView> mViews = new SparseArray<>();
     private final SparseArray<PendingAppWidgetHostView> mPendingViews = new SparseArray<>();
+    private final SparseArray<LauncherAppWidgetHostView> mDeferredViews = new SparseArray<>();
+    private final SparseArray<RemoteViews> mCachedRemoteViews = new SparseArray<>();
 
     private final Context mContext;
     private int mFlags = FLAG_STATE_IS_NORMAL;
 
     private IntConsumer mAppWidgetRemovedCallback = null;
 
-
     public LauncherAppWidgetHost(Context context) {
         this(context, null);
     }
@@ -95,6 +98,11 @@
         if (mPendingViews.get(appWidgetId) != null) {
             view = mPendingViews.get(appWidgetId);
             mPendingViews.remove(appWidgetId);
+        } else if (mDeferredViews.get(appWidgetId) != null) {
+            // In case the widget view is deferred, we will simply return the deferred view as
+            // opposed to instantiate a new instance of LauncherAppWidgetHostView since launcher
+            // already added the former to the workspace.
+            view = mDeferredViews.get(appWidgetId);
         } else {
             view = new LauncherAppWidgetHostView(context);
         }
@@ -120,12 +128,25 @@
             // widgets upon bind anyway. See issue 14255011 for more context.
         }
 
-        // We go in reverse order and inflate any deferred widget
+        // We go in reverse order and inflate any deferred or cached widget
         for (int i = mViews.size() - 1; i >= 0; i--) {
             LauncherAppWidgetHostView view = mViews.valueAt(i);
             if (view instanceof DeferredAppWidgetHostView) {
                 view.reInflate();
             }
+            if (FeatureFlags.ENABLE_CACHED_WIDGET.get()) {
+                final int appWidgetId = mViews.keyAt(i);
+                if (view == mDeferredViews.get(appWidgetId)) {
+                    // If the widget view was deferred, we'll need to call super.createView here
+                    // to make the binder call to system process to fetch cumulative updates to this
+                    // widget, as well as setting up this view for future updates.
+                    super.createView(view.mLauncher, appWidgetId, view.getAppWidgetInfo());
+                    // At this point #onCreateView should have been called, which in turn returned
+                    // the deferred view. There's no reason to keep the reference anymore, so we
+                    // removed it here.
+                    mDeferredViews.remove(appWidgetId);
+                }
+            }
         }
     }
 
@@ -221,10 +242,28 @@
             CustomWidgetManager.INSTANCE.get(context).onViewCreated(lahv);
             return lahv;
         } else if ((mFlags & FLAG_LISTENING) == 0) {
-            DeferredAppWidgetHostView view = new DeferredAppWidgetHostView(context);
-            view.setAppWidget(appWidgetId, appWidget);
-            mViews.put(appWidgetId, view);
-            return view;
+            // Since the launcher hasn't started listening to widget updates, we can't simply call
+            // super.createView here because the later will make a binder call to retrieve
+            // RemoteViews from system process.
+            // TODO: have launcher always listens to widget updates in background so that this
+            //  check can be removed altogether.
+            if (FeatureFlags.ENABLE_CACHED_WIDGET.get()
+                    && mCachedRemoteViews.get(appWidgetId) != null) {
+                // We've found RemoteViews from cache for this widget, so we will instantiate a
+                // widget host view and populate it with the cached RemoteViews.
+                final LauncherAppWidgetHostView view = new LauncherAppWidgetHostView(context);
+                view.setAppWidget(appWidgetId, appWidget);
+                view.updateAppWidget(mCachedRemoteViews.get(appWidgetId));
+                mDeferredViews.put(appWidgetId, view);
+                mViews.put(appWidgetId, view);
+                return view;
+            } else {
+                // When cache misses, a placeholder for the widget will be returned instead.
+                DeferredAppWidgetHostView view = new DeferredAppWidgetHostView(context);
+                view.setAppWidget(appWidgetId, appWidget);
+                mViews.put(appWidgetId, view);
+                return view;
+            }
         } else {
             try {
                 return super.createView(context, appWidgetId, appWidget);
@@ -281,6 +320,16 @@
     @Override
     public void clearViews() {
         super.clearViews();
+        if (FeatureFlags.ENABLE_CACHED_WIDGET.get()) {
+            // First, we clear any previously cached content from existing widgets
+            mCachedRemoteViews.clear();
+            // Then we proceed to cache the content from the widgets
+            for (int i = 0; i < mViews.size(); i++) {
+                final int appWidgetId = mViews.keyAt(i);
+                final LauncherAppWidgetHostView view = mViews.get(appWidgetId);
+                mCachedRemoteViews.put(appWidgetId, view.mLastRemoteViews);
+            }
+        }
         mViews.clear();
     }
 
diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
index 0865152..fc1e880 100644
--- a/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/widget/LauncherAppWidgetHostView.java
@@ -43,6 +43,7 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
@@ -85,7 +86,7 @@
     private Runnable mAutoAdvanceRunnable;
 
     private long mDeferUpdatesUntilMillis = 0;
-    private RemoteViews mDeferredRemoteViews;
+    RemoteViews mLastRemoteViews;
     private boolean mHasDeferredColorChange = false;
     private @Nullable SparseIntArray mDeferredColorChange = null;
 
@@ -150,11 +151,18 @@
                     TRACE_METHOD_NAME + getAppWidgetInfo().provider, getAppWidgetId());
             mTrackingWidgetUpdate = false;
         }
-        if (isDeferringUpdates()) {
-            mDeferredRemoteViews = remoteViews;
-            return;
+        if (FeatureFlags.ENABLE_CACHED_WIDGET.get()) {
+            mLastRemoteViews = remoteViews;
+            if (isDeferringUpdates()) {
+                return;
+            }
+        } else {
+            if (isDeferringUpdates()) {
+                mLastRemoteViews = remoteViews;
+                return;
+            }
+            mLastRemoteViews = null;
         }
-        mDeferredRemoteViews = null;
 
         super.updateAppWidget(remoteViews);
 
@@ -218,8 +226,7 @@
         SparseIntArray deferredColors;
         boolean hasDeferredColors;
         mDeferUpdatesUntilMillis = 0;
-        remoteViews = mDeferredRemoteViews;
-        mDeferredRemoteViews = null;
+        remoteViews = mLastRemoteViews;
         deferredColors = mDeferredColorChange;
         hasDeferredColors = mHasDeferredColorChange;
         mDeferredColorChange = null;
diff --git a/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java b/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java
deleted file mode 100644
index 716dcf3..0000000
--- a/src/com/android/launcher3/widget/picker/SearchAndRecommendationsScrollController.java
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.widget.picker;
-
-import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
-
-import android.animation.Animator;
-import android.animation.ObjectAnimator;
-import android.util.FloatProperty;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.TextView;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.recyclerview.widget.RecyclerView;
-
-import com.android.launcher3.R;
-import com.android.launcher3.widget.picker.WidgetsSpaceViewHolderBinder.EmptySpaceView;
-import com.android.launcher3.widget.picker.search.WidgetsSearchBar;
-
-/**
- * A controller which measures & updates {@link WidgetsFullSheet}'s views padding, margin and
- * vertical displacement upon scrolling.
- */
-final class SearchAndRecommendationsScrollController implements
-        RecyclerView.OnChildAttachStateChangeListener {
-
-    private static final FloatProperty<SearchAndRecommendationsScrollController> SCROLL_OFFSET =
-            new FloatProperty<SearchAndRecommendationsScrollController>("scrollAnimOffset") {
-        @Override
-        public void setValue(SearchAndRecommendationsScrollController controller, float offset) {
-            controller.mScrollOffset = offset;
-            controller.updateHeaderScroll();
-        }
-
-        @Override
-        public Float get(SearchAndRecommendationsScrollController controller) {
-            return controller.mScrollOffset;
-        }
-    };
-
-    private static final MotionEventProxyMethod INTERCEPT_PROXY = ViewGroup::onInterceptTouchEvent;
-    private static final MotionEventProxyMethod TOUCH_PROXY = ViewGroup::onTouchEvent;
-
-    final SearchAndRecommendationsView mContainer;
-    final View mSearchBarContainer;
-    final WidgetsSearchBar mSearchBar;
-    final TextView mHeaderTitle;
-    final WidgetsRecommendationTableLayout mRecommendedWidgetsTable;
-    @Nullable final View mTabBar;
-
-    private WidgetsRecyclerView mCurrentRecyclerView;
-    private EmptySpaceView mCurrentEmptySpaceView;
-
-    private float mLastScroll = 0;
-    private float mScrollOffset = 0;
-    private Animator mOffsetAnimator;
-
-    private boolean mShouldForwardToRecyclerView = false;
-
-    private int mHeaderHeight;
-
-    SearchAndRecommendationsScrollController(
-            SearchAndRecommendationsView searchAndRecommendationContainer) {
-        mContainer = searchAndRecommendationContainer;
-        mSearchBarContainer = mContainer.findViewById(R.id.search_bar_container);
-        mSearchBar = mContainer.findViewById(R.id.widgets_search_bar);
-        mHeaderTitle = mContainer.findViewById(R.id.title);
-        mRecommendedWidgetsTable = mContainer.findViewById(R.id.recommended_widget_table);
-        mTabBar = mContainer.findViewById(R.id.tabs);
-
-        mContainer.setSearchAndRecommendationScrollController(this);
-    }
-
-    public void setCurrentRecyclerView(WidgetsRecyclerView currentRecyclerView) {
-        boolean animateReset = mCurrentRecyclerView != null;
-        if (mCurrentRecyclerView != null) {
-            mCurrentRecyclerView.removeOnChildAttachStateChangeListener(this);
-        }
-        mCurrentRecyclerView = currentRecyclerView;
-        mCurrentRecyclerView.addOnChildAttachStateChangeListener(this);
-        findCurrentEmptyView();
-        reset(animateReset);
-    }
-
-    public int getHeaderHeight() {
-        return mHeaderHeight;
-    }
-
-    private void updateHeaderScroll() {
-        mLastScroll = getCurrentScroll();
-        mHeaderTitle.setTranslationY(mLastScroll);
-        mRecommendedWidgetsTable.setTranslationY(mLastScroll);
-
-        float searchYDisplacement = Math.max(mLastScroll, -mSearchBarContainer.getTop());
-        mSearchBarContainer.setTranslationY(searchYDisplacement);
-
-        if (mTabBar != null) {
-            float tabsDisplacement = Math.max(mLastScroll, -mTabBar.getTop()
-                    + mSearchBarContainer.getHeight());
-            mTabBar.setTranslationY(tabsDisplacement);
-        }
-    }
-
-    private float getCurrentScroll() {
-        return mScrollOffset + (mCurrentEmptySpaceView == null ? 0 : mCurrentEmptySpaceView.getY());
-    }
-
-    /**
-     * Updates the scrollable header height
-     *
-     * @return {@code true} if the header height or dependent property changed.
-     */
-    public boolean updateHeaderHeight() {
-        boolean hasSizeUpdated = false;
-
-        int headerHeight = mContainer.getMeasuredHeight();
-        if (headerHeight != mHeaderHeight) {
-            mHeaderHeight = headerHeight;
-            hasSizeUpdated = true;
-        }
-
-        if (mCurrentEmptySpaceView != null
-                && mCurrentEmptySpaceView.setFixedHeight(mHeaderHeight)) {
-            hasSizeUpdated = true;
-        }
-        return hasSizeUpdated;
-    }
-
-    /** Resets any previous view translation. */
-    public void reset(boolean animate) {
-        if (mOffsetAnimator != null) {
-            mOffsetAnimator.cancel();
-            mOffsetAnimator = null;
-        }
-
-        mScrollOffset = 0;
-        if (!animate) {
-            updateHeaderScroll();
-        } else {
-            float startValue = mLastScroll - getCurrentScroll();
-            mOffsetAnimator = ObjectAnimator.ofFloat(this, SCROLL_OFFSET, startValue, 0);
-            mOffsetAnimator.addListener(forEndCallback(() -> mOffsetAnimator = null));
-            mOffsetAnimator.start();
-        }
-    }
-
-    /**
-     * Returns {@code true} if a touch event should be intercepted by this controller.
-     */
-    public boolean onInterceptTouchEvent(MotionEvent event) {
-        return (mShouldForwardToRecyclerView = proxyMotionEvent(event, INTERCEPT_PROXY));
-    }
-
-    /**
-     * Returns {@code true} if this controller has intercepted and consumed a touch event.
-     */
-    public boolean onTouchEvent(MotionEvent event) {
-        return mShouldForwardToRecyclerView && proxyMotionEvent(event, TOUCH_PROXY);
-    }
-
-    private boolean proxyMotionEvent(MotionEvent event, MotionEventProxyMethod method) {
-        float dx = mCurrentRecyclerView.getLeft() - mContainer.getLeft();
-        float dy = mCurrentRecyclerView.getTop() - mContainer.getTop();
-        event.offsetLocation(dx, dy);
-        try {
-            return method.proxyEvent(mCurrentRecyclerView, event);
-        } finally {
-            event.offsetLocation(-dx, -dy);
-        }
-    }
-
-    @Override
-    public void onChildViewAttachedToWindow(@NonNull View view) {
-        if (view instanceof EmptySpaceView) {
-            findCurrentEmptyView();
-        }
-    }
-
-    @Override
-    public void onChildViewDetachedFromWindow(@NonNull View view) {
-        if (view == mCurrentEmptySpaceView) {
-            findCurrentEmptyView();
-        }
-    }
-
-    private void findCurrentEmptyView() {
-        if (mCurrentEmptySpaceView != null) {
-            mCurrentEmptySpaceView.setOnYChangeCallback(null);
-            mCurrentEmptySpaceView = null;
-        }
-        int childCount = mCurrentRecyclerView.getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            View view = mCurrentRecyclerView.getChildAt(i);
-            if (view instanceof EmptySpaceView) {
-                mCurrentEmptySpaceView = (EmptySpaceView) view;
-                mCurrentEmptySpaceView.setFixedHeight(getHeaderHeight());
-                mCurrentEmptySpaceView.setOnYChangeCallback(this::updateHeaderScroll);
-                return;
-            }
-        }
-    }
-
-    private interface MotionEventProxyMethod {
-
-        boolean proxyEvent(ViewGroup view, MotionEvent event);
-    }
-}
diff --git a/src/com/android/launcher3/widget/picker/SearchAndRecommendationsView.java b/src/com/android/launcher3/widget/picker/SearchAndRecommendationsView.java
deleted file mode 100644
index 0d7d2b5..0000000
--- a/src/com/android/launcher3/widget/picker/SearchAndRecommendationsView.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3.widget.picker;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.MotionEvent;
-import android.widget.LinearLayout;
-
-/**
- * A {@link LinearLayout} container for holding search and widgets recommendation.
- *
- * <p>This class intercepts touch events and dispatch them to the right view.
- */
-public class SearchAndRecommendationsView extends LinearLayout {
-    private SearchAndRecommendationsScrollController mController;
-
-    public SearchAndRecommendationsView(Context context) {
-        this(context, /* attrs= */ null);
-    }
-
-    public SearchAndRecommendationsView(Context context, AttributeSet attrs) {
-        this(context, attrs, /* defStyleAttr= */ 0);
-    }
-
-    public SearchAndRecommendationsView(Context context, AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, /* defStyleRes= */ 0);
-    }
-
-    public SearchAndRecommendationsView(Context context, AttributeSet attrs, int defStyleAttr,
-            int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-    }
-
-    public void setSearchAndRecommendationScrollController(
-            SearchAndRecommendationsScrollController controller) {
-        mController = controller;
-    }
-
-    @Override
-    public boolean onInterceptTouchEvent(MotionEvent event) {
-        return mController.onInterceptTouchEvent(event) || super.onInterceptTouchEvent(event);
-    }
-
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        return mController.onTouchEvent(event) || super.onTouchEvent(event);
-    }
-}
diff --git a/src/com/android/launcher3/widget/picker/WidgetPagedView.java b/src/com/android/launcher3/widget/picker/WidgetPagedView.java
new file mode 100644
index 0000000..c95ec5f
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/WidgetPagedView.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.widget.picker;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+
+import com.android.launcher3.PagedView;
+import com.android.launcher3.workprofile.PersonalWorkPagedView;
+
+/**
+ * A {@link PagedView} for showing different widgets for the personal and work profile.
+ */
+public class WidgetPagedView extends PersonalWorkPagedView {
+
+    public WidgetPagedView(Context context) {
+        this(context, null);
+    }
+
+    public WidgetPagedView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public WidgetPagedView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        setPageSpacing(getPaddingLeft());
+    }
+
+    @Override
+    public void getDrawingRect(Rect outRect) {
+        super.getDrawingRect(outRect);
+        outRect.left += getPaddingLeft();
+        outRect.right -= getPaddingRight();
+    }
+}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index a49cdc0..88d9723 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -62,11 +62,13 @@
 import com.android.launcher3.views.ArrowTipView;
 import com.android.launcher3.views.RecyclerViewFastScroller;
 import com.android.launcher3.views.SpringRelativeLayout;
+import com.android.launcher3.views.StickyHeaderLayout;
 import com.android.launcher3.views.WidgetsEduView;
 import com.android.launcher3.widget.BaseWidgetSheet;
 import com.android.launcher3.widget.LauncherAppWidgetHost.ProviderChangedListener;
 import com.android.launcher3.widget.model.WidgetsListBaseEntry;
 import com.android.launcher3.widget.picker.search.SearchModeListener;
+import com.android.launcher3.widget.picker.search.WidgetsSearchBar;
 import com.android.launcher3.widget.util.WidgetsTableUtils;
 import com.android.launcher3.workprofile.PersonalWorkPagedView;
 import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip.OnActivePageChangedListener;
@@ -161,7 +163,13 @@
     private boolean mIsNoWidgetsViewNeeded;
     private int mMaxSpansPerRow = DEFAULT_MAX_HORIZONTAL_SPANS;
     private TextView mNoWidgetsView;
-    private SearchAndRecommendationsScrollController mSearchScrollController;
+
+    private StickyHeaderLayout mSearchScrollView;
+    private WidgetsRecommendationTableLayout mRecommendedWidgetsTable;
+    private View mTabBar;
+    private View mSearchBarContainer;
+    private WidgetsSearchBar mSearchBar;
+    private TextView mHeaderTitle;
 
     public WidgetsFullSheet(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
@@ -214,17 +222,23 @@
         }
 
         mNoWidgetsView = findViewById(R.id.no_widgets_text);
-        mSearchScrollController = new SearchAndRecommendationsScrollController(
-                findViewById(R.id.search_and_recommendations_container));
-        mSearchScrollController.setCurrentRecyclerView(
-                findViewById(R.id.primary_widgets_list_view));
-        mSearchScrollController.mRecommendedWidgetsTable.setWidgetCellLongClickListener(this);
-        mSearchScrollController.mRecommendedWidgetsTable.setWidgetCellOnClickListener(this);
+
+        mSearchScrollView = findViewById(R.id.search_and_recommendations_container);
+        mSearchScrollView.setCurrentRecyclerView(findViewById(R.id.primary_widgets_list_view));
+
+        mRecommendedWidgetsTable = mSearchScrollView.findViewById(R.id.recommended_widget_table);
+        mRecommendedWidgetsTable.setWidgetCellLongClickListener(this);
+        mRecommendedWidgetsTable.setWidgetCellOnClickListener(this);
+
+        mTabBar = mSearchScrollView.findViewById(R.id.tabs);
+        mSearchBarContainer = mSearchScrollView.findViewById(R.id.search_bar_container);
+        mSearchBar = mSearchScrollView.findViewById(R.id.widgets_search_bar);
+        mHeaderTitle = mSearchScrollView.findViewById(R.id.title);
 
         onRecommendedWidgetsBound();
         onWidgetsBound();
 
-        mSearchScrollController.mSearchBar.initialize(
+        mSearchBar.initialize(
                 mActivityContext.getPopupDataProvider(), /* searchModeListener= */ this);
 
         setUpEducationViewsIfNeeded();
@@ -258,7 +272,7 @@
             reset();
             resetExpandedHeaders();
             mCurrentWidgetsRecyclerView = recyclerView;
-            mSearchScrollController.setCurrentRecyclerView(recyclerView);
+            mSearchScrollView.setCurrentRecyclerView(recyclerView);
         }
     }
 
@@ -285,7 +299,7 @@
             mAdapters.get(AdapterHolder.WORK).mWidgetsRecyclerView.scrollToTop();
         }
         mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView.scrollToTop();
-        mSearchScrollController.reset(/* animate= */ true);
+        mSearchScrollView.reset(/* animate= */ true);
     }
 
     @VisibleForTesting
@@ -355,8 +369,7 @@
 
     @Override
     protected void onContentHorizontalMarginChanged(int contentHorizontalMarginInPx) {
-        setContentViewChildHorizontalMargin(mSearchScrollController.mContainer,
-                contentHorizontalMarginInPx);
+        setContentViewChildHorizontalMargin(mSearchScrollView, contentHorizontalMarginInPx);
         if (mViewPager == null) {
             setContentViewChildHorizontalPadding(
                     mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView,
@@ -390,16 +403,8 @@
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         doMeasure(widthMeasureSpec, heightMeasureSpec);
 
-        if (mSearchScrollController.updateHeaderHeight()) {
-            doMeasure(widthMeasureSpec, heightMeasureSpec);
-        }
-
         if (updateMaxSpansPerRow()) {
             doMeasure(widthMeasureSpec, heightMeasureSpec);
-
-            if (mSearchScrollController.updateHeaderHeight()) {
-                doMeasure(widthMeasureSpec, heightMeasureSpec);
-            }
         }
     }
 
@@ -460,7 +465,7 @@
 
         if (mHasWorkProfile) {
             mViewPager.setVisibility(VISIBLE);
-            mSearchScrollController.mTabBar.setVisibility(VISIBLE);
+            mTabBar.setVisibility(VISIBLE);
             AdapterHolder workUserAdapterHolder = mAdapters.get(AdapterHolder.WORK);
             workUserAdapterHolder.mWidgetsListAdapter.setWidgets(allWidgets);
             onActivePageChanged(mViewPager.getCurrentPage());
@@ -508,10 +513,10 @@
     private void setViewVisibilityBasedOnSearch(boolean isInSearchMode) {
         mIsInSearchMode = isInSearchMode;
         if (isInSearchMode) {
-            mSearchScrollController.mRecommendedWidgetsTable.setVisibility(GONE);
+            mRecommendedWidgetsTable.setVisibility(GONE);
             if (mHasWorkProfile) {
                 mViewPager.setVisibility(GONE);
-                mSearchScrollController.mTabBar.setVisibility(GONE);
+                mTabBar.setVisibility(GONE);
             } else {
                 mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView.setVisibility(GONE);
             }
@@ -539,7 +544,6 @@
         }
         List<WidgetItem> recommendedWidgets =
                 mActivityContext.getPopupDataProvider().getRecommendedWidgets();
-        WidgetsRecommendationTableLayout table = mSearchScrollController.mRecommendedWidgetsTable;
         if (recommendedWidgets.size() > 0) {
             float noWidgetsViewHeight = 0;
             if (mIsNoWidgetsViewNeeded) {
@@ -562,9 +566,10 @@
             List<ArrayList<WidgetItem>> recommendedWidgetsInTable =
                     WidgetsTableUtils.groupWidgetItemsIntoTableWithoutReordering(
                             recommendedWidgets, mMaxSpansPerRow);
-            table.setRecommendedWidgets(recommendedWidgetsInTable, maxTableHeight);
+            mRecommendedWidgetsTable.setRecommendedWidgets(
+                    recommendedWidgetsInTable, maxTableHeight);
         } else {
-            table.setVisibility(GONE);
+            mRecommendedWidgetsTable.setVisibility(GONE);
         }
     }
 
@@ -619,10 +624,9 @@
                 mNoIntercept = !getRecyclerView().shouldContainerScroll(ev, getPopupContainer());
             }
 
-            if (mSearchScrollController.mSearchBar.isSearchBarFocused()
-                    && !getPopupContainer().isEventOverView(
-                    mSearchScrollController.mSearchBarContainer, ev)) {
-                mSearchScrollController.mSearchBar.clearSearchBarFocus();
+            if (mSearchBar.isSearchBarFocused()
+                    && !getPopupContainer().isEventOverView(mSearchBarContainer, ev)) {
+                mSearchBar.clearSearchBarFocus();
             }
         }
         return super.onControllerInterceptTouchEvent(ev);
@@ -663,8 +667,8 @@
 
     @Override
     public int getHeaderViewHeight() {
-        return measureHeightWithVerticalMargins(mSearchScrollController.mHeaderTitle)
-                + measureHeightWithVerticalMargins(mSearchScrollController.mSearchBarContainer);
+        return measureHeightWithVerticalMargins(mHeaderTitle)
+                + measureHeightWithVerticalMargins(mSearchBarContainer);
     }
 
     /** private the height, in pixel, + the vertical margins of a given view. */
@@ -681,14 +685,14 @@
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
         if (mIsInSearchMode) {
-            mSearchScrollController.mSearchBar.reset();
+            mSearchBar.reset();
         }
     }
 
     @Override
     public boolean onBackPressed() {
         if (mIsInSearchMode) {
-            mSearchScrollController.mSearchBar.reset();
+            mSearchBar.reset();
             return true;
         }
         return super.onBackPressed();
@@ -701,10 +705,9 @@
     }
 
     @Nullable private View getViewToShowEducationTip() {
-        if (mSearchScrollController.mRecommendedWidgetsTable.getVisibility() == VISIBLE
-                && mSearchScrollController.mRecommendedWidgetsTable.getChildCount() > 0) {
-            return ((ViewGroup) mSearchScrollController.mRecommendedWidgetsTable.getChildAt(0))
-                    .getChildAt(0);
+        if (mRecommendedWidgetsTable.getVisibility() == VISIBLE
+                && mRecommendedWidgetsTable.getChildCount() > 0) {
+            return ((ViewGroup) mRecommendedWidgetsTable.getChildAt(0)).getChildAt(0);
         }
 
         AdapterHolder adapterHolder = mAdapters.get(mIsInSearchMode
@@ -801,7 +804,7 @@
         }
 
         private int getEmptySpaceHeight() {
-            return mSearchScrollController.getHeaderHeight();
+            return mSearchScrollView.getHeaderHeight();
         }
 
         void setup(WidgetsRecyclerView recyclerView) {
diff --git a/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
index 4c0e0d5..35fa7a4 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsRecyclerView.java
@@ -173,11 +173,12 @@
     @Override
     protected int getItemsHeight(int untilIndex) {
         // Initialize cache
-        int childCount = Math.min(getChildCount(), getAdapter().getItemCount());
+        int childCount = getChildCount();
         int startPosition;
         if (childCount > 0
                 && ((startPosition = getChildAdapterPosition(getChildAt(0))) != NO_POSITION)) {
-            for (int i = 0; i < childCount; i++) {
+            int loopCount = Math.min(getChildCount(), getAdapter().getItemCount() - startPosition);
+            for (int i = 0; i < loopCount; i++) {
                 mCachedSizes.put(
                         mAdapter.getItemViewType(startPosition + i),
                         getChildAt(i).getMeasuredHeight());
diff --git a/src/com/android/launcher3/widget/picker/WidgetsSpaceViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsSpaceViewHolderBinder.java
index 1aa5753..0c4f7aa 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsSpaceViewHolderBinder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsSpaceViewHolderBinder.java
@@ -15,16 +15,12 @@
  */
 package com.android.launcher3.widget.picker;
 
-import static android.view.View.MeasureSpec.EXACTLY;
-import static android.view.View.MeasureSpec.makeMeasureSpec;
-
-import android.content.Context;
-import android.view.View;
 import android.view.ViewGroup;
 
 import androidx.recyclerview.widget.RecyclerView.ViewHolder;
 
 import com.android.launcher3.recyclerview.ViewHolderBinder;
+import com.android.launcher3.views.StickyHeaderLayout.EmptySpaceView;
 import com.android.launcher3.widget.model.WidgetListSpaceEntry;
 
 import java.util.List;
@@ -52,64 +48,4 @@
             @ListPosition int position, List<Object> payloads) {
         ((EmptySpaceView) holder.itemView).setFixedHeight(mEmptySpaceHeightProvider.getAsInt());
     }
-
-    /**
-     * Empty view which allows listening for 'Y' changes
-     */
-    public static class EmptySpaceView extends View {
-
-        private Runnable mOnYChangeCallback;
-        private int mHeight = 0;
-
-        private EmptySpaceView(Context context) {
-            super(context);
-            animate().setUpdateListener(v -> notifyYChanged());
-        }
-
-        /**
-         * Sets the height for the empty view
-         * @return true if the height changed, false otherwise
-         */
-        public boolean setFixedHeight(int height) {
-            if (mHeight != height) {
-                mHeight = height;
-                requestLayout();
-                return true;
-            }
-            return false;
-        }
-
-        @Override
-        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-            super.onMeasure(widthMeasureSpec, makeMeasureSpec(mHeight, EXACTLY));
-        }
-
-        public void setOnYChangeCallback(Runnable callback) {
-            mOnYChangeCallback = callback;
-        }
-
-        @Override
-        protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-            super.onLayout(changed, left, top, right, bottom);
-            notifyYChanged();
-        }
-
-        @Override
-        public void offsetTopAndBottom(int offset) {
-            super.offsetTopAndBottom(offset);
-            notifyYChanged();
-        }
-
-        @Override
-        public void setTranslationY(float translationY) {
-            super.setTranslationY(translationY);
-            notifyYChanged();
-        }
-
-        private void notifyYChanged() {
-            if (mOnYChangeCallback != null) {
-                mOnYChangeCallback.run();
-            }
-        }
-    }
 }
diff --git a/src/com/android/launcher3/workprofile/PersonalWorkSlidingTabStrip.java b/src/com/android/launcher3/workprofile/PersonalWorkSlidingTabStrip.java
index 11185fb..49db2a0 100644
--- a/src/com/android/launcher3/workprofile/PersonalWorkSlidingTabStrip.java
+++ b/src/com/android/launcher3/workprofile/PersonalWorkSlidingTabStrip.java
@@ -23,7 +23,9 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.pageindicators.PageIndicator;
+import com.android.launcher3.views.ActivityContext;
 
 /**
  * Supports two indicator colors, dedicated for personal and work tabs.
@@ -72,6 +74,26 @@
         return false;
     }
 
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        if (getPaddingLeft() == 0 && getPaddingRight() == 0) {
+            // If any padding is not specified, restrict the width to emulate padding
+            int size = MeasureSpec.getSize(widthMeasureSpec);
+            size = getTabWidth(getContext(), size);
+            widthMeasureSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
+        }
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+    }
+
+    /**
+     * Returns distance between left and right app icons
+     */
+    public static int getTabWidth(Context context, int totalWidth) {
+        DeviceProfile grid = ActivityContext.lookupContext(context).getDeviceProfile();
+        int iconPadding = totalWidth / grid.numShownAllAppsColumns - grid.allAppsIconSizePx;
+        return totalWidth - iconPadding;
+    }
+
     /**
      * Interface definition for a callback to be invoked when an active page has been changed.
      */
diff --git a/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt b/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt
index 6d0fcb6..9a76336 100644
--- a/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt
+++ b/tests/src/com/android/launcher3/DeviceProfileBaseTest.kt
@@ -23,6 +23,8 @@
 import org.junit.Before
 import org.mockito.ArgumentMatchers.any
 import org.mockito.Mockito.mock
+import java.io.PrintWriter
+import java.io.StringWriter
 import org.mockito.Mockito.`when` as whenever
 
 abstract class DeviceProfileBaseTest {
@@ -55,8 +57,9 @@
         isGestureMode
     )
 
-    protected fun initializeVarsForPhone(isLandscape: Boolean = false) {
-        val (x, y) = if (isLandscape)
+    protected fun initializeVarsForPhone(isGestureMode: Boolean = true,
+                                         isVerticalBar: Boolean = false) {
+        val (x, y) = if (isVerticalBar)
             Pair(3120, 1440)
         else
             Pair(1440, 3120)
@@ -65,11 +68,19 @@
 
         whenever(info.isTablet(any())).thenReturn(false)
         whenever(info.getDensityDpi()).thenReturn(560)
+        whenever(info.smallestSizeDp(any())).thenReturn(411f)
 
-        inv = newScalableInvariantDeviceProfile()
+        this.isGestureMode = isGestureMode
+
+        inv = newScalableInvariantDeviceProfile().apply {
+            deviceType = InvariantDeviceProfile.TYPE_PHONE
+            transposeLayoutWithOrientation = isVerticalBar
+        }
     }
 
-    protected fun initializeVarsForTablet(isLandscape: Boolean = false) {
+    protected fun initializeVarsForTablet(isLandscape: Boolean = false,
+                                          isTwoPanel: Boolean = false,
+                                          isGestureMode: Boolean = true) {
         val (x, y) = if (isLandscape)
             Pair(2560, 1600)
         else
@@ -79,8 +90,21 @@
 
         whenever(info.isTablet(any())).thenReturn(true)
         whenever(info.getDensityDpi()).thenReturn(320)
+        whenever(info.smallestSizeDp(any())).thenReturn(800f)
 
-        inv = newScalableInvariantDeviceProfile()
+        this.isGestureMode = isGestureMode
+        useTwoPanels = isTwoPanel
+
+        inv = newScalableInvariantDeviceProfile().apply {
+            deviceType = if (isTwoPanel)
+                InvariantDeviceProfile.TYPE_MULTI_DISPLAY else InvariantDeviceProfile.TYPE_TABLET
+            inlineQsb = booleanArrayOf(
+                    false,
+                    isLandscape,
+                    false,
+                    false
+            )
+        }
     }
 
     /**
@@ -110,6 +134,8 @@
             ).toTypedArray()
             hotseatBorderSpaces = FloatArray(4) { 16f }
             hotseatColumnSpan = IntArray(4) { 4 }
+            hotseatBarBottomSpace = FloatArray(4) { 48f }
+            hotseatQsbSpace = FloatArray(4) { 36f }
             iconSize = FloatArray(4) { 56f }
             allAppsIconSize = FloatArray(4) { 56f }
             iconTextSize = FloatArray(4) { 14f }
@@ -133,4 +159,12 @@
                 false
             )
         }
+
+    fun dump(dp: DeviceProfile): StringWriter {
+        val stringWriter = StringWriter()
+        val printWriter = PrintWriter(stringWriter)
+        dp.dump("", printWriter)
+        printWriter.flush()
+        return stringWriter
+    }
 }
\ No newline at end of file
diff --git a/tests/src/com/android/launcher3/DeviceProfileGridDimensionsTest.kt b/tests/src/com/android/launcher3/DeviceProfileGridDimensionsTest.kt
deleted file mode 100644
index 80259a5..0000000
--- a/tests/src/com/android/launcher3/DeviceProfileGridDimensionsTest.kt
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3
-
-import android.graphics.PointF
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.launcher3.util.WindowBounds
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers
-import org.mockito.Mockito.`when` as whenever
-
-/**
- * Test for [DeviceProfile] grid dimensions.
- *
- * This includes workspace, cell layout, shortcut and widget container, cell sizes, etc.
- */
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class DeviceProfileGridDimensionsTest : DeviceProfileBaseTest() {
-
-    @Test
-    fun getCellLayoutWidth_twoPanelLandscapeScalable4By4GridTablet_equalsSinglePanelWidth() {
-        val tabletWidth = 2560
-        val tabletHeight = 1600
-        val availableWidth = 2560
-        val availableHeight = 1500
-        windowBounds = WindowBounds(tabletWidth, tabletHeight, availableWidth, availableHeight, 0)
-        useTwoPanels = true
-        whenever(info.isTablet(ArgumentMatchers.any())).thenReturn(true)
-        whenever(info.densityDpi).thenReturn(320)
-        inv = newScalableInvariantDeviceProfile()
-
-        val dp = newDP()
-
-        val expectedWorkspaceWidth = availableWidth
-        val expectedCellLayoutWidth =
-                (expectedWorkspaceWidth - (dp.workspacePadding.right + dp.workspacePadding.left)) /
-                        dp.panelCount
-        assertThat(dp.cellLayoutWidth).isEqualTo(expectedCellLayoutWidth)
-    }
-
-    @Test
-    fun getCellLayoutHeight_twoPanelLandscapeScalable4By4GridTablet_equalsSinglePanelHeight() {
-        val tabletWidth = 2560
-        val tabletHeight = 1600
-        val availableWidth = 2560
-        val availableHeight = 1500
-        windowBounds = WindowBounds(tabletWidth, tabletHeight, availableWidth, availableHeight, 0)
-        useTwoPanels = true
-        whenever(info.isTablet(ArgumentMatchers.any())).thenReturn(true)
-        whenever(info.densityDpi).thenReturn(320)
-        inv = newScalableInvariantDeviceProfile()
-
-        val dp = newDP()
-
-        val expectedWorkspaceHeight = availableHeight
-        val expectedCellLayoutHeight =
-                expectedWorkspaceHeight - (dp.workspacePadding.top + dp.workspacePadding.bottom)
-        assertThat(dp.cellLayoutHeight).isEqualTo(expectedCellLayoutHeight)
-    }
-
-    @Test
-    fun getCellSize_twoPanelLandscapeScalable4By4GridTablet_equalsSinglePanelWidth() {
-        val tabletWidth = 2560
-        val tabletHeight = 1600
-        val availableWidth = 2560
-        val availableHeight = 1500
-        windowBounds = WindowBounds(tabletWidth, tabletHeight, availableWidth, availableHeight, 0)
-        useTwoPanels = true
-        whenever(info.isTablet(ArgumentMatchers.any())).thenReturn(true)
-        whenever(info.densityDpi).thenReturn(320)
-        inv = newScalableInvariantDeviceProfile()
-
-        val dp = newDP()
-
-        val expectedWorkspaceWidth = availableWidth
-        val expectedCellLayoutWidth =
-                (expectedWorkspaceWidth - (dp.workspacePadding.right + dp.workspacePadding.left)) /
-                        dp.panelCount
-        val expectedShortcutAndWidgetContainerWidth =
-                expectedCellLayoutWidth -
-                        (dp.cellLayoutPaddingPx.left + dp.cellLayoutPaddingPx.right)
-        assertThat(dp.getCellSize().x).isEqualTo(
-                (expectedShortcutAndWidgetContainerWidth -
-                        ((inv!!.numColumns - 1) * dp.cellLayoutBorderSpacePx.x)) / inv!!.numColumns)
-        val expectedWorkspaceHeight = availableHeight
-        val expectedCellLayoutHeight =
-                expectedWorkspaceHeight - (dp.workspacePadding.top + dp.workspacePadding.bottom)
-        val expectedShortcutAndWidgetContainerHeight = expectedCellLayoutHeight -
-                (dp.cellLayoutPaddingPx.top + dp.cellLayoutPaddingPx.bottom)
-        assertThat(dp.getCellSize().y).isEqualTo(
-                (expectedShortcutAndWidgetContainerHeight -
-                        ((inv!!.numRows - 1) * dp.cellLayoutBorderSpacePx.y)) / inv!!.numRows)
-    }
-
-    @Test
-    fun getPanelCount_twoPanelLandscapeScalable4By4GridTablet_equalsTwoPanels() {
-        val tabletWidth = 2560
-        val tabletHeight = 1600
-        val availableWidth = 2560
-        val availableHeight = 1500
-        windowBounds = WindowBounds(tabletWidth, tabletHeight, availableWidth, availableHeight, 0)
-        useTwoPanels = true
-        whenever(info.isTablet(ArgumentMatchers.any())).thenReturn(true)
-        whenever(info.densityDpi).thenReturn(320)
-        inv = newScalableInvariantDeviceProfile()
-
-        val dp = newDP()
-
-        assertThat(dp.panelCount).isEqualTo(2)
-    }
-}
\ No newline at end of file
diff --git a/tests/src/com/android/launcher3/HotseatShownIconsTest.kt b/tests/src/com/android/launcher3/HotseatShownIconsTest.kt
index 593239d..5dabb33 100644
--- a/tests/src/com/android/launcher3/HotseatShownIconsTest.kt
+++ b/tests/src/com/android/launcher3/HotseatShownIconsTest.kt
@@ -32,33 +32,6 @@
 class HotseatShownIconsTest : DeviceProfileBaseTest() {
 
     @Test
-    fun hotseat_size_is_normal_for_handhelds() {
-        initializeVarsForPhone()
-        inv = newScalableInvariantDeviceProfile().apply {
-            deviceType = TYPE_PHONE
-        }
-
-        val dp = newDP()
-
-        assertThat(dp.isQsbInline).isFalse()
-        assertThat(dp.numShownHotseatIcons).isEqualTo(4)
-    }
-
-    @Test
-    fun hotseat_size_is_max_when_large_screen() {
-        initializeVarsForTablet(isLandscape = true)
-        inv = newScalableInvariantDeviceProfile().apply {
-            deviceType = TYPE_MULTI_DISPLAY
-        }
-        useTwoPanels = true
-
-        val dp = newDP()
-
-        assertThat(dp.isQsbInline).isFalse()
-        assertThat(dp.numShownHotseatIcons).isEqualTo(6)
-    }
-
-    @Test
     fun hotseat_size_is_shrunk_if_needed_when_large_screen() {
         initializeVarsForTablet(isLandscape = true)
         inv = newScalableInvariantDeviceProfile().apply {
diff --git a/tests/src/com/android/launcher3/InlineQsbTest.kt b/tests/src/com/android/launcher3/InlineQsbTest.kt
deleted file mode 100644
index 905c1e1..0000000
--- a/tests/src/com/android/launcher3/InlineQsbTest.kt
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3
-
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-import org.junit.runner.RunWith
-
-/**
- * Test for [DeviceProfile]
- */
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class InlineQsbTest : DeviceProfileBaseTest() {
-
-    @Test
-    fun qsb_is_not_inline_for_phones() {
-        initializeVarsForPhone()
-
-        val dp = newDP()
-
-        assertThat(dp.isQsbInline).isFalse()
-    }
-
-    @Test
-    fun qsb_is_inline_for_tablet_portrait() {
-        initializeVarsForTablet()
-        inv = newScalableInvariantDeviceProfile().apply {
-            inlineQsb = booleanArrayOf(
-                false,
-                true, // landscape
-                false,
-                false
-            )
-        }
-
-        val dp = DeviceProfile(
-            context,
-            inv,
-            info,
-            windowBounds,
-            isMultiWindowMode,
-            transposeLayoutWithOrientation,
-            useTwoPanels,
-            isGestureMode
-        )
-
-        assertThat(dp.isQsbInline).isFalse()
-    }
-
-    @Test
-    fun qsb_is_inline_for_tablet_landscape() {
-        initializeVarsForTablet(isLandscape = true)
-        inv = newScalableInvariantDeviceProfile().apply {
-            inlineQsb = booleanArrayOf(
-                false,
-                true, // landscape
-                false,
-                false
-            )
-            numColumns = 6
-            numRows = 5
-            numShownHotseatIcons = 6
-        }
-
-        val dp = newDP()
-
-        if (dp.hotseatQsbHeight > 0) {
-            assertThat(dp.isQsbInline).isTrue()
-        } else { // Launcher3 doesn't have QSB height
-            assertThat(dp.isQsbInline).isFalse()
-        }
-    }
-
-    /**
-     * This test is to make sure that a tablet doesn't inline the QSB if the layout doesn't support
-     */
-    @Test
-    fun qsb_is_not_inline_for_tablet_landscape_without_inline() {
-        initializeVarsForTablet(isLandscape = true)
-        useTwoPanels = true
-
-        val dp = newDP()
-
-        assertThat(dp.isQsbInline).isFalse()
-    }
-
-}
\ No newline at end of file
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index 0f29abc..03bf4af 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -25,6 +25,7 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
 
 import android.content.Intent;
 import android.graphics.Point;
@@ -53,6 +54,7 @@
 import com.android.launcher3.widget.picker.WidgetsRecyclerView;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -191,6 +193,17 @@
     }
 
     @Test
+    @PortraitLandscape
+    public void testAllAppsDeadzoneForTablet() throws Exception {
+        assumeTrue(mLauncher.isTablet());
+
+        mLauncher.getWorkspace().switchToAllApps().dismissByTappingOutsideForTablet(
+                true /* tapRight */);
+        mLauncher.getWorkspace().switchToAllApps().dismissByTappingOutsideForTablet(
+                false /* tapRight */);
+    }
+
+    @Test
     @ScreenRecord // b/202433017
     public void testWorkspace() throws Exception {
         final Workspace workspace = mLauncher.getWorkspace();
@@ -376,6 +389,7 @@
     @Test
     @PortraitLandscape
     @ScreenRecord
+    @Ignore // b/233075289
     public void testDragToFolder() {
         // TODO: add the use case to drag an icon to an existing folder. Currently it either fails
         // on tablets or phones due to difference in resolution.
@@ -540,6 +554,18 @@
         }
     }
 
+    @Test
+    @PortraitLandscape
+    public void testAddDeleteShortcutOnHotseat() {
+        mLauncher.getWorkspace()
+                .deleteAppIcon(mLauncher.getWorkspace().getHotseatAppIcon(0))
+                .switchToAllApps()
+                .getAppIcon(APP_NAME)
+                .dragToHotseat(0);
+        mLauncher.getWorkspace().deleteAppIcon(
+                mLauncher.getWorkspace().getHotseatAppIcon(APP_NAME));
+    }
+
     /**
      * @return List of workspace grid coordinates. Those are not pixels. See {@link
      *     Workspace#getIconGridDimensions()}
diff --git a/tests/tapl/com/android/launcher3/tapl/Folder.java b/tests/tapl/com/android/launcher3/tapl/Folder.java
index 26f0a8b..1352cc0 100644
--- a/tests/tapl/com/android/launcher3/tapl/Folder.java
+++ b/tests/tapl/com/android/launcher3/tapl/Folder.java
@@ -16,11 +16,6 @@
 
 package com.android.launcher3.tapl;
 
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.os.SystemClock;
-import android.view.MotionEvent;
-
 import androidx.annotation.NonNull;
 import androidx.test.uiautomator.UiObject2;
 
@@ -50,25 +45,15 @@
         }
     }
 
-    private void touchOutsideFolder() {
-        Rect containerBounds = mLauncher.getVisibleBounds(this.mContainer);
-        final long downTime = SystemClock.uptimeMillis();
-        Point containerLeftTopCorner = new Point(containerBounds.left - 1, containerBounds.top - 1);
-        mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN,
-                containerLeftTopCorner, LauncherInstrumentation.GestureScope.INSIDE);
-        mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_UP,
-                containerLeftTopCorner, LauncherInstrumentation.GestureScope.INSIDE);
-    }
-
     /**
-     * CLose opened folder if possible. It throws assertion error if the folder is already closed.
+     * Close opened folder if possible. It throws assertion error if the folder is already closed.
      */
     public Workspace close() {
         try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
              LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
                      "Want to close opened folder")) {
             mLauncher.waitForLauncherObject(FOLDER_CONTENT_RES_ID);
-            touchOutsideFolder();
+            mLauncher.touchOutsideContainer(this.mContainer, false /* tapRight */);
             mLauncher.waitUntilLauncherObjectGone(FOLDER_CONTENT_RES_ID);
             return mLauncher.getWorkspace();
         }
diff --git a/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java b/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
index c275f3b..7123de4 100644
--- a/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/HomeAllApps.java
@@ -19,6 +19,7 @@
 import androidx.test.uiautomator.UiObject2;
 
 public class HomeAllApps extends AllApps {
+    private static final String BOTTOM_SHEET_RES_ID = "bottom_sheet_background";
 
     HomeAllApps(LauncherInstrumentation launcher) {
         super(launcher);
@@ -45,4 +46,23 @@
     protected boolean hasSearchBox() {
         return true;
     }
+
+    /**
+     * Taps outside bottom sheet to dismiss and return to workspace. Available on tablets only.
+     * @param tapRight Tap on the right of bottom sheet if true, or left otherwise.
+     */
+    public Workspace dismissByTappingOutsideForTablet(boolean tapRight) {
+        try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+             LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                     "want to tap outside AllApps bottom sheet on the "
+                             + (tapRight ? "right" : "left"))) {
+            final UiObject2 allAppsBottomSheet =
+                    mLauncher.waitForLauncherObject(BOTTOM_SHEET_RES_ID);
+            mLauncher.touchOutsideContainer(allAppsBottomSheet, tapRight);
+            try (LauncherInstrumentation.Closable tapped = mLauncher.addContextLayer(
+                    "tapped outside AllApps bottom sheet")) {
+                return mLauncher.getWorkspace();
+            }
+        }
+    }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/HomeAppIcon.java b/tests/tapl/com/android/launcher3/tapl/HomeAppIcon.java
index 7546504..693baa0 100644
--- a/tests/tapl/com/android/launcher3/tapl/HomeAppIcon.java
+++ b/tests/tapl/com/android/launcher3/tapl/HomeAppIcon.java
@@ -22,6 +22,8 @@
 import androidx.annotation.NonNull;
 import androidx.test.uiautomator.UiObject2;
 
+import java.util.function.Supplier;
+
 /**
  * App icon on the workspace or all apps.
  */
@@ -100,9 +102,46 @@
         }
     }
 
+    /**
+     * Drag an object to the given cell in hotseat. The target cell should be expected to be empty.
+     *
+     * @param cellInd zero based index number of the hotseat cells.
+     * @return the workspace app icon.
+     */
+    @NonNull
+    public WorkspaceAppIcon dragToHotseat(int cellInd) {
+        try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+             LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                     String.format("want to drag the icon to hotseat cell %d", cellInd))
+        ) {
+            final Supplier<Point> dest = () -> Workspace.getHotseatCellCenter(mLauncher, cellInd);
+
+            Workspace.dragIconToHotseat(
+                    mLauncher,
+                    this,
+                    dest,
+                    () -> addExpectedEventsForLongClick(),
+                    /*expectDropEvents= */ null);
+            try (LauncherInstrumentation.Closable ignore = mLauncher.addContextLayer("dragged")) {
+                WorkspaceAppIcon appIcon =
+                        (WorkspaceAppIcon) mLauncher.getWorkspace().getHotseatAppIcon(mAppName);
+                mLauncher.assertTrue(
+                        String.format("The %s icon should be in the hotseat cell %d.", mAppName,
+                                cellInd),
+                        appIcon.isInHotseatCell(cellInd));
+                return appIcon;
+            }
+        }
+    }
+
     /** This method requires public access, however should not be called in tests. */
     @Override
     public Launchable getLaunchable() {
         return this;
     }
+
+    boolean isInHotseatCell(int cellInd) {
+        final Point center = Workspace.getHotseatCellCenter(mLauncher, cellInd);
+        return mObject.getVisibleBounds().contains(center.x, center.y);
+    }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 99cab84..fa6141a 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -84,7 +84,6 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Optional;
-import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 import java.util.function.Consumer;
 import java.util.function.Function;
@@ -101,7 +100,6 @@
     private static final String TAG = "Tapl";
     private static final int ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME = 15;
     private static final int GESTURE_STEP_MS = 16;
-    private static final long FORCE_PAUSE_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(2);
 
     static final Pattern EVENT_TOUCH_DOWN = getTouchEventPattern("ACTION_DOWN");
     static final Pattern EVENT_TOUCH_UP = getTouchEventPattern("ACTION_UP");
@@ -362,10 +360,6 @@
         return getRealDisplaySize().x / 2f;
     }
 
-    private void setForcePauseTimeout(long timeout) {
-        getTestInfo(TestProtocol.REQUEST_SET_FORCE_PAUSE_TIMEOUT, Long.toString(timeout));
-    }
-
     public void setEnableRotation(boolean on) {
         getTestInfo(TestProtocol.REQUEST_ENABLE_ROTATION, Boolean.toString(on));
     }
@@ -379,6 +373,17 @@
         sActiveContainer = new WeakReference<>(container);
     }
 
+    /**
+     * Sets the accesibility interactive timeout to be effectively indefinite (UI using this
+     * accesibility timeout will not automatically dismiss if true).
+     */
+    void setIndefiniteAccessibilityInteractiveUiTimeout(boolean indefiniteTimeout) {
+        final String cmd = indefiniteTimeout
+                ? "settings put secure accessibility_interactive_ui_timeout_ms 10000"
+                : "settings delete secure accessibility_interactive_ui_timeout_ms";
+        logShellCommand(cmd);
+    }
+
     public NavigationModel getNavigationModel() {
         final Context baseContext = mInstrumentation.getTargetContext();
         try {
@@ -886,7 +891,6 @@
             final String action;
             if (getNavigationModel() == NavigationModel.ZERO_BUTTON) {
                 checkForAnomaly(false, true);
-                setForcePauseTimeout(FORCE_PAUSE_TIMEOUT_MS);
 
                 final Point displaySize = getRealDisplaySize();
                 // The swipe up to home gesture starts from inside the launcher when the user is
@@ -1831,4 +1835,26 @@
         return ResourceUtils.getBoolByName(
                 "config_supportsRoundedCornersOnWindows", resources, false);
     }
+
+    /**
+     * Taps outside container to dismiss.
+     * @param container container to be dismissed
+     * @param tapRight tap on the right of the container if true, or left otherwise
+     */
+    void touchOutsideContainer(UiObject2 container, boolean tapRight) {
+        try (LauncherInstrumentation.Closable c = addContextLayer(
+                "want to tap outside container on the " + (tapRight ? "right" : "left"))) {
+            Rect containerBounds = getVisibleBounds(container);
+            final long downTime = SystemClock.uptimeMillis();
+            final Point tapTarget = new Point(
+                    tapRight
+                            ? (containerBounds.right + getRealDisplaySize().x) / 2
+                            : containerBounds.left / 2,
+                    containerBounds.top + 1);
+            sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, tapTarget,
+                    LauncherInstrumentation.GestureScope.INSIDE);
+            sendPointer(downTime, downTime, MotionEvent.ACTION_UP, tapTarget,
+                    LauncherInstrumentation.GestureScope.INSIDE);
+        }
+    }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewActions.java b/tests/tapl/com/android/launcher3/tapl/OverviewActions.java
index 2f44bb6..d1b1a84 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewActions.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewActions.java
@@ -41,6 +41,8 @@
         try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
              LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
                      "want to click screenshot button and exit screenshot ui")) {
+            mLauncher.setIndefiniteAccessibilityInteractiveUiTimeout(true);
+
             UiObject2 screenshot = mLauncher.waitForObjectInContainer(mOverviewActions,
                     "action_screenshot");
 
@@ -62,6 +64,8 @@
                     return new Overview(mLauncher);
                 }
             }
+        } finally {
+            mLauncher.setIndefiniteAccessibilityInteractiveUiTimeout(false);
         }
     }
 
diff --git a/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java b/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java
index 82652c7..ddeeac2 100644
--- a/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java
+++ b/tests/tapl/com/android/launcher3/tapl/SearchResultFromQsb.java
@@ -26,6 +26,7 @@
 public class SearchResultFromQsb {
     // The input resource id in the search box.
     private static final String INPUT_RES = "input";
+    private static final String BOTTOM_SHEET_RES_ID = "bottom_sheet_background";
     private final LauncherInstrumentation mLauncher;
 
     SearchResultFromQsb(LauncherInstrumentation launcher) {
@@ -47,4 +48,23 @@
         UiObject2 icon = mLauncher.waitForLauncherObject(By.clazz(TextView.class).text(appName));
         return new AllAppsAppIcon(mLauncher, icon);
     }
+
+    /**
+     * Taps outside bottom sheet to dismiss and return to workspace. Available on tablets only.
+     * @param tapRight Tap on the right of bottom sheet if true, or left otherwise.
+     */
+    public Workspace dismissByTappingOutsideForTablet(boolean tapRight) {
+        try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+             LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                     "want to tap outside AllApps bottom sheet on the "
+                             + (tapRight ? "right" : "left"))) {
+            final UiObject2 allAppsBottomSheet =
+                    mLauncher.waitForLauncherObject(BOTTOM_SHEET_RES_ID);
+            mLauncher.touchOutsideContainer(allAppsBottomSheet, tapRight);
+            try (LauncherInstrumentation.Closable tapped = mLauncher.addContextLayer(
+                    "tapped outside AllApps bottom sheet")) {
+                return mLauncher.getWorkspace();
+            }
+        }
+    }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index 42ba18c..5e5fdec 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -39,6 +39,7 @@
 import androidx.test.uiautomator.UiObject2;
 import androidx.test.uiautomator.Until;
 
+import com.android.launcher3.testing.HotseatCellCenterRequest;
 import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.testing.WorkspaceCellCenterRequest;
 
@@ -251,6 +252,24 @@
     }
 
     /**
+     * Returns an icon for the given cell; fails if the icon doesn't exist.
+     *
+     * @param cellInd zero based index number of the hotseat cells.
+     * @return app icon.
+     */
+    @NonNull
+    public HomeAppIcon getHotseatAppIcon(int cellInd) {
+        List<UiObject2> icons = mHotseat.findObjects(AppIcon.getAnyAppIconSelector());
+        final Point center = getHotseatCellCenter(mLauncher, cellInd);
+        return icons.stream()
+                .filter(icon -> icon.getVisibleBounds().contains(center.x, center.y))
+                .findFirst()
+                .map(icon -> new WorkspaceAppIcon(mLauncher, icon))
+                .orElseThrow(() ->
+                        new AssertionError("Unable to get a hotseat icon on " + cellInd));
+    }
+
+    /**
      * @return map of text -> center of the view. In case of icons with the same name, the one with
      *     lower x coordinate is selected.
      */
@@ -360,6 +379,11 @@
                 TestProtocol.TEST_INFO_RESPONSE_FIELD);
     }
 
+    static Point getHotseatCellCenter(LauncherInstrumentation launcher, int cellInd) {
+        return launcher.getTestInfo(HotseatCellCenterRequest.builder()
+                .setCellInd(cellInd).build()).getParcelable(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+    }
+
     /**
      * Finds folder icons in the current workspace.
      *
@@ -471,6 +495,25 @@
                 () -> "Page scroll didn't happen", "Scrolling page");
     }
 
+    static void dragIconToHotseat(
+            LauncherInstrumentation launcher,
+            Launchable launchable,
+            Supplier<Point> dest,
+            Runnable expectLongClickEvents,
+            @Nullable Runnable expectDropEvents) {
+        final long downTime = SystemClock.uptimeMillis();
+        Point dragStart = launchable.startDrag(
+                downTime,
+                expectLongClickEvents,
+                /* runToSpringLoadedState= */ true);
+        Point targetDest = dest.get();
+
+        launcher.movePointer(dragStart, targetDest, DEFAULT_DRAG_STEPS, true,
+                downTime, SystemClock.uptimeMillis(), false,
+                LauncherInstrumentation.GestureScope.INSIDE);
+        dropDraggedIcon(launcher, targetDest, downTime, expectDropEvents);
+    }
+
     /**
      * Flings to get to screens on the right. Waits for scrolling and a possible overscroll
      * recoil to complete.