Merge "Recycle thumbnailViews in DesktopTaskView" into main
diff --git a/go/quickstep/res/layout/overview_actions_container.xml b/go/quickstep/res/layout/overview_actions_container.xml
index df09124..b1a6202 100644
--- a/go/quickstep/res/layout/overview_actions_container.xml
+++ b/go/quickstep/res/layout/overview_actions_container.xml
@@ -121,6 +121,17 @@
             android:layout_weight="1"
             android:visibility="gone" />
 
+    </LinearLayout>
+
+    <!-- Unused. Included only for compatibility with parent class. -->
+    <LinearLayout
+        android:id="@+id/group_action_buttons"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/overview_actions_height"
+        android:layout_gravity="top|center_horizontal"
+        android:orientation="horizontal"
+        android:visibility="gone">
+
         <Button
             android:id="@+id/action_save_app_pair"
             style="@style/GoOverviewActionButton"
@@ -128,8 +139,8 @@
             android:layout_height="wrap_content"
             android:drawableStart="@drawable/ic_save_app_pair_up_down"
             android:text="@string/action_save_app_pair"
-            android:theme="@style/ThemeControlHighlightWorkspaceColor"
-            android:visibility="gone" />
+            android:theme="@style/ThemeControlHighlightWorkspaceColor" />
+
     </LinearLayout>
 
 </com.android.quickstep.views.GoOverviewActionsView>
\ No newline at end of file
diff --git a/quickstep/res/layout/overview_actions_container.xml b/quickstep/res/layout/overview_actions_container.xml
index d086da4..7aaf744 100644
--- a/quickstep/res/layout/overview_actions_container.xml
+++ b/quickstep/res/layout/overview_actions_container.xml
@@ -45,14 +45,24 @@
             android:theme="@style/ThemeControlHighlightWorkspaceColor"
             android:visibility="gone" />
 
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/group_action_buttons"
+        android:layout_width="wrap_content"
+        android:layout_height="@dimen/overview_actions_height"
+        android:layout_gravity="bottom|center_horizontal"
+        android:orientation="horizontal"
+        android:visibility="gone">
+
         <Button
             android:id="@+id/action_save_app_pair"
             style="@style/OverviewActionButton"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:text="@string/action_save_app_pair"
-            android:theme="@style/ThemeControlHighlightWorkspaceColor"
-            android:visibility="gone" />
+            android:theme="@style/ThemeControlHighlightWorkspaceColor" />
+
     </LinearLayout>
 
 </com.android.quickstep.views.OverviewActionsView>
\ No newline at end of file
diff --git a/quickstep/res/values-de/strings.xml b/quickstep/res/values-de/strings.xml
index 5097ff9..af35fc3 100644
--- a/quickstep/res/values-de/strings.xml
+++ b/quickstep/res/values-de/strings.xml
@@ -95,7 +95,7 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Einstellungen der Systemsteuerung"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Teilen"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Screenshot"</string>
-    <string name="action_split" msgid="2098009717623550676">"Teilen"</string>
+    <string name="action_split" msgid="2098009717623550676">"Splitscreen"</string>
     <string name="action_save_app_pair" msgid="5974823919237645229">"App-Paar speichern"</string>
     <string name="toast_split_select_app" msgid="8464310533320556058">"Für Splitscreen auf weitere App tippen"</string>
     <string name="toast_contextual_split_select_app" msgid="433510957123687090">"Für Splitscreen andere App auswählen"</string>
diff --git a/quickstep/res/values-es/strings.xml b/quickstep/res/values-es/strings.xml
index 159226e..0d43f1e 100644
--- a/quickstep/res/values-es/strings.xml
+++ b/quickstep/res/values-es/strings.xml
@@ -118,7 +118,7 @@
     <string name="taskbar_edu_pinning_title" msgid="210102174154211712">"Mostrar siempre la barra de tareas"</string>
     <string name="taskbar_edu_pinning_standalone" msgid="2636919474366410467">"Para mostrar siempre la barra de tareas en la parte inferior, mantén pulsada la línea divisoria"</string>
     <string name="taskbar_search_edu_title" msgid="5569194922234364530">"Mantén pulsada la tecla de acción para buscar lo que ves en pantalla"</string>
-    <string name="taskbar_edu_search_disclosure" msgid="8734536088447779686">"El producto usa la parte seleccionada de tu pantalla para hacer búsquedas. Se aplican la <xliff:g id="BEGIN_PRIVACY_LINK">&lt;a href="%1$s"&gt;</xliff:g>Política de Privacidad<xliff:g id="END_PRIVACY_LINK">&lt;/a&gt;</xliff:g> y los <xliff:g id="BEGIN_TOS_LINK">&lt;a href="%2$s"&gt;</xliff:g>Términos del Servicio<xliff:g id="END_TOS_LINK">&lt;/a&gt;</xliff:g> de Google."</string>
+    <string name="taskbar_edu_search_disclosure" msgid="8734536088447779686">"Este producto usa la parte seleccionada de tu pantalla para hacer búsquedas. Se aplican la <xliff:g id="BEGIN_PRIVACY_LINK">&lt;a href="%1$s"&gt;</xliff:g>Política de Privacidad<xliff:g id="END_PRIVACY_LINK">&lt;/a&gt;</xliff:g> y los <xliff:g id="BEGIN_TOS_LINK">&lt;a href="%2$s"&gt;</xliff:g>Términos del Servicio<xliff:g id="END_TOS_LINK">&lt;/a&gt;</xliff:g> de Google."</string>
     <string name="taskbar_edu_close" msgid="887022990168191073">"Cerrar"</string>
     <string name="taskbar_edu_done" msgid="6880178093977704569">"Hecho"</string>
     <string name="taskbar_button_home" msgid="2151398979630664652">"Inicio"</string>
diff --git a/quickstep/res/values-et/strings.xml b/quickstep/res/values-et/strings.xml
index 766dabb..c445bc8 100644
--- a/quickstep/res/values-et/strings.xml
+++ b/quickstep/res/values-et/strings.xml
@@ -95,7 +95,7 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Süsteemi navigeerimisseaded"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Jaga"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Ekraanipilt"</string>
-    <string name="action_split" msgid="2098009717623550676">"Eralda"</string>
+    <string name="action_split" msgid="2098009717623550676">"Jaga pooleks"</string>
     <string name="action_save_app_pair" msgid="5974823919237645229">"Salvesta rakendusepaar"</string>
     <string name="toast_split_select_app" msgid="8464310533320556058">"Jagatud ekraanikuva kasutamiseks puudutage muud rakendust"</string>
     <string name="toast_contextual_split_select_app" msgid="433510957123687090">"Valige jagatud ekraanikuva jaoks muu rakendus."</string>
diff --git a/quickstep/res/values-fi/strings.xml b/quickstep/res/values-fi/strings.xml
index b0694c1..040976d 100644
--- a/quickstep/res/values-fi/strings.xml
+++ b/quickstep/res/values-fi/strings.xml
@@ -95,7 +95,7 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"Järjestelmän navigointiasetukset"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"Jaa"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"Kuvakaappaus"</string>
-    <string name="action_split" msgid="2098009717623550676">"Jaa"</string>
+    <string name="action_split" msgid="2098009717623550676">"Jaettu näyttö"</string>
     <string name="action_save_app_pair" msgid="5974823919237645229">"Tallenna pari"</string>
     <string name="toast_split_select_app" msgid="8464310533320556058">"Avaa jaettu näyttö napauttamalla toista sovellusta"</string>
     <string name="toast_contextual_split_select_app" msgid="433510957123687090">"Käytä jaettua näyttöä valitsemalla toinen sovellus"</string>
diff --git a/quickstep/res/values-my/strings.xml b/quickstep/res/values-my/strings.xml
index e696faf..ecde5a3 100644
--- a/quickstep/res/values-my/strings.xml
+++ b/quickstep/res/values-my/strings.xml
@@ -95,7 +95,7 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"စနစ် လမ်းညွှန် ဆက်တင်များ"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"မျှဝေရန်"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"ဖန်သားပြင်ဓာတ်ပုံ"</string>
-    <string name="action_split" msgid="2098009717623550676">"ခွဲထုတ်ရန်"</string>
+    <string name="action_split" msgid="2098009717623550676">"ခွဲရန်"</string>
     <string name="action_save_app_pair" msgid="5974823919237645229">"အက်ပ်အတွဲ သိမ်းရန်"</string>
     <string name="toast_split_select_app" msgid="8464310533320556058">"မျက်နှာပြင် ခွဲ၍ပြသရန် အက်ပ်နောက်တစ်ခုကို တို့ပါ"</string>
     <string name="toast_contextual_split_select_app" msgid="433510957123687090">"မျက်နှာပြင် ခွဲ၍ပြသခြင်းသုံးရန် နောက်အက်ပ်တစ်ခုရွေးပါ"</string>
diff --git a/quickstep/res/values-zh-rCN/strings.xml b/quickstep/res/values-zh-rCN/strings.xml
index 1086f9f..727f3e7 100644
--- a/quickstep/res/values-zh-rCN/strings.xml
+++ b/quickstep/res/values-zh-rCN/strings.xml
@@ -95,7 +95,7 @@
     <string name="allset_navigation_settings" msgid="4713404605961476027"><annotation id="link">"系统导航设置"</annotation></string>
     <string name="action_share" msgid="2648470652637092375">"分享"</string>
     <string name="action_screenshot" msgid="8171125848358142917">"屏幕截图"</string>
-    <string name="action_split" msgid="2098009717623550676">"拆分"</string>
+    <string name="action_split" msgid="2098009717623550676">"分屏"</string>
     <string name="action_save_app_pair" msgid="5974823919237645229">"保存应用组合"</string>
     <string name="toast_split_select_app" msgid="8464310533320556058">"点按另一个应用即可使用分屏"</string>
     <string name="toast_contextual_split_select_app" msgid="433510957123687090">"另外选择一个应用才可使用分屏模式"</string>
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt
index 90c3ea7..a59e81b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt
@@ -158,6 +158,7 @@
     override fun setAlpha(alpha: Int) {
         paint.alpha = alpha
         arrowDrawable.alpha = alpha
+        invalidateSelf()
     }
 
     override fun getAlpha(): Int {
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
index de93ba5..4334807 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
@@ -98,23 +98,6 @@
     private static final float FADE_IN_ANIM_POSITION_SHIFT = 1 / 60f;
 
     /**
-     * Custom property to set translationX value for the bar view while a bubble is being dragged.
-     * Skips applying translation to the dragged bubble.
-     */
-    private static final FloatProperty<BubbleBarView> BUBBLE_DRAG_TRANSLATION_X =
-            new FloatProperty<>("bubbleDragTranslationX") {
-                @Override
-                public void setValue(BubbleBarView bubbleBarView, float translationX) {
-                    bubbleBarView.setTranslationXDuringBubbleDrag(translationX);
-                }
-
-                @Override
-                public Float get(BubbleBarView bubbleBarView) {
-                    return bubbleBarView.mTranslationXDuringDrag;
-                }
-            };
-
-    /**
      * Custom property to set alpha value for the bar view while a bubble is being dragged.
      * Skips applying alpha to the dragged bubble.
      */
@@ -189,8 +172,7 @@
 
     @Nullable
     private BubbleView mDraggedBubbleView;
-    private float mTranslationXDuringDrag = 0f;
-    private float mAlphaDuringDrag = 0f;
+    private float mAlphaDuringDrag = 1f;
 
     private int mPreviousLayoutDirection = LayoutDirection.UNDEFINED;
 
@@ -264,6 +246,17 @@
         });
     }
 
