Merge "Update test for change in calculation of split portrait clipping" into tm-qpr-dev
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 5a58bc2..baf097e 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -282,6 +282,7 @@
 
     <!-- Transient taskbar -->
     <dimen name="transient_taskbar_size">76dp</dimen>
+    <dimen name="transient_taskbar_two_panels_size">72dp</dimen>
     <dimen name="transient_taskbar_margin">24dp</dimen>
     <dimen name="transient_taskbar_shadow_blur">40dp</dimen>
     <dimen name="transient_taskbar_key_shadow_distance">10dp</dimen>
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index 59dbd4b..2aa0af4 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -482,6 +482,10 @@
             final View appsView = mLauncher.getAppsView();
             final float startAlpha = appsView.getAlpha();
             final float startScale = SCALE_PROPERTY.get(appsView);
+            if (mDeviceProfile.isTablet) {
+                // AllApps should not fade at all in tablets.
+                alphas = new float[]{1, 1};
+            }
             appsView.setAlpha(alphas[0]);
 
             ObjectAnimator alpha = ObjectAnimator.ofFloat(appsView, View.ALPHA, alphas);
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 8775359..02206ce 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -356,6 +356,12 @@
     }
 
     @Override
+    public boolean isHotseatIconOnTopWhenAligned() {
+        return mTaskbarLauncherStateController.isInHotseatOnTopStates()
+                && getInAppDisplayProgress(MINUS_ONE_PAGE_PROGRESS_INDEX) == 0;
+    }
+
+    @Override
     public void dumpLogs(String prefix, PrintWriter pw) {
         super.dumpLogs(prefix, pw);
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index d9d46d4..01ec5f3 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -85,6 +85,7 @@
 import com.android.launcher3.testing.TestLogging;
 import com.android.launcher3.testing.shared.TestProtocol;
 import com.android.launcher3.touch.ItemClickHandler;
+import com.android.launcher3.touch.ItemClickHandler.ItemClickProxy;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.NavigationMode;
 import com.android.launcher3.util.PackageManagerHelper;
@@ -269,9 +270,11 @@
     }
 
     private void updateIconSize(Resources resources) {
-        float taskbarIconSize = DisplayController.isTransientTaskbar(this)
-                ? resources.getDimension(R.dimen.transient_taskbar_icon_size)
-                : resources.getDimension(R.dimen.taskbar_icon_size);
+        float taskbarIconSize = resources.getDimension(DisplayController.isTransientTaskbar(this)
+                ? mDeviceProfile.isTwoPanels
+                        ? R.dimen.transient_taskbar_two_panels_icon_size
+                        : R.dimen.transient_taskbar_icon_size
+                : R.dimen.taskbar_icon_size);
         mDeviceProfile.updateIconSize(1, resources);
         float iconScale = taskbarIconSize / mDeviceProfile.iconSizePx;
         mDeviceProfile.updateIconSize(iconScale, resources);
@@ -680,7 +683,10 @@
         }
 
         if (DisplayController.isTransientTaskbar(this)) {
-            return resources.getDimensionPixelSize(R.dimen.transient_taskbar_size)
+            int taskbarSize = resources.getDimensionPixelSize(mDeviceProfile.isTwoPanels
+                    ? R.dimen.transient_taskbar_two_panels_size
+                    : R.dimen.transient_taskbar_size);
+            return taskbarSize
                     + (2 * resources.getDimensionPixelSize(R.dimen.transient_taskbar_margin))
                     + resources.getDimensionPixelSize(R.dimen.transient_taskbar_shadow_blur);
         }
@@ -828,6 +834,8 @@
                 mControllers.uiController.onTaskbarIconLaunched((AppInfo) tag);
             }
             mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
+        } else if (tag instanceof ItemClickProxy) {
+            ((ItemClickProxy) tag).onItemClicked(view);
         } else {
             Log.e(TAG, "Unknown type clicked: " + tag);
         }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index d790b4b..b74dd21 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -385,6 +385,13 @@
         }
     }
 
