Merge "Revert "Sending broadcast message when home screen is ready.""
diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml
index 3fd7375..c581415 100644
--- a/AndroidManifest-common.xml
+++ b/AndroidManifest-common.xml
@@ -50,13 +50,11 @@
     -->
     <permission
         android:name="${packageName}.permission.READ_SETTINGS"
-        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
         android:protectionLevel="signatureOrSystem"
         android:label="@string/permlab_read_settings"
         android:description="@string/permdesc_read_settings"/>
     <permission
         android:name="${packageName}.permission.WRITE_SETTINGS"
-        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
         android:protectionLevel="signatureOrSystem"
         android:label="@string/permlab_write_settings"
         android:description="@string/permdesc_write_settings"/>
diff --git a/quickstep/res/drawable/taskbar_icon_click_feedback_roundrect.xml b/quickstep/res/drawable/taskbar_icon_click_feedback_roundrect.xml
index d6160de..534f241 100644
--- a/quickstep/res/drawable/taskbar_icon_click_feedback_roundrect.xml
+++ b/quickstep/res/drawable/taskbar_icon_click_feedback_roundrect.xml
@@ -16,7 +16,7 @@
   -->
 
 <ripple xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@color/taskbar_icon_selection_ripple">
+    android:color="@color/taskbar_nav_icon_selection_ripple">
     <item android:id="@android:id/mask">
         <shape android:shape="rectangle">
             <solid android:color="@android:color/white" />
diff --git a/quickstep/res/layout/taskbar_nav_button.xml b/quickstep/res/layout/taskbar_nav_button.xml
index 4ffb8d8..aea4885 100644
--- a/quickstep/res/layout/taskbar_nav_button.xml
+++ b/quickstep/res/layout/taskbar_nav_button.xml
@@ -15,7 +15,10 @@
 -->
 <ImageView
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="@dimen/taskbar_nav_buttons_size"
     android:layout_height="@dimen/taskbar_nav_buttons_size"
     android:background="@drawable/taskbar_icon_click_feedback_roundrect"
-    android:scaleType="center"/>
\ No newline at end of file
+    android:scaleType="center"
+    android:tint="@color/taskbar_nav_icon_light_color"
+    tools:ignore="UseAppTint" />
\ No newline at end of file
diff --git a/quickstep/res/values/colors.xml b/quickstep/res/values/colors.xml
index f237d26..671a617 100644
--- a/quickstep/res/values/colors.xml
+++ b/quickstep/res/values/colors.xml
@@ -25,14 +25,14 @@
 
     <!-- Taskbar -->
     <color name="taskbar_background">@color/overview_scrim_dark</color>
-    <color name="taskbar_icon_selection_ripple">#E0E0E0</color>
-
+    <color name="taskbar_nav_icon_selection_ripple">#E0E0E0</color>
+    <color name="taskbar_nav_icon_light_color">#ffffff</color>
+    <!-- The dark navigation button color is only used in the rare cases that taskbar isn't drawing
+    its background and the underlying app has requested dark buttons. -->
+    <color name="taskbar_nav_icon_dark_color">#99000000</color>
     <color name="taskbar_stashed_handle_light_color">#EBffffff</color>
     <color name="taskbar_stashed_handle_dark_color">#99000000</color>
 
-    <color name="rotation_button_light_color">#FFF</color>
-    <color name="rotation_button_dark_color">#99000000</color>
-
     <!-- Gesture navigation tutorial -->
     <color name="gesture_tutorial_back_arrow_color">#FFFFFFFF</color>
 
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index e48c9e8..4e6b7b9 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -28,6 +28,7 @@
     <dimen name="task_menu_item_corner_radius">4dp</dimen>
     <dimen name="task_menu_spacing">2dp</dimen>
     <dimen name="task_menu_width_grid">234dp</dimen>
+    <dimen name="task_menu_horizontal_padding">8dp</dimen>
     <dimen name="overview_proactive_row_height">48dp</dimen>
     <dimen name="overview_proactive_row_bottom_margin">16dp</dimen>
 
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index 6e2d2a9..38e8e72 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -301,7 +301,8 @@
         mActionsView = findViewById(R.id.overview_actions_view);
         RecentsView overviewPanel = (RecentsView) getOverviewPanel();
         SplitSelectStateController controller =
-                new SplitSelectStateController(mHandler, SystemUiProxy.INSTANCE.get(this));
+                new SplitSelectStateController(mHandler, SystemUiProxy.INSTANCE.get(this),
+                        getStateManager(), getDepthController());
         overviewPanel.init(mActionsView, controller);
         mActionsView.setDp(getDeviceProfile());
         mActionsView.updateVerticalMargin(SysUINavigationMode.getMode(this));
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index 4b6dacd..0565f7e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -15,11 +15,8 @@
  */
 package com.android.launcher3.taskbar;
 
-import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
-
 import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
 import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_A11Y;
-import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_A11Y_LONG_CLICK;
 import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_BACK;
 import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_HOME;
 import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_IME_SWITCH;
@@ -36,11 +33,11 @@
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
 
+import android.animation.ArgbEvaluator;
 import android.animation.ObjectAnimator;
 import android.annotation.DrawableRes;
 import android.annotation.IdRes;
 import android.annotation.LayoutRes;
-import android.content.Context;
 import android.content.res.ColorStateList;
 import android.graphics.Rect;
 import android.graphics.Region;
@@ -92,9 +89,8 @@
 
     private static final int MASK_IME_SWITCHER_VISIBLE = FLAG_SWITCHER_SUPPORTED | FLAG_IME_VISIBLE;
 
-    private View.OnLongClickListener mA11yLongClickListener;
     private final ArrayList<StatePropertyHolder> mPropertyHolders = new ArrayList<>();
-    private final ArrayList<View> mAllButtons = new ArrayList<>();
+    private final ArrayList<ImageView> mAllButtons = new ArrayList<>();
     private int mState;
 
     private final TaskbarActivityContext mContext;
@@ -103,11 +99,17 @@
     // Used for IME+A11Y buttons
     private final ViewGroup mEndContextualContainer;
     private final ViewGroup mStartContextualContainer;
+    private final int mLightIconColor;
+    private final int mDarkIconColor;
 
     private final AnimatedFloat mTaskbarNavButtonTranslationY = new AnimatedFloat(
             this::updateNavButtonTranslationY);
     private final AnimatedFloat mNavButtonTranslationYMultiplier = new AnimatedFloat(
             this::updateNavButtonTranslationY);
+    private final AnimatedFloat mTaskbarNavButtonDarkIntensity = new AnimatedFloat(
+            this::updateNavButtonDarkIntensity);
+    private final AnimatedFloat mNavButtonDarkIntensityMultiplier = new AnimatedFloat(
+            this::updateNavButtonDarkIntensity);
     private final RotationButtonListener mRotationButtonListener = new RotationButtonListener();
 
     private final Rect mFloatingRotationButtonBounds = new Rect();
@@ -125,6 +127,9 @@
         mNavButtonContainer = mNavButtonsView.findViewById(R.id.end_nav_buttons);
         mEndContextualContainer = mNavButtonsView.findViewById(R.id.end_contextual_buttons);
         mStartContextualContainer = mNavButtonsView.findViewById(R.id.start_contextual_buttons);
+
+        mLightIconColor = context.getColor(R.color.taskbar_nav_icon_light_color);
+        mDarkIconColor = context.getColor(R.color.taskbar_nav_icon_dark_color);
     }
 
     /**
@@ -135,11 +140,6 @@
         mNavButtonsView.getLayoutParams().height = mContext.getDeviceProfile().taskbarSize;
         mNavButtonTranslationYMultiplier.value = 1;
 
-        mA11yLongClickListener = view -> {
-            mControllers.navButtonController.onButtonClick(BUTTON_A11Y_LONG_CLICK);
-            return true;
-        };
-
         mPropertyHolders.add(new StatePropertyHolder(
                 mControllers.taskbarViewController.getTaskbarIconAlpha()
                         .getProperty(ALPHA_INDEX_IME),
@@ -278,7 +278,6 @@
         mPropertyHolders.add(new StatePropertyHolder(mA11yButton,
                 flags -> (flags & FLAG_A11Y_VISIBLE) != 0
                         && (flags & FLAG_ROTATION_BUTTON_VISIBLE) == 0));
-        mA11yButton.setOnLongClickListener(mA11yLongClickListener);
     }
 
     private void parseSystemUiFlags(int sysUiStateFlags) {
@@ -379,6 +378,16 @@
         return mTaskbarNavButtonTranslationY;
     }
 
+    /** Use to set the dark intensity for the all nav+contextual buttons */
+    public AnimatedFloat getTaskbarNavButtonDarkIntensity() {
+        return mTaskbarNavButtonDarkIntensity;
+    }
+
+    /** Use to determine whether to use the dark intensity requested by the underlying app */
+    public AnimatedFloat getNavButtonDarkIntensityMultiplier() {
+        return mNavButtonDarkIntensityMultiplier;
+    }
+
     /**
      * Does not call {@link #applyState()}. Don't forget to!
      */
@@ -402,6 +411,16 @@
                 * mNavButtonTranslationYMultiplier.value);
     }
 