+    @Override
+    public void setTranslationX(float translationX) {
+        super.setTranslationX(translationX);
+        if (mDraggedBubbleView != null) {
+            // Apply reverse of the translation as an offset to the dragged view. This ensures
+            // that the dragged bubble stays at the current location on the screen and its
+            // position is not affected by the parent translation.
+            mDraggedBubbleView.setOffsetX(-translationX);
+        }
+    }
+
     /**
      * Sets new icon size and spacing between icons and bubble bar borders.
      *
@@ -322,11 +315,9 @@
         final boolean onLeft = mBubbleBarLocation.isOnLeft(isLayoutRtl());
         mBubbleBarBackground.setAnchorLeft(onLeft);
         mRelativePivotX = onLeft ? 0f : 1f;
-        if (getLayoutParams() instanceof LayoutParams lp) {
-            lp.gravity = Gravity.BOTTOM | (onLeft ? Gravity.LEFT : Gravity.RIGHT);
-            setLayoutParams(lp);
-        }
-        invalidate();
+        LayoutParams lp = (LayoutParams) getLayoutParams();
+        lp.gravity = Gravity.BOTTOM | (onLeft ? Gravity.LEFT : Gravity.RIGHT);
+        setLayoutParams(lp); // triggers a relayout
     }
 
     /**
@@ -340,11 +331,6 @@
      * Update {@link BubbleBarLocation}
      */
     public void setBubbleBarLocation(BubbleBarLocation bubbleBarLocation) {
-        if (mBubbleBarLocationAnimator != null) {
-            mBubbleBarLocationAnimator.removeAllListeners();
-            mBubbleBarLocationAnimator.cancel();
-            mBubbleBarLocationAnimator = null;
-        }
         resetDragAnimation();
         if (bubbleBarLocation != mBubbleBarLocation) {
             mBubbleBarLocation = bubbleBarLocation;
@@ -361,6 +347,10 @@
         }
         mDragging = dragging;
         setElevation(dragging ? mDragElevation : mBubbleElevation);
+        if (!mDragging) {
+            // Relayout after dragging to ensure that the dragged bubble is positioned correctly
+            requestLayout();
+        }
     }
 
     /**
@@ -459,14 +449,13 @@
     }
 
     private Animator getLocationUpdateFadeOutAnimator(BubbleBarLocation newLocation) {
-        final FloatProperty<? super BubbleBarView> txProp = getLocationAnimTranslationXProperty();
         final float shift =
                 getResources().getDisplayMetrics().widthPixels * FADE_OUT_ANIM_POSITION_SHIFT;
         final boolean onLeft = newLocation.isOnLeft(isLayoutRtl());
-        final float tx = txProp.get(this) + (onLeft ? -shift : shift);
+        final float tx = getTranslationX() + (onLeft ? -shift : shift);
 
-        ObjectAnimator positionAnim = ObjectAnimator.ofFloat(this, txProp, tx).setDuration(
-                FADE_OUT_ANIM_POSITION_DURATION_MS);
+        ObjectAnimator positionAnim = ObjectAnimator.ofFloat(this, VIEW_TRANSLATE_X, tx)
+                .setDuration(FADE_OUT_ANIM_POSITION_DURATION_MS);
         positionAnim.setInterpolator(EMPHASIZED_ACCELERATE);
 
         ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(this, getLocationAnimAlphaProperty(), 0f)
@@ -505,7 +494,7 @@
                 .setEndValue(finalTx)
                 .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)
                 .setStiffness(FADE_IN_ANIM_POSITION_SPRING_STIFFNESS)
-                .build(this, getLocationAnimTranslationXProperty());
+                .build(this, VIEW_TRANSLATE_X);
 
         ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(this, getLocationAnimAlphaProperty(), 1f)
                 .setDuration(FADE_IN_ANIM_ALPHA_DURATION_MS);
@@ -516,15 +505,6 @@
     }
 
     /**
-     * Get property that can be used to animate the translation-x value for the bar.
-     * When a bubble is being dragged, uses {@link #BUBBLE_DRAG_TRANSLATION_X}.
-     * Falls back to {@link com.android.launcher3.LauncherAnimUtils#VIEW_TRANSLATE_X} otherwise.
-     */
-    private FloatProperty<? super BubbleBarView> getLocationAnimTranslationXProperty() {
-        return mDraggedBubbleView == null ? VIEW_TRANSLATE_X : BUBBLE_DRAG_TRANSLATION_X;
-    }
-
-    /**
      * Get property that can be used to animate the alpha value for the bar.
      * When a bubble is being dragged, uses {@link #BUBBLE_DRAG_ALPHA}.
      * Falls back to {@link com.android.launcher3.LauncherAnimUtils#VIEW_ALPHA} otherwise.
@@ -534,31 +514,6 @@
     }
 
     /**
-     * Set translation-x value for the bar while a bubble is being dragged.
-     * We can not update translation on the bar directly because the dragged bubble would be
-     * affected as well. As it is a child view.
-     * Instead, while a bubble is being dragged, set translation on each child view, that is not the
-     * dragged view. And set a translation on the background.
-     * This allows for the dragged bubble view to remain in position while the bar moves during
-     * animation.
-     */
-    private void setTranslationXDuringBubbleDrag(float translationX) {
-        mTranslationXDuringDrag = translationX;
-        final int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            BubbleView view = (BubbleView) getChildAt(i);
-            if (view != mDraggedBubbleView) {
-                view.setBubbleBarTranslationX(translationX);
-            }
-        }
-        if (mBubbleBarBackground != null) {
-            mTempBackgroundBounds.set(mBubbleBarBackground.getBounds());
-            mTempBackgroundBounds.offsetTo((int) translationX, 0);
-            mBubbleBarBackground.setBounds(mTempBackgroundBounds);
-        }
-    }
-
-    /**
      * Set alpha value for the bar while a bubble is being dragged.
      * We can not update the alpha on the bar directly because the dragged bubble would be affected
      * as well. As it is a child view.
@@ -587,7 +542,6 @@
             mBubbleBarLocationAnimator.cancel();
             mBubbleBarLocationAnimator = null;
         }
-        setTranslationXDuringBubbleDrag(0f);
         setAlphaDuringBubbleDrag(1f);
         setTranslationX(0f);
         setAlpha(1f);
@@ -706,6 +660,10 @@
                 // Skip the dragged bubble. Its translation is managed by the drag controller.
                 continue;
             }
+            // Clear out drag translation and offset
+            bv.setDragTranslationX(0f);
+            bv.setOffsetX(0f);
+
             bv.setTranslationY(ty);
 
             // the position of the bubble when the bar is fully expanded
@@ -851,10 +809,6 @@
     public void setDraggedBubble(@Nullable BubbleView view) {
         if (mDraggedBubbleView != null) {
             mDraggedBubbleView.setZ(0);
-            if (view == null) {
-                // We are clearing the dragged bubble, reset drag
-                resetDragAnimation();
-            }
         }
         mDraggedBubbleView = view;
         if (view != null) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragAnimator.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragAnimator.java
index 49f114a..287e906 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragAnimator.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragAnimator.java
@@ -60,6 +60,7 @@
     private final float mBubbleFocusedScale;
     private final float mBubbleCapturedScale;
     private final float mDismissCapturedScale;
+    private final FloatPropertyCompat<View> mTranslationXProperty;
 
     /**
      * Should be initialised for each dragged view
@@ -81,9 +82,28 @@
         if (view instanceof BubbleBarView) {
             mBubbleFocusedScale = SCALE_BUBBLE_BAR_FOCUSED;
             mBubbleCapturedScale = mDismissCapturedScale;
+            mTranslationXProperty = DynamicAnimation.TRANSLATION_X;
         } else {
             mBubbleFocusedScale = SCALE_BUBBLE_FOCUSED;
             mBubbleCapturedScale = SCALE_BUBBLE_CAPTURED;
+            // Wrap BubbleView.DRAG_TRANSLATION_X as it can't be cast to FloatPropertyCompat<View>
+            mTranslationXProperty = new FloatPropertyCompat<>(
+                    BubbleView.DRAG_TRANSLATION_X.getName()) {
+                @Override
+                public float getValue(View object) {
+                    if (object instanceof BubbleView bubbleView) {
+                        return BubbleView.DRAG_TRANSLATION_X.get(bubbleView);
+                    }
+                    return 0;
+                }
+
+                @Override
+                public void setValue(View object, float value) {
+                    if (object instanceof BubbleView bubbleView) {
+                        BubbleView.DRAG_TRANSLATION_X.setValue(bubbleView, value);
+                    }
+                }
+            };
         }
     }
 
@@ -120,7 +140,7 @@
         mBubbleAnimator
                 .spring(DynamicAnimation.SCALE_X, 1f)
                 .spring(DynamicAnimation.SCALE_Y, 1f)
-                .spring(DynamicAnimation.TRANSLATION_X, restingPosition.x, velocity.x,
+                .spring(mTranslationXProperty, restingPosition.x, velocity.x,
                         mTranslationConfig)
                 .spring(DynamicAnimation.TRANSLATION_Y, restingPosition.y, velocity.y,
                         mTranslationConfig)
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
index 1764f75..15de1b8 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleDragController.java
@@ -104,7 +104,9 @@
             }
 
             @Override
-            protected void onDragUpdate(float x, float y) {
+            protected void onDragUpdate(float x, float y, float newTx, float newTy) {
+                bubbleView.setDragTranslationX(newTx);
+                bubbleView.setTranslationY(newTy);
                 mBubblePinController.onDragUpdate(x, y);
             }
 
@@ -145,12 +147,7 @@
             private BubbleBarLocation mReleasedLocation = BubbleBarLocation.DEFAULT;
 
             private final LocationChangeListener mLocationChangeListener =
-                    new LocationChangeListener() {
-                        @Override
-                        public void onRelease(@NonNull BubbleBarLocation location) {
-                            mReleasedLocation = location;
-                        }
-                    };
+                    location -> mReleasedLocation = location;
 
             @Override
             protected boolean onTouchDown(@NonNull View view, @NonNull MotionEvent event) {
@@ -172,7 +169,9 @@
             }
 
             @Override
-            protected void onDragUpdate(float x, float y) {
+            protected void onDragUpdate(float x, float y, float newTx, float newTy) {
+                bubbleBarView.setTranslationX(newTx);
+                bubbleBarView.setTranslationY(newTy);
                 mBubbleBarPinController.onDragUpdate(x, y);
             }
 
@@ -265,8 +264,7 @@
          * Called when bubble is dragged to new coordinates.
          * Not called while bubble is stuck to the dismiss target.
          */