+    /**
+     * Returns if the current Launcher state has hotseat on top of other elemnets.
+     */
+    public boolean isInHotseatOnTopStates() {
+        return mLauncherState != LauncherState.ALL_APPS;
+    }
+
     private void playStateTransitionAnim(AnimatorSet animatorSet, long duration,
             boolean committed) {
         boolean isInStashedState = mLauncherState.isTaskbarStashed(mLauncher);
@@ -438,14 +445,16 @@
 
     private void updateIconAlphaForHome(float alpha) {
         mIconAlphaForHome.setValue(alpha);
-
+        boolean hotseatVisible = alpha == 0
+                || (!mControllers.uiController.isHotseatIconOnTopWhenAligned()
+                && mIconAlignment.value > 0);
         /*
          * Hide Launcher Hotseat icons when Taskbar icons have opacity. Both icon sets
          * should not be visible at the same time.
          */
-        mLauncher.getHotseat().setIconsAlpha(alpha > 0 ? 0 : 1);
+        mLauncher.getHotseat().setIconsAlpha(hotseatVisible ? 1 : 0);
         mLauncher.getHotseat().setQsbAlpha(
-                mLauncher.getDeviceProfile().isQsbInline && alpha > 0 ? 0 : 1);
+                mLauncher.getDeviceProfile().isQsbInline && !hotseatVisible ? 0 : 1);
     }
 
     private final class TaskBarRecentsAnimationListener implements
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index e62e533..a82e7be 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -17,6 +17,8 @@
 
 import static android.view.HapticFeedbackConstants.LONG_PRESS;
 
+import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
+import static com.android.launcher3.anim.Interpolators.INSTANT;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_LONGPRESS_HIDE;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_LONGPRESS_SHOW;
 import static com.android.launcher3.taskbar.Utilities.appendFlag;