+    private void updateNavButtonDarkIntensity() {
+        float darkIntensity = mTaskbarNavButtonDarkIntensity.value
+                * mNavButtonDarkIntensityMultiplier.value;
+        int iconColor = (int) ArgbEvaluator.getInstance().evaluate(darkIntensity, mLightIconColor,
+                mDarkIconColor);
+        for (ImageView button : mAllButtons) {
+            button.setImageTintList(ColorStateList.valueOf(iconColor));
+        }
+    }
+
     private ImageView addButton(@DrawableRes int drawableId, @TaskbarButton int buttonType,
             ViewGroup parent, TaskbarNavButtonController navButtonController, @IdRes int id) {
         return addButton(drawableId, buttonType, parent, navButtonController, id,
@@ -414,6 +433,8 @@
         ImageView buttonView = addButton(parent, id, layoutId);
         buttonView.setImageResource(drawableId);
         buttonView.setOnClickListener(view -> navButtonController.onButtonClick(buttonType));
+        buttonView.setOnLongClickListener(view ->
+                navButtonController.onButtonLongClick(buttonType));
         return buttonView;
     }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 55e9887..508ca4e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -154,8 +154,8 @@
                 buttonController,
                 new NavbarButtonsViewController(this, navButtonsView),
                 new RotationButtonController(this,
-                        c.getColor(R.color.rotation_button_light_color),
-                        c.getColor(R.color.rotation_button_dark_color),
+                        c.getColor(R.color.taskbar_nav_icon_light_color),
+                        c.getColor(R.color.taskbar_nav_icon_dark_color),
                         R.drawable.ic_sysbar_rotate_button_ccw_start_0,
                         R.drawable.ic_sysbar_rotate_button_ccw_start_90,
                         R.drawable.ic_sysbar_rotate_button_cw_start_0,
@@ -408,6 +408,11 @@
         mControllers.rotationButtonController.onBehaviorChanged(displayId, behavior);
     }
 
+    public void onNavButtonsDarkIntensityChanged(float darkIntensity) {
+        mControllers.navbarButtonsViewController.getTaskbarNavButtonDarkIntensity()
+                .updateValue(darkIntensity);
+    }
+
     /**
      * Updates the TaskbarContainer to MATCH_PARENT vs original Taskbar size.
      */
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
index 81039d4..248c40d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
@@ -52,6 +52,9 @@
 
     // Initialized in init.
     private TaskbarControllers mControllers;
+    private AnimatedFloat mNavButtonDarkIntensityMultiplier;
+
+    private float mLastSetBackgroundAlpha;
 
     public TaskbarDragLayerController(TaskbarActivityContext activity,
             TaskbarDragLayer taskbarDragLayer) {
@@ -65,6 +68,9 @@
         mControllers = controllers;
         mTaskbarDragLayer.init(new TaskbarDragLayerCallbacks());
 
+        mNavButtonDarkIntensityMultiplier = mControllers.navbarButtonsViewController
+                .getNavButtonDarkIntensityMultiplier();
+
         mBgTaskbar.value = 1;
         mKeyguardBgTaskbar.value = 1;
         mNotificationShadeBgTaskbar.value = 1;
@@ -114,13 +120,22 @@
         final float bgNavbar = mBgNavbar.value;
         final float bgTaskbar = mBgTaskbar.value * mKeyguardBgTaskbar.value
                 * mNotificationShadeBgTaskbar.value;
-        mTaskbarDragLayer.setTaskbarBackgroundAlpha(
-                mBgOverride.value * Math.max(bgNavbar, bgTaskbar)
-        );
+        mLastSetBackgroundAlpha = mBgOverride.value * Math.max(bgNavbar, bgTaskbar);
+        mTaskbarDragLayer.setTaskbarBackgroundAlpha(mLastSetBackgroundAlpha);
+
+        updateNavBarDarkIntensityMultiplier();
     }
 
     private void updateBackgroundOffset() {
         mTaskbarDragLayer.setTaskbarBackgroundOffset(mBgOffset.value);
+
+        updateNavBarDarkIntensityMultiplier();
+    }
+
+    private void updateNavBarDarkIntensityMultiplier() {
+        // Zero out the app-requested dark intensity when we're drawing our own background.
+        float effectiveBgAlpha = mLastSetBackgroundAlpha * (1 - mBgOffset.value);
+        mNavButtonDarkIntensityMultiplier.updateValue(1 - effectiveBgAlpha);
     }
 
     /**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index 19fdca1..8a0e39f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -43,7 +43,6 @@
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.statemanager.StatefulActivity;
-import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.DisplayController;
 import com.android.launcher3.util.DisplayController.Info;
 import com.android.launcher3.util.SettingsCache;
@@ -273,6 +272,12 @@
         }
     }
 
+    public void onNavButtonsDarkIntensityChanged(float darkIntensity) {
+        if (mTaskbarActivityContext != null) {
+            mTaskbarActivityContext.onNavButtonsDarkIntensityChanged(darkIntensity);
+        }
+    }
+
     /**
      * Called when the manager is no longer needed
      */
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
index a8a0b59..ae23eda 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarNavButtonController.java
@@ -16,9 +16,11 @@
 
 package com.android.launcher3.taskbar;
 
-import static android.view.Display.DEFAULT_DISPLAY;
 
-import android.view.inputmethod.InputMethodManager;
+import static com.android.internal.app.AssistUtils.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS;
+import static com.android.internal.app.AssistUtils.INVOCATION_TYPE_KEY;
+
+import android.os.Bundle;
 
 import androidx.annotation.IntDef;
 
@@ -35,11 +37,9 @@
  * Controller for 3 button mode in the taskbar.
  * Handles all the functionality of the various buttons, making/routing the right calls into
  * launcher or sysui/system.
- *
- * TODO: Create callbacks to hook into UI layer since state will change for more context buttons/
- *       assistant invocation.
  */
 public class TaskbarNavButtonController {
+
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(value = {
             BUTTON_BACK,
@@ -47,7 +47,6 @@
             BUTTON_RECENTS,
             BUTTON_IME_SWITCH,
             BUTTON_A11Y,
-            BUTTON_A11Y_LONG_CLICK
     })
 
     public @interface TaskbarButton {}
@@ -57,7 +56,6 @@
     static final int BUTTON_RECENTS = BUTTON_HOME << 1;
     static final int BUTTON_IME_SWITCH = BUTTON_RECENTS << 1;
     static final int BUTTON_A11Y = BUTTON_IME_SWITCH << 1;
-    static final int BUTTON_A11Y_LONG_CLICK = BUTTON_A11Y << 1;
 
     private final TouchInteractionService mService;
 
@@ -82,9 +80,22 @@
             case BUTTON_A11Y:
                 notifyImeClick(false /* longClick */);
                 break;
-            case BUTTON_A11Y_LONG_CLICK:
+        }
+    }
+
+    public boolean onButtonLongClick(@TaskbarButton int buttonType) {
+        switch (buttonType) {
+            case BUTTON_HOME:
+                startAssistant();
+                return true;
+            case BUTTON_A11Y:
                 notifyImeClick(true /* longClick */);
-                break;
+                return true;
+            case BUTTON_BACK:
+            case BUTTON_IME_SWITCH:
+            case BUTTON_RECENTS:
+            default:
+                return false;
         }
     }
 
@@ -113,4 +124,11 @@
             systemUiProxy.notifyAccessibilityButtonClicked(mService.getDisplayId());
         }
     }
+
+    private void startAssistant() {
+        Bundle args = new Bundle();
+        args.putInt(INVOCATION_TYPE_KEY, INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS);
+        SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.getNoCreate();
+        systemUiProxy.startAssistant(args);
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index acb4aa8..60842c8 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -282,6 +282,12 @@
             // Just hide/show the icons instead of stashing into a handle.
             mAnimator.play(mIconAlphaForStash.animateToValue(isStashed ? 0 : 1)
                     .setDuration(duration));
+            mAnimator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    mAnimator = null;
+                }
+            });
             return;
         }
 
diff --git a/quickstep/src/com/android/quickstep/KtR.java b/quickstep/src/com/android/quickstep/KtR.java
index 57dad08..a768ef5 100644
--- a/quickstep/src/com/android/quickstep/KtR.java
+++ b/quickstep/src/com/android/quickstep/KtR.java
@@ -30,6 +30,7 @@
 
     public static final class dimen {
         public static int task_menu_spacing = R.dimen.task_menu_spacing;
+        public static int task_menu_horizontal_padding = R.dimen.task_menu_horizontal_padding;
     }
 
     public static final class layout {
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 09a0b7d..d6efc71 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -128,7 +128,8 @@
         SYSUI_PROGRESS.set(getRootView().getSysUiScrim(), 0f);
 
         SplitSelectStateController controller =
-                new SplitSelectStateController(mHandler, SystemUiProxy.INSTANCE.get(this));
+                new SplitSelectStateController(mHandler, SystemUiProxy.INSTANCE.get(this),
+                        getStateManager(), null /*depthController*/);
         mDragLayer.recreateControllers();
         mFallbackRecentsView.init(mActionsView, controller);
 
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index ac97dd6..e539a8c 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -24,6 +24,7 @@
 import android.app.ActivityManager;
 import android.content.ComponentCallbacks2;
 import android.content.Context;
+import android.content.Intent;
 import android.os.Build;
 import android.os.Process;
 import android.os.UserHandle;
@@ -176,7 +177,7 @@
 
     @Override
     public void onTaskRemoved(int taskId) {
-        Task.TaskKey stubKey = new Task.TaskKey(taskId, 0, null, null, 0, 0);
+        Task.TaskKey stubKey = new Task.TaskKey(taskId, 0, new Intent(), null, 0, 0);
         mThumbnailCache.remove(stubKey);
         mIconCache.onTaskRemoved(stubKey);
     }
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 4239739..68b7558 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -39,7 +39,6 @@
 import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl;
 
-import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.MainThreadInitializedObject;
 import com.android.launcher3.util.SplitConfigurationOptions;
 import com.android.systemui.shared.recents.ISystemUiProxy;
@@ -52,12 +51,12 @@
 import com.android.wm.shell.pip.IPipAnimationListener;
 import com.android.wm.shell.recents.IRecentTasks;
 import com.android.wm.shell.recents.IRecentTasksListener;
-import com.android.wm.shell.util.GroupedRecentTaskInfo;
 import com.android.wm.shell.splitscreen.ISplitScreen;
 import com.android.wm.shell.splitscreen.ISplitScreenListener;
 import com.android.wm.shell.startingsurface.IStartingWindow;
 import com.android.wm.shell.startingsurface.IStartingWindowListener;
 import com.android.wm.shell.transition.IShellTransitions;
+import com.android.wm.shell.util.GroupedRecentTaskInfo;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -469,8 +468,6 @@
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed call handleImageBundleAsScreenshot");
             }
-        } else if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.NO_SCREENSHOT, "sysuiproxy, no proxy available");
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
index c45159e..0246849 100644
--- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
@@ -209,13 +209,6 @@
         }
 
         /**
-         * Called when the current task's thumbnail has changed.
-         */
-        public void refreshActionVisibility(ThumbnailData thumbnail) {
-            getActionsView().updateDisabledFlags(DISABLED_NO_THUMBNAIL, thumbnail == null);
-        }
-
-        /**
          * End rendering live tile in Overview.
          *
          * @param callback callback to run, after switching to screenshot
diff --git a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
index eaa43cf..3175ba8 100644
--- a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
+++ b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
@@ -134,7 +134,8 @@
         Preconditions.assertUIThread();
 
         boolean lowResolution = !mHighResLoadingState.isEnabled();
-        if (task.thumbnail != null && (!task.thumbnail.reducedResolution || lowResolution)) {
+        if (task.thumbnail != null && task.thumbnail.thumbnail != null
+                && (!task.thumbnail.reducedResolution || lowResolution)) {
             // Nothing to load, the thumbnail is already high-resolution or matches what the
             // request, so just callback
             callback.accept(task.thumbnail);
@@ -152,7 +153,8 @@
         Preconditions.assertUIThread();
 
         ThumbnailData cachedThumbnail = mCache.getAndInvalidateIfModified(key);
-        if (cachedThumbnail != null && (!cachedThumbnail.reducedResolution || lowResolution)) {
+        if (cachedThumbnail != null &&  cachedThumbnail.thumbnail != null
+                && (!cachedThumbnail.reducedResolution || lowResolution)) {
             // Already cached, lets use that thumbnail
             callback.accept(cachedThumbnail);
             return null;
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index 6addfe3..e77ec78 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -56,6 +56,7 @@
 import android.window.TransitionInfo;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import com.android.launcher3.BaseActivity;
 import com.android.launcher3.DeviceProfile;
@@ -72,6 +73,7 @@
 import com.android.quickstep.util.SurfaceTransactionApplier;
 import com.android.quickstep.util.TaskViewSimulator;
 import com.android.quickstep.util.TransformParams;
+import com.android.quickstep.views.GroupedTaskView;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskThumbnailView;
 import com.android.quickstep.views.TaskView;
@@ -149,10 +151,12 @@
         return taskView;
     }
 
-    public static void createRecentsWindowAnimator(TaskView v, boolean skipViewChanges,
-            RemoteAnimationTargetCompat[] appTargets,
-            RemoteAnimationTargetCompat[] wallpaperTargets,
-            RemoteAnimationTargetCompat[] nonAppTargets, DepthController depthController,
+    public static void createRecentsWindowAnimator(
+            @NonNull TaskView v, boolean skipViewChanges,
+            @NonNull RemoteAnimationTargetCompat[] appTargets,
+            @NonNull RemoteAnimationTargetCompat[] wallpaperTargets,
+            @NonNull RemoteAnimationTargetCompat[] nonAppTargets,
+            @Nullable DepthController depthController,
             PendingAnimation out) {
         RecentsView recentsView = v.getRecentsView();
         boolean isQuickSwitch = v.isEndQuickswitchCuj();
@@ -192,7 +196,8 @@
         boolean showAsGrid = dp.overviewShowAsGrid;
         boolean parallaxCenterAndAdjacentTask =
                 taskIndex != recentsView.getCurrentPage() && !showAsGrid;
-        int startScroll = recentsView.getScrollOffset(taskIndex);
+        int taskRectTranslationPrimary = recentsView.getScrollOffset(taskIndex);
+        int taskRectTranslationSecondary = showAsGrid ? (int) v.getGridTranslationY() : 0;
 
         RemoteTargetHandle[] topMostSimulators = null;
 
@@ -209,9 +214,10 @@
 
                 tvsLocal.fullScreenProgress.value = 0;
                 tvsLocal.recentsViewScale.value = 1;
-                tvsLocal.setScroll(startScroll);
                 tvsLocal.setIsGridTask(v.isGridTask());
-                tvsLocal.setGridTranslationY(v.getGridTranslationY());
+                tvsLocal.getOrientationState().getOrientationHandler().set(tvsLocal,
+                        TaskViewSimulator::setTaskRectTranslation, taskRectTranslationPrimary,
+                        taskRectTranslationSecondary);
 
                 // Fade in the task during the initial 20% of the animation
                 out.addFloat(targetHandle.getTransformParams(), TransformParams.TARGET_ALPHA, 0, 1,
@@ -418,15 +424,46 @@
         finishCallback.run();
     }
 
-    /** Legacy version (until shell transitions are enabled) */
-    public static void composeRecentsSplitLaunchAnimatorLegacy(@NonNull Task initialTask,
+    /**
+     * Legacy version (until shell transitions are enabled)
+     *
+     * If {@param launchingTaskView} is not null, then this will play the tasks launch animation
+     * from the position of the GroupedTaskView (when user taps on the TaskView to start it).
+     * Technically this case should be taken care of by
+     * {@link #composeRecentsSplitLaunchAnimatorLegacy()} below, but the way we launch tasks whether
+     * it's a single task or multiple tasks results in different entry-points.
+     *
+     * If it is null, then it will simply fade in the starting apps and fade out launcher (for the
+     * case where launcher handles animating starting split tasks from app icon) */
+    public static void composeRecentsSplitLaunchAnimatorLegacy(
+            @Nullable GroupedTaskView launchingTaskView,
+            @NonNull Task initialTask,
             @NonNull Task secondTask, @NonNull RemoteAnimationTargetCompat[] appTargets,
             @NonNull RemoteAnimationTargetCompat[] wallpaperTargets,
             @NonNull RemoteAnimationTargetCompat[] nonAppTargets,
+            @NonNull StateManager stateManager,
+            @Nullable DepthController depthController,
             @NonNull Runnable finishCallback) {
+        if (launchingTaskView != null) {
+            AnimatorSet animatorSet = new AnimatorSet();
+            RecentsView recentsView = launchingTaskView.getRecentsView();
+            animatorSet.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    super.onAnimationEnd(animation);
+                    finishCallback.run();
+                }
+            });
+            composeRecentsLaunchAnimator(animatorSet, launchingTaskView,
+                    appTargets, wallpaperTargets, nonAppTargets,
+                    true, stateManager,
+                    recentsView, depthController);
+            animatorSet.start();
+            return;
+        }
+
         final ArrayList<SurfaceControl> openingTargets = new ArrayList<>();
         final ArrayList<SurfaceControl> closingTargets = new ArrayList<>();