-        protected void onDragUpdate(float x, float y) {
-        }
+        protected abstract void onDragUpdate(float x, float y, float newTx, float newTy);
 
         /**
          * Called when the dragging interaction has ended and all the animations have completed
@@ -411,9 +409,9 @@
         private void drag(@NonNull View view, @NonNull MotionEvent event, float dx, float dy,
                 float x, float y) {
             if (mBubbleDismissController.handleTouchEvent(event)) return;
-            view.setTranslationX(mViewInitialPosition.x + dx);
-            view.setTranslationY(mViewInitialPosition.y + dy);
-            onDragUpdate(x, y);
+            final float newTx = mViewInitialPosition.x + dx;
+            final float newTy = mViewInitialPosition.y + dy;
+            onDragUpdate(x, y, newTx, newTy);
         }
 
         private void stopDragging(@NonNull View view, @NonNull MotionEvent event) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
index 3dc4ebc..61a6bce 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
@@ -22,11 +22,13 @@
 import android.graphics.Outline;
 import android.graphics.Rect;
 import android.util.AttributeSet;
+import android.util.FloatProperty;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewOutlineProvider;
 import android.widget.ImageView;
 
+import androidx.annotation.NonNull;
 import androidx.constraintlayout.widget.ConstraintLayout;
 
 import com.android.launcher3.R;
@@ -47,6 +49,25 @@
     public static final int DEFAULT_PATH_SIZE = 100;
 
     /**
+     * Property to update drag translation value.
+     *
+     * @see BubbleView#getDragTranslationX()
+     * @see BubbleView#setDragTranslationX(float)
+     */
+    public static final FloatProperty<BubbleView> DRAG_TRANSLATION_X = new FloatProperty<>(
+            "dragTranslationX") {
+        @Override
+        public void setValue(@NonNull BubbleView bubbleView, float value) {
+            bubbleView.setDragTranslationX(value);
+        }
+
+        @Override
+        public Float get(BubbleView bubbleView) {
+            return bubbleView.getDragTranslationX();
+        }
+    };
+
+    /**
      * Flags that suppress the visibility of the 'new' dot or the app badge, for one reason or
      * another. If any of these flags are set, the dot will not be shown.
      * If {@link SuppressionFlag#BEHIND_STACK} then the app badge will not be shown.
@@ -66,8 +87,8 @@
     private final ImageView mAppIcon;
     private final int mBubbleSize;
 
-    private float mBubbleBarTranslationX = 0f;
-    private float mTranslationX = 0f;
+    private float mDragTranslationX;
+    private float mOffsetX;
 
     private DotRenderer mDotRenderer;
     private DotRenderer.DrawParams mDrawParams;
@@ -129,33 +150,37 @@
         outline.setOval(inset, inset, inset + normalizedSize, inset + normalizedSize);
     }
 
-    @Override
-    public void setTranslationX(float translationX) {
-        // Overriding setting translationX as it can be a combination of the parent translation
-        // and current view translation.
-        // When a BubbleView is being dragged to pin the bubble bar to other side, we animate the
-        // bar to the new location during the drag.
-        // One part of the animation is updating the translation of the bubble bar. But doing
-        // that also updates the translation for the child views, like the dragged bubble.
-        // To get around that, we instead apply translation on each child view of bubble bar. It
-        // is applied as bubble bar translation. This results in BubbleView's translation being a
-        // sum of the translation it has and the parent bubble bar translation.
-        mTranslationX = translationX;
-        applyTranslation();
+    /**
+     * Set translation-x while this bubble is being dragged.
+     * Translation applied to the view is a sum of {@code translationX} and offset defined by
+     * {@link #setOffsetX(float)}.
+     */
+    public void setDragTranslationX(float translationX) {
+        mDragTranslationX = translationX;
+        applyDragTranslation();
     }
 
     /**
-     * Translation of the bubble bar that hosts this bubble.
-     * Is applied together with translation applied on the view through
-     * {@link #setTranslationX(float)}.
+     * Get translation value applied via {@link #setDragTranslationX(float)}.
      */