@@ -201,7 +203,9 @@
             Resources resources = mActivity.getResources();
             boolean isTransientTaskbar = DisplayController.isTransientTaskbar(mActivity);
             mUnstashedHeight = resources.getDimensionPixelSize(isTransientTaskbar
-                    ? R.dimen.transient_taskbar_size
+                    ? (mActivity.getDeviceProfile().isTwoPanels
+                            ? R.dimen.transient_taskbar_two_panels_size
+                            : R.dimen.transient_taskbar_size)
                     : R.dimen.taskbar_size);
             mStashedHeight = resources.getDimensionPixelSize(isTransientTaskbar
                     ? R.dimen.transient_taskbar_stashed_size
@@ -535,6 +539,8 @@
         final float firstHalfDurationScale;
         final float secondHalfDurationScale;
 
+        boolean isHotseatIconOnTopWhenAligned =
+                mControllers.uiController.isHotseatIconOnTopWhenAligned();
         if (isStashed) {
             firstHalfDurationScale = 0.75f;
             secondHalfDurationScale = 0.5f;
@@ -555,6 +561,12 @@
             secondHalfAnimatorSet.playTogether(
                     mTaskbarStashedHandleAlpha.animateToValue(1)
             );
+
+            // If Hotseat is not the top element, an already stashed Taskbar should fade in.
+            if (!isHotseatIconOnTopWhenAligned) {
+                fullLengthAnimatorSet.setInterpolator(INSTANT);
+                firstHalfAnimatorSet.setInterpolator(INSTANT);
+            }
         } else  {
             firstHalfDurationScale = 0.5f;
             secondHalfDurationScale = 0.75f;
@@ -575,6 +587,13 @@
             secondHalfAnimatorSet.playTogether(
                     mIconAlphaForStash.animateToValue(1)
             );
+
+            // If Hotseat is not the top element, the stashed Taskbar should fade out without
+            // unstashing.
+            if (!isHotseatIconOnTopWhenAligned) {
+                fullLengthAnimatorSet.setInterpolator(FINAL_FRAME);
+                secondHalfAnimatorSet.setInterpolator(FINAL_FRAME);
+            }
         }
 
         fullLengthAnimatorSet.play(mControllers.stashedHandleViewController
@@ -916,6 +935,7 @@
         private final IntPredicate mStashCondition;
 
         private boolean mIsStashed;
+        private boolean mIsHotseatIconOnTopWhenAligned;
         private int mPrevFlags;
 
         StatePropertyHolder(IntPredicate stashCondition) {
@@ -945,7 +965,13 @@
                 mPrevFlags = flags;
             }
             boolean isStashed = mStashCondition.test(flags);
-            if (mIsStashed != isStashed) {
+            boolean isHotseatIconOnTopWhenAligned =
+                    mControllers.uiController.isHotseatIconOnTopWhenAligned();
+            // If an animation has started and mIsHotseatIconOnTopWhenAligned is changed, we need
+            // to restart the animation with new parameters.
+            if (mIsStashed != isStashed
+                    || (mIsHotseatIconOnTopWhenAligned != isHotseatIconOnTopWhenAligned
+                    && mAnimator != null && mAnimator.isStarted())) {
                 if (TestProtocol.sDebugTracing) {
                     Log.d(TestProtocol.TASKBAR_IN_APP_STATE, String.format(
                             "setState: mIsStashed=%b, isStashed=%b, duration=%d, start=:%b",
@@ -955,6 +981,7 @@
                             start));
                 }
                 mIsStashed = isStashed;
+                mIsHotseatIconOnTopWhenAligned = isHotseatIconOnTopWhenAligned;
 
                 // This sets mAnimator.
                 createAnimToIsStashed(mIsStashed, duration, startDelay, /* animateBg= */ true);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index 6c6b002..a059295 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -128,6 +128,13 @@
         return false;
     }
 
+    /**
+     * Returns true if hotseat icons are on top of view hierarchy when aligned in the current state.
+     */
+    public boolean isHotseatIconOnTopWhenAligned() {
+        return true;
+    }
+
     @CallSuper
     protected void dumpLogs(String prefix, PrintWriter pw) {
         pw.println(String.format(
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index b5e6fac..d14eeab 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -19,6 +19,7 @@
 import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
 import static com.android.launcher3.Utilities.squaredHypot;
+import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_ALLAPPS_BUTTON_TAP;
 import static com.android.launcher3.taskbar.TaskbarManager.isPhoneMode;
@@ -32,6 +33,7 @@
 import android.util.Log;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.animation.Interpolator;
 
 import androidx.annotation.Nullable;
 import androidx.core.graphics.ColorUtils;
@@ -112,6 +114,7 @@
     private Runnable mOnControllerPreCreateCallback = NO_OP;
 
     private int mThemeIconsColor;
+    private boolean mIsHotseatIconOnTopWhenAligned;
 
     private final DeviceProfile.OnDeviceProfileChangeListener mDeviceProfileChangeListener =
             dp -> commitRunningAppsToUI();
@@ -293,7 +296,12 @@
      *                       1 => fully aligned
      */
     public void setLauncherIconAlignment(float alignmentRatio, DeviceProfile launcherDp) {
-        if (mIconAlignControllerLazy == null) {
+        boolean isHotseatIconOnTopWhenAligned =
+                mControllers.uiController.isHotseatIconOnTopWhenAligned();
+        // When mIsHotseatIconOnTopWhenAligned changes, animation needs to be re-created.
+        if (mIconAlignControllerLazy == null
+                || mIsHotseatIconOnTopWhenAligned != isHotseatIconOnTopWhenAligned) {
+            mIsHotseatIconOnTopWhenAligned = isHotseatIconOnTopWhenAligned;
             mIconAlignControllerLazy = createIconAlignmentController(launcherDp);
         }
         mIconAlignControllerLazy.setPlayFraction(alignmentRatio);
@@ -318,10 +326,15 @@
                 borderSpacing,
                 launcherDp.numShownHotseatIcons);
 
+        boolean isToHome = mControllers.uiController.isIconAlignedWithHotseat();
+        // If Hotseat is not the top element, Taskbar should maintain in-app state as it fades out,
+        // or fade in while already in in-app state.
+        Interpolator interpolator = mIsHotseatIconOnTopWhenAligned ? LINEAR : FINAL_FRAME;
+
         int offsetY = launcherDp.getTaskbarOffsetY();
-        setter.setFloat(mTaskbarIconTranslationYForHome, VALUE, -offsetY, LINEAR);
-        setter.setFloat(mTaskbarNavButtonTranslationY, VALUE, -offsetY, LINEAR);
-        setter.setFloat(mTaskbarNavButtonTranslationYForInAppDisplay, VALUE, offsetY, LINEAR);
+        setter.setFloat(mTaskbarIconTranslationYForHome, VALUE, -offsetY, interpolator);
+        setter.setFloat(mTaskbarNavButtonTranslationY, VALUE, -offsetY, interpolator);
+        setter.setFloat(mTaskbarNavButtonTranslationYForInAppDisplay, VALUE, offsetY, interpolator);
 
         if (Utilities.isDarkTheme(mTaskbarView.getContext())) {
             setter.addFloat(mThemeIconsBackground, VALUE, 0f, 1f, LINEAR);
@@ -332,27 +345,24 @@
         setter.addOnFrameListener(anim -> mActivity.setTaskbarWindowHeight(
                 anim.getAnimatedFraction() > 0 ? expandedHeight : collapsedHeight));
 
-        boolean isToHome = mControllers.uiController.isIconAlignedWithHotseat();
         for (int i = 0; i < mTaskbarView.getChildCount(); i++) {
             View child = mTaskbarView.getChildAt(i);
             int positionInHotseat;
-            if (FeatureFlags.ENABLE_ALL_APPS_IN_TASKBAR.get()
-                    && child == mTaskbarView.getAllAppsButtonView()) {
-                // Note that there is no All Apps button in the hotseat, this position is only used
-                // as its convenient for animation purposes.
-                positionInHotseat = Utilities.isRtl(child.getResources())
-                        ? -1
-                        : taskbarDp.numShownHotseatIcons;
+            boolean isAllAppsButton = FeatureFlags.ENABLE_ALL_APPS_IN_TASKBAR.get()
+                    && child == mTaskbarView.getAllAppsButtonView();
+            if (!mIsHotseatIconOnTopWhenAligned) {
+                // When going to home, the EMPHASIZED interpolator in TaskbarLauncherStateController
+                // plays iconAlignment to 1 really fast, therefore moving the fading towards the end
+                // to avoid icons disappearing rather than fading out visually.
+                setter.setViewAlpha(child, 0, Interpolators.clampToProgress(LINEAR, 0.8f, 1f));
+            } else if ((isAllAppsButton && !FeatureFlags.ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT.get())) {
+                setter.setViewAlpha(child, 0,
+                        isToHome
+                                ? Interpolators.clampToProgress(LINEAR, 0f, 0.17f)
+                                : Interpolators.clampToProgress(LINEAR, 0.72f, 0.84f));
+            }
 
-                if (!FeatureFlags.ENABLE_ALL_APPS_BUTTON_IN_HOTSEAT.get()) {
-                    setter.setViewAlpha(child, 0,
-                            isToHome
-                                    ? Interpolators.clampToProgress(LINEAR, 0f, 0.17f)
-                                    : Interpolators.clampToProgress(LINEAR, 0.72f, 0.84f));
-                }
-            } else if (child.getTag() instanceof ItemInfo) {
-                positionInHotseat = ((ItemInfo) child.getTag()).screenId;
-            } else if (child == mTaskbarView.getQsb()) {
+            if (child == mTaskbarView.getQsb()) {
                 boolean isRtl = Utilities.isRtl(child.getResources());
                 float hotseatIconCenter = isRtl
                         ? launcherDp.widthPx - hotseatPadding.right + borderSpacing
@@ -363,26 +373,38 @@
                         (launcherDp.hotseatQsbWidth - taskbarDp.iconSizePx) / 2f;
                 setter.addFloat(child, ICON_TRANSLATE_X,
                         isRtl ? -halfQsbIconWidthDiff : halfQsbIconWidthDiff,
-                        hotseatIconCenter - childCenter, LINEAR);
+                        hotseatIconCenter - childCenter, interpolator);
 
                 float scale = ((float) taskbarDp.iconSizePx) / launcherDp.hotseatQsbVisualHeight;
-                setter.addFloat(child, SCALE_PROPERTY, scale, 1f, LINEAR);
+                setter.addFloat(child, SCALE_PROPERTY, scale, 1f, interpolator);
 
-                setter.setFloat(child, VIEW_TRANSLATE_Y, mTaskbarBottomMargin, LINEAR);
+                setter.setFloat(child, VIEW_TRANSLATE_Y, mTaskbarBottomMargin, interpolator);
 
-                setter.addFloat(child, VIEW_ALPHA, 0f, 1f,
-                        isToHome
-                                ? Interpolators.clampToProgress(LINEAR, 0f, 0.35f)
-                                : Interpolators.clampToProgress(LINEAR, 0.84f, 1f));
+                if (mIsHotseatIconOnTopWhenAligned) {
+                    setter.addFloat(child, VIEW_ALPHA, 0f, 1f,
+                            isToHome
+                                    ? Interpolators.clampToProgress(LINEAR, 0f, 0.35f)
+                                    : Interpolators.clampToProgress(LINEAR, 0.84f, 1f));
+                }
                 setter.addOnFrameListener(animator -> AlphaUpdateListener.updateVisibility(child));
 
                 float qsbInsetFraction = halfQsbIconWidthDiff / launcherDp.hotseatQsbWidth;
-                if (child instanceof  HorizontalInsettableView) {
+                if (child instanceof HorizontalInsettableView) {
                     setter.addFloat((HorizontalInsettableView) child,
                             HorizontalInsettableView.HORIZONTAL_INSETS, qsbInsetFraction, 0,
-                            LINEAR);
+                            interpolator);
                 }
                 continue;
+            }
+
+            if (isAllAppsButton) {
+                // Note that there is no All Apps button in the hotseat, this position is only used
+                // as its convenient for animation purposes.
+                positionInHotseat = Utilities.isRtl(child.getResources())
+                        ? -1
+                        : taskbarDp.numShownHotseatIcons;
+            } else if (child.getTag() instanceof ItemInfo) {
+                positionInHotseat = ((ItemInfo) child.getTag()).screenId;
             } else {
                 Log.w(TAG, "Unsupported view found in createIconAlignmentController, v=" + child);
                 continue;
@@ -393,11 +415,11 @@
                     + hotseatCellSize / 2f;
 
             float childCenter = (child.getLeft() + child.getRight()) / 2f;
-            setter.setFloat(child, ICON_TRANSLATE_X, hotseatIconCenter - childCenter, LINEAR);
+            setter.setFloat(child, ICON_TRANSLATE_X, hotseatIconCenter - childCenter, interpolator);
 
-            setter.setFloat(child, VIEW_TRANSLATE_Y, mTaskbarBottomMargin, LINEAR);
+            setter.setFloat(child, VIEW_TRANSLATE_Y, mTaskbarBottomMargin, interpolator);
 
-            setter.setFloat(child, SCALE_PROPERTY, scaleUp, LINEAR);
+            setter.setFloat(child, SCALE_PROPERTY, scaleUp, interpolator);
         }
 
         AnimatorPlaybackController controller = setter.createPlaybackController();
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index db604f2..dc53552 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -362,8 +362,10 @@
     <dimen name="min_hotseat_qsb_width">0dp</dimen>
     <dimen name="taskbar_icon_size">44dp</dimen>
     <dimen name="transient_taskbar_icon_size">57dp</dimen>
+    <dimen name="transient_taskbar_two_panels_icon_size">50dp</dimen>
     <!-- Transient taskbar (placeholders to compile in Launcher3 without Quickstep) -->
     <dimen name="transient_taskbar_size">0dp</dimen>
+    <dimen name="transient_taskbar_two_panels_size">0dp</dimen>
     <dimen name="transient_taskbar_margin">0dp</dimen>
     <dimen name="transient_taskbar_shadow_blur">0dp</dimen>
     <dimen name="transient_taskbar_key_shadow_distance">0dp</dimen>
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 3f4d036..828066a 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -319,7 +319,9 @@
 
         if (isTaskbarPresent) {
             if (DisplayController.isTransientTaskbar(context)) {
-                taskbarSize = res.getDimensionPixelSize(R.dimen.transient_taskbar_size);
+                taskbarSize = res.getDimensionPixelSize(isTwoPanels
+                        ? R.dimen.transient_taskbar_two_panels_size
+                        : R.dimen.transient_taskbar_size);
                 stashedTaskbarSize =
                         res.getDimensionPixelSize(R.dimen.transient_taskbar_stashed_size);
                 transientTaskbarMargin =
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 5cce407..1a6c68d 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -313,6 +313,12 @@
     private static final FloatProperty<Hotseat> HOTSEAT_WIDGET_SCALE =
             HOTSEAT_SCALE_PROPERTY_FACTORY.get(SCALE_INDEX_WIDGET_TRANSITION);
 
+    private static final boolean DESKTOP_MODE_1_SUPPORTED =
+            "1".equals(Utilities.getSystemProperty("persist.wm.debug.desktop_mode", "0"));
+
+    private static final boolean DESKTOP_MODE_2_SUPPORTED =
+            "1".equals(Utilities.getSystemProperty("persist.wm.debug.desktop_mode_2", "0"));
+
     @Thunk
     Workspace<?> mWorkspace;
     @Thunk
@@ -3154,6 +3160,10 @@
     }
 
     private void updateDisallowBack() {
+        if (DESKTOP_MODE_1_SUPPORTED || DESKTOP_MODE_2_SUPPORTED) {
+            // Do not disable back in launcher when prototype behavior is enabled
+            return;
+        }
         LauncherRootView rv = getRootView();
         if (rv != null) {
             boolean disableBack = getStateManager().getState() == NORMAL
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index ce009a1..b10256e 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -73,7 +73,6 @@
 import com.android.launcher3.icons.ThemedIconDrawable;
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.ItemInfoWithIcon;
-import com.android.launcher3.model.data.SearchActionItemInfo;
 import com.android.launcher3.pm.ShortcutConfigActivityInfo;
 import com.android.launcher3.shortcuts.ShortcutKey;
 import com.android.launcher3.shortcuts.ShortcutRequest;
@@ -591,8 +590,8 @@
             outObj[0] = icon;
             return icon;
         } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SEARCH_ACTION
-                && info instanceof SearchActionItemInfo) {
-            return ((SearchActionItemInfo) info).bitmap.newIcon(context);
+                && info instanceof ItemInfoWithIcon) {
+            return ((ItemInfoWithIcon) info).bitmap.newIcon(context);
         } else {
             return null;
         }
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index e34d4c8..9933ffb 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -31,8 +31,8 @@
 import com.android.launcher3.util.ScrollableLayoutManager;
 import com.android.launcher3.views.ActivityContext;
 
-import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
 
 /**
  * The grid view adapter of all the apps.
@@ -44,7 +44,8 @@
 
     public static final String TAG = "AppsGridAdapter";
     private final AppsGridLayoutManager mGridLayoutMgr;
-    private final List<OnLayoutCompletedListener> mOnLayoutCompletedListeners = new ArrayList<>();
+    private final CopyOnWriteArrayList<OnLayoutCompletedListener> mOnLayoutCompletedListeners =
+            new CopyOnWriteArrayList<>();
 
     /**
      * Listener for {@link RecyclerView.LayoutManager#onLayoutCompleted(RecyclerView.State)}
diff --git a/src/com/android/launcher3/model/data/SearchActionItemInfo.java b/src/com/android/launcher3/model/data/SearchActionItemInfo.java
deleted file mode 100644
index 04042ea..0000000
--- a/src/com/android/launcher3/model/data/SearchActionItemInfo.java
+++ /dev/null
@@ -1,189 +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.model.data;
-
-import static com.android.launcher3.LauncherSettings.Favorites.EXTENDED_CONTAINERS;
-
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.drawable.Icon;
-import android.os.Process;
-import android.os.UserHandle;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.logger.LauncherAtom.ItemInfo;
-import com.android.launcher3.logger.LauncherAtom.SearchActionItem;
-
-/**
- * Represents a SearchAction with in launcher
- */
-public class SearchActionItemInfo extends ItemInfoWithIcon implements WorkspaceItemFactory {
-
-    public static final int FLAG_SHOULD_START = 1 << 1;
-    public static final int FLAG_SHOULD_START_FOR_RESULT = FLAG_SHOULD_START | 1 << 2;
-    public static final int FLAG_BADGE_WITH_PACKAGE = 1 << 3;
-    public static final int FLAG_PRIMARY_ICON_FROM_TITLE = 1 << 4;
-    public static final int FLAG_BADGE_WITH_COMPONENT_NAME = 1 << 5;
-    public static final int FLAG_ALLOW_PINNING = 1 << 6;
-    public static final int FLAG_SEARCH_IN_APP = 1 << 7;
-
-    private String mFallbackPackageName;
-    private int mFlags = 0;
-    private Icon mIcon;
-
-    // If true title does not contain any personal info and eligible for logging.
-    private boolean mIsPersonalTitle;
-    private Intent mIntent;
-
-    private PendingIntent mPendingIntent;
-
-    public SearchActionItemInfo(Icon icon, String packageName, UserHandle user,
-            CharSequence title, boolean isPersonalTitle) {
-        mIsPersonalTitle = isPersonalTitle;
-        this.itemType = LauncherSettings.Favorites.ITEM_TYPE_SEARCH_ACTION;
-        this.user = user == null ? Process.myUserHandle() : user;
-        this.title = title;
-        this.container = EXTENDED_CONTAINERS;
-        mFallbackPackageName = packageName;
-        mIcon = icon;
-    }
-
-    private SearchActionItemInfo(SearchActionItemInfo info) {
-        super(info);
-    }
-
-    @Override
-    public void copyFrom(@NonNull com.android.launcher3.model.data.ItemInfo info) {
-        super.copyFrom(info);
-        SearchActionItemInfo itemInfo = (SearchActionItemInfo) info;
-        this.mFallbackPackageName = itemInfo.mFallbackPackageName;
-        this.mIcon = itemInfo.mIcon;
-        this.mFlags = itemInfo.mFlags;
-        this.mIsPersonalTitle = itemInfo.mIsPersonalTitle;
-    }
-
-    /**
-     * Returns if multiple flags are all available.
-     */
-    public boolean hasFlags(int flags) {
-        return (mFlags & flags) != 0;
-    }
-
-    public void setFlags(int flags) {
-        mFlags |= flags;
-    }
-
-    @Override
-    @Nullable
-    public Intent getIntent() {
-        return mIntent;
-    }
-
-    /**
-     * Setter for mIntent with assertion for null value mPendingIntent
-     */
-    public void setIntent(Intent intent) {
-        if (mPendingIntent != null && intent != null) {
-            throw new RuntimeException(
-                    "SearchActionItemInfo can only have either an Intent or a PendingIntent");
-        }
-        mIntent = intent;
-    }
-
-    public PendingIntent getPendingIntent() {
-        return mPendingIntent;
-    }
-
-    /**
-     * Setter of mPendingIntent with assertion for null value mIntent
-     */
-    public void setPendingIntent(PendingIntent pendingIntent) {
-        if (mIntent != null && pendingIntent != null) {
-            throw new RuntimeException(
-                    "SearchActionItemInfo can only have either an Intent or a PendingIntent");
-        }
-        mPendingIntent = pendingIntent;
-    }
-
-    @Nullable
-    public Icon getIcon() {
-        return mIcon;
-    }
-
-    @Override
-    public ItemInfoWithIcon clone() {
-        return new SearchActionItemInfo(this);
-    }
-
-    @NonNull
-    @Override
-    public ItemInfo buildProto(@Nullable FolderInfo fInfo) {
-        SearchActionItem.Builder itemBuilder = SearchActionItem.newBuilder()
-                .setPackageName(mFallbackPackageName);
-
-        if (!mIsPersonalTitle) {
-            itemBuilder.setTitle(title.toString());
-        }
-        return getDefaultItemInfoBuilder()
-                .setSearchActionItem(itemBuilder)
-                .setContainerInfo(getContainerInfo())
-                .build();
-    }
-
-    /**
-     * Returns true if result supports drag/drop to home screen
-     */
-    public boolean supportsPinning() {
-        return hasFlags(FLAG_ALLOW_PINNING) && getIntentPackageName() != null;
-    }
-
-    /**
-     * Creates a {@link WorkspaceItemInfo} coorsponding to search action to be stored in launcher db
-     */
-    @Override
-    public WorkspaceItemInfo makeWorkspaceItem(Context context) {
-        WorkspaceItemInfo info = new WorkspaceItemInfo();
-        info.title = title;
-        info.bitmap = bitmap;
-        info.intent = mIntent;
-
-        if (hasFlags(FLAG_SHOULD_START_FOR_RESULT)) {
-            info.options |= WorkspaceItemInfo.FLAG_START_FOR_RESULT;
-        }
-        LauncherAppState app = LauncherAppState.getInstance(context);
-        app.getModel().updateAndBindWorkspaceItem(() -> {
-            PackageItemInfo pkgInfo = new PackageItemInfo(getIntentPackageName(), user);
-            app.getIconCache().getTitleAndIconForApp(pkgInfo, false);
-            info.bitmap = info.bitmap.withBadgeInfo(pkgInfo.bitmap);
-            return info;
-        });
-        return info;
-    }
-
-    @Nullable
-    private String getIntentPackageName() {
-        if (mIntent != null) {
-            if (mIntent.getPackage() != null) return mIntent.getPackage();
-            return mFallbackPackageName;
-        }
-        return null;
-    }
-}
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
index dbab700..a2353d8 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
@@ -51,6 +51,7 @@
 import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.popup.PopupContainerWithArrow;
 import com.android.launcher3.popup.PopupDataProvider;
+import com.android.launcher3.touch.ItemClickHandler.ItemClickProxy;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.IntSet;
 import com.android.launcher3.util.OnboardingPrefs;
@@ -332,7 +333,9 @@
         if (v.getWindowToken() == null) return;
 
         Object tag = v.getTag();
-        if (tag instanceof ItemInfo) {
+        if (tag instanceof ItemClickProxy) {
+            ((ItemClickProxy) tag).onItemClicked(v);
+        } else if (tag instanceof ItemInfo) {
             ItemInfo item = (ItemInfo) tag;
             Intent intent;
             if (item instanceof ItemInfoWithIcon
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index 098cf80..b7e0105 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -17,8 +17,6 @@
 
 import static com.android.launcher3.Launcher.REQUEST_BIND_PENDING_APPWIDGET;
 import static com.android.launcher3.Launcher.REQUEST_RECONFIGURE_APPWIDGET;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_SEARCHINAPP_LAUNCH;
-import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_OPEN;
 import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_BY_PUBLISHER;
 import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER;
@@ -27,11 +25,8 @@
 import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED;
 
 import android.app.AlertDialog;
-import android.app.PendingIntent;
-import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentSender;
 import android.content.pm.LauncherApps;
 import android.content.pm.PackageInstaller.SessionInfo;
 import android.os.Process;
@@ -57,7 +52,6 @@
 import com.android.launcher3.model.data.ItemInfo;
 import com.android.launcher3.model.data.ItemInfoWithIcon;
 import com.android.launcher3.model.data.LauncherAppWidgetInfo;
-import com.android.launcher3.model.data.SearchActionItemInfo;
 import com.android.launcher3.model.data.WorkspaceItemInfo;
 import com.android.launcher3.pm.InstallSessionHelper;
 import com.android.launcher3.shortcuts.ShortcutKey;
@@ -106,8 +100,8 @@
             if (v instanceof PendingAppWidgetHostView) {
                 onClickPendingWidget((PendingAppWidgetHostView) v, launcher);
             }
-        } else if (tag instanceof SearchActionItemInfo) {
-            onClickSearchAction(launcher, (SearchActionItemInfo) tag);
+        } else if (tag instanceof ItemClickProxy) {
+            ((ItemClickProxy) tag).onItemClicked(v);
         }
     }
 
@@ -311,48 +305,6 @@
         startAppShortcutOrInfoActivity(v, shortcut, launcher);
     }
 
-    /**
-     * Event handler for a {@link SearchActionItemInfo} click
-     */
-    public static void onClickSearchAction(Launcher launcher, SearchActionItemInfo itemInfo) {
-        if (itemInfo.getIntent() != null) {
-            try {
-                if (itemInfo.hasFlags(SearchActionItemInfo.FLAG_SHOULD_START_FOR_RESULT)) {
-                    launcher.startActivityForResult(itemInfo.getIntent(), 0);
-                } else {
-                    launcher.startActivity(itemInfo.getIntent());
-                }
-            } catch (ActivityNotFoundException e) {
-                Toast.makeText(launcher,
-                        launcher.getResources().getText(R.string.shortcut_not_available),
-                        Toast.LENGTH_SHORT).show();
-            }
-        } else if (itemInfo.getPendingIntent() != null) {
-            try {
-                PendingIntent pendingIntent = itemInfo.getPendingIntent();
-                if (!itemInfo.hasFlags(SearchActionItemInfo.FLAG_SHOULD_START)) {
-                    pendingIntent.send();
-                } else if (itemInfo.hasFlags(SearchActionItemInfo.FLAG_SHOULD_START_FOR_RESULT)) {
-                    launcher.startIntentSenderForResult(pendingIntent.getIntentSender(), 0, null, 0,
-                            0, 0);
-                } else {
-                    launcher.startIntentSender(pendingIntent.getIntentSender(), null, 0, 0, 0);
-                }
-            } catch (PendingIntent.CanceledException | IntentSender.SendIntentException e) {
-                Toast.makeText(launcher,
-                        launcher.getResources().getText(R.string.shortcut_not_available),
-                        Toast.LENGTH_SHORT).show();
-            }
-        }
-        if (itemInfo.hasFlags(SearchActionItemInfo.FLAG_SEARCH_IN_APP)) {
-            launcher.getStatsLogManager().logger().withItemInfo(itemInfo).log(
-                    LAUNCHER_ALLAPPS_SEARCHINAPP_LAUNCH);
-        } else {
-            launcher.getStatsLogManager().logger().withItemInfo(itemInfo).log(
-                    LAUNCHER_APP_LAUNCH_TAP);
-        }
-    }
-
     private static void startAppShortcutOrInfoActivity(View v, ItemInfo item, Launcher launcher) {
         TestLogging.recordEvent(
                 TestProtocol.SEQUENCE_MAIN, "start: startAppShortcutOrInfoActivity");
@@ -393,4 +345,15 @@
         }
         launcher.startActivitySafely(v, intent, item);
     }
+
+    /**
+     * Interface to indicate that an item will handle the click itself.
+     */
+    public interface ItemClickProxy {
+
+        /**
+         * Called when the item is clicked
+         */
+        void onItemClicked(View view);
+    }
 }
diff --git a/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java b/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java
index 9ba3f7b..d3b6d37 100644
--- a/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java
+++ b/tests/src/com/android/launcher3/celllayout/ReorderWidgets.java
@@ -35,13 +35,13 @@
 import com.android.launcher3.tapl.WidgetResizeFrame;
 import com.android.launcher3.ui.AbstractLauncherUiTest;
 import com.android.launcher3.ui.TaplTestsLauncher3;
-import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
 import com.android.launcher3.util.rule.ShellCommandRule;
 import com.android.launcher3.views.DoubleShadowBubbleTextView;
 import com.android.launcher3.widget.LauncherAppWidgetHostView;
 
 import org.junit.Assume;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -49,7 +49,6 @@
 import java.util.Map;
 import java.util.concurrent.ExecutionException;
 
-
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class ReorderWidgets extends AbstractLauncherUiTest {
@@ -146,26 +145,26 @@
         runTestCase(testCaseMap.get(iconGridDimensions));
     }
 
-    @ScreenRecord // b/242323136
+    @Ignore //b/261178121
     @Test
     public void simpleReorder()  throws ExecutionException, InterruptedException {
         runTestCaseMap(SimpleReorderCase.TEST_BY_GRID_SIZE,
                 SimpleReorderCase.class.getSimpleName());
     }
 
-    @ScreenRecord // b/242323136
+    @Ignore //b/261178121
     @Test
     public void pushTest()  throws ExecutionException, InterruptedException {
         runTestCaseMap(PushReorderCase.TEST_BY_GRID_SIZE, PushReorderCase.class.getSimpleName());
     }
 
-    @ScreenRecord // b/242323136
+    @Ignore //b/261178121
     @Test
     public void fullReorder()  throws ExecutionException, InterruptedException {
         runTestCaseMap(FullReorderCase.TEST_BY_GRID_SIZE, FullReorderCase.class.getSimpleName());
     }
 
-    @ScreenRecord // b/242323136
+    @Ignore //b/261178121
     @Test
     public void moveOutReorder()  throws ExecutionException, InterruptedException {
         runTestCaseMap(MoveOutReorderCase.TEST_BY_GRID_SIZE,