-
         for (RemoteAnimationTargetCompat appTarget : appTargets) {
             final int taskId = appTarget.taskInfo != null ? appTarget.taskInfo.taskId : -1;
             final int mode = appTarget.mode;
@@ -490,7 +527,7 @@
             @NonNull RemoteAnimationTargetCompat[] wallpaperTargets,
             @NonNull RemoteAnimationTargetCompat[] nonAppTargets, boolean launcherClosing,
             @NonNull StateManager stateManager, @NonNull RecentsView recentsView,
-            @NonNull DepthController depthController) {
+            @Nullable DepthController depthController) {
         boolean skipLauncherChanges = !launcherClosing;
 
         TaskView taskView = findTaskViewToLaunch(recentsView, v, appTargets);
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 4bb666e..e1894ae 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -281,6 +281,12 @@
                     .onSystemBarAttributesChanged(displayId, behavior));
         }
 
+        @Override
+        public void onNavButtonsDarkIntensityChanged(float darkIntensity) {
+            executeForTaskbarManager(() -> mTaskbarManager
+                    .onNavButtonsDarkIntensityChanged(darkIntensity));
+        }
+
         private void executeForTaskbarManager(final Runnable r) {
             MAIN_EXECUTOR.execute(() -> {
                 if (mTaskbarManager == null) {
diff --git a/quickstep/src/com/android/quickstep/ViewUtils.java b/quickstep/src/com/android/quickstep/ViewUtils.java
index 184ab17..e290be8 100644
--- a/quickstep/src/com/android/quickstep/ViewUtils.java
+++ b/quickstep/src/com/android/quickstep/ViewUtils.java
@@ -15,8 +15,10 @@
  */
 package com.android.quickstep;
 
+import android.graphics.HardwareRenderer;
 import android.os.Handler;
 import android.view.View;
+import android.view.ViewRootImpl;
 
 import com.android.launcher3.Utilities;
 import com.android.systemui.shared.system.ViewRootImplCompat;
@@ -45,9 +47,9 @@
         return new FrameHandler(view, onFinishRunnable, canceled).schedule();
     }
 
-    private static class FrameHandler implements LongConsumer {
+    private static class FrameHandler implements HardwareRenderer.FrameDrawingCallback {
 
-        final ViewRootImplCompat mViewRoot;
+        final ViewRootImpl mViewRoot;
         final Runnable mFinishCallback;
         final BooleanSupplier mCancelled;
         final Handler mHandler;
@@ -55,14 +57,14 @@
         int mDeferFrameCount = 1;
 
         FrameHandler(View view, Runnable finishCallback, BooleanSupplier cancelled) {
-            mViewRoot = new ViewRootImplCompat(view);
+            mViewRoot = view.getViewRootImpl();
             mFinishCallback = finishCallback;
             mCancelled = cancelled;
             mHandler = new Handler();
         }
 
         @Override
-        public void accept(long l) {
+        public void onFrameDraw(long frame) {
             Utilities.postAsyncCallback(mHandler, this::onFrame);
         }
 
@@ -83,7 +85,7 @@
         }
 
         private boolean schedule() {
-            if (mViewRoot.isValid()) {
+            if (mViewRoot.getView() != null) {
                 mViewRoot.registerRtFrameCallback(this);
                 mViewRoot.getView().invalidate();
                 return true;
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/AssistantInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/AssistantInputConsumer.java
index 510820a..162ace4 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/AssistantInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/AssistantInputConsumer.java
@@ -24,6 +24,8 @@
 import static android.view.MotionEvent.ACTION_POINTER_UP;
 import static android.view.MotionEvent.ACTION_UP;
 
+import static com.android.internal.app.AssistUtils.INVOCATION_TYPE_GESTURE;
+import static com.android.internal.app.AssistUtils.INVOCATION_TYPE_KEY;
 import static com.android.launcher3.Utilities.squaredHypot;
 
 import android.animation.Animator;
@@ -64,8 +66,6 @@
     private static final String OPA_BUNDLE_TRIGGER = "triggered_by";
     // From //java/com/google/android/apps/gsa/assistant/shared/proto/opa_trigger.proto.
     private static final int OPA_BUNDLE_TRIGGER_DIAG_SWIPE_GESTURE = 83;
-    private static final String INVOCATION_TYPE_KEY = "invocation_type";
-    private static final int INVOCATION_TYPE_GESTURE = 1;
 
     private final PointF mDownPos = new PointF();
     private final PointF mLastPos = new PointF();
diff --git a/quickstep/src/com/android/quickstep/util/ImageActionUtils.java b/quickstep/src/com/android/quickstep/util/ImageActionUtils.java
index 2858cbd..51a9915 100644
--- a/quickstep/src/com/android/quickstep/util/ImageActionUtils.java
+++ b/quickstep/src/com/android/quickstep/util/ImageActionUtils.java
@@ -49,7 +49,6 @@
 
 import com.android.internal.app.ChooserActivity;
 import com.android.launcher3.BuildConfig;
-import com.android.launcher3.testing.TestProtocol;
 import com.android.quickstep.SystemUiProxy;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.utilities.BitmapUtil;
@@ -78,9 +77,6 @@
     public static void saveScreenshot(SystemUiProxy systemUiProxy, Bitmap screenshot,
             Rect screenshotBounds,
             Insets visibleInsets, Task.TaskKey task) {
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.NO_SCREENSHOT, "image action utils calling into sysuiproxy");
-        }
         systemUiProxy.handleImageBundleAsScreenshot(BitmapUtil.hardwareBitmapToBundle(screenshot),
                 screenshotBounds, visibleInsets, task);
     }
diff --git a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
index 9c6fd3d..8ccab71 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
+++ b/quickstep/src/com/android/quickstep/util/RecentsOrientedState.java
@@ -162,7 +162,7 @@
      */
     public void setDeviceProfile(DeviceProfile deviceProfile) {
         boolean oldMultipleOrientationsSupported = isMultipleOrientationSupportedByDevice();
-        setFlag(FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_DENSITY, !deviceProfile.allowRotation);
+        setFlag(FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_DENSITY, !deviceProfile.isTablet);
         if (mListenersInitialized) {
             boolean newMultipleOrientationsSupported = isMultipleOrientationSupportedByDevice();
             // If isMultipleOrientationSupportedByDevice is changed, init or destroy listeners
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index b32c4e5..c784d82 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -24,19 +24,24 @@
 import android.app.ActivityOptions;
 import android.app.ActivityThread;
 import android.graphics.Rect;
-import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.view.RemoteAnimationAdapter;
 import android.view.SurfaceControl;
 import android.window.TransitionInfo;
 
-import com.android.launcher3.Utilities;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.statehandlers.DepthController;
+import com.android.launcher3.statemanager.StateManager;
 import com.android.launcher3.util.SplitConfigurationOptions;
 import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
 import com.android.quickstep.SystemUiProxy;
 import com.android.quickstep.TaskAnimationManager;
 import com.android.quickstep.TaskViewUtils;
+import com.android.quickstep.views.GroupedTaskView;
+import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
 import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
@@ -54,23 +59,32 @@
 
     private final Handler mHandler;
     private final SystemUiProxy mSystemUiProxy;
+    private final StateManager mStateManager;
+    private final DepthController mDepthController;
     private @StagePosition int mStagePosition;
     private Task mInitialTask;
     private Task mSecondTask;
     private Rect mInitialBounds;
     private boolean mRecentsAnimationRunning;
+    /** If not null, this is the TaskView we want to launch from */
+    @Nullable
+    private GroupedTaskView mLaunchingTaskView;
 
-    public SplitSelectStateController(Handler handler, SystemUiProxy systemUiProxy) {
+    public SplitSelectStateController(Handler handler, SystemUiProxy systemUiProxy,
+            StateManager stateManager,
+            DepthController depthController) {
         mHandler = handler;
         mSystemUiProxy = systemUiProxy;
+        mStateManager = stateManager;
+        mDepthController = depthController;
     }
 
     /**
      * To be called after first task selected
      */
-    public void setInitialTaskSelect(Task taskView, @StagePosition int stagePosition,
+    public void setInitialTaskSelect(Task task, @StagePosition int stagePosition,
             Rect initialBounds) {
-        mInitialTask = taskView;
+        mInitialTask = task;
         mStagePosition = stagePosition;
         mInitialBounds = initialBounds;
     }
@@ -78,13 +92,25 @@
     /**
      * To be called after second task selected
      */
-    public void setSecondTaskId(Task taskView, Consumer<Boolean> callback) {
-        mSecondTask = taskView;
+    public void setSecondTaskId(Task task, Consumer<Boolean> callback) {
+        mSecondTask = task;
         launchTasks(mInitialTask, mSecondTask, mStagePosition, callback,
                 false /* freezeTaskList */);
     }
 
     /**
+     * To be called when we want to launch split pairs from an existing GroupedTaskView.
+     */
+    public void launchTasks(GroupedTaskView groupedTaskView,
+            Consumer<Boolean> callback, boolean freezeTaskList) {
+        mLaunchingTaskView = groupedTaskView;
+        TaskView.TaskIdAttributeContainer[] taskIdAttributeContainers =
+                groupedTaskView.getTaskIdAttributeContainers();
+        launchTasks(taskIdAttributeContainers[0].getTask(), taskIdAttributeContainers[1].getTask(),
+                taskIdAttributeContainers[0].getStagePosition(), callback, freezeTaskList);
+    }
+
+    /**
      * @param stagePosition representing location of task1
      */
     public void launchTasks(Task task1, Task task2, @StagePosition int stagePosition,
@@ -171,8 +197,9 @@
                 RemoteAnimationTargetCompat[] wallpapers, RemoteAnimationTargetCompat[] nonApps,
                 Runnable finishedCallback) {
             postAsyncCallback(mHandler,
-                    () -> TaskViewUtils.composeRecentsSplitLaunchAnimatorLegacy(mInitialTask,
-                            mSecondTask, apps, wallpapers, nonApps, () -> {
+                    () -> TaskViewUtils.composeRecentsSplitLaunchAnimatorLegacy(
+                            mLaunchingTaskView, mInitialTask, mSecondTask, apps, wallpapers,
+                            nonApps, mStateManager, mDepthController, () -> {
                                 finishedCallback.run();
                                 if (mSuccessCallback != null) {
                                     mSuccessCallback.accept(true);
@@ -203,6 +230,7 @@
         mStagePosition = SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
         mInitialBounds = null;
         mRecentsAnimationRunning = false;
+        mLaunchingTaskView = null;
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java b/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
index 3b4fd31..3b1c150 100644
--- a/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
+++ b/quickstep/src/com/android/quickstep/util/SurfaceTransactionApplier.java
@@ -22,10 +22,10 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
 import android.view.View;
+import android.view.ViewRootImpl;
 
 import com.android.quickstep.RemoteAnimationTargets.ReleaseCheck;
 import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
-import com.android.systemui.shared.system.ViewRootImplCompat;
 
 import java.util.function.Consumer;
 
@@ -41,7 +41,7 @@
     private static final int MSG_UPDATE_SEQUENCE_NUMBER = 0;
 
     private final SurfaceControl mBarrierSurfaceControl;
-    private final ViewRootImplCompat mTargetViewRootImpl;
+    private final ViewRootImpl mTargetViewRootImpl;
     private final Handler mApplyHandler;
 
     private int mLastSequenceNumber = 0;
@@ -50,8 +50,8 @@
      * @param targetView The view in the surface that acts as synchronization anchor.
      */
     public SurfaceTransactionApplier(View targetView) {
-        mTargetViewRootImpl = new ViewRootImplCompat(targetView);
-        mBarrierSurfaceControl = mTargetViewRootImpl.getRenderSurfaceControl();
+        mTargetViewRootImpl = targetView.getViewRootImpl();
+        mBarrierSurfaceControl = mTargetViewRootImpl.getSurfaceControl();
         mApplyHandler = new Handler(this::onApplyMessage);
     }
 
@@ -109,7 +109,7 @@
         if (targetView == null) {
             // No target view, no applier
             callback.accept(null);
-        } else if (new ViewRootImplCompat(targetView).isValid()) {
+        } else if (targetView.isAttachedToWindow()) {
             // Already attached, we're good to go
             callback.accept(new SurfaceTransactionApplier(targetView));
         } else {
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
index 7d396ba..f676091 100644
--- a/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -102,7 +102,8 @@
     private StagedSplitBounds mStagedSplitBounds;
     private boolean mDrawsBelowRecents;
     private boolean mIsGridTask;
-    private float mGridTranslationY;
+    private int mTaskRectTranslationX;
+    private int mTaskRectTranslationY;
 
     public TaskViewSimulator(Context context, BaseActivityInterface sizeStrategy) {
         mContext = context;
@@ -157,15 +158,11 @@
             fullTaskSize = new Rect(mTaskRect);
             mOrientationState.getOrientationHandler()
                     .setSplitTaskSwipeRect(mDp, mTaskRect, mStagedSplitBounds, mStagePosition);
-            if (mIsGridTask) {
-                mTaskRect.offset(0, (int) mGridTranslationY);
-            }
+            mTaskRect.offset(mTaskRectTranslationX, mTaskRectTranslationY);
         } else {
             fullTaskSize = mTaskRect;
         }
-        if (mIsGridTask) {
-            fullTaskSize.offset(0, (int) mGridTranslationY);
-        }
+        fullTaskSize.offset(mTaskRectTranslationX, mTaskRectTranslationY);
         return mOrientationState.getFullScreenScaleAndPivot(fullTaskSize, mDp, mPivot);
     }
 
@@ -225,10 +222,11 @@
     }
 
     /**
-     * Sets the y-translation when overview is in grid.
+     * Apply translations on TaskRect's starting location.
      */
-    public void setGridTranslationY(float gridTranslationY) {
-        mGridTranslationY = gridTranslationY;
+    public void setTaskRectTranslation(int taskRectTranslationX, int taskRectTranslationY) {
+        mTaskRectTranslationX = taskRectTranslationX;
+        mTaskRectTranslationY = taskRectTranslationY;
     }
 
     /**
@@ -336,19 +334,19 @@
 
         // Apply TaskView matrix: taskRect, translate
         mMatrix.postTranslate(mTaskRect.left, mTaskRect.top);
-        mOrientationState.getOrientationHandler().set(mMatrix, MATRIX_POST_TRANSLATE,
+        mOrientationState.getOrientationHandler().setPrimary(mMatrix, MATRIX_POST_TRANSLATE,
                 taskPrimaryTranslation.value);
         mOrientationState.getOrientationHandler().setSecondary(mMatrix, MATRIX_POST_TRANSLATE,
                 taskSecondaryTranslation.value);
+        mOrientationState.getOrientationHandler().setPrimary(
+                mMatrix, MATRIX_POST_TRANSLATE, recentsViewScroll.value);
 
         // Apply RecentsView matrix
         mMatrix.postScale(recentsViewScale.value, recentsViewScale.value, mPivot.x, mPivot.y);
         mOrientationState.getOrientationHandler().setSecondary(mMatrix, MATRIX_POST_TRANSLATE,
                 recentsViewSecondaryTranslation.value);
-        mOrientationState.getOrientationHandler().set(mMatrix, MATRIX_POST_TRANSLATE,
+        mOrientationState.getOrientationHandler().setPrimary(mMatrix, MATRIX_POST_TRANSLATE,
                 recentsViewPrimaryTranslation.value);
-        mOrientationState.getOrientationHandler().set(
-                mMatrix, MATRIX_POST_TRANSLATE, recentsViewScroll.value);
         applyWindowToHomeRotation(mMatrix);
 
         // Crop rect is the inverse of thumbnail matrix
diff --git a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
index 5a5e3da..325ec04 100644
--- a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java
@@ -71,7 +71,6 @@
         mImageView.setLayerType(LAYER_TYPE_HARDWARE, null);
         mSplitPlaceholderView = findViewById(R.id.split_placeholder);
         mSplitPlaceholderView.setAlpha(0);
-        mSplitPlaceholderView.setBackgroundColor(getResources().getColor(android.R.color.white));
     }
 
     private void init(StatefulActivity launcher, TaskView originalView, RectF positionOut) {
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
index df99d27..4771d1e 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.java
@@ -4,7 +4,6 @@
 import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
 
 import android.content.Context;
-import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 
@@ -50,8 +49,6 @@
     private final float[] mIcon2CenterCoords = new float[2];
     private TransformingTouchDelegate mIcon2TouchDelegate;
     @Nullable private StagedSplitBounds mSplitBoundsConfig;
-    private final Rect mPrimaryTempRect = new Rect();
-    private final Rect mSecondaryTempRect = new Rect();
 
     public GroupedTaskView(Context context) {
         super(context);
@@ -79,7 +76,7 @@
         mSecondaryTask = secondary;
         mTaskIdContainer[1] = secondary.key.id;
         mTaskIdAttributeContainer[1] = new TaskIdAttributeContainer(secondary, mSnapshotView2,
-                STAGE_POSITION_BOTTOM_OR_RIGHT);
+                mIconView2, STAGE_POSITION_BOTTOM_OR_RIGHT);
         mTaskIdAttributeContainer[0].setStagePosition(STAGE_POSITION_TOP_OR_LEFT);
         mSnapshotView2.bind(secondary);
         mSplitBoundsConfig = splitBoundsConfig;
@@ -120,14 +117,6 @@
         }
     }
 
-    protected boolean showTaskMenuWithContainer(IconView iconView) {
-        if (mActivity.getDeviceProfile().overviewShowAsGrid) {
-            return TaskMenuViewWithArrow.Companion.showForTask(mTaskIdAttributeContainer[0]);
-        } else {
-            return TaskMenuView.showForTask(mTaskIdAttributeContainer[0]);
-        }
-    }
-
     public void updateSplitBoundsConfig(StagedSplitBounds stagedSplitBounds) {
         mSplitBoundsConfig = stagedSplitBounds;
         invalidate();
@@ -159,8 +148,8 @@
     @Nullable
     @Override
     public RunnableList launchTaskAnimated() {
-        getRecentsView().getSplitPlaceholder().launchTasks(mTask, mSecondaryTask,
-                STAGE_POSITION_TOP_OR_LEFT, null /*callback*/,
+        getRecentsView().getSplitPlaceholder().launchTasks(this /*groupedTaskView*/,
+                null /*callback*/,
                 false /* freezeTaskList */);
         return null;
     }
@@ -239,10 +228,8 @@
         int taskIconHeight = deviceProfile.overviewTaskIconSizePx;
         boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
 
-        mSnapshotView.getBoundsOnScreen(mPrimaryTempRect);
-        mSnapshotView2.getBoundsOnScreen(mSecondaryTempRect);
         getPagedOrientationHandler().setSplitIconParams(mIconView, mIconView2,
-                taskIconHeight, mPrimaryTempRect, mSecondaryTempRect,
+                taskIconHeight, mSnapshotView.getWidth(), mSnapshotView.getHeight(),
                 isRtl, deviceProfile, mSplitBoundsConfig);
     }
 
diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
index f3b6a63..b6bf59f 100644
--- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
@@ -20,7 +20,6 @@
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.widget.FrameLayout;
@@ -31,7 +30,6 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Insettable;
 import com.android.launcher3.R;
-import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.util.MultiValueAlpha;
 import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
 import com.android.quickstep.SysUINavigationMode;
@@ -114,10 +112,6 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
         findViewById(R.id.action_screenshot).setOnClickListener(this);
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.NO_SCREENSHOT, "Inflated OverviewActionsView and added screenshot"
-                    + " listener.");
-        }
 
         mSplitButton = findViewById(R.id.action_split);
         mSplitButton.setOnClickListener(this);
@@ -129,19 +123,11 @@
      * @param callbacks for callbacks, or {@code null} to clear the listener.
      */
     public void setCallbacks(T callbacks) {
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.NO_SCREENSHOT, "OverviewActionsView setCallbacks: " + callbacks);
-        }
         mCallbacks = callbacks;
     }
 
     @Override
     public void onClick(View view) {
-        if (TestProtocol.sDebugTracing) {
-            Log.d(TestProtocol.NO_SCREENSHOT, "OverviewActionsView - onClick"
-                    + " callbacks: " + mCallbacks + "  view id: " + view.getId() + " "
-                    + " is screenshot? " + (view.getId() == R.id.action_screenshot));
-        }
         if (mCallbacks == null) {
             return;
         }
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 7b5a6b9..ddd2a82 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -747,7 +747,7 @@
 
             int primarySize = mOrientationHandler.getPrimaryValue(getWidth(), getHeight());
             int scroll = OverScroll.dampedScroll(getUndampedOverScrollShift(), primarySize);
-            mOrientationHandler.set(canvas, CANVAS_TRANSLATE, scroll);
+            mOrientationHandler.setPrimary(canvas, CANVAS_TRANSLATE, scroll);
 
             if (mOverScrollShift != scroll) {
                 mOverScrollShift = scroll;
@@ -1897,6 +1897,7 @@
             remoteTargetHandle.getTransformParams().setTargetSet(null);
             remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(false);
         });
+        resetFromSplitSelectionState();
         mSplitSelectStateController.resetState();
 
         // These are relatively expensive and don't need to be done this frame (RecentsView isn't
@@ -4356,15 +4357,22 @@
         RemoteTargetGluer gluer = new RemoteTargetGluer(getContext(), getSizeStrategy());
         mRemoteTargetHandles = gluer.assignTargetsForSplitScreen(recentsAnimationTargets);
         mSplitBoundsConfig = gluer.getStagedSplitBounds();
-        if (mSyncTransactionApplier != null) {
-            // Add release check to the targets from the RemoteTargetGluer and not the targets
-            // passed in because in the event we're in split screen, we use the passed in targets
-            // to create new RemoteAnimationTargets in assignTargetsForSplitScreen(), and the
-            // mSyncTransactionApplier doesn't get transferred over
-            runActionOnRemoteHandles(remoteTargetHandle -> remoteTargetHandle
-                    .getTransformParams().getTargetSet()
-                    .addReleaseCheck(mSyncTransactionApplier));
-        }
+        // Add release check to the targets from the RemoteTargetGluer and not the targets
+        // passed in because in the event we're in split screen, we use the passed in targets
+        // to create new RemoteAnimationTargets in assignTargetsForSplitScreen(), and the
+        // mSyncTransactionApplier doesn't get transferred over
+        runActionOnRemoteHandles(remoteTargetHandle -> {
+            final TransformParams params = remoteTargetHandle.getTransformParams();
+            if (mSyncTransactionApplier != null) {
+                params.setSyncTransactionApplier(mSyncTransactionApplier);
+                params.getTargetSet().addReleaseCheck(mSyncTransactionApplier);
+            }
+
+            TaskViewSimulator tvs = remoteTargetHandle.getTaskViewSimulator();
+            tvs.setOrientationState(mOrientationState);
+            tvs.setDp(mActivity.getDeviceProfile());
+            tvs.recentsViewScale.value = 1;
+        });
 
         TaskView runningTaskView = getRunningTaskView();
         if (runningTaskView instanceof GroupedTaskView) {
@@ -4374,12 +4382,6 @@
             // notified.
             ((GroupedTaskView) runningTaskView).updateSplitBoundsConfig(mSplitBoundsConfig);
         }
-        for (RemoteTargetHandle remoteTargetHandle : mRemoteTargetHandles) {
-            TaskViewSimulator tvs = remoteTargetHandle.getTaskViewSimulator();
-            tvs.setOrientationState(mOrientationState);
-            tvs.setDp(mActivity.getDeviceProfile());
-            tvs.recentsViewScale.value = 1;
-        }
     }
 
     /** Helper to avoid writing some for-loops to iterate over {@link #mRemoteTargetHandles} */
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt b/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt
index 5059f8b..cd1691b 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuViewWithArrow.kt
@@ -23,14 +23,18 @@
 import android.graphics.drawable.ShapeDrawable
 import android.graphics.drawable.shapes.RectShape
 import android.util.AttributeSet
+import android.view.Gravity
 import android.view.MotionEvent
 import android.view.View
 import android.view.ViewGroup
+import android.widget.FrameLayout
 import android.widget.LinearLayout
 import com.android.launcher3.BaseDraggingActivity
 import com.android.launcher3.DeviceProfile
+import com.android.launcher3.InsettableFrameLayout
 import com.android.launcher3.R
 import com.android.launcher3.popup.ArrowPopup
+import com.android.launcher3.popup.RoundedArrowDrawable
 import com.android.launcher3.popup.SystemShortcut
 import com.android.launcher3.util.Themes
 import com.android.quickstep.KtR
@@ -43,9 +47,13 @@
 
         fun showForTask(taskContainer: TaskIdAttributeContainer): Boolean {
             val activity = BaseDraggingActivity
-                    .fromContext<BaseDraggingActivity>(taskContainer.taskView.context)
+                .fromContext<BaseDraggingActivity>(taskContainer.taskView.context)
             val taskMenuViewWithArrow = activity.layoutInflater
-                    .inflate(KtR.layout.task_menu_with_arrow, activity.dragLayer, false) as TaskMenuViewWithArrow<*>
+                .inflate(
+                    KtR.layout.task_menu_with_arrow,
+                    activity.dragLayer,
+                    false
+                ) as TaskMenuViewWithArrow<*>
 
             return taskMenuViewWithArrow.populateAndShowForTask(taskContainer)
         }
@@ -53,10 +61,21 @@
 
     constructor(context: Context) : super(context)
     constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
-    constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
+    constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(
+        context,
+        attrs,
+        defStyleAttr
+    )
 
     init {
         clipToOutline = true
+
+        shouldScaleArrow = true
+        // This synchronizes the arrow and menu to open at the same time
+        OPEN_CHILD_FADE_START_DELAY = OPEN_FADE_START_DELAY
+        OPEN_CHILD_FADE_DURATION = OPEN_FADE_DURATION
+        CLOSE_FADE_START_DELAY = CLOSE_CHILD_FADE_START_DELAY
+        CLOSE_FADE_DURATION = CLOSE_CHILD_FADE_DURATION
     }
 
     private val menuWidth = context.resources.getDimensionPixelSize(R.dimen.task_menu_width_grid)
@@ -65,10 +84,17 @@
     private lateinit var optionLayout: LinearLayout
     private lateinit var taskContainer: TaskIdAttributeContainer
 
+    private var optionMeasuredHeight = 0
+    private val arrowHorizontalPadding: Int
+        get() = if (taskView.isFocusedTask)
+            resources.getDimensionPixelSize(KtR.dimen.task_menu_horizontal_padding)
+        else
+            0
+
     override fun isOfType(type: Int): Boolean = type and TYPE_TASK_MENU != 0
 
     override fun getTargetObjectLocation(outPos: Rect?) {
-        popupContainer.getDescendantRectRelativeToSelf(taskView.iconView, outPos)
+        popupContainer.getDescendantRectRelativeToSelf(taskContainer.iconView, outPos)
     }
 
     override fun onControllerInterceptTouchEvent(ev: MotionEvent?): Boolean {
@@ -147,7 +173,10 @@
     }
 
     override fun assignMarginsAndBackgrounds(viewGroup: ViewGroup) {
-        assignMarginsAndBackgrounds(this, Themes.getAttrColor(context, com.android.internal.R.attr.colorSurface))
+        assignMarginsAndBackgrounds(
+            this,
+            Themes.getAttrColor(context, com.android.internal.R.attr.colorSurface)
+        )
     }
 
     override fun onCreateOpenAnimation(anim: AnimatorSet) {
@@ -164,4 +193,90 @@
             ObjectAnimator.ofFloat(taskContainer.thumbnailView, TaskThumbnailView.DIM_ALPHA, 0f)
         )
     }
+
+    /**
+     * Orients this container to the left or right of the given icon, aligning with the first option
+     * or second.
+     *
+     * These are the preferred orientations, in order (RTL prefers right-aligned over left):
+     * - Right and first option aligned
+     * - Right and second option aligned
+     * - Left and first option aligned
+     * - Left and second option aligned
+     *
+     * So we always align right if there is enough horizontal space
+     */
+    override fun orientAboutObject() {
+        measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED)
+        // Needed for offsets later
+        optionMeasuredHeight = optionLayout.getChildAt(0).measuredHeight
+        val extraHorizontalSpace = (mArrowHeight + mArrowOffsetVertical + arrowHorizontalPadding)
+
+        val widthWithArrow = measuredWidth + paddingLeft + paddingRight + extraHorizontalSpace
+        getTargetObjectLocation(mTempRect)
+        val dragLayer: InsettableFrameLayout = popupContainer
+        val insets = dragLayer.insets
+
+        // Put to the right of the icon if there is space, which means left aligned with the menu
+        val rightAlignedMenuStartX = mTempRect.left - widthWithArrow
+        val leftAlignedMenuStartX = mTempRect.right + extraHorizontalSpace
+        mIsLeftAligned = if (mIsRtl) {
+            rightAlignedMenuStartX + insets.left < 0
+        } else {
+            leftAlignedMenuStartX + (widthWithArrow - extraHorizontalSpace) + insets.left <
+                    dragLayer.width - insets.right
+        }
+
+        var menuStartX = if (mIsLeftAligned) leftAlignedMenuStartX else rightAlignedMenuStartX
+
+        // Offset y so that the arrow and first row are center-aligned with the original icon.
+        val iconHeight = mTempRect.height()
+        val optionHeight = optionMeasuredHeight
+        val yOffset = (optionHeight - iconHeight) / 2
+        var menuStartY = mTempRect.top - yOffset
+
+        // Insets are added later, so subtract them now.
+        menuStartX -= insets.left
+        menuStartY -= insets.top
+
+        setX(menuStartX.toFloat())
+        setY(menuStartY.toFloat())
+
+        val lp = layoutParams as FrameLayout.LayoutParams
+        val arrowLp = mArrow.layoutParams as FrameLayout.LayoutParams
+        lp.gravity = Gravity.TOP
+        arrowLp.gravity = lp.gravity
+    }
+
+    override fun addArrow() {
+        popupContainer.addView(mArrow)
+        mArrow.x = getArrowX()
+        mArrow.y = y + (optionMeasuredHeight / 2) - (mArrowHeight / 2)
+
+        updateArrowColor()
+
+        // This is inverted (x = height, y = width) because the arrow is rotated
+        mArrow.pivotX = if (mIsLeftAligned) 0f else mArrowHeight.toFloat()
+        mArrow.pivotY = 0f
+    }
+
+    private fun getArrowX(): Float {
+        return if (mIsLeftAligned)
+            x - mArrowHeight
+        else
+            x + measuredWidth + mArrowOffsetVertical
+    }
+
+    override fun updateArrowColor() {
+        mArrow.background = RoundedArrowDrawable(
+            mArrowWidth.toFloat(),
+            mArrowHeight.toFloat(),
+            mArrowPointRadius.toFloat(),
+            mIsLeftAligned,
+            mArrowColor
+        )
+        elevation = mElevation
+        mArrow.elevation = mElevation
+    }
+
 }
\ No newline at end of file
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
index f8368ae..d91669a 100644
--- a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -148,10 +148,11 @@
     public void setThumbnail(@Nullable Task task, @Nullable ThumbnailData thumbnailData,
             boolean refreshNow) {
         mTask = task;
+        boolean thumbnailWasNull = mThumbnailData == null;
         mThumbnailData =
                 (thumbnailData != null && thumbnailData.thumbnail != null) ? thumbnailData : null;
         if (refreshNow) {
-            refresh();
+            refresh(thumbnailWasNull && mThumbnailData != null);
         }
     }
 
@@ -162,14 +163,22 @@
 
     /** Updates the shader, paint, matrix to redraw. */
     public void refresh() {
+        refresh(false);
+    }
+
+    /**
+     * Updates the shader, paint, matrix to redraw.
+     * @param shouldRefreshOverlay whether to re-initialize overlay
+     */
+    private void refresh(boolean shouldRefreshOverlay) {
         if (mThumbnailData != null && mThumbnailData.thumbnail != null) {
             Bitmap bm = mThumbnailData.thumbnail;
             bm.prepareToDraw();
             mBitmapShader = new BitmapShader(bm, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
             mPaint.setShader(mBitmapShader);
             updateThumbnailMatrix();
-            if (mOverlayEnabled) {
-                getTaskOverlay().refreshActionVisibility(mThumbnailData);
+            if (shouldRefreshOverlay) {
+                refreshOverlay();
             }
         } else {
             mBitmapShader = null;
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index e9a3779..1ff2a88 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -539,7 +539,7 @@
         mTask = task;
         mTaskIdContainer[0] = mTask.key.id;
         mTaskIdAttributeContainer[0] = new TaskIdAttributeContainer(task, mSnapshotView,
-                STAGE_POSITION_UNDEFINED);
+                mIconView, STAGE_POSITION_UNDEFINED);
         mSnapshotView.bind(task);
         setOrientationState(orientedState);
     }
@@ -648,6 +648,15 @@
                     recentsView.getDepthController());
             anim.addListener(new AnimatorListenerAdapter() {
                 @Override
+                public void onAnimationStart(Animator animation) {
+                    recentsView.runActionOnRemoteHandles(
+                            (Consumer<RemoteTargetHandle>) remoteTargetHandle ->
+                                    remoteTargetHandle
+                                            .getTaskViewSimulator()
+                                            .setDrawsBelowRecents(false));
+                }
+
+                @Override
                 public void onAnimationEnd(Animator animator) {
                     mIsClickableAsLiveTile = true;
                 }
@@ -828,10 +837,12 @@
     }
 
     protected boolean showTaskMenuWithContainer(IconView iconView) {
+        TaskIdAttributeContainer menuContainer =
+                mTaskIdAttributeContainer[iconView == mIconView ? 0 : 1];
         if (mActivity.getDeviceProfile().overviewShowAsGrid) {
-            return TaskMenuViewWithArrow.Companion.showForTask(mTaskIdAttributeContainer[0]);
+            return TaskMenuViewWithArrow.Companion.showForTask(menuContainer);
         } else {
-            return TaskMenuView.showForTask(mTaskIdAttributeContainer[0]);
+            return TaskMenuView.showForTask(menuContainer);
         }
     }
 
@@ -1553,13 +1564,15 @@
     public class TaskIdAttributeContainer {
         private final TaskThumbnailView mThumbnailView;
         private final Task mTask;
+        private final IconView mIconView;
         /** Defaults to STAGE_POSITION_UNDEFINED if in not a split screen task view */
         private @SplitConfigurationOptions.StagePosition int mStagePosition;
 
         public TaskIdAttributeContainer(Task task, TaskThumbnailView thumbnailView,
-                int stagePosition) {
+                IconView iconView, int stagePosition) {
             this.mTask = task;
             this.mThumbnailView = thumbnailView;
+            this.mIconView = iconView;
             this.mStagePosition = stagePosition;
         }
 
@@ -1579,6 +1592,10 @@
             return TaskView.this;
         }
 
+        public IconView getIconView() {
+            return mIconView;
+        }
+
         public int getStagePosition() {
             return mStagePosition;
         }
diff --git a/res/layout/floating_split_select_view.xml b/res/layout/floating_split_select_view.xml
index e184b91..8d47f4e 100644
--- a/res/layout/floating_split_select_view.xml
+++ b/res/layout/floating_split_select_view.xml
@@ -14,7 +14,7 @@
         android:id="@+id/split_placeholder"
         android:layout_width="match_parent"
         android:layout_height="@dimen/split_placeholder_size"
-        android:background="@android:color/white"
+        android:background="?android:colorPrimary"
         android:visibility="gone" />
 
 </com.android.quickstep.views.FloatingTaskView>
\ No newline at end of file
diff --git a/res/layout/widget_cell_content.xml b/res/layout/widget_cell_content.xml
index b27b505..0f6fc6c 100644
--- a/res/layout/widget_cell_content.xml
+++ b/res/layout/widget_cell_content.xml
@@ -33,6 +33,14 @@
             android:layout_height="match_parent"
             android:importantForAccessibility="no"
             android:layout_gravity="fill"/>
+
+        <ImageView
+            android:id="@+id/widget_badge"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:importantForAccessibility="no"
+            android:layout_gravity="end|bottom"
+            android:layout_margin="@dimen/profile_badge_margin"/>
     </com.android.launcher3.widget.WidgetCellPreview>
 
     <!-- The name of the widget. -->
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 3758093..0235ef0 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -36,8 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Tocca e tieni premuto il widget per spostarlo nella schermata Home"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Aggiungi a schermata Home"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> aggiunto alla schermata Home"</string>
-    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widget}}"</string>
-    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# scorciatoia}other{# scorciatoie}}"</string>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}one{# widget}other{# widget}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# scorciatoia}one{# scorciatoia}other{# scorciatoie}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widget"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Cerca"</string>
@@ -86,7 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"Questa è un\'app di sistema e non può essere disinstallata."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Modifica nome"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"App <xliff:g id="APP_NAME">%1$s</xliff:g> disattivata"</string>
-    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} ha # notifica}other{{app_name} ha # notifiche}}"</string>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{{app_name} ha # notifica}one{{app_name} ha # notifica}other{{app_name} ha # notifiche}}"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Pagina %1$d di %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Schermata Home %1$d di %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Nuova pagina Schermata Home"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 09a1200..a3c225a 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -36,8 +36,8 @@
     <string name="add_item_request_drag_hint" msgid="5653291305078645405">"Toque sem soltar no widget para o mover à volta do ecrã principal"</string>
     <string name="add_to_home_screen" msgid="8631549138215492708">"Adicionar ao ecrã principal"</string>
     <string name="added_to_home_screen_accessibility_text" msgid="4451545765448884415">"Widget <xliff:g id="WIDGET_NAME">%1$s</xliff:g> adicionado ao ecrã principal"</string>
-    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}other{# widgets}}"</string>
-    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# atalho}other{# atalhos}}"</string>
+    <string name="widgets_count" msgid="6467746476364652096">"{count,plural, =1{# widget}one{# widget(s)}other{# widgets}}"</string>
+    <string name="shortcuts_count" msgid="8471715556199592381">"{count,plural, =1{# atalho}one{# atalho(s)}other{# atalhos}}"</string>
     <string name="widgets_and_shortcuts_count" msgid="7209136747878365116">"<xliff:g id="WIDGETS_COUNT">%1$s</xliff:g>, <xliff:g id="SHORTCUTS_COUNT">%2$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="widgets_full_sheet_search_bar_hint" msgid="8484659090860596457">"Pesquisar"</string>
@@ -86,7 +86,7 @@
     <string name="uninstall_system_app_text" msgid="4172046090762920660">"É uma app de sistema e não pode ser desinstalada."</string>
     <string name="folder_hint_text" msgid="5174843001373488816">"Edite o nome"</string>
     <string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> desativado"</string>
-    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{A app {app_name} tem # notificação}other{A app {app_name} tem # notificações}}"</string>
+    <string name="dotted_app_label" msgid="1704091277755818896">"{count,plural,offset:1 =1{A app {app_name} tem # notificação}one{A app {app_name} tem # notificação(ões)}other{A app {app_name} tem # notificações}}"</string>
     <string name="default_scroll_format" msgid="7475544710230993317">"Página %1$d de %2$d"</string>
     <string name="workspace_scroll_format" msgid="8458889198184077399">"Ecrã principal %1$d de %2$d"</string>
     <string name="workspace_new_page" msgid="257366611030256142">"Nova página do ecrã principal"</string>
diff --git a/res/values/config.xml b/res/values/config.xml
index 25911e6..d5629b5 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -87,6 +87,8 @@
 
     <!-- Default packages -->
     <string name="wallpaper_picker_package" translatable="false"></string>
+    <string name="custom_activity_picker" translatable="false">
+        com.android.customization.picker.CustomizationPickerActivity</string>
     <string name="local_colors_extraction_class" translatable="false"></string>
 
     <!-- Accessibility actions -->
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 21b0edc..5e67bf0 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -16,13 +16,10 @@
 
 package com.android.launcher3;
 
-import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE;
-
 import static com.android.launcher3.ResourceUtils.pxFromDp;
 import static com.android.launcher3.Utilities.dpiFromPx;
 import static com.android.launcher3.Utilities.pxFromSp;
 import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.ICON_OVERLAP_FACTOR;
-import static com.android.launcher3.util.WindowManagerCompat.MIN_TABLET_WIDTH;
 
 import android.annotation.SuppressLint;
 import android.content.Context;
@@ -64,7 +61,6 @@
     public final boolean isPhone;
     public final boolean transposeLayoutWithOrientation;
     public final boolean isTwoPanels;
-    public final boolean allowRotation;
 
     // Device properties in current orientation
     public final boolean isLandscape;
@@ -244,12 +240,7 @@
         availableHeightPx = windowBounds.availableSize.y;
 
         mInfo = info;
-        // If the device's pixel density was scaled (usually via settings for A11y), use the
-        // original dimensions to determine if rotation is allowed of not.
-        float originalSmallestWidth = dpiFromPx(Math.min(widthPx, heightPx), DENSITY_DEVICE_STABLE);
-        allowRotation = originalSmallestWidth >= MIN_TABLET_WIDTH;
-        // Tablet UI does not support emulated landscape.
-        isTablet = allowRotation && info.isTablet(windowBounds);
+        isTablet = info.isTablet(windowBounds);
         isPhone = !isTablet;
         isTwoPanels = isTablet && useTwoPanels;
 
@@ -1032,7 +1023,6 @@
         writer.println(prefix + "DeviceProfile:");
         writer.println(prefix + "\t1 dp = " + mMetrics.density + " px");
 
-        writer.println(prefix + "\tallowRotation:" + allowRotation);
         writer.println(prefix + "\tisTablet:" + isTablet);
         writer.println(prefix + "\tisPhone:" + isPhone);
         writer.println(prefix + "\ttransposeLayoutWithOrientation:"
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index cefadf7..1ce7ebe 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -252,7 +252,7 @@
         if (0 <= mCurrentPage && mCurrentPage < getPageCount()) {
             newPosition = getScrollForPage(mCurrentPage) + mCurrentPageScrollDiff;
         }
-        mOrientationHandler.set(this, VIEW_SCROLL_TO, newPosition);
+        mOrientationHandler.setPrimary(this, VIEW_SCROLL_TO, newPosition);
         mScroller.startScroll(mScroller.getCurrX(), 0, newPosition - mScroller.getCurrX(), 0);
         forceFinishScroller();
     }
@@ -556,7 +556,7 @@
             int oldPos = mOrientationHandler.getPrimaryScroll(this);
             int newPos = mScroller.getCurrX();
             if (oldPos != newPos) {
-                mOrientationHandler.set(this, VIEW_SCROLL_TO, mScroller.getCurrX());
+                mOrientationHandler.setPrimary(this, VIEW_SCROLL_TO, mScroller.getCurrX());
             }
 
             if (mAllowOverScroll) {
@@ -1280,7 +1280,7 @@
                 mLastMotionRemainder = delta - movedDelta;
 
                 if (delta != 0) {
-                    mOrientationHandler.set(this, VIEW_SCROLL_BY, movedDelta);
+                    mOrientationHandler.setPrimary(this, VIEW_SCROLL_BY, movedDelta);
 
                     if (mAllowOverScroll) {
                         final float pulledToX = oldScroll + delta;
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index c4d8166..98cb5ae 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -72,6 +72,7 @@
             "PROMISE_APPS_NEW_INSTALLS", true,
             "Adds a promise icon to the home screen for new install sessions.");
 
+    // TODO: b/206508141: Long pressing on some icons on home screen cause launcher to crash.
     public static final BooleanFlag ENABLE_LOCAL_COLOR_POPUPS = getDebugFlag(
             "ENABLE_LOCAL_COLOR_POPUPS", false, "Enable local color extraction for popups.");
 
diff --git a/src/com/android/launcher3/popup/ArrowPopup.java b/src/com/android/launcher3/popup/ArrowPopup.java
index 5a1e4bf..b1a4109 100644
--- a/src/com/android/launcher3/popup/ArrowPopup.java
+++ b/src/com/android/launcher3/popup/ArrowPopup.java
@@ -77,44 +77,45 @@
         extends AbstractFloatingView {
 
     // Duration values (ms) for popup open and close animations.
-    private static final int OPEN_DURATION = 276;
-    private static final int OPEN_FADE_START_DELAY = 0;
-    private static final int OPEN_FADE_DURATION = 38;
-    private static final int OPEN_CHILD_FADE_START_DELAY = 38;
-    private static final int OPEN_CHILD_FADE_DURATION = 76;
+    protected int OPEN_DURATION = 276;
+    protected int OPEN_FADE_START_DELAY = 0;
+    protected int OPEN_FADE_DURATION = 38;
+    protected int OPEN_CHILD_FADE_START_DELAY = 38;
+    protected int OPEN_CHILD_FADE_DURATION = 76;
 
-    private static final int CLOSE_DURATION = 200;
-    private static final int CLOSE_FADE_START_DELAY = 140;
-    private static final int CLOSE_FADE_DURATION = 50;
-    private static final int CLOSE_CHILD_FADE_START_DELAY = 0;
-    private static final int CLOSE_CHILD_FADE_DURATION = 140;
+    protected int CLOSE_DURATION = 200;
+    protected int CLOSE_FADE_START_DELAY = 140;
+    protected int CLOSE_FADE_DURATION = 50;
+    protected int CLOSE_CHILD_FADE_START_DELAY = 0;
+    protected int CLOSE_CHILD_FADE_DURATION = 140;
 
     // Index used to get background color when using local wallpaper color extraction,
     private static final int DARK_COLOR_EXTRACTION_INDEX = android.R.color.system_neutral2_800;
     private static final int LIGHT_COLOR_EXTRACTION_INDEX = android.R.color.system_accent2_50;
 
-    private final Rect mTempRect = new Rect();
+    protected final Rect mTempRect = new Rect();
 
     protected final LayoutInflater mInflater;
-    private final float mOutlineRadius;
+    protected final float mOutlineRadius;
     protected final T mActivityContext;
     protected final boolean mIsRtl;
 
-    private final int mArrowOffsetVertical;
-    private final int mArrowOffsetHorizontal;
-    private final int mArrowWidth;
-    private final int mArrowHeight;
-    private final int mArrowPointRadius;
-    private final View mArrow;
+    protected final int mArrowOffsetVertical;
+    protected final int mArrowOffsetHorizontal;
+    protected final int mArrowWidth;
+    protected final int mArrowHeight;
+    protected final int mArrowPointRadius;
+    protected final View mArrow;
 
     private final int mMargin;
 
     protected boolean mIsLeftAligned;
     protected boolean mIsAboveIcon;
-    private int mGravity;
+    protected int mGravity;
 
     protected AnimatorSet mOpenCloseAnimator;
     protected boolean mDeferContainerRemoval;
+    protected boolean shouldScaleArrow = false;
 
     private final GradientDrawable mRoundedTop;
     private final GradientDrawable mRoundedBottom;
@@ -122,10 +123,10 @@
     private Runnable mOnCloseCallback = () -> { };
 
     // The rect string of the view that the arrow is attached to, in screen reference frame.
-    private int mArrowColor;
+    protected int mArrowColor;
     protected final List<LocalColorExtractor> mColorExtractors;
 
-    private final float mElevation;
+    protected final float mElevation;
     private final int mBackgroundColor;
 
     private final String mIterateChildrenTag;
@@ -729,6 +730,14 @@
         scale.setInterpolator(interpolator);
         animatorSet.play(scale);
 
+        if (shouldScaleArrow) {
+            Animator arrowScaleAnimator = ObjectAnimator.ofFloat(mArrow, View.SCALE_Y,
+                    scaleValues);
+            arrowScaleAnimator.setDuration(totalDuration);
+            arrowScaleAnimator.setInterpolator(interpolator);
+            animatorSet.play(arrowScaleAnimator);
+        }
+
         fadeInChildViews(this, alphaValues, childFadeStartDelay, childFadeDuration, animatorSet);
 
         return animatorSet;
diff --git a/src/com/android/launcher3/popup/RoundedArrowDrawable.java b/src/com/android/launcher3/popup/RoundedArrowDrawable.java
index e662d5c..436aa51 100644
--- a/src/com/android/launcher3/popup/RoundedArrowDrawable.java
+++ b/src/com/android/launcher3/popup/RoundedArrowDrawable.java
@@ -78,6 +78,32 @@
         mPath.transform(pathTransform);
     }
 
+    /**
+     * Constructor for an arrow that points to the left or right.
+     *
+     * @param width        of the arrow.
+     * @param height       of the arrow.
+     * @param radius       of the tip of the arrow.
+     * @param isPointingLeft or not.
+     * @param color        to draw the triangle.
+     */
+    public RoundedArrowDrawable(float width, float height, float radius, boolean isPointingLeft,
+            int color) {
+        mPath = new Path();
+        mPaint = new Paint();
+        mPaint.setColor(color);
+        mPaint.setStyle(Paint.Style.FILL);
+        mPaint.setAntiAlias(true);
+
+        // Make the drawable with the triangle pointing down...
+        addDownPointingRoundedTriangleToPath(width, height, radius, mPath);
+
+        // ... then rotate it to the side it needs to point.
+        Matrix pathTransform = new Matrix();
+        pathTransform.setRotate(isPointingLeft ? 90 : -90, width * 0.5f, height * 0.5f);
+        mPath.transform(pathTransform);
+    }
+
     @Override
     public void draw(Canvas canvas) {
         canvas.drawPath(mPath, mPaint);
diff --git a/src/com/android/launcher3/settings/SettingsActivity.java b/src/com/android/launcher3/settings/SettingsActivity.java
index f348a33..0c39632 100644
--- a/src/com/android/launcher3/settings/SettingsActivity.java
+++ b/src/com/android/launcher3/settings/SettingsActivity.java
@@ -49,6 +49,7 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.model.WidgetsModel;
+import com.android.launcher3.states.RotationHelper;
 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
 
 import java.util.Collections;
@@ -253,12 +254,13 @@
                 case ALLOW_ROTATION_PREFERENCE_KEY:
                     DeviceProfile deviceProfile = InvariantDeviceProfile.INSTANCE.get(
                             getContext()).getDeviceProfile(getContext());
-                    if (deviceProfile.allowRotation) {
+                    if (deviceProfile.isTablet) {
                         // Launcher supports rotation by default. No need to show this setting.
                         return false;
                     }
                     // Initialize the UI once
-                    preference.setDefaultValue(false);
+                    preference.setDefaultValue(
+                            RotationHelper.getAllowRotationDefaultValue(deviceProfile));
                     return true;
 
                 case FLAGS_PREFERENCE_KEY:
diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java
index 87871b1..867fd99 100644
--- a/src/com/android/launcher3/states/RotationHelper.java
+++ b/src/com/android/launcher3/states/RotationHelper.java
@@ -18,6 +18,10 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE;
+
+import static com.android.launcher3.Utilities.dpiFromPx;
+import static com.android.launcher3.util.WindowManagerCompat.MIN_TABLET_WIDTH;
 
 import android.content.SharedPreferences;
 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
@@ -25,7 +29,6 @@
 import com.android.launcher3.BaseActivity;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.util.ActivityTracker;
 import com.android.launcher3.util.UiThreadHelper;
 
 /**
@@ -38,6 +41,17 @@
 
     public static final String ALLOW_ROTATION_PREFERENCE_KEY = "pref_allowRotation";
 
+    /**
+     * Returns the default value of {@link #ALLOW_ROTATION_PREFERENCE_KEY} preference.
+     */
+    public static boolean getAllowRotationDefaultValue(DeviceProfile deviceProfile) {
+        // If the device's pixel density was scaled (usually via settings for A11y), use the
+        // original dimensions to determine if rotation is allowed of not.
+        float originalSmallestWidth = dpiFromPx(
+                Math.min(deviceProfile.widthPx, deviceProfile.heightPx), DENSITY_DEVICE_STABLE);
+        return originalSmallestWidth >= MIN_TABLET_WIDTH;
+    }
+
     public static final int REQUEST_NONE = 0;
     public static final int REQUEST_ROTATE = 1;
     public static final int REQUEST_LOCK = 2;
@@ -51,7 +65,7 @@
 
     /**
      * Rotation request made by
-     * {@link ActivityTracker.SchedulerCallback}.
+     * {@link com.android.launcher3.util.ActivityTracker.SchedulerCallback}.
      * This supersedes any other request.
      */
     private int mStateHandlerRequest = REQUEST_NONE;
@@ -84,7 +98,7 @@
                 mSharedPrefs.registerOnSharedPreferenceChangeListener(this);
             }
             mHomeRotationEnabled = mSharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY,
-                    mActivity.getDeviceProfile().allowRotation);
+                    getAllowRotationDefaultValue(mActivity.getDeviceProfile()));
         } else {
             if (mSharedPrefs != null) {
                 mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this);
@@ -98,7 +112,7 @@
         if (mDestroyed) return;
         boolean wasRotationEnabled = mHomeRotationEnabled;
         mHomeRotationEnabled = mSharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY,
-                mActivity.getDeviceProfile().allowRotation);
+                getAllowRotationDefaultValue(mActivity.getDeviceProfile()));
         if (mHomeRotationEnabled != wasRotationEnabled) {
             notifyChange();
         }
@@ -106,7 +120,7 @@
 
     @Override
     public void onDeviceProfileChanged(DeviceProfile dp) {
-        boolean ignoreAutoRotateSettings = dp.allowRotation;
+        boolean ignoreAutoRotateSettings = dp.isTablet;
         if (mIgnoreAutoRotateSettings != ignoreAutoRotateSettings) {
             setIgnoreAutoRotateSettings(ignoreAutoRotateSettings);
             notifyChange();
@@ -143,7 +157,7 @@
     public void initialize() {
         if (!mInitialized) {
             mInitialized = true;
-            setIgnoreAutoRotateSettings(mActivity.getDeviceProfile().allowRotation);
+            setIgnoreAutoRotateSettings(mActivity.getDeviceProfile().isTablet);
             mActivity.addOnDeviceProfileChangeListener(this);
             notifyChange();
         }
diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java
index 9a74fb1..62b8a48 100644
--- a/src/com/android/launcher3/testing/TestProtocol.java
+++ b/src/com/android/launcher3/testing/TestProtocol.java
@@ -126,5 +126,4 @@
     public static final String TASK_VIEW_ID_CRASH = "b/195430732";
     public static final String NO_DROP_TARGET = "b/195031154";
     public static final String NULL_INT_SET = "b/200572078";
-    public static final String NO_SCREENSHOT = "b/202414125";
 }
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index 93e3ea7..e127074 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -101,12 +101,12 @@
     }
 
     @Override
-    public <T> void set(T target, Int2DAction<T> action, int param) {
+    public <T> void setPrimary(T target, Int2DAction<T> action, int param) {
         action.call(target, 0, param);
     }
 
     @Override
-    public <T> void set(T target, Float2DAction<T> action, float param) {
+    public <T> void setPrimary(T target, Float2DAction<T> action, float param) {
         action.call(target, 0, param);
     }
 
@@ -116,6 +116,12 @@
     }
 
     @Override
+    public <T> void set(T target, Int2DAction<T> action, int primaryParam,
+            int secondaryParam) {
+        action.call(target, secondaryParam, primaryParam);
+    }
+
+    @Override
     public float getPrimaryDirection(MotionEvent event, int pointerIndex) {
         return event.getY(pointerIndex);
     }
@@ -429,7 +435,7 @@
 
     @Override
     public void setSplitIconParams(View primaryIconView, View secondaryIconView,
-            int taskIconHeight, Rect primarySnapshotBounds, Rect secondarySnapshotBounds,
+            int taskIconHeight, int primarySnapshotWidth, int primarySnapshotHeight,
             boolean isRtl, DeviceProfile deviceProfile, StagedSplitBounds splitConfig) {
         FrameLayout.LayoutParams primaryIconParams =
                 (FrameLayout.LayoutParams) primaryIconView.getLayoutParams();
@@ -439,13 +445,12 @@
                 splitConfig.visualDividerBounds.height() :
                 splitConfig.visualDividerBounds.width());
 
-        int primaryHeight = primarySnapshotBounds.height();
         primaryIconParams.gravity = (isRtl ? START : END) | TOP;
-        primaryIconView.setTranslationY(primaryHeight - primaryIconView.getHeight() / 2f);
+        primaryIconView.setTranslationY(primarySnapshotHeight - primaryIconView.getHeight() / 2f);
         primaryIconView.setTranslationX(0);
 
         secondaryIconParams.gravity = (isRtl ? START : END) | TOP;
-        secondaryIconView.setTranslationY(primaryHeight + taskIconHeight + dividerBar);
+        secondaryIconView.setTranslationY(primarySnapshotHeight + taskIconHeight + dividerBar);
         secondaryIconView.setTranslationX(0);
         primaryIconView.setLayoutParams(primaryIconParams);
         secondaryIconView.setLayoutParams(secondaryIconParams);
diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java
index 2ff2feb..d954552 100644
--- a/src/com/android/launcher3/touch/PagedOrientationHandler.java
+++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java
@@ -65,9 +65,10 @@
     Float2DAction<Canvas> CANVAS_TRANSLATE = Canvas::translate;
     Float2DAction<Matrix> MATRIX_POST_TRANSLATE = Matrix::postTranslate;
 
-    <T> void set(T target, Int2DAction<T> action, int param);
-    <T> void set(T target, Float2DAction<T> action, float param);
+    <T> void setPrimary(T target, Int2DAction<T> action, int param);
+    <T> void setPrimary(T target, Float2DAction<T> action, float param);
     <T> void setSecondary(T target, Float2DAction<T> action, float param);
+    <T> void set(T target, Int2DAction<T> action, int primaryParam, int secondaryParam);
     float getPrimaryDirection(MotionEvent event, int pointerIndex);
     float getPrimaryVelocity(VelocityTracker velocityTracker, int pointerId);
     int getMeasuredSize(View view);
@@ -152,7 +153,7 @@
     void setIconAndSnapshotParams(View iconView, int taskIconMargin, int taskIconHeight,
             FrameLayout.LayoutParams snapshotParams, boolean isRtl);
     void setSplitIconParams(View primaryIconView, View secondaryIconView,
-            int taskIconHeight, Rect primarySnapshotBounds, Rect secondarySnapshotBounds,
+            int taskIconHeight, int primarySnapshotWidth, int primarySnapshotHeight,
             boolean isRtl, DeviceProfile deviceProfile, StagedSplitBounds splitConfig);
 
     /*
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index ba9d09c..fbc335c 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -101,12 +101,12 @@
     }
 
     @Override
-    public <T> void set(T target, Int2DAction<T> action, int param) {
+    public <T> void setPrimary(T target, Int2DAction<T> action, int param) {
         action.call(target, param, 0);
     }
 
     @Override
-    public <T> void set(T target, Float2DAction<T> action, float param) {
+    public <T> void setPrimary(T target, Float2DAction<T> action, float param) {
         action.call(target, param, 0);
     }
 
@@ -116,6 +116,12 @@
     }
 
     @Override
+    public <T> void set(T target, Int2DAction<T> action, int primaryParam,
+            int secondaryParam) {
+        action.call(target, primaryParam, secondaryParam);
+    }
+
+    @Override
     public float getPrimaryDirection(MotionEvent event, int pointerIndex) {
         return event.getX(pointerIndex);
     }
@@ -523,7 +529,7 @@
 
     @Override
     public void setSplitIconParams(View primaryIconView, View secondaryIconView,
-            int taskIconHeight, Rect primarySnapshotBounds, Rect secondarySnapshotBounds,
+            int taskIconHeight, int primarySnapshotWidth, int primarySnapshotHeight,
             boolean isRtl, DeviceProfile deviceProfile, StagedSplitBounds splitConfig) {
         FrameLayout.LayoutParams primaryIconParams =
                 (FrameLayout.LayoutParams) primaryIconView.getLayoutParams();
@@ -533,15 +539,12 @@
                 splitConfig.visualDividerBounds.height() :
                 splitConfig.visualDividerBounds.width());
 
-        int primaryWidth = primarySnapshotBounds.width();
         if (deviceProfile.isLandscape) {
             primaryIconParams.gravity = TOP | START;
-            primaryIconView.setTranslationX(primaryWidth - primaryIconView.getWidth());
+            primaryIconView.setTranslationX(primarySnapshotWidth - primaryIconView.getWidth());
             primaryIconView.setTranslationY(0);
-
             secondaryIconParams.gravity = TOP | START;
-            secondaryIconView.setTranslationX(primaryWidth + dividerBar);
-            secondaryIconView.setTranslationY(0);
+            secondaryIconView.setTranslationX(primarySnapshotWidth + dividerBar);
         } else {
             primaryIconParams.gravity = TOP | CENTER_HORIZONTAL;
             primaryIconView.setTranslationX(-(primaryIconView.getWidth()) / 2f);
@@ -549,8 +552,8 @@
 
             secondaryIconParams.gravity = TOP | CENTER_HORIZONTAL;
             secondaryIconView.setTranslationX(secondaryIconView.getWidth() / 2f);
-            secondaryIconView.setTranslationY(0);
         }
+        secondaryIconView.setTranslationY(0);
         primaryIconView.setLayoutParams(primaryIconParams);
         secondaryIconView.setLayoutParams(secondaryIconParams);
     }
diff --git a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
index a0dde22..539e3f8 100644
--- a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
@@ -132,10 +132,10 @@
 
     @Override
     public void setSplitIconParams(View primaryIconView, View secondaryIconView,
-            int taskIconHeight, Rect primarySnapshotBounds, Rect secondarySnapshotBounds,
+            int taskIconHeight, int primarySnapshotWidth, int primarySnapshotHeight,
             boolean isRtl, DeviceProfile deviceProfile, StagedSplitBounds splitConfig) {
         super.setSplitIconParams(primaryIconView, secondaryIconView, taskIconHeight,
-                primarySnapshotBounds, secondarySnapshotBounds, isRtl, deviceProfile, splitConfig);
+                primarySnapshotWidth, primarySnapshotHeight, isRtl, deviceProfile, splitConfig);
         FrameLayout.LayoutParams primaryIconParams =
                 (FrameLayout.LayoutParams) primaryIconView.getLayoutParams();
         FrameLayout.LayoutParams secondaryIconParams =
diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java
index 2068c29..c050c6c 100644
--- a/src/com/android/launcher3/util/DisplayController.java
+++ b/src/com/android/launcher3/util/DisplayController.java
@@ -261,6 +261,13 @@
             PortraitSize realSize = new PortraitSize(newInfo.currentSize.x, newInfo.currentSize.y);
             PortraitSize expectedSize = oldInfo.mInternalDisplays.get(
                     ApiWrapper.getUniqueId(display));
+            if (newInfo.supportedBounds.size() != oldInfo.supportedBounds.size()) {
+                Log.e("b/198965093",
+                        "Inconsistent number of displays"
+                                + "\ndisplay state: " + display.getState()
+                                + "\noldInfo.supportedBounds: " + oldInfo.supportedBounds
+                                + "\nnewInfo.supportedBounds: " + newInfo.supportedBounds);
+            }
             if (!realSize.equals(expectedSize) && display.getState() == Display.STATE_OFF) {
                 Log.e("b/198965093", "Display size changed while display is off, ignoring change");
                 return;
diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java
index 08ec591..f42d304 100644
--- a/src/com/android/launcher3/util/PackageManagerHelper.java
+++ b/src/com/android/launcher3/util/PackageManagerHelper.java
@@ -202,7 +202,8 @@
     public static Intent getStyleWallpapersIntent(Context context) {
         return new Intent(Intent.ACTION_SET_WALLPAPER).setComponent(
                 new ComponentName(context.getString(R.string.wallpaper_picker_package),
-                "com.android.customization.picker.CustomizationPickerActivity"));
+                    context.getString(R.string.custom_activity_picker)
+                ));
     }
 
     /**
diff --git a/src/com/android/launcher3/widget/DatabaseWidgetPreviewLoader.java b/src/com/android/launcher3/widget/DatabaseWidgetPreviewLoader.java
index aacb9c5..784f4f0 100644
--- a/src/com/android/launcher3/widget/DatabaseWidgetPreviewLoader.java
+++ b/src/com/android/launcher3/widget/DatabaseWidgetPreviewLoader.java
@@ -25,11 +25,15 @@
 import android.graphics.Paint;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
 import android.graphics.RectF;
+import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.AsyncTask;
 import android.os.Handler;
 import android.os.Process;
+import android.os.UserHandle;
+import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Size;
 
@@ -40,6 +44,7 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.icons.BitmapRenderer;
+import com.android.launcher3.icons.FastBitmapDrawable;
 import com.android.launcher3.icons.LauncherIcons;
 import com.android.launcher3.icons.ShadowGenerator;
 import com.android.launcher3.icons.cache.HandlerRunnable;
@@ -60,6 +65,9 @@
     private final Context mContext;
     private final float mPreviewBoxCornerRadius;
 
+    private final UserHandle mMyUser = Process.myUserHandle();
+    private final ArrayMap<UserHandle, Bitmap> mUserBadges = new ArrayMap<>();
+
     public DatabaseWidgetPreviewLoader(Context context) {
         mContext = context;
         float previewCornerRadius = RoundedCornerEnforcement.computeEnforcedRadius(context);
@@ -101,6 +109,52 @@
     }
 
     /**
+     * Returns a drawable that can be used as a badge for the user or null.
+     */
+   // @UiThread
+    public Drawable getBadgeForUser(UserHandle user, int badgeSize) {
+        if (mMyUser.equals(user)) {
+            return null;
+        }
+
+        Bitmap badgeBitmap = getUserBadge(user, badgeSize);
+        FastBitmapDrawable d = new FastBitmapDrawable(badgeBitmap);
+        d.setFilterBitmap(true);
+        d.setBounds(0, 0, badgeBitmap.getWidth(), badgeBitmap.getHeight());
+        return d;
+    }
+
+    private Bitmap getUserBadge(UserHandle user, int badgeSize) {
+        synchronized (mUserBadges) {
+            Bitmap badgeBitmap = mUserBadges.get(user);
+            if (badgeBitmap != null) {
+                return badgeBitmap;
+            }
+
+            final Resources res = mContext.getResources();
+            badgeBitmap = Bitmap.createBitmap(badgeSize, badgeSize, Bitmap.Config.ARGB_8888);
+
+            Drawable drawable = mContext.getPackageManager().getUserBadgedDrawableForDensity(
+                    new BitmapDrawable(res, badgeBitmap), user,
+                    new Rect(0, 0, badgeSize, badgeSize),
+                    0);
+            if (drawable instanceof BitmapDrawable) {
+                badgeBitmap = ((BitmapDrawable) drawable).getBitmap();
+            } else {
+                badgeBitmap.eraseColor(Color.TRANSPARENT);
+                Canvas c = new Canvas(badgeBitmap);
+                drawable.setBounds(0, 0, badgeSize, badgeSize);
+                drawable.draw(c);
+                c.setBitmap(null);
+            }
+
+            mUserBadges.put(user, badgeBitmap);
+            return badgeBitmap;
+        }
+    }
+
+
+    /**
      * Generates the widget preview from either the {@link WidgetManagerHelper} or cache
      * and add badge at the bottom right corner.
      *
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index f1ac656..c92fe5a 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -36,6 +36,7 @@
 import android.view.ViewPropertyAnimator;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.FrameLayout;
+import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.RemoteViews;
 import android.widget.TextView;
@@ -47,6 +48,7 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.R;
+import com.android.launcher3.icons.BaseIconFactory;
 import com.android.launcher3.icons.FastBitmapDrawable;
 import com.android.launcher3.icons.RoundDrawableWrapper;
 import com.android.launcher3.icons.cache.HandlerRunnable;
@@ -111,6 +113,7 @@
 
     private FrameLayout mWidgetImageContainer;
     private WidgetImageView mWidgetImage;
+    private ImageView mWidgetBadge;
     private TextView mWidgetName;
     private TextView mWidgetDims;
     private TextView mWidgetDescription;
@@ -166,6 +169,7 @@
 
         mWidgetImageContainer = findViewById(R.id.widget_preview_container);
         mWidgetImage = findViewById(R.id.widget_preview);
+        mWidgetBadge = findViewById(R.id.widget_badge);
         mWidgetName = findViewById(R.id.widget_name);
         mWidgetDims = findViewById(R.id.widget_dims);
         mWidgetDescription = findViewById(R.id.widget_description);
@@ -195,6 +199,8 @@
         mWidgetImage.animate().cancel();
         mWidgetImage.setDrawable(null);
         mWidgetImage.setVisibility(View.VISIBLE);
+        mWidgetBadge.setImageDrawable(null);
+        mWidgetBadge.setVisibility(View.GONE);
         mWidgetName.setText(null);
         mWidgetDims.setText(null);
         mWidgetDescription.setText(null);
@@ -349,6 +355,7 @@
                 mAppWidgetHostViewPreview = null;
             }
         }
+
         if (mAnimatePreview) {
             mWidgetImageContainer.setAlpha(0f);
             ViewPropertyAnimator anim = mWidgetImageContainer.animate();
@@ -362,6 +369,20 @@
         }
     }
 
+    /** Used to show the badge when the widget is in the recommended section
+     */
+    public void showBadge() {
+        Drawable badge = mWidgetPreviewLoader.getBadgeForUser(mItem.user,
+                BaseIconFactory.getBadgeSizeForIconSize(
+                        mActivity.getDeviceProfile().allAppsIconSizePx));
+        if (badge == null) {
+            mWidgetBadge.setVisibility(View.GONE);
+        } else {
+            mWidgetBadge.setVisibility(View.VISIBLE);
+            mWidgetBadge.setImageDrawable(badge);
+        }
+    }
+
     private void setContainerSize(int width, int height) {
         LayoutParams layoutParams = (LayoutParams) mWidgetImageContainer.getLayoutParams();
         layoutParams.width = width;
diff --git a/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java b/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
index c986007..06cc65e 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
@@ -109,6 +109,7 @@
             for (WidgetItem widgetItem : widgetItems) {
                 WidgetCell widgetCell = addItemCell(tableRow);
                 widgetCell.applyFromCellItem(widgetItem, data.mPreviewScale);
+                widgetCell.showBadge();
             }
             addView(tableRow);
         }
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 44f2719..19dca45 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -38,6 +38,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.system.OsConstants;
 import android.util.Log;
 
 import androidx.test.InstrumentationRegistry;
@@ -83,6 +84,7 @@
 import java.util.concurrent.Callable;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 import java.util.function.Consumer;
 import java.util.function.Function;
 import java.util.function.Supplier;
@@ -327,7 +329,12 @@
      */
     protected <T> T getOnUiThread(final Callable<T> callback) {
         try {
-            return mMainThreadExecutor.submit(callback).get();
+            return mMainThreadExecutor.submit(callback).get(DEFAULT_UI_TIMEOUT,
+                    TimeUnit.MILLISECONDS);
+        } catch (TimeoutException e) {
+            Log.e(TAG, "Timeout in getOnUiThread, sending SIGABRT", e);
+            Process.sendSignal(Process.myPid(), OsConstants.SIGABRT);
+            throw new RuntimeException(e);
         } catch (Throwable e) {
             throw new RuntimeException(e);
         }
diff --git a/tests/src/com/android/launcher3/ui/WorkProfileTest.java b/tests/src/com/android/launcher3/ui/WorkProfileTest.java
index 45d20e2..939cfe1 100644
--- a/tests/src/com/android/launcher3/ui/WorkProfileTest.java
+++ b/tests/src/com/android/launcher3/ui/WorkProfileTest.java
@@ -32,7 +32,6 @@
 import com.android.launcher3.allapps.WorkEduCard;
 import com.android.launcher3.allapps.WorkProfileManager;
 import com.android.launcher3.tapl.LauncherInstrumentation;
-import com.android.launcher3.util.rule.ScreenRecordRule.ScreenRecord;
 
 import org.junit.After;
 import org.junit.Before;
@@ -96,7 +95,6 @@
     }
 
     @Test
-    @ScreenRecord // b/202735477
     public void workTabExists() {
         waitForLauncherCondition("Personal tab is missing",
                 launcher -> launcher.getAppsView().isPersonalTabVisible(),
diff --git a/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java b/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java
index de36d5f..f33a50a 100644
--- a/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java
+++ b/tests/src/com/android/launcher3/util/rule/TestStabilityRule.java
@@ -41,7 +41,7 @@
             Pattern.compile("^("
                     + "(?<local>(BuildFromAndroidStudio|"
                     + "([0-9]+|[A-Z])-eng\\.[a-z]+\\.[0-9]+\\.[0-9]+))|"
-                    + "(?<platform>[A-Z]([a-z]|[0-9])*)"
+                    + "(?<platform>([A-Z][a-z]*[0-9]*|[0-9]+)*)"
                     + ")$");
     private static final Pattern PLATFORM_BUILD =
             Pattern.compile("^("
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 2fbe460..3485dd1 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -544,11 +544,11 @@
                     : TestHelpers.getSystemHealthMessage(getContext(), mTestStartTime);
 
             if (systemHealth != null) {
-                return message
-                        + ";\nPerhaps linked to system health problems:\n<<<<<<<<<<<<<<<<<<\n"
+                message += ";\nPerhaps linked to system health problems:\n<<<<<<<<<<<<<<<<<<\n"
                         + systemHealth + "\n>>>>>>>>>>>>>>>>>>";
             }
         }
+        Log.d(TAG, "About to throw the error: " + message, new Exception());
         return message;
     }