-    void setBubbleBarTranslationX(float translationX) {
-        mBubbleBarTranslationX = translationX;
-        applyTranslation();
+    public float getDragTranslationX() {
+        return mDragTranslationX;
     }
 
-    private void applyTranslation() {
-        super.setTranslationX(mTranslationX + mBubbleBarTranslationX);
+    /**
+     * Set offset on x-axis while dragging.
+     * Used to counter parent translation in order to keep the dragged view at the current position
+     * on screen.
+     * Translation applied to the view is a sum of {@code offsetX} and translation defined by
+     * {@link #setDragTranslationX(float)}
+     */
+    public void setOffsetX(float offsetX) {
+        mOffsetX = offsetX;
+        applyDragTranslation();
+    }
+
+    private void applyDragTranslation() {
+        setTranslationX(mDragTranslationX + mOffsetX);
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index 6c1d4b1..317e6f2 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -21,7 +21,6 @@
 import static com.android.launcher3.LauncherState.OVERVIEW_ACTIONS;
 import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_ACTIONS_FADE;
-import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VALUE;
 import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
 import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
 import static com.android.quickstep.views.RecentsView.TASK_MODALNESS;
@@ -50,6 +49,7 @@
 import com.android.quickstep.util.SplitAnimationTimings;
 import com.android.quickstep.views.ClearAllButton;
 import com.android.quickstep.views.LauncherRecentsView;
+import com.android.quickstep.views.OverviewActionsView;
 import com.android.quickstep.views.RecentsView;
 
 /**
@@ -167,8 +167,8 @@
         propertySetter.setFloat(mRecentsView.getClearAllButton(), ClearAllButton.VISIBILITY_ALPHA,
                 clearAllButtonAlpha, LINEAR);
         float overviewButtonAlpha = state.areElementsVisible(mLauncher, OVERVIEW_ACTIONS) ? 1 : 0;
-        propertySetter.setFloat(mLauncher.getActionsView().getVisibilityAlpha(),
-                MULTI_PROPERTY_VALUE, overviewButtonAlpha, config.getInterpolator(
+        propertySetter.setFloat(mLauncher.getActionsView().getVisibilityAlphaSetter(),
+                OverviewActionsView.FLOAT_SETTER, overviewButtonAlpha, config.getInterpolator(
                         ANIM_OVERVIEW_ACTIONS_FADE, LINEAR));
     }
 
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
index 527a776..fc0df76 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/NoButtonQuickSwitchTouchController.java
@@ -248,7 +248,7 @@
         TASK_THUMBNAIL_SPLASH_ALPHA.set(mRecentsView, fromState.showTaskThumbnailSplash() ? 1f : 0);
         mRecentsView.setContentAlpha(1);
         mRecentsView.setFullscreenProgress(fromState.getOverviewFullscreenProgress());
-        mLauncher.getActionsView().getVisibilityAlpha().setValue(
+        mLauncher.getActionsView().getVisibilityAlphaSetter().accept(
                 (fromState.getVisibleElements(mLauncher) & OVERVIEW_ACTIONS) != 0 ? 1f : 0f);
         mRecentsView.setTaskIconScaledDown(true);
 
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index bf985af..28ae3d2 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -148,6 +148,7 @@
 import com.android.systemui.shared.system.SysUiStatsLog;
 import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.shared.system.TaskStackChangeListeners;
+import com.android.window.flags.Flags;
 import com.android.wm.shell.common.TransactionPool;
 import com.android.wm.shell.startingsurface.SplashScreenExitAnimationUtils;
 
@@ -1259,13 +1260,16 @@
                 ? mRecentsView.getNextPageTaskView() : null;
         TaskView currentPageTaskView = mRecentsView != null
                 ? mRecentsView.getCurrentPageTaskView() : null;
-        if ((nextPageTaskView instanceof DesktopTaskView
-                || currentPageTaskView instanceof DesktopTaskView)
-                && endTarget == NEW_TASK) {
-            // TODO(b/268075592): add support for quickswitch to/from desktop
-            return LAST_TASK;
-        }
 
+        if (Flags.enableDesktopWindowingMode()
+                && !(Flags.enableDesktopWindowingWallpaperActivity()
+                && Flags.enableDesktopWindowingQuickSwitch())) {
+            if ((nextPageTaskView instanceof DesktopTaskView
+                    || currentPageTaskView instanceof DesktopTaskView)
+                    && endTarget == NEW_TASK) {
+                return LAST_TASK;
+            }
+        }
         return endTarget;
     }
 
@@ -1422,14 +1426,27 @@
             mGestureState.setState(STATE_RECENTS_SCROLLING_FINISHED);
             setClampScrollOffset(false);
         };
-        if (mRecentsView != null && (mRecentsView.getCurrentPageTaskView() != null
-                && !(mRecentsView.getCurrentPageTaskView() instanceof DesktopTaskView))) {
-            ActiveGestureLog.INSTANCE.trackEvent(ActiveGestureErrorDetector.GestureEvent
-                    .SET_ON_PAGE_TRANSITION_END_CALLBACK);
-            // TODO(b/268075592): add support for quickswitch to/from desktop
-            mRecentsView.setOnPageTransitionEndCallback(onPageTransitionEnd);
+
+        if (Flags.enableDesktopWindowingMode()
+                && !(Flags.enableDesktopWindowingWallpaperActivity()
+                && Flags.enableDesktopWindowingQuickSwitch())) {
+            if (mRecentsView != null && (mRecentsView.getCurrentPageTaskView() != null
+                    && !(mRecentsView.getCurrentPageTaskView() instanceof DesktopTaskView))) {
+                ActiveGestureLog.INSTANCE.trackEvent(ActiveGestureErrorDetector.GestureEvent
+                        .SET_ON_PAGE_TRANSITION_END_CALLBACK);
+                mRecentsView.setOnPageTransitionEndCallback(onPageTransitionEnd);
+            } else {
+                onPageTransitionEnd.run();
+            }
         } else {
-            onPageTransitionEnd.run();
+            if (mRecentsView != null) {
+                ActiveGestureLog.INSTANCE.trackEvent(
+                        ActiveGestureErrorDetector
+                                .GestureEvent.SET_ON_PAGE_TRANSITION_END_CALLBACK);
+                mRecentsView.setOnPageTransitionEndCallback(onPageTransitionEnd);
+            } else {
+                onPageTransitionEnd.run();
+            }
         }
 
         animateToProgress(startShift, endShift, duration, interpolator, endTarget, velocityPxPerMs);
@@ -2251,11 +2268,14 @@
                     mRecentsAnimationController, mRecentsAnimationTargets);
         });
 
-        if (mRecentsView.getNextPageTaskView() instanceof DesktopTaskView
-                || mRecentsView.getCurrentPageTaskView() instanceof DesktopTaskView) {
-            // TODO(b/268075592): add support for quickswitch to/from desktop
-            mRecentsViewScrollLinked = false;
-            return;
+        if (Flags.enableDesktopWindowingMode()
+                && !(Flags.enableDesktopWindowingWallpaperActivity()
+                        && Flags.enableDesktopWindowingQuickSwitch())) {
+            if (mRecentsView.getNextPageTaskView() instanceof DesktopTaskView
+                    || mRecentsView.getCurrentPageTaskView() instanceof DesktopTaskView) {
+                mRecentsViewScrollLinked = false;
+                return;
+            }
         }
 
         // Disable scrolling in RecentsView for trackpad 3-finger swipe up gesture.
diff --git a/quickstep/src/com/android/quickstep/DeviceConfigWrapper.kt b/quickstep/src/com/android/quickstep/DeviceConfigWrapper.kt
index f68f793..0f844e1 100644
--- a/quickstep/src/com/android/quickstep/DeviceConfigWrapper.kt
+++ b/quickstep/src/com/android/quickstep/DeviceConfigWrapper.kt
@@ -154,7 +154,7 @@
     }
 
     companion object {
-        val configHelper by lazy { DeviceConfigHelper(::DeviceConfigWrapper) }
+        @JvmStatic val configHelper by lazy { DeviceConfigHelper(::DeviceConfigWrapper) }
 
         @JvmStatic fun get() = configHelper.config
     }
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index 4b4f914..54466f3 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -33,6 +33,7 @@
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DREAMING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DIALOG_SHOWING;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DISABLE_GESTURE_SPLIT_INVOCATION;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_MAGNIFICATION_OVERLAP;
@@ -399,7 +400,8 @@
                 && (mSystemUiStateFlags & SYSUI_STATE_MAGNIFICATION_OVERLAP) == 0
                 && ((mSystemUiStateFlags & SYSUI_STATE_HOME_DISABLED) == 0
                         || (mSystemUiStateFlags & SYSUI_STATE_OVERVIEW_DISABLED) == 0)
-                && (mSystemUiStateFlags & SYSUI_STATE_DEVICE_DREAMING) == 0;
+                && (mSystemUiStateFlags & SYSUI_STATE_DEVICE_DREAMING) == 0
+                && (mSystemUiStateFlags & SYSUI_STATE_DISABLE_GESTURE_SPLIT_INVOCATION) == 0;
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
index 2e76356..1bf129c 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
@@ -46,12 +46,10 @@
 import com.android.launcher3.anim.PropertySetter;
 import com.android.launcher3.statemanager.StateManager.StateHandler;
 import com.android.launcher3.states.StateAnimationConfig;
-import com.android.launcher3.util.MultiPropertyFactory;
 import com.android.quickstep.RecentsActivity;
 import com.android.quickstep.views.ClearAllButton;
 import com.android.quickstep.views.OverviewActionsView;
 import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.RecentsViewContainer;
 
 /**
  * State controller for fallback recents activity
@@ -98,8 +96,8 @@
         setter.setFloat(mRecentsView.getClearAllButton(), ClearAllButton.VISIBILITY_ALPHA,
                 clearAllButtonAlpha, LINEAR);
         float overviewButtonAlpha = state.hasOverviewActions() ? 1 : 0;
-        setter.setFloat(mActivity.getActionsView().getVisibilityAlpha(),
-                MultiPropertyFactory.MULTI_PROPERTY_VALUE, overviewButtonAlpha, LINEAR);
+        setter.setFloat(mActivity.getActionsView().getVisibilityAlphaSetter(),
+                OverviewActionsView.FLOAT_SETTER, overviewButtonAlpha, LINEAR);
 
         float[] scaleAndOffset = state.getOverviewScaleAndOffset(mActivity);
         setter.setFloat(mRecentsView, RECENTS_SCALE_PROPERTY, scaleAndOffset[0],
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index e3e14ae..3cae4dc 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -392,6 +392,20 @@
                 case LAUNCHER_ALLAPPS_VERTICAL_SWIPE_END:
                     InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_ALL_APPS_SCROLL);
                     break;
+                case LAUNCHER_PRIVATE_SPACE_LOCK_ANIMATION_BEGIN:
+                    InteractionJankMonitorWrapper.begin(view, Cuj.CUJ_LAUNCHER_PRIVATE_SPACE_LOCK);
+                    break;
+                case LAUNCHER_PRIVATE_SPACE_LOCK_ANIMATION_END:
+                    InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_PRIVATE_SPACE_LOCK);
+                    break;
+                case LAUNCHER_PRIVATE_SPACE_UNLOCK_ANIMATION_BEGIN:
+                    InteractionJankMonitorWrapper.begin(
+                            view,
+                            Cuj.CUJ_LAUNCHER_PRIVATE_SPACE_UNLOCK);
+                    break;
+                case LAUNCHER_PRIVATE_SPACE_UNLOCK_ANIMATION_END:
+                    InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_PRIVATE_SPACE_UNLOCK);
+                    break;
                 default:
                     break;
             }
diff --git a/quickstep/src/com/android/quickstep/util/DeviceConfigHelper.kt b/quickstep/src/com/android/quickstep/util/DeviceConfigHelper.kt
index 544c64d..d36dc7e 100644
--- a/quickstep/src/com/android/quickstep/util/DeviceConfigHelper.kt
+++ b/quickstep/src/com/android/quickstep/util/DeviceConfigHelper.kt
@@ -48,12 +48,14 @@
                 PropReader(
                     object : PropProvider {
                         override fun <T : Any> get(key: String, fallback: T): T {
-                            if (fallback is Int)
+                            if (fallback is Int) {
+                                allKeys.add(key)
                                 return DeviceConfig.getInt(NAMESPACE_LAUNCHER, key, fallback) as T
-                            else if (fallback is Boolean)
+                            } else if (fallback is Boolean) {
+                                allKeys.add(key)
                                 return DeviceConfig.getBoolean(NAMESPACE_LAUNCHER, key, fallback)
                                     as T
-                            else return fallback
+                            } else return fallback
                         }
                     }
                 )
diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
index 5188d4a..8a917d6 100644
--- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
@@ -20,6 +20,7 @@
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.util.AttributeSet;
+import android.util.FloatProperty;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.widget.Button;
@@ -33,9 +34,7 @@
 import com.android.launcher3.Flags;
 import com.android.launcher3.Insettable;
 import com.android.launcher3.R;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.DisplayController;
-import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
 import com.android.launcher3.util.MultiValueAlpha;
 import com.android.launcher3.util.NavigationMode;
 import com.android.quickstep.TaskOverlayFactory.OverlayUICallbacks;
@@ -43,12 +42,26 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.function.Consumer;
 
 /**
  * View for showing action buttons in Overview
  */
 public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayout
         implements OnClickListener, Insettable {
+    public static final FloatProperty<Consumer<Float>> FLOAT_SETTER =
+            new FloatProperty<>("floatSetter") {
+                @Override
+                public void setValue(Consumer<Float> consumer, float v) {
+                    consumer.accept(v);
+                }
+
+                @Override
+                public Float get(Consumer<Float> consumer) {
+                    return -1f;
+                }
+            };
 
     private final Rect mInsets = new Rect();
 
@@ -89,30 +102,24 @@
     private static final int INDEX_HIDDEN_FLAGS_ALPHA = 3;
     private static final int INDEX_SHARE_TARGET_ALPHA = 4;
     private static final int INDEX_SCROLL_ALPHA = 5;
-    private static final int NUM_ALPHAS = 6;
-
-    public @interface ScreenshotButtonHiddenFlags { }
-    public static final int FLAG_MULTIPLE_TASKS_HIDE_SCREENSHOT = 1 << 0;
+    private static final int INDEX_GROUPED_ALPHA = 6;
+    private static final int INDEX_3P_LAUNCHER = 7;
+    private static final int NUM_ALPHAS = 8;
 
     public @interface SplitButtonHiddenFlags { }
     public static final int FLAG_SMALL_SCREEN_HIDE_SPLIT = 1 << 0;
-    public static final int FLAG_MULTIPLE_TASKS_HIDE_SPLIT = 1 << 1;
 
-    public @interface SplitButtonDisabledFlags { }
-    public static final int FLAG_SINGLE_TASK_DISABLE_SPLIT = 1 << 0;
+    /** Holds MultiValueAlpha values for all actions buttons */
+    private final MultiValueAlpha[] mMultiValueAlphas = new MultiValueAlpha[2];
+    /** Index used for single-task actions in the mMultiValueAlphas array */
+    private static final int ACTIONS_ALPHAS = 0;
+    /** Index used for grouped-task actions in the mMultiValueAlphas array */
+    private static final int GROUP_ACTIONS_ALPHAS = 1;
 
-    public @interface AppPairButtonHiddenFlags { }
-    public static final int FLAG_SINGLE_TASK_HIDE_APP_PAIR = 1 << 0;
-    public static final int FLAG_SMALL_SCREEN_HIDE_APP_PAIR = 1 << 1;
-    public static final int FLAG_3P_LAUNCHER_HIDE_APP_PAIR = 1 << 2;
-
-    private MultiValueAlpha mMultiValueAlpha;
-
+    /** Container for the action buttons below a focused, non-split Overview tile. */
     protected LinearLayout mActionButtons;
-    // The screenshot button is implemented as a Button in launcher3 and NexusLauncher, but is an
-    // ImageButton in go launcher (does not share a common class with Button). Take care when
-    // casting this.
-    private View mScreenshotButton;
+    /** Container for the action buttons below a focused, split Overview tile. */
+    protected LinearLayout mGroupActionButtons;
     private Button mSplitButton;
     private Button mSaveAppPairButton;
 
@@ -122,21 +129,16 @@
     @ActionsDisabledFlags
     protected int mDisabledFlags;
 
-    @ScreenshotButtonHiddenFlags
-    private int mScreenshotButtonHiddenFlags;
-
     @SplitButtonHiddenFlags
     private int mSplitButtonHiddenFlags;
 
-    @AppPairButtonHiddenFlags
-    private int mAppPairButtonHiddenFlags;
-
     @Nullable
     protected T mCallbacks;
 
     @Nullable
     protected DeviceProfile mDp;
     private final Rect mTaskSize = new Rect();
+    private boolean mIsGroupedTask = false;
 
     public OverviewActionsView(Context context) {
         this(context, null);
@@ -153,12 +155,21 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
+        // Initialize 2 view containers: one for single tasks, one for grouped tasks.
+        // These will take up the same space on the screen and alternate visibility as needed.
         mActionButtons = findViewById(R.id.action_buttons);
-        mMultiValueAlpha = new MultiValueAlpha(mActionButtons, NUM_ALPHAS);
-        mMultiValueAlpha.setUpdateVisibility(true);
+        mGroupActionButtons = findViewById(R.id.group_action_buttons);
+        // Initialize a list to set alpha on mActionButtons and mGroupActionButtons simultaneously.
+        mMultiValueAlphas[ACTIONS_ALPHAS] = new MultiValueAlpha(mActionButtons, NUM_ALPHAS);
+        mMultiValueAlphas[GROUP_ACTIONS_ALPHAS] =
+                new MultiValueAlpha(mGroupActionButtons, NUM_ALPHAS);
+        Arrays.stream(mMultiValueAlphas).forEach(a -> a.setUpdateVisibility(true));
 
-        mScreenshotButton = findViewById(R.id.action_screenshot);
-        mScreenshotButton.setOnClickListener(this);
+        // The screenshot button is implemented as a Button in launcher3 and NexusLauncher, but is
+        // an ImageButton in go launcher (does not share a common class with Button). Take care when
+        // casting this.
+        View screenshotButton = findViewById(R.id.action_screenshot);
+        screenshotButton.setOnClickListener(this);
         mSplitButton = findViewById(R.id.action_split);
         mSplitButton.setOnClickListener(this);
         mSaveAppPairButton = findViewById(R.id.action_save_app_pair);
@@ -209,7 +220,7 @@
             mHiddenFlags &= ~visibilityFlags;
         }
         boolean isHidden = mHiddenFlags != 0;
-        mMultiValueAlpha.get(INDEX_HIDDEN_FLAGS_ALPHA).setValue(isHidden ? 0 : 1);
+        setActionsAlpha(INDEX_HIDDEN_FLAGS_ALPHA, isHidden ? 0 : 1);
     }
 
     /**
@@ -236,12 +247,8 @@
      * @param isGroupedTask True if the focused task is a grouped task.
      */
     public void updateForGroupedTask(boolean isGroupedTask) {
-        // Update flags to see if split button should be hidden.
-        updateSplitButtonHiddenFlags(FLAG_MULTIPLE_TASKS_HIDE_SPLIT, isGroupedTask);
-        // Update flags to see if screenshot button should be hidden.
-        updateScreenshotButtonHiddenFlags(FLAG_MULTIPLE_TASKS_HIDE_SCREENSHOT, isGroupedTask);
-        // Update flags to see if save app pair button should be hidden.
-        updateAppPairButtonHiddenFlags(FLAG_SINGLE_TASK_HIDE_APP_PAIR, !isGroupedTask);
+        mIsGroupedTask = isGroupedTask;
+        updateActionButtonsVisibility();
     }
 
     /**
@@ -251,36 +258,30 @@
         assert mDp != null;
         // Update flags to see if split button should be hidden.
         updateSplitButtonHiddenFlags(FLAG_SMALL_SCREEN_HIDE_SPLIT, !mDp.isTablet);
-        // Update flags to see if save app pair button should be hidden.
-        updateAppPairButtonHiddenFlags(FLAG_SMALL_SCREEN_HIDE_APP_PAIR, !mDp.isTablet);
+        updateActionButtonsVisibility();
+    }
+
+    private void updateActionButtonsVisibility() {
+        assert mDp != null;
+        boolean showSingleTaskActions = !mIsGroupedTask;
+        boolean showGroupActions = mIsGroupedTask && mDp.isTablet;
+        getActionsAlphas().get(INDEX_GROUPED_ALPHA).setValue(showSingleTaskActions ? 1 : 0);
+        getGroupActionsAlphas().get(INDEX_GROUPED_ALPHA).setValue(showGroupActions ? 1 : 0);
     }
 
     /**
      * Updates flags to hide and show actions buttons for 1p/3p launchers.
      */
     public void updateFor3pLauncher(boolean is3pLauncher) {
-        updateAppPairButtonHiddenFlags(FLAG_3P_LAUNCHER_HIDE_APP_PAIR, is3pLauncher);
+        getGroupActionsAlphas().get(INDEX_3P_LAUNCHER).setValue(is3pLauncher ? 0 : 1);
     }
 
-    /**
-     * Updates the proper flags to indicate whether the "Screenshot" button should be hidden.
-     *
-     * @param flag   The flag to update.
-     * @param enable Whether to enable the hidden flag: True will cause view to be hidden.
-     */
-    private void updateScreenshotButtonHiddenFlags(@ScreenshotButtonHiddenFlags int flag,
-            boolean enable) {
-        if (mScreenshotButton == null) return;
-        if (enable) {
-            mScreenshotButtonHiddenFlags |= flag;
-        } else {
-            mScreenshotButtonHiddenFlags &= ~flag;
-        }
-        int desiredVisibility = mScreenshotButtonHiddenFlags == 0 ? VISIBLE : GONE;
-        if (mScreenshotButton.getVisibility() != desiredVisibility) {
-            mScreenshotButton.setVisibility(desiredVisibility);
-            mActionButtons.requestLayout();
-        }
+    private MultiValueAlpha getActionsAlphas() {
+        return mMultiValueAlphas[ACTIONS_ALPHAS];
+    }
+
+    private MultiValueAlpha getGroupActionsAlphas() {
+        return mMultiValueAlphas[GROUP_ACTIONS_ALPHAS];
     }
 
     /**
@@ -304,56 +305,36 @@
         }
     }
 
-    /**
-     * Updates the proper flags to indicate whether the "Save app pair" button should be disabled.
-     *
-     * @param flag   The flag to update.
-     * @param enable Whether to enable the hidden flag: True will cause view to be hidden.
-     */
-    private void updateAppPairButtonHiddenFlags(
-            @AppPairButtonHiddenFlags int flag, boolean enable) {
-        if (!FeatureFlags.enableAppPairs()) {
-            return;
-        }
-
-        if (mSaveAppPairButton == null) return;
-        if (enable) {
-            mAppPairButtonHiddenFlags |= flag;
-        } else {
-            mAppPairButtonHiddenFlags &= ~flag;
-        }
-        int desiredVisibility = mAppPairButtonHiddenFlags == 0 ? VISIBLE : GONE;
-        if (mSaveAppPairButton.getVisibility() != desiredVisibility) {
-            mSaveAppPairButton.setVisibility(desiredVisibility);
-            mActionButtons.requestLayout();
-        }
+    private void setActionsAlpha(int index, float value) {
+        Arrays.stream(mMultiValueAlphas).forEach(a -> a.get(index).setValue(value));
     }
 
-    public MultiProperty getContentAlpha() {
-        return mMultiValueAlpha.get(INDEX_CONTENT_ALPHA);
+    public Consumer<Float> getContentAlphaSetter() {
+        return v -> setActionsAlpha(INDEX_CONTENT_ALPHA, v);
     }
 
-    public MultiProperty getVisibilityAlpha() {
-        return mMultiValueAlpha.get(INDEX_VISIBILITY_ALPHA);
+    public Consumer<Float> getVisibilityAlphaSetter() {
+        return v -> setActionsAlpha(INDEX_VISIBILITY_ALPHA, v);
     }
 
-    public MultiProperty getFullscreenAlpha() {
-        return mMultiValueAlpha.get(INDEX_FULLSCREEN_ALPHA);
+    public Consumer<Float> getFullscreenAlphaSetter() {
+        return v -> setActionsAlpha(INDEX_FULLSCREEN_ALPHA, v);
     }
 
-    public MultiProperty getShareTargetAlpha() {
-        return mMultiValueAlpha.get(INDEX_SHARE_TARGET_ALPHA);
+    public Consumer<Float> getShareTargetAlphaSetter() {
+        return v -> setActionsAlpha(INDEX_SHARE_TARGET_ALPHA, v);
     }
 
-    public MultiProperty getIndexScrollAlpha() {
-        return mMultiValueAlpha.get(INDEX_SCROLL_ALPHA);
+    public Consumer<Float> getIndexScrollAlphaSetter() {
+        return v -> setActionsAlpha(INDEX_SCROLL_ALPHA, v);
     }
 
     /**
      * Returns the visibility of the overview actions buttons.
      */
-    public @Visibility int getActionsButtonVisibility() {
-        return mActionButtons.getVisibility();
+    public boolean areActionsButtonsVisible() {
+        return mActionButtons.getVisibility() == View.VISIBLE
+                || mGroupActionButtons.getVisibility() == View.VISIBLE;
     }
 
     /**
@@ -366,10 +347,17 @@
 
     /** Updates vertical margins for different navigation mode or configuration changes. */
     public void updateVerticalMargin(NavigationMode mode) {
+        updateActionBarPosition(mActionButtons);
+        updateActionBarPosition(mGroupActionButtons);
+    }
+
+    /** Positions actions buttons according to device settings and insets. */
+    private void updateActionBarPosition(LinearLayout actionBar) {
         if (mDp == null) {
             return;
         }
-        LayoutParams actionParams = (LayoutParams) mActionButtons.getLayoutParams();
+
+        LayoutParams actionParams = (LayoutParams) actionBar.getLayoutParams();
         actionParams.setMargins(
                 actionParams.leftMargin, mDp.overviewActionsTopMarginPx,
                 actionParams.rightMargin, getBottomMargin());
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index eceeaec..b5ce7f7 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -2019,7 +2019,7 @@
         mClearAllButton.setFullscreenProgress(fullscreenProgress);
 
         // Fade out the actions view quickly (0.1 range)
-        mActionsView.getFullscreenAlpha().setValue(
+        mActionsView.getFullscreenAlphaSetter().accept(
                 mapToRange(fullscreenProgress, 0, 0.1f, 1f, 0f, LINEAR));
     }
 
@@ -2270,8 +2270,8 @@
     }
 
     private void animateActionsViewAlpha(float alphaValue, long duration) {
-        mActionsViewAlphaAnimator = ObjectAnimator.ofFloat(
-                mActionsView.getVisibilityAlpha(), MULTI_PROPERTY_VALUE, alphaValue);
+        mActionsViewAlphaAnimator = ObjectAnimator.ofFloat(mActionsView.getVisibilityAlphaSetter(),
+                OverviewActionsView.FLOAT_SETTER, alphaValue);
         mActionsViewAlphaAnimatorFinalValue = alphaValue;
         mActionsViewAlphaAnimator.setDuration(duration);
         // Set autocancel to prevent race-conditiony setting of alpha from other animations
@@ -2290,7 +2290,7 @@
         mClearAllButton.onRecentsViewScroll(scroll, mOverviewGridEnabled);
 
         // Clear all button alpha was set by the previous line.
-        mActionsView.getIndexScrollAlpha().setValue(1 - mClearAllButton.getScrollAlpha());
+        mActionsView.getIndexScrollAlphaSetter().accept(1 - mClearAllButton.getScrollAlpha());
     }
 
     @Override
@@ -4295,7 +4295,7 @@
         int alphaInt = Math.round(alpha * 255);
         mEmptyMessagePaint.setAlpha(alphaInt);
         mEmptyIcon.setAlpha(alphaInt);
-        mActionsView.getContentAlpha().setValue(mContentAlpha);
+        mActionsView.getContentAlphaSetter().accept(mContentAlpha);
 
         if (alpha > 0) {
             setVisibility(VISIBLE);
diff --git a/quickstep/tests/src/com/android/quickstep/TaplPrivateSpaceTest.java b/quickstep/tests/src/com/android/quickstep/TaplPrivateSpaceTest.java
index c64ac23..50f74c2 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplPrivateSpaceTest.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplPrivateSpaceTest.java
@@ -35,6 +35,7 @@
 import com.android.launcher3.util.rule.ScreenRecordRule;
 
 import org.junit.After;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -167,6 +168,7 @@
 
     @Test
     @ScreenRecordRule.ScreenRecord // b/334946529
+    @Ignore("b/339179262")
     public void testPrivateSpaceLockingBehaviour() throws IOException {
         // Scroll to the bottom of All Apps
         executeOnLauncher(launcher -> launcher.getAppsView().resetAndScrollToPrivateSpaceHeader());
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java b/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
index 2a54057..bfd7bdb 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
@@ -130,7 +130,7 @@
                             .tapMenu()
                             .hasMenuItem("Save app pair"));
         } else {
-            overview.getOverviewActions().assertHasAction("Save app pair");
+            overview.getOverviewGroupActions().assertHasAction("Save app pair");
         }
     }
 
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index 28fb119..8b69318 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -44,7 +44,7 @@
     <string name="add_to_home_screen" msgid="9168649446635919791">"Dodaj na početni ekran"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Dodali ste vidžet <xliff:g id="WIDGET_NAME">%1$s</xliff:g> na početni ekran"</string>
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Predlozi"</string>
-    <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Neophodne aplikacije"</string>
+    <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Osnovno"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Novosti i časopisi"</string>
     <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Zona za opuštanje"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Zabava"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index b05002d..736eaab 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -44,7 +44,7 @@
     <string name="add_to_home_screen" msgid="9168649446635919791">"Přidat na plochu"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> byl přidán na plochu"</string>
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Návrhy"</string>
-    <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Základní"</string>
+    <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Nejdůležitější aplikace"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Zprávy a časopisy"</string>
     <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Vaše klidová zóna"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Zábava"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 4aaa44b..623b183 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -48,7 +48,7 @@
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Noticias y revistas"</string>
     <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Zona de descanso"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Entretenimiento"</string>
-    <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Social"</string>
+    <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Redes sociales"</string>
     <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Salud y bienestar"</string>
     <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"Clima"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Sugerencias para ti"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index c91e510..4df953a 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -48,7 +48,7 @@
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Noticias y revistas"</string>
     <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Tu zona de descanso"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Entretenimiento"</string>
-    <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Social"</string>
+    <string name="social_widget_recommendation_category_label" msgid="689147679536384717">"Redes sociales"</string>
     <string name="fitness_widget_recommendation_category_label" msgid="2756483898236585324">"Salud y actividad física"</string>
     <string name="weather_widget_recommendation_category_label" msgid="3059715991930798039">"El tiempo"</string>
     <string name="others_widget_recommendation_category_label" msgid="5555987036267226245">"Sugerencias para ti"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 4f6f121..4c72f00 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -44,7 +44,7 @@
     <string name="add_to_home_screen" msgid="9168649446635919791">"Ajouter à l\'écran d\'accueil"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> ajouté à l\'écran d\'accueil"</string>
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Suggestions"</string>
-    <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Les bases"</string>
+    <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Indispensables"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Actualités et magazines"</string>
     <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Votre espace détente"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Divertissement"</string>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index b6af6b3..da6a711 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -44,7 +44,7 @@
     <string name="add_to_home_screen" msgid="9168649446635919791">"Dodaj do ekranu głównego"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widżet <xliff:g id="WIDGET_NAME">%1$s</xliff:g> został dodany do ekranu głównego"</string>
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Sugestie"</string>
-    <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Niezbędne"</string>
+    <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Najbardziej przydatne"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Wiadomości i czasopisma"</string>
     <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Strefa relaksu"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Rozrywka"</string>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index bb82b67..9eaae37 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -44,7 +44,7 @@
     <string name="add_to_home_screen" msgid="9168649446635919791">"Додај на почетни екран"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Додали сте виџет <xliff:g id="WIDGET_NAME">%1$s</xliff:g> на почетни екран"</string>
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"Предлози"</string>
-    <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Неопходне апликације"</string>
+    <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"Основно"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"Новости и часописи"</string>
     <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"Зона за опуштање"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"Забава"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 2975e97..a18aab6 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -44,7 +44,7 @@
     <string name="add_to_home_screen" msgid="9168649446635919791">"添加到主屏幕"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"已将“<xliff:g id="WIDGET_NAME">%1$s</xliff:g>”微件添加到主屏幕"</string>
     <string name="suggested_widgets_header_title" msgid="1844314680798145222">"建议"</string>
-    <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"必备"</string>
+    <string name="productivity_widget_recommendation_category_label" msgid="3811812719618323750">"必备之选"</string>
     <string name="news_widget_recommendation_category_label" msgid="6756167867113741310">"新闻与杂志"</string>
     <string name="social_and_entertainment_widget_recommendation_category_label" msgid="2923840997302308191">"您的休闲区"</string>
     <string name="entertainment_widget_recommendation_category_label" msgid="3973107268630717874">"娱乐"</string>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index c971223..c3d4273 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -485,7 +485,7 @@
     <!-- Description for Private Space Transition button -->
     <string name="ps_container_transition">Private Space Transitioning</string>
     <!-- Title for Private Space install app icon -->
-    <string name="ps_add_button_label">Install apps</string>
+    <string name="ps_add_button_label">Install</string>
     <!-- Content description for install app icon -->
     <string name="ps_add_button_content_description">Install apps to Private Space</string>
 
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 03de334..869b995 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -206,7 +206,7 @@
     }
 
     private void refreshAndReloadLauncher() {
-        LauncherIcons.clearPool();
+        LauncherIcons.clearPool(mContext);
         mIconCache.updateIconParams(
                 mInvariantDeviceProfile.fillResIconDpi, mInvariantDeviceProfile.iconBitmapSize);
         mModel.forceReload();
diff --git a/src/com/android/launcher3/allapps/PrivateProfileManager.java b/src/com/android/launcher3/allapps/PrivateProfileManager.java
index ae0e80c..53f9cfc 100644
--- a/src/com/android/launcher3/allapps/PrivateProfileManager.java
+++ b/src/com/android/launcher3/allapps/PrivateProfileManager.java
@@ -20,7 +20,6 @@
 import static android.view.View.INVISIBLE;
 import static android.view.View.VISIBLE;
 
-import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
 import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_PRIVATESPACE;
 import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.MAIN;
 import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_ICON;
@@ -28,8 +27,12 @@
 import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_PRIVATE_SPACE_SYS_APPS_DIVIDER;
 import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_NOTHING;
 import static com.android.launcher3.anim.AnimatorListeners.forEndCallback;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_LOCK_ANIMATION_BEGIN;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_LOCK_ANIMATION_END;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_LOCK_TAP;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_SETTINGS_TAP;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_UNLOCK_ANIMATION_BEGIN;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_UNLOCK_ANIMATION_END;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_UNLOCK_TAP;
 import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED;
 import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_NOT_PINNABLE;
@@ -635,8 +638,6 @@
             return;
         }
         ViewGroup settingsAndLockGroup = mPSHeader.findViewById(R.id.settingsAndLockGroup);
-        ViewGroup lockButton = mPSHeader.findViewById(R.id.ps_lock_unlock_button);
-        TextView lockText = lockButton.findViewById(R.id.lock_text);
         if (settingsAndLockGroup.getLayoutTransition() == null) {
             // Set a new transition if the current ViewGroup does not already contain one as each
             // transition should only happen once when applied.
@@ -646,21 +647,30 @@
                 LayoutTransition.CHANGING,
                 expand ? SETTINGS_AND_LOCK_GROUP_TRANSITION_DELAY : NO_DELAY);
         PropertySetter headerSetter = new AnimatedPropertySetter();
-        ImageButton settingsButton = mPSHeader.findViewById(R.id.ps_settings_button);
-        updateSettingsGearAlpha(settingsButton, expand, headerSetter);
-        updateLockTextAlpha(lockText, expand, headerSetter);
+        headerSetter.add(updateSettingsGearAlpha(expand));
+        headerSetter.add(updateLockTextAlpha(expand));
         AnimatorSet animatorSet = headerSetter.buildAnim();
         animatorSet.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationStart(Animator animation) {
+                mStatsLogManager.logger().sendToInteractionJankMonitor(
+                        expand
+                                ? LAUNCHER_PRIVATE_SPACE_UNLOCK_ANIMATION_BEGIN
+                                : LAUNCHER_PRIVATE_SPACE_LOCK_ANIMATION_BEGIN,
+                        mAllApps.getActiveRecyclerView());
                 // Animate the collapsing of the text at the same time while updating lock button.
-                lockText.setVisibility(expand ? VISIBLE : GONE);
+                mPSHeader.findViewById(R.id.lock_text).setVisibility(expand ? VISIBLE : GONE);
                 setAnimationRunning(true);
             }
         });
         animatorSet.addListener(forEndCallback(() -> {
             setAnimationRunning(false);
             getMainRecyclerView().setChildAttachedConsumer(child -> child.setAlpha(1));
+            mStatsLogManager.logger().sendToInteractionJankMonitor(
+                    expand
+                            ? LAUNCHER_PRIVATE_SPACE_UNLOCK_ANIMATION_END
+                            : LAUNCHER_PRIVATE_SPACE_LOCK_ANIMATION_END,
+                    mAllApps.getActiveRecyclerView());
             if (!expand) {
                 // Call onAppsUpdated() because it may be canceled when this animation occurs.
                 mAllApps.getPersonalAppList().onAppsUpdated();
@@ -728,19 +738,44 @@
     }
 
     /** Change the settings gear alpha when expanded or collapsed. */
-    private void updateSettingsGearAlpha(ImageButton settingsButton, boolean expand,
-            PropertySetter setter) {
-        float toAlpha = expand ? 1 : 0;
-        setter.setFloat(settingsButton, VIEW_ALPHA, toAlpha, Interpolators.LINEAR)
-                .setDuration(SETTINGS_OPACITY_DURATION).setStartDelay(expand ?
-                        SETTINGS_OPACITY_DELAY : NO_DELAY);
+    private ValueAnimator updateSettingsGearAlpha(boolean expand) {
+        if (mPSHeader == null) {
+            return new ValueAnimator();
+        }
+        float from = expand ? 0 : 1;
+        float to = expand ? 1 : 0;
+        ValueAnimator settingsAlphaAnim = ObjectAnimator.ofFloat(from, to);
+        settingsAlphaAnim.setDuration(SETTINGS_OPACITY_DURATION);
+        settingsAlphaAnim.setStartDelay(expand ? SETTINGS_OPACITY_DELAY : NO_DELAY);
+        settingsAlphaAnim.setInterpolator(Interpolators.LINEAR);
+        settingsAlphaAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator valueAnimator) {
+                mPSHeader.findViewById(R.id.ps_settings_button)
+                        .setAlpha((float) valueAnimator.getAnimatedValue());
+            }
+        });
+        return settingsAlphaAnim;
     }
 
-    private void updateLockTextAlpha(TextView textView, boolean expand, PropertySetter setter) {
-        float toAlpha = expand ? 1 : 0;
-        setter.setFloat(textView, VIEW_ALPHA, toAlpha, Interpolators.LINEAR)
-                .setDuration(expand ? TEXT_UNLOCK_OPACITY_DURATION : TEXT_LOCK_OPACITY_DURATION)
-                .setStartDelay(expand ? LOCK_TEXT_OPACITY_DELAY : NO_DELAY);
+    private ValueAnimator updateLockTextAlpha(boolean expand) {
+        if (mPSHeader == null) {
+            return new ValueAnimator();
+        }
+        float from = expand ? 0 : 1;
+        float to = expand ? 1 : 0;
+        ValueAnimator alphaAnim = ObjectAnimator.ofFloat(from, to);
+        alphaAnim.setDuration(expand ? TEXT_UNLOCK_OPACITY_DURATION : TEXT_LOCK_OPACITY_DURATION);
+        alphaAnim.setStartDelay(expand ? LOCK_TEXT_OPACITY_DELAY : NO_DELAY);
+        alphaAnim.setInterpolator(Interpolators.LINEAR);
+        alphaAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator valueAnimator) {
+                mPSHeader.findViewById(R.id.lock_text).setAlpha(
+                        (float) valueAnimator.getAnimatedValue());
+            }
+        });
+        return alphaAnim;
     }
 
     void expandPrivateSpace() {
diff --git a/src/com/android/launcher3/allapps/UserProfileManager.java b/src/com/android/launcher3/allapps/UserProfileManager.java
index 6a1f37a..3351ee3 100644
--- a/src/com/android/launcher3/allapps/UserProfileManager.java
+++ b/src/com/android/launcher3/allapps/UserProfileManager.java
@@ -52,11 +52,11 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface UserProfileState { }
 
+    protected final StatsLogManager mStatsLogManager;
     @UserProfileState
     private int mCurrentState;
 
     private final UserManager mUserManager;
-    private final StatsLogManager mStatsLogManager;
     private final UserCache mUserCache;
 
     protected UserProfileManager(UserManager userManager,
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index 1f388c2..ae8f1d5 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -76,7 +76,6 @@
 import com.android.launcher3.celllayout.CellPosMapper;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.folder.FolderIcon;
-import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.model.BgDataModel;
 import com.android.launcher3.model.BgDataModel.FixedContainerItems;
 import com.android.launcher3.model.WidgetItem;
@@ -107,7 +106,6 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.concurrent.ConcurrentLinkedQueue;
 
 /**
  * Utility class for generating the preview of Launcher for a given InvariantDeviceProfile.
@@ -126,44 +124,12 @@
      */
     public static class PreviewContext extends SandboxContext {
 
-        private final InvariantDeviceProfile mIdp;
-        private final ConcurrentLinkedQueue<LauncherIconsForPreview> mIconPool =
-                new ConcurrentLinkedQueue<>();
-
         public PreviewContext(Context base, InvariantDeviceProfile idp) {
             super(base);
-            mIdp = idp;
             putObject(InvariantDeviceProfile.INSTANCE, idp);
             putObject(LauncherAppState.INSTANCE,
                     new LauncherAppState(this, null /* iconCacheFileName */));
         }
-
-        /**
-         * Creates a new LauncherIcons for the preview, skipping the global pool
-         */
-        public LauncherIcons newLauncherIcons(Context context) {
-            LauncherIconsForPreview launcherIconsForPreview = mIconPool.poll();
-            if (launcherIconsForPreview != null) {
-                return launcherIconsForPreview;
-            }
-            return new LauncherIconsForPreview(context, mIdp.fillResIconDpi, mIdp.iconBitmapSize,
-                    -1 /* poolId */);
-        }
-
-        private final class LauncherIconsForPreview extends LauncherIcons {
-
-            private LauncherIconsForPreview(Context context, int fillResIconDpi, int iconBitmapSize,
-                    int poolId) {
-                super(context, fillResIconDpi, iconBitmapSize, poolId);
-            }
-
-            @Override
-            public void recycle() {
-                // Clear any temporary state variables
-                clear();
-                mIconPool.offer(this);
-            }
-        }
     }
 
     private final List<OnDeviceProfileChangeListener> mDpChangeListeners = new ArrayList<>();
diff --git a/src/com/android/launcher3/icons/LauncherIcons.java b/src/com/android/launcher3/icons/LauncherIcons.java
index 7331c6f..e90a1e0 100644
--- a/src/com/android/launcher3/icons/LauncherIcons.java
+++ b/src/com/android/launcher3/icons/LauncherIcons.java
@@ -25,79 +25,53 @@
 import com.android.launcher3.Flags;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.graphics.IconShape;
-import com.android.launcher3.graphics.LauncherPreviewRenderer;
 import com.android.launcher3.pm.UserCache;
+import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.launcher3.util.SafeCloseable;
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.UserIconInfo;
 
+import java.util.concurrent.ConcurrentLinkedQueue;
+
 /**
  * Wrapper class to provide access to {@link BaseIconFactory} and also to provide pool of this class
  * that are threadsafe.
  */
 public class LauncherIcons extends BaseIconFactory implements AutoCloseable {
 
-    private static final Object sPoolSync = new Object();
-    private static LauncherIcons sPool;
-    private static int sPoolId = 0;
+    private static final MainThreadInitializedObject<Pool> POOL =
+            new MainThreadInitializedObject<>(Pool::new);
 
     /**
      * Return a new Message instance from the global pool. Allows us to
      * avoid allocating new objects in many cases.
      */
     public static LauncherIcons obtain(Context context) {
-        if (context instanceof LauncherPreviewRenderer.PreviewContext) {
-            return ((LauncherPreviewRenderer.PreviewContext) context).newLauncherIcons(context);
-        }
-
-        int poolId;
-        synchronized (sPoolSync) {
-            if (sPool != null) {
-                LauncherIcons m = sPool;
-                sPool = m.next;
-                m.next = null;
-                return m;
-            }
-            poolId = sPoolId;
-        }
-
-        InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(context);
-        return new LauncherIcons(context, idp.fillResIconDpi, idp.iconBitmapSize, poolId);
+        return POOL.get(context).obtain();
     }
 
-    public static void clearPool() {
-        synchronized (sPoolSync) {
-            sPool = null;
-            sPoolId++;
-        }
+    public static void clearPool(Context context) {
+        POOL.get(context).close();
     }
 
-    private final int mPoolId;
-
-    private LauncherIcons next;
+    private final ConcurrentLinkedQueue<LauncherIcons> mPool;
 
     private MonochromeIconFactory mMonochromeIconFactory;
 
-    protected LauncherIcons(Context context, int fillResIconDpi, int iconBitmapSize, int poolId) {
+    protected LauncherIcons(Context context, int fillResIconDpi, int iconBitmapSize,
+            ConcurrentLinkedQueue<LauncherIcons> pool) {
         super(context, fillResIconDpi, iconBitmapSize,
                 IconShape.INSTANCE.get(context).getShape().enableShapeDetection());
         mMonoIconEnabled = Themes.isThemedIconEnabled(context);
-        mPoolId = poolId;
+        mPool = pool;
     }
 
     /**
      * Recycles a LauncherIcons that may be in-use.
      */
     public void recycle() {
-        synchronized (sPoolSync) {
-            if (sPoolId != mPoolId) {
-                return;
-            }
-            // Clear any temporary state variables
-            clear();
-
-            next = sPool;
-            sPool = this;
-        }
+        clear();
+        mPool.add(this);
     }
 
     @Override
@@ -122,4 +96,33 @@
     public void close() {
         recycle();
     }
+
+    private static class Pool implements SafeCloseable {
+
+        private final Context mContext;
+
+        @NonNull
+        private ConcurrentLinkedQueue<LauncherIcons> mPool = new ConcurrentLinkedQueue<>();
+
+        private Pool(Context context) {
+            mContext = context;
+        }
+
+        public LauncherIcons obtain() {
+            ConcurrentLinkedQueue<LauncherIcons> pool = mPool;
+            LauncherIcons m = pool.poll();
+
+            if (m == null) {
+                InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(mContext);
+                return new LauncherIcons(mContext, idp.fillResIconDpi, idp.iconBitmapSize, pool);
+            } else {
+                return m;
+            }
+        }
+
+        @Override
+        public void close() {
+            mPool = new ConcurrentLinkedQueue<>();
+        }
+    }
 }
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 2f3c2b6..25eeacb 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -769,7 +769,19 @@
         LAUNCHER_PRIVATE_SPACE_USER_INSTALLED_APPS_COUNT(1672),
 
         @UiEvent(doc = "Number of preinstalled Private profile apps, shown under separator line")
-        LAUNCHER_PRIVATE_SPACE_PREINSTALLED_APPS_COUNT(1673)
+        LAUNCHER_PRIVATE_SPACE_PREINSTALLED_APPS_COUNT(1673),
+
+        @UiEvent(doc = "Private space lock animation started")
+        LAUNCHER_PRIVATE_SPACE_LOCK_ANIMATION_BEGIN(1725),
+
+        @UiEvent(doc = "Private space lock animation finished")
+        LAUNCHER_PRIVATE_SPACE_LOCK_ANIMATION_END(1726),
+
+        @UiEvent(doc = "Private space unlock animation started")
+        LAUNCHER_PRIVATE_SPACE_UNLOCK_ANIMATION_BEGIN(1727),
+
+        @UiEvent(doc = "Private space unlock animation finished")
+        LAUNCHER_PRIVATE_SPACE_UNLOCK_ANIMATION_END(1728)
 
         // ADD MORE
         ;
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index 8892a18..eabacbf 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -166,6 +166,13 @@
         }
     }
 
+    @Override
+    protected float getShiftRange() {
+        // We add the extra height added during predictive back / swipe up to the shift range, so
+        // that the idle interpolator knows to animate the view off fully.
+        return mContent.getHeight() + getBottomOffsetPx();
+    }
+
     /**
      * Click handler for tap to add button.
      */
diff --git a/tests/Android.bp b/tests/Android.bp
index 5ec2263..11177de 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -105,6 +105,7 @@
 android_library {
     name: "Launcher3TestResources",
     resource_dirs: ["res"],
+    asset_dirs: ["assets"],
     // TODO(b/319712088): re-enable use_resource_processor
     use_resource_processor: false,
 }
diff --git a/tests/src/com/android/launcher3/celllayout/CellLayoutTestCaseReader.java b/tests/multivalentTests/src/com/android/launcher3/celllayout/CellLayoutTestCaseReader.java
similarity index 100%
rename from tests/src/com/android/launcher3/celllayout/CellLayoutTestCaseReader.java
rename to tests/multivalentTests/src/com/android/launcher3/celllayout/CellLayoutTestCaseReader.java
diff --git a/tests/src/com/android/launcher3/celllayout/CellLayoutTestUtils.java b/tests/multivalentTests/src/com/android/launcher3/celllayout/CellLayoutTestUtils.java
similarity index 100%
rename from tests/src/com/android/launcher3/celllayout/CellLayoutTestUtils.java
rename to tests/multivalentTests/src/com/android/launcher3/celllayout/CellLayoutTestUtils.java
diff --git a/tests/src/com/android/launcher3/celllayout/HotseatReorderUnitTest.kt b/tests/multivalentTests/src/com/android/launcher3/celllayout/HotseatReorderUnitTest.kt
similarity index 97%
rename from tests/src/com/android/launcher3/celllayout/HotseatReorderUnitTest.kt
rename to tests/multivalentTests/src/com/android/launcher3/celllayout/HotseatReorderUnitTest.kt
index 13dfd5e..c32461e 100644
--- a/tests/src/com/android/launcher3/celllayout/HotseatReorderUnitTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/celllayout/HotseatReorderUnitTest.kt
@@ -22,6 +22,8 @@
 import android.view.View
 import androidx.core.view.get
 import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
 import com.android.launcher3.CellLayout
 import com.android.launcher3.celllayout.board.CellLayoutBoard
 import com.android.launcher3.celllayout.board.IconPoint
@@ -34,6 +36,7 @@
 import org.junit.Assert
 import org.junit.Rule
 import org.junit.Test
+import org.junit.runner.RunWith
 
 private class HotseatReorderTestCase(
     val startBoard: CellLayoutBoard,
@@ -44,6 +47,8 @@
     }
 }
 
+@SmallTest
+@RunWith(AndroidJUnit4::class)
 class HotseatReorderUnitTest {
 
     private val applicationContext: Context =
diff --git a/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java b/tests/multivalentTests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java
similarity index 100%
rename from tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java
rename to tests/multivalentTests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTest.java
diff --git a/tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTestCase.java b/tests/multivalentTests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTestCase.java
similarity index 100%
rename from tests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTestCase.java
rename to tests/multivalentTests/src/com/android/launcher3/celllayout/ReorderAlgorithmUnitTestCase.java
diff --git a/tests/src/com/android/launcher3/celllayout/ReorderPreviewAnimationTest.kt b/tests/multivalentTests/src/com/android/launcher3/celllayout/ReorderPreviewAnimationTest.kt
similarity index 95%
rename from tests/src/com/android/launcher3/celllayout/ReorderPreviewAnimationTest.kt
rename to tests/multivalentTests/src/com/android/launcher3/celllayout/ReorderPreviewAnimationTest.kt
index 0bec1b2..a9355ec 100644
--- a/tests/src/com/android/launcher3/celllayout/ReorderPreviewAnimationTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/celllayout/ReorderPreviewAnimationTest.kt
@@ -141,11 +141,14 @@
             ReorderPreviewAnimation.MODE_PREVIEW,
             AnimationValues(dx = 0, dy = 0, scale = 100)
         )
-        testAnimationAtGivenProgress(
-            PREVIEW_DURATION * 99,
-            ReorderPreviewAnimation.MODE_PREVIEW,
-            AnimationValues(dx = 5, dy = -10, scale = 96)
-        )
+        // (b/339313407) Temporarily disable this test as the behavior is
+        // inconsistent between Soong & Gradle builds.
+        //
+        // testAnimationAtGivenProgress(
+        //     PREVIEW_DURATION * 99,
+        //     ReorderPreviewAnimation.MODE_PREVIEW,
+        //     AnimationValues(dx = 5, dy = -10, scale = 96)
+        // )
         testAnimationAtGivenProgress(
             PREVIEW_DURATION * 98,
             ReorderPreviewAnimation.MODE_PREVIEW,
diff --git a/tests/src/com/android/launcher3/celllayout/ReorderTestCase.java b/tests/multivalentTests/src/com/android/launcher3/celllayout/ReorderTestCase.java
similarity index 100%
rename from tests/src/com/android/launcher3/celllayout/ReorderTestCase.java
rename to tests/multivalentTests/src/com/android/launcher3/celllayout/ReorderTestCase.java
diff --git a/tests/src/com/android/launcher3/celllayout/UnitTestCellLayoutBuilderRule.kt b/tests/multivalentTests/src/com/android/launcher3/celllayout/UnitTestCellLayoutBuilderRule.kt
similarity index 100%
rename from tests/src/com/android/launcher3/celllayout/UnitTestCellLayoutBuilderRule.kt
rename to tests/multivalentTests/src/com/android/launcher3/celllayout/UnitTestCellLayoutBuilderRule.kt
diff --git a/tests/src/com/android/launcher3/celllayout/testgenerator/DeterministicRandomGenerator.kt b/tests/multivalentTests/src/com/android/launcher3/celllayout/testgenerator/DeterministicRandomGenerator.kt
similarity index 100%
rename from tests/src/com/android/launcher3/celllayout/testgenerator/DeterministicRandomGenerator.kt
rename to tests/multivalentTests/src/com/android/launcher3/celllayout/testgenerator/DeterministicRandomGenerator.kt
diff --git a/tests/src/com/android/launcher3/celllayout/testgenerator/RandomBoardGenerator.kt b/tests/multivalentTests/src/com/android/launcher3/celllayout/testgenerator/RandomBoardGenerator.kt
similarity index 100%
rename from tests/src/com/android/launcher3/celllayout/testgenerator/RandomBoardGenerator.kt
rename to tests/multivalentTests/src/com/android/launcher3/celllayout/testgenerator/RandomBoardGenerator.kt
diff --git a/tests/src/com/android/launcher3/celllayout/testgenerator/RandomMultiBoardGenerator.kt b/tests/multivalentTests/src/com/android/launcher3/celllayout/testgenerator/RandomMultiBoardGenerator.kt
similarity index 100%
rename from tests/src/com/android/launcher3/celllayout/testgenerator/RandomMultiBoardGenerator.kt
rename to tests/multivalentTests/src/com/android/launcher3/celllayout/testgenerator/RandomMultiBoardGenerator.kt
diff --git a/tests/src/com/android/launcher3/popup/SystemShortcutTest.java b/tests/src/com/android/launcher3/popup/SystemShortcutTest.java
index e459956..89a4544 100644
--- a/tests/src/com/android/launcher3/popup/SystemShortcutTest.java
+++ b/tests/src/com/android/launcher3/popup/SystemShortcutTest.java
@@ -244,7 +244,6 @@
         SystemShortcut systemShortcut = SystemShortcut.PRIVATE_PROFILE_INSTALL
                 .getShortcut(mTestContext, mAppInfo, mView);
 
-        verify(mPrivateProfileManager, times(2)).getProfileUser();
         assertNull(systemShortcut);
     }
 
@@ -266,7 +265,6 @@
         SystemShortcut systemShortcut = SystemShortcut.PRIVATE_PROFILE_INSTALL
                 .getShortcut(mTestContext, mAppInfo, mView);
 
-        verify(mPrivateProfileManager, times(3)).getProfileUser();
         verify(mPrivateProfileManager).isEnabled();
         assertNotNull(systemShortcut);
     }
diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
index 68829e0..2e3944d 100644
--- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
+++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
@@ -359,6 +359,21 @@
     }
 
     /**
+     * Gets Overview Actions specific to grouped tasks.
+     *
+     * @return The Overview group actions bar
+     */
+    @NonNull
+    public OverviewActions getOverviewGroupActions() {
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "want to get overview group actions")) {
+            verifyActiveContainer();
+            UiObject2 groupActions = mLauncher.waitForOverviewObject("group_action_buttons");
+            return new OverviewActions(groupActions, mLauncher);
+        }
+    }
+
+    /**
      * Returns if clear all button is visible.
      */
     public boolean isClearAllVisible() {
@@ -449,10 +464,18 @@
     private void verifyActionsViewVisibility() {
         try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
                 "want to assert overview actions view visibility")) {
+            boolean isTablet = mLauncher.isTablet();
+            OverviewTask task = isTablet ? getFocusedTaskForTablet() : getCurrentTask();
+
             if (isActionsViewVisible()) {
-                mLauncher.waitForOverviewObject("action_buttons");
+                if (task.isTaskSplit()) {
+                    mLauncher.waitForOverviewObject("group_action_buttons");
+                } else {
+                    mLauncher.waitForOverviewObject("action_buttons");
+                }
             } else {
                 mLauncher.waitUntilOverviewObjectGone("action_buttons");
+                mLauncher.waitUntilOverviewObjectGone("group_action_buttons");
             }
         }
     }
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index aa8d339..68b0a36 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -108,7 +108,7 @@
 public final class LauncherInstrumentation {
 
     private static final String TAG = "Tapl";
-    private static final int ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME = 15;
+    private static final int ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME = 5;
     private static final int GESTURE_STEP_MS = 16;
 
     static final Pattern EVENT_PILFER_POINTERS = Pattern.compile("pilferPointers");
diff --git a/tests/tapl/com/android/launcher3/tapl/PrivateSpaceContainer.java b/tests/tapl/com/android/launcher3/tapl/PrivateSpaceContainer.java
index c21bae3..a2814f0 100644
--- a/tests/tapl/com/android/launcher3/tapl/PrivateSpaceContainer.java
+++ b/tests/tapl/com/android/launcher3/tapl/PrivateSpaceContainer.java
@@ -25,7 +25,7 @@
  */
 public class PrivateSpaceContainer {
     private static final String PS_HEADER_RES_ID = "ps_header_layout";
-    private static final String INSTALL_APP_TITLE = "Install apps";
+    private static final String INSTALL_APP_TITLE = "Install";
     private static final String DIVIDER_RES_ID = "private_space_divider";
 
     private final LauncherInstrumentation mLauncher;