[automerger skipped] Merge Android U (ab/10368041) am: 41dd5f48d6 -s ours

am skip reason: Merged-In Ia7c5c2101a30d0eaae0fc92801186e66baec449d with SHA-1 5dc19d8fd4 is already in history

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Launcher3/+/24182806

Change-Id: I141746c27c9aa0bb1d44699878653ccf87de1fe0
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
index e5fd605..f3e62c1 100644
--- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
+++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java
@@ -18,6 +18,7 @@
 
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.provider.Settings.Secure.LAUNCHER_TASKBAR_EDUCATION_SHOWING;
 import static android.view.RemoteAnimationTarget.MODE_CLOSING;
 import static android.view.RemoteAnimationTarget.MODE_OPENING;
@@ -144,6 +145,7 @@
 import com.android.quickstep.util.SurfaceTransaction;
 import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties;
 import com.android.quickstep.util.SurfaceTransactionApplier;
+import com.android.quickstep.util.TaskRestartedDuringLaunchListener;
 import com.android.quickstep.util.WorkspaceRevealAnim;
 import com.android.quickstep.views.FloatingWidgetView;
 import com.android.quickstep.views.RecentsView;
@@ -156,6 +158,7 @@
 import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
 import com.android.wm.shell.startingsurface.IStartingWindowListener;
 
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -229,6 +232,7 @@
     private RemoteAnimationProvider mRemoteAnimationProvider;
     // Strong refs to runners which are cleared when the launcher activity is destroyed
     private RemoteAnimationFactory mWallpaperOpenRunner;
+    private RemoteAnimationFactory mAppLaunchRunner;
     private RemoteAnimationFactory mKeyguardGoingAwayRunner;
 
     private RemoteAnimationFactory mWallpaperOpenTransitionRunner;
@@ -298,17 +302,23 @@
         boolean fromRecents = isLaunchingFromRecents(v, null /* targets */);
         RunnableList onEndCallback = new RunnableList();
 
-        RemoteAnimationFactory delegateRunner = new AppLaunchAnimationRunner(v, onEndCallback);
+        // Handle the case where an already visible task is launched which results in no transition
+        TaskRestartedDuringLaunchListener restartedListener =
+                new TaskRestartedDuringLaunchListener();
+        restartedListener.register(onEndCallback::executeAllAndDestroy);
+        onEndCallback.add(restartedListener::unregister);
+
+        mAppLaunchRunner = new AppLaunchAnimationRunner(v, onEndCallback);
         ItemInfo tag = (ItemInfo) v.getTag();
         if (tag != null && tag.shouldUseBackgroundAnimation()) {
             ContainerAnimationRunner containerAnimationRunner =
                     ContainerAnimationRunner.from(v, mStartingWindowListener, onEndCallback);
             if (containerAnimationRunner != null) {
-                delegateRunner = containerAnimationRunner;
+                mAppLaunchRunner = containerAnimationRunner;
             }
         }
         RemoteAnimationRunnerCompat runner = new LauncherAnimationRunner(
-                mHandler, delegateRunner, true /* startAtFrontOfQueue */);
+                mHandler, mAppLaunchRunner, true /* startAtFrontOfQueue */);
 
         // Note that this duration is a guess as we do not know if the animation will be a
         // recents launch or not for sure until we know the opening app targets.
@@ -468,6 +478,9 @@
         });
     }
 
+    /** Dump debug logs to bug report. */
+    public void dump(@NonNull String prefix, @NonNull PrintWriter printWriter) {}
+
     /**
      * Content is everything on screen except the background and the floating view (if any).
      *
@@ -1037,7 +1050,7 @@
         boolean allowBlurringLauncher = mLauncher.getStateManager().getState() != OVERVIEW
                 && BlurUtils.supportsBlursOnWindows();
 
-        MyDepthController depthController = new MyDepthController(mLauncher);
+        LaunchDepthController depthController = new LaunchDepthController(mLauncher);
         ObjectAnimator backgroundRadiusAnim = ObjectAnimator.ofFloat(depthController.stateDepth,
                         MULTI_PROPERTY_VALUE, BACKGROUND_APP.getDepth(mLauncher))
                 .setDuration(APP_LAUNCH_DURATION);
@@ -1164,6 +1177,7 @@
             // Also clear strong references to the runners registered with the remote animation
             // definition so we don't have to wait for the system gc
             mWallpaperOpenRunner = null;
+            mAppLaunchRunner = null;
             mKeyguardGoingAwayRunner = null;
         }
     }
@@ -1201,14 +1215,15 @@
         return false;
     }
 
-    private boolean hasMultipleTargetsWithMode(RemoteAnimationTarget[] targets, int mode) {
+    private boolean shouldPlayFallbackClosingAnimation(RemoteAnimationTarget[] targets) {
         int numTargets = 0;
         for (RemoteAnimationTarget target : targets) {
-            if (target.mode == mode) {
+            if (target.mode == MODE_CLOSING) {
                 numTargets++;
-            }
-            if (numTargets > 1) {
-                return true;
+                if (numTargets > 1 || target.windowConfiguration.getWindowingMode()
+                        == WINDOWING_MODE_MULTI_WINDOW) {
+                    return true;
+                }
             }
         }
         return false;
@@ -1595,7 +1610,7 @@
             boolean playFallBackAnimation = (launcherView == null
                     && launcherIsForceInvisibleOrOpening)
                     || mLauncher.getWorkspace().isOverlayShown()
-                    || hasMultipleTargetsWithMode(appTargets, MODE_CLOSING);
+                    || shouldPlayFallbackClosingAnimation(appTargets);
 
             boolean playWorkspaceReveal = true;
             boolean skipAllAppsScale = false;
@@ -2036,11 +2051,14 @@
         }
     }
 
-    private static class MyDepthController extends DepthController {
-        MyDepthController(Launcher l) {
-            super(l);
+    private static class LaunchDepthController extends DepthController {
+        LaunchDepthController(QuickstepLauncher launcher) {
+            super(launcher);
             setCrossWindowBlursEnabled(
                     CrossWindowBlurListeners.getInstance().isCrossWindowBlurEnabled());
+            // Make sure that the starting value matches the current depth set by the main
+            // controller.
+            stateDepth.setValue(launcher.getDepthController().stateDepth.getValue());
         }
     }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index ba6f165..abf49eb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -50,6 +50,7 @@
 import com.android.quickstep.views.RecentsView;
 
 import java.io.PrintWriter;
+import java.util.Arrays;
 
 /**
  * A data source which integrates with a Launcher instance
@@ -105,6 +106,10 @@
 
         // Restore the in-app display progress from before Taskbar was recreated.
         float[] prevProgresses = mControllers.getSharedState().inAppDisplayProgressMultiPropValues;
+        // Make a copy of the previous progress to set since updating the multiprop will update
+        // the property which also calls onInAppDisplayProgressChanged() which writes the current
+        // values into the shared state
+        prevProgresses = Arrays.copyOf(prevProgresses, prevProgresses.length);
         for (int i = 0; i < prevProgresses.length; i++) {
             mTaskbarInAppDisplayProgressMultiProp.get(i).setValue(prevProgresses[i]);
         }
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index 9a9e0ba..9c463cb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -923,6 +923,15 @@
         insetsInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION);
     }
 
+    /**
+     * Called whenever a new ui controller is set, and should update anything that depends on the
+     * ui controller.
+     */
+    public void onUiControllerChanged() {
+        updateNavButtonInAppDisplayProgressForSysui();
+        updateNavButtonTranslationY();
+    }
+
     @Override
     public void dumpLogs(String prefix, PrintWriter pw) {
         pw.println(prefix + "NavbarButtonsViewController:");
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 43feec7..a1390ae 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -591,9 +591,7 @@
      * Sets a new data-source for this taskbar instance
      */
     public void setUIController(@NonNull TaskbarUIController uiController) {
-        mControllers.uiController.onDestroy();
-        mControllers.uiController = uiController;
-        mControllers.uiController.init(mControllers);
+        mControllers.setUiController(uiController);
     }
 
     /**
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
index 66c2eb3..d3f80e3 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
@@ -196,6 +196,19 @@
         mPostInitCallbacks.clear();
     }
 
+    /**
+     * Sets the ui controller.
+     */
+    public void setUiController(@NonNull TaskbarUIController newUiController) {
+        uiController.onDestroy();
+        uiController = newUiController;
+        uiController.init(this);
+        uiController.updateStateForSysuiFlags(mSharedState.sysuiStateFlags);
+
+        // Notify that the ui controller has changed
+        navbarButtonsViewController.onUiControllerChanged();
+    }
+
     @Nullable
     public TaskbarSharedState getSharedState() {
         // This should only be null if called before init() and after destroy().
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt
index a347908..17d10d3 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt
@@ -101,10 +101,10 @@
     @SuppressLint("UseSwitchCompatOrMaterialCode")
     override fun onFinishInflate() {
         super.onFinishInflate()
-        val taskbarSwitchOption = findViewById<LinearLayout>(R.id.taskbar_switch_option)
-        val alwaysShowTaskbarSwitch = findViewById<Switch>(R.id.taskbar_pinning_switch)
+        val taskbarSwitchOption = requireViewById<LinearLayout>(R.id.taskbar_switch_option)
+        val alwaysShowTaskbarSwitch = requireViewById<Switch>(R.id.taskbar_pinning_switch)
         val navigationModeChangeOption =
-            findViewById<LinearLayout>(R.id.navigation_mode_switch_option)
+            requireViewById<LinearLayout>(R.id.navigation_mode_switch_option)
         alwaysShowTaskbarSwitch.isChecked = alwaysShowTaskbarOn
         taskbarSwitchOption.setOnClickListener {
             alwaysShowTaskbarSwitch.isClickable = true
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index 17e7e1b..ddea51f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -202,6 +202,11 @@
                 public void onStateTransitionComplete(LauncherState finalState) {
                     mLauncherState = finalState;
                     updateStateForFlag(FLAG_LAUNCHER_IN_STATE_TRANSITION, false);
+                    // TODO(b/279514548) Cleans up bad state that can occur when user interacts with
+                    // taskbar on top of transparent activity.
+                    if (finalState == LauncherState.NORMAL && mLauncher.isResumed()) {
+                        updateStateForFlag(FLAG_RESUMED, true);
+                    }
                     applyState();
                     boolean disallowLongClick = finalState == LauncherState.OVERVIEW_SPLIT_SELECT;
                     com.android.launcher3.taskbar.Utilities.setOverviewDragState(
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
index 5eec726..512b77a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarPopupController.java
@@ -177,7 +177,6 @@
                     systemShortcuts);
         }
 
-        icon.clearAccessibilityFocus();
         container.addOnAttachStateChangeListener(
                 new PopupLiveUpdateHandler<BaseTaskbarContext>(context, container) {
                     @Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
index a8b7698..3e1d0f1 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PredictedAppIcon.java
@@ -189,7 +189,7 @@
         } else {
             float[] hctPlateColor = new float[3];
             ColorUtils.colorToM3HCT(mDotParams.appColor, hctPlateColor);
-            newPlateColor = ColorUtils.M3HCTtoColor(hctPlateColor[0], 36, 85);
+            newPlateColor = ColorUtils.M3HCTToColor(hctPlateColor[0], 36, 85);
         }
 
         if (!animate) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 658bf2d..b621a28 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -204,6 +204,8 @@
 
     public static final boolean GO_LOW_RAM_RECENTS_ENABLED = false;
 
+    protected static final String RING_APPEAR_ANIMATION_PREFIX = "RingAppearAnimation\t";
+
     private FixedContainerItems mAllAppsPredictions;
     private HotseatPredictionController mHotseatPredictionController;
     private DepthController mDepthController;
@@ -345,11 +347,13 @@
 
     @Override
     public RunnableList startActivitySafely(View v, Intent intent, ItemInfo item) {
-        // Only pause is taskbar controller is not present
+        // Only pause is taskbar controller is not present until the transition (if it exists) ends
         mHotseatPredictionController.setPauseUIUpdate(getTaskbarUIController() == null);
         RunnableList result = super.startActivitySafely(v, intent, item);
-        if (getTaskbarUIController() == null && result == null) {
-            mHotseatPredictionController.setPauseUIUpdate(false);
+        if (result == null) {
+            if (getTaskbarUIController() == null) {
+                mHotseatPredictionController.setPauseUIUpdate(false);
+            }
         } else {
             result.add(() -> mHotseatPredictionController.setPauseUIUpdate(false));
         }
@@ -470,6 +474,10 @@
     public void onDestroy() {
         mAppTransitionManager.onActivityDestroyed();
         if (mUnfoldTransitionProgressProvider != null) {
+            if (FeatureFlags.RECEIVE_UNFOLD_EVENTS_FROM_SYSUI.get()) {
+                SystemUiProxy.INSTANCE.get(this).setUnfoldAnimationListener(null);
+            }
+
             mUnfoldTransitionProgressProvider.destroy();
         }
 
@@ -1330,5 +1338,8 @@
         if (recentsView != null) {
             recentsView.getSplitSelectController().dump(prefix, writer);
         }
+        if (mAppTransitionManager != null) {
+            mAppTransitionManager.dump(prefix + "\t" + RING_APPEAR_ANIMATION_PREFIX, writer);
+        }
     }
 }
diff --git a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
index 1913091..eff53f3 100644
--- a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -139,6 +139,7 @@
         mTmpMatrix.setScale(scale, scale,
                 app.localBounds.exactCenterX(), app.localBounds.exactCenterY());
         builder.setMatrix(mTmpMatrix).setAlpha(alpha);
+        builder.setShow();
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
index 523a98e..2256cbf 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
@@ -15,6 +15,7 @@
  */
 package com.android.quickstep;
 
+import static android.view.RemoteAnimationTarget.MODE_CLOSING;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
 
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
@@ -37,6 +38,7 @@
 import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Set;
 
@@ -101,9 +103,22 @@
             RemoteAnimationTarget[] appTargets,
             RemoteAnimationTarget[] wallpaperTargets,
             Rect homeContentInsets, Rect minimizedHomeBounds) {
+        long appCount = Arrays.stream(appTargets)
+                .filter(app -> app.mode == MODE_CLOSING)
+                .count();
+        if (appCount == 0) {
+            // Edge case, if there are no closing app targets, then Launcher has nothing to handle
+            ActiveGestureLog.INSTANCE.addLog(
+                    /* event= */ "RecentsAnimationCallbacks.onAnimationStart (canceled)",
+                    /* extras= */ 0,
+                    /* gestureEvent= */ START_RECENTS_ANIMATION);
+            notifyAnimationCanceled();
+            animationController.finish(false /* toHome */, false /* sendUserLeaveHint */);
+            return;
+        }
+
         mController = new RecentsAnimationController(animationController,
                 mAllowMinimizeSplitScreen, this::onAnimationFinished);
-
         if (mCancelled) {
             Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(),
                     mController::finishAnimationToApp);
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index 410ba21..eacca0d 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -19,6 +19,7 @@
 
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import static com.android.launcher3.util.NavigationMode.NO_BUTTON;
 import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
 import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_INITIALIZED;
 import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_STARTED;
@@ -37,6 +38,7 @@
 
 import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.util.DisplayController;
 import com.android.quickstep.TopTaskTracker.CachedTaskInfo;
 import com.android.quickstep.util.ActiveGestureLog;
 import com.android.quickstep.views.DesktopTaskView;
@@ -162,10 +164,16 @@
 
                 for (RemoteAnimationTarget compat : appearedTaskTargets) {
                     if (compat.windowConfiguration.getActivityType() == ACTIVITY_TYPE_HOME
-                            && activityInterface.getCreatedActivity() instanceof RecentsActivity) {
-                        // When receive opening home activity while recents is running, enter home
-                        // and dismiss recents.
-                        ((RecentsActivity) activityInterface.getCreatedActivity()).startHome();
+                            && activityInterface.getCreatedActivity() instanceof RecentsActivity
+                            && DisplayController.getNavigationMode(mCtx) != NO_BUTTON) {
+                        // The only time we get onTasksAppeared() in button navigation with a
+                        // 3p launcher is if the user goes to overview first, and in this case we
+                        // can immediately finish the transition
+                        RecentsView recentsView =
+                                activityInterface.getCreatedActivity().getOverviewPanel();
+                        if (recentsView != null) {
+                            recentsView.finishRecentsAnimation(true, null);
+                        }
                         return;
                     }
                 }
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index 499a260..1238819 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -473,16 +473,14 @@
                     throw new IllegalStateException(
                             "Expected task to be showing, but it is " + mode);
                 }
-                if (change.getParent() == null) {
-                    throw new IllegalStateException("Initiating multi-split launch but the split"
-                            + "root of " + taskId + " is already visible or has broken hierarchy.");
-                }
             }
             if (taskId == initialTaskId) {
-                splitRoot1 = transitionInfo.getChange(change.getParent());
+                splitRoot1 = change.getParent() == null ? change :
+                        transitionInfo.getChange(change.getParent());
             }
             if (taskId == secondTaskId) {
-                splitRoot2 = transitionInfo.getChange(change.getParent());
+                splitRoot2 = change.getParent() == null ? change :
+                        transitionInfo.getChange(change.getParent());
             }
         }
 
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java
index ab70272..eac09ad 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java
@@ -103,7 +103,8 @@
         mStateCallback = new MultiStateCallback(STATE_NAMES);
         mStateCallback.runOnceAtState(STATE_TARGET_RECEIVED | STATE_HANDLER_INVALIDATED,
                 this::endRemoteAnimation);
-        mStateCallback.runOnceAtState(STATE_FLING_FINISHED, this::onFlingFinished);
+        mStateCallback.runOnceAtState(STATE_TARGET_RECEIVED | STATE_FLING_FINISHED,
+                this::onFlingFinished);
 
         mSwipeDetector = new SingleAxisSwipeDetector(mContext, this, VERTICAL);
         mSwipeDetector.setDetectableScrollConditions(DIRECTION_POSITIVE, false);
diff --git a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
index 2eaff46..6619dd8 100644
--- a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
+++ b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java
@@ -254,6 +254,9 @@
         mBinder.setSwipeUpProxy(isResumed() ? this::createSwipeUpProxy : null);
         mBinder.setOverviewTargetChangeListener(mBinder::preloadOverviewForSUWAllSet);
         mBinder.preloadOverviewForSUWAllSet();
+        if (mTaskbarManager != null) {
+            mLauncherStartAnim = mTaskbarManager.createLauncherStartFromSuwAnim(MAX_SWIPE_DURATION);
+        }
     }
 
     @Override
@@ -328,12 +331,9 @@
         mRootView.setAlpha(alpha);
         mRootView.setTranslationY((alpha - 1) * mSwipeUpShift);
 
-        if (mLauncherStartAnim == null && mTaskbarManager != null) {
-            mLauncherStartAnim = mTaskbarManager.createLauncherStartFromSuwAnim(MAX_SWIPE_DURATION);
-        }
         if (mLauncherStartAnim != null) {
-            mLauncherStartAnim.setPlayFraction(Utilities.mapBoundToRange(
-                    mSwipeProgress.value, 0, 1, 0, 1, FAST_OUT_SLOW_IN));
+            mLauncherStartAnim.setPlayFraction(
+                    FAST_OUT_SLOW_IN.getInterpolation(mSwipeProgress.value));
         }
         maybeResumeOrPauseBackgroundAnimation();
     }
diff --git a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
index 6d15e8b..e0b5272 100644
--- a/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
+++ b/quickstep/src/com/android/quickstep/util/LauncherUnfoldAnimationController.java
@@ -60,6 +60,8 @@
     private final NaturalRotationUnfoldProgressProvider mNaturalOrientationProgressProvider;
     private final UnfoldMoveFromCenterHotseatAnimator mUnfoldMoveFromCenterHotseatAnimator;
     private final UnfoldMoveFromCenterWorkspaceAnimator mUnfoldMoveFromCenterWorkspaceAnimator;
+    private final TransitionStatusProvider mExternalTransitionStatusProvider =
+            new TransitionStatusProvider();
     private PreemptiveUnfoldTransitionProgressProvider mPreemptiveProgressProvider = null;
     private Boolean mIsTablet = null;
 
@@ -88,6 +90,8 @@
                     unfoldTransitionProgressProvider);
         }
 
+        unfoldTransitionProgressProvider.addCallback(mExternalTransitionStatusProvider);
+
         mUnfoldMoveFromCenterHotseatAnimator = new UnfoldMoveFromCenterHotseatAnimator(launcher,
                 windowManager, rotationChangeProvider);
         mUnfoldMoveFromCenterWorkspaceAnimator = new UnfoldMoveFromCenterWorkspaceAnimator(launcher,
@@ -166,11 +170,26 @@
         }
 
         if (mIsTablet != null && dp.isTablet != mIsTablet) {
-            if (dp.isTablet && SystemUiProxy.INSTANCE.get(mLauncher).isActive()) {
+            // We should preemptively start the animation only if:
+            // - We changed to the unfolded screen
+            // - SystemUI IPC connection is alive, so we won't end up in a situation that we won't
+            //   receive transition progress events from SystemUI later because there was no
+            //   IPC connection established (e.g. because of SystemUI crash)
+            // - SystemUI has not already sent unfold animation progress events. This might happen
+            //   if Launcher was not open during unfold, in this case we receive the configuration
+            //   change only after we went back to home screen and we don't want to start the
+            //   animation in this case.
+            if (dp.isTablet
+                    && SystemUiProxy.INSTANCE.get(mLauncher).isActive()
+                    && !mExternalTransitionStatusProvider.hasRun()) {
                 // Preemptively start the unfold animation to make sure that we have drawn
                 // the first frame of the animation before the screen gets unblocked
                 preemptivelyStartAnimationOnNextFrame();
             }
+
+            if (!dp.isTablet) {
+                mExternalTransitionStatusProvider.onFolded();
+            }
         }
 
         mIsTablet = dp.isTablet;
@@ -222,4 +241,48 @@
             HOTSEAT_SCALE_PROPERTY.setValue(mLauncher.getHotseat(), value);
         }
     }
+
+    /**
+     * Class to track the current status of the external transition provider (the events are coming
+     * from the SystemUI side through IPC), it allows to check if the transition has already
+     * finished or currently running on the SystemUI side since last unfold.
+     */
+    private static class TransitionStatusProvider implements TransitionProgressListener {
+
+        private boolean mHasRun = false;
+
+        @Override
+        public void onTransitionStarted() {
+            markAsRun();
+        }
+
+        @Override
+        public void onTransitionProgress(float progress) {
+            markAsRun();
+        }
+
+        @Override
+        public void onTransitionFinished() {
+            markAsRun();
+        }
+
+        /**
+         * Called when the device is folded, so we can reset the status of the animation
+         */
+        public void onFolded() {
+            mHasRun = false;
+        }
+
+        /**
+         * Returns true if there was an animation already (or it is currently running) after
+         * unfolding the device
+         */
+        public boolean hasRun() {
+            return mHasRun;
+        }
+
+        private void markAsRun() {
+            mHasRun = true;
+        }
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectDataHolder.kt b/quickstep/src/com/android/quickstep/util/SplitSelectDataHolder.kt
index e073264..3ff461a 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectDataHolder.kt
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectDataHolder.kt
@@ -156,18 +156,23 @@
      */
     fun setSecondTask(pendingIntent: PendingIntent) {
         secondPendingIntent = pendingIntent
-        secondUser = pendingIntent.creatorUserHandle!!
+        secondUser = pendingIntent.creatorUserHandle
     }
 
     private fun getShortcutInfo(intent: Intent?, user: UserHandle?): ShortcutInfo? {
-        if (intent?.getPackage() == null) {
+        val intentPackage = intent?.getPackage()
+        if (intentPackage == null) {
             return null
         }
         val shortcutId = intent.getStringExtra(ShortcutKey.EXTRA_SHORTCUT_ID)
                 ?: return null
         try {
-            val context: Context = context.createPackageContextAsUser(
-                    intent.getPackage(), 0 /* flags */, user)
+            val context: Context =
+                if (user != null) {
+                    context.createPackageContextAsUser(intentPackage, 0 /* flags */, user)
+                } else {
+                    context.createPackageContext(intentPackage, 0 /* *flags */)
+                }
             return ShortcutInfo.Builder(context, shortcutId).build()
         } catch (e: PackageManager.NameNotFoundException) {
             Log.w(TAG, "Failed to create a ShortcutInfo for " + intent.getPackage())
@@ -250,7 +255,7 @@
      * convert [secondIntent]
      */
     private fun convertIntentsToFinalTypes() {
-        initialShortcut = getShortcutInfo(initialIntent, initialUser)
+        initialShortcut = getShortcutInfo(initialIntent, checkNotNull(initialUser))
         initialPendingIntent = getPendingIntent(initialIntent, initialUser)
         initialIntent = null
 
@@ -264,7 +269,7 @@
             return
         }
 
-        secondShortcut = getShortcutInfo(secondIntent, secondUser)
+        secondShortcut = getShortcutInfo(secondIntent, checkNotNull(secondUser))
         secondPendingIntent = getPendingIntent(secondIntent, secondUser)
         secondIntent = null
     }
@@ -405,4 +410,4 @@
         writer.println("$prefix\tinitialShortcut= $initialShortcut")
         writer.println("$prefix\tsecondShortcut= $secondShortcut")
     }
-}
\ No newline at end of file
+}
diff --git a/quickstep/src/com/android/quickstep/util/TaskRestartedDuringLaunchListener.java b/quickstep/src/com/android/quickstep/util/TaskRestartedDuringLaunchListener.java
new file mode 100644
index 0000000..91e8376
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/TaskRestartedDuringLaunchListener.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.util;
+
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import com.android.launcher3.util.ActivityLifecycleCallbacksAdapter;
+import com.android.quickstep.RecentsModel;
+import com.android.systemui.shared.system.TaskStackChangeListener;
+import com.android.systemui.shared.system.TaskStackChangeListeners;
+
+/**
+ * This class tracks the failure of a task launch through the Launcher.startActivitySafely() call,
+ * in an edge case in which a task may already be visible on screen (ie. in PIP) and no transition
+ * will be run in WM, which results in expected callbacks to not be processed.
+ *
+ * We transiently register a task stack listener during a task launch and if the restart signal is
+ * received, then the registered callback will be notified.
+ */
+public class TaskRestartedDuringLaunchListener implements TaskStackChangeListener {
+
+    private static final String TAG = "TaskRestartedDuringLaunchListener";
+
+    private @NonNull Runnable mTaskRestartedCallback = null;
+
+    /**
+     * Registers a failure listener callback if it detects a scenario in which an app launch
+     * resulted in an already existing task to be "restarted".
+     */
+    public void register(@NonNull Runnable taskRestartedCallback) {
+        TaskStackChangeListeners.getInstance().registerTaskStackListener(this);
+        mTaskRestartedCallback = taskRestartedCallback;
+    }
+
+    /**
+     * Unregisters the failure listener.
+     */
+    public void unregister() {
+        TaskStackChangeListeners.getInstance().unregisterTaskStackListener(this);
+        mTaskRestartedCallback = null;
+    }
+
+    @Override
+    public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
+            boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) {
+        if (wasVisible) {
+            Log.d(TAG, "Detected activity restart during launch for task=" + task.taskId);
+            mTaskRestartedCallback.run();
+            unregister();
+        }
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 17d83ec..83a5c72 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -841,17 +841,20 @@
                 // the actual overview state
                 failureListener.register(mActivity, mTask.key.id, () -> {
                     notifyTaskLaunchFailed(TAG);
-                    // Disable animations for now, as it is an edge case and the app usually covers
-                    // launcher and also any state transition animation also gets clobbered by
-                    // QuickstepTransitionManager.createWallpaperOpenAnimations when launcher
-                    // shows again
-                    getRecentsView().startHome(false /* animated */);
                     RecentsView rv = getRecentsView();
-                    if (rv != null && rv.mSizeStrategy.getTaskbarController() != null) {
-                        // LauncherTaskbarUIController depends on the launcher state when checking
-                        // whether to handle resume, but that can come in before startHome() changes
-                        // the state, so force-refresh here to ensure the taskbar is updated
-                        rv.mSizeStrategy.getTaskbarController().refreshResumedState();
+                    if (rv != null) {
+                        // Disable animations for now, as it is an edge case and the app usually
+                        // covers launcher and also any state transition animation also gets
+                        // clobbered by QuickstepTransitionManager.createWallpaperOpenAnimations
+                        // when launcher shows again
+                        rv.startHome(false /* animated */);
+                        if (rv.mSizeStrategy.getTaskbarController() != null) {
+                            // LauncherTaskbarUIController depends on the launcher state when
+                            // checking whether to handle resume, but that can come in before
+                            // startHome() changes the state, so force-refresh here to ensure the
+                            // taskbar is updated
+                            rv.mSizeStrategy.getTaskbarController().refreshResumedState();
+                        }
                     }
                 });
             }
diff --git a/res/color-v31/surface.xml b/res/color-v31/surface.xml
deleted file mode 100644
index da4571a..0000000
--- a/res/color-v31/surface.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2021, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<selector
-    xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:color="?attr/materialColorSurfaceContainerHighest"/>
-</selector>
-
diff --git a/res/drawable/add_item_dialog_background.xml b/res/drawable/add_item_dialog_background.xml
index c3a8269..e279fa0 100644
--- a/res/drawable/add_item_dialog_background.xml
+++ b/res/drawable/add_item_dialog_background.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
     android:shape="rectangle" >
-    <solid android:color="@color/surface" />
+    <solid android:color="@color/material_color_surface_container_highest" />
     <corners
         android:topLeftRadius="?android:attr/dialogCornerRadius"
         android:topRightRadius="?android:attr/dialogCornerRadius" />
diff --git a/res/drawable/button_top_rounded_bordered_ripple.xml b/res/drawable/button_top_rounded_bordered_ripple.xml
index f15a4a0..f5b6886 100644
--- a/res/drawable/button_top_rounded_bordered_ripple.xml
+++ b/res/drawable/button_top_rounded_bordered_ripple.xml
@@ -25,7 +25,7 @@
                     android:topRightRadius="12dp"
                     android:bottomLeftRadius="4dp"
                     android:bottomRightRadius="4dp"  />
-                <solid android:color="@color/surface"/>
+                <solid android:color="@color/material_color_surface_container_highest"/>
                 <stroke
                     android:width="2dp"
                     android:color="@color/button_bg"/>
diff --git a/res/layout/hotseat.xml b/res/layout/hotseat.xml
index 95ebd94..82b0b8d 100644
--- a/res/layout/hotseat.xml
+++ b/res/layout/hotseat.xml
@@ -21,5 +21,4 @@
     android:layout_height="match_parent"
     android:theme="@style/HomeScreenElementTheme"
     android:importantForAccessibility="no"
-    android:preferKeepClear="true"
     launcher:containerType="hotseat" />
\ No newline at end of file
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 25eb160..6669043 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -2194,6 +2194,8 @@
 
     /**
      * Returns the CellLayout of the specified container at the specified screen.
+     *
+     * @param screenId must be presenterPos and not modelPos.
      */
     public CellLayout getCellLayout(int container, int screenId) {
         return (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT)
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 8b124dc..6097fb1 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -331,8 +331,7 @@
      * Gets the translation provider for workspace pages.
      */
     public PageTranslationProvider getWorkspacePageTranslationProvider(Launcher launcher) {
-        if (this != SPRING_LOADED
-                || this != EDIT_MODE
+        if (!(this == SPRING_LOADED || this == EDIT_MODE)
                 || !launcher.getDeviceProfile().isTwoPanels) {
             return DEFAULT_PAGE_TRANSLATION_PROVIDER;
         }
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index d4140d8..1977704 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -43,7 +43,6 @@
 import android.util.FloatProperty;
 import android.util.Log;
 import android.util.SparseArray;
-import android.util.TypedValue;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
@@ -272,9 +271,8 @@
                 0,
                 0 // Bottom left
         };
-        final TypedValue value = new TypedValue();
-        getContext().getTheme().resolveAttribute(android.R.attr.colorBackground, value, true);
-        mBottomSheetBackgroundColor = value.data;
+        mBottomSheetBackgroundColor =
+                Themes.getAttrColor(getContext(), R.attr.materialColorSurfaceDim);
         updateBackgroundVisibility(mActivityContext.getDeviceProfile());
         mSearchUiManager.initializeSearch(this);
     }
diff --git a/src/com/android/launcher3/celllayout/MulticellReorderAlgorithm.java b/src/com/android/launcher3/celllayout/MulticellReorderAlgorithm.java
index cb12161..a2e26b3 100644
--- a/src/com/android/launcher3/celllayout/MulticellReorderAlgorithm.java
+++ b/src/com/android/launcher3/celllayout/MulticellReorderAlgorithm.java
@@ -19,8 +19,10 @@
 
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.MultipageCellLayout;
+import com.android.launcher3.ShortcutAndWidgetContainer;
 import com.android.launcher3.util.GridOccupancy;
 
+import java.util.Arrays;
 import java.util.function.Supplier;
 
 /**
@@ -79,7 +81,7 @@
         lp.canReorder = false;
         mcl.setCountX(mcl.getCountX() + 1);
         mcl.getShortcutsAndWidgets().addViewInLayout(mSeam, lp);
-        mcl.setOccupied(createGridOccupancyWithSeam(mcl.getOccupied()));
+        mcl.setOccupied(createGridOccupancyWithSeam());
         mcl.mTmpOccupied = new GridOccupancy(mcl.getCountX(), mcl.getCountY());
     }
 
@@ -93,7 +95,8 @@
 
     /**
      * The function supplied here will execute while the CellLayout has a simulated seam added.
-     * @param f function to run under simulation
+     *
+     * @param f   function to run under simulation
      * @param <T> return value of the supplied function
      * @return Value of supplied function
      */
@@ -110,18 +113,17 @@
         return res;
     }
 
-    GridOccupancy createGridOccupancyWithSeam(GridOccupancy gridOccupancy) {
+    GridOccupancy createGridOccupancyWithSeam() {
+        ShortcutAndWidgetContainer shortcutAndWidgets = mCellLayout.getShortcutsAndWidgets();
         GridOccupancy grid = new GridOccupancy(mCellLayout.getCountX(), mCellLayout.getCountY());
-        for (int x = 0; x < mCellLayout.getCountX(); x++) {
-            for (int y = 0; y < mCellLayout.getCountY(); y++) {
-                int offset = x >= mCellLayout.getCountX() / 2 ? 1 : 0;
-                if (x == mCellLayout.getCountX() / 2) {
-                    grid.cells[x][y] = true;
-                } else {
-                    grid.cells[x][y] = gridOccupancy.cells[x - offset][y];
-                }
-            }
+        for (int i = 0; i < shortcutAndWidgets.getChildCount(); i++) {
+            View view = shortcutAndWidgets.getChildAt(i);
+            CellLayoutLayoutParams lp = (CellLayoutLayoutParams) view.getLayoutParams();
+            int seamOffset = lp.getCellX() >= mCellLayout.getCountX() / 2 && lp.canReorder ? 1 : 0;
+            grid.markCells(lp.getCellX() + seamOffset, lp.getCellY(), lp.cellHSpan, lp.cellVSpan,
+                    true);
         }
+        Arrays.fill(grid.cells[mCellLayout.getCountX() / 2], true);
         return grid;
     }
 }
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index f5f6769..76574b6 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -194,7 +194,7 @@
             "In foldables, when reordering the icons and widgets, is now going to use both sides");
 
     public static final BooleanFlag FOLDABLE_SINGLE_PAGE = getDebugFlag(270395274,
-            "FOLDABLE_SINGLE_PAGE", ENABLED, "Use a single page for the workspace");
+            "FOLDABLE_SINGLE_PAGE", DISABLED, "Use a single page for the workspace");
 
     // TODO(Block 12): Clean up flags
     public static final BooleanFlag ENABLE_MULTI_INSTANCE = getDebugFlag(270396680,
@@ -302,7 +302,7 @@
             "Enable widget transition animation when resizing the widgets");
 
     public static final BooleanFlag PREEMPTIVE_UNFOLD_ANIMATION_START = getDebugFlag(270397209,
-            "PREEMPTIVE_UNFOLD_ANIMATION_START", DISABLED,
+            "PREEMPTIVE_UNFOLD_ANIMATION_START", ENABLED,
             "Enables starting the unfold animation preemptively when unfolding, without"
                     + "waiting for SystemUI and then merging the SystemUI progress whenever we "
                     + "start receiving the events");
diff --git a/src/com/android/launcher3/folder/LauncherDelegate.java b/src/com/android/launcher3/folder/LauncherDelegate.java
index f15dc83..7ac2472 100644
--- a/src/com/android/launcher3/folder/LauncherDelegate.java
+++ b/src/com/android/launcher3/folder/LauncherDelegate.java
@@ -93,7 +93,7 @@
                         // Move the item from the folder to the workspace, in the position of the
                         // folder
                         CellLayout cellLayout = mLauncher.getCellLayout(info.container,
-                                info.screenId);
+                                mLauncher.getCellPosMapper().mapModelToPresenter(info).screenId);
                         if (cellLayout == null) {
                             return;
                         }
diff --git a/src/com/android/launcher3/graphics/PreloadIconDrawable.java b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
index d366c4a..2dd92a1 100644
--- a/src/com/android/launcher3/graphics/PreloadIconDrawable.java
+++ b/src/com/android/launcher3/graphics/PreloadIconDrawable.java
@@ -155,19 +155,19 @@
         // Progress color
         float[] m3HCT = new float[3];
         ColorUtils.colorToM3HCT(primaryIconColor, m3HCT);
-        mProgressColor = ColorUtils.M3HCTtoColor(
+        mProgressColor = ColorUtils.M3HCTToColor(
                 m3HCT[0],
                 m3HCT[1],
                 isDarkMode ? Math.max(m3HCT[2], 55) : Math.min(m3HCT[2], 40));
 
         // Track color
-        mTrackColor = ColorUtils.M3HCTtoColor(
+        mTrackColor = ColorUtils.M3HCTToColor(
                 m3HCT[0],
                 16,
                 isDarkMode ? 30 : 90
         );
         // Plate color
-        mPlateColor = ColorUtils.M3HCTtoColor(
+        mPlateColor = ColorUtils.M3HCTToColor(
                 m3HCT[0],
                 isDarkMode ? 36 : 24,
                 isDarkMode ? (isThemed() ? 10 : 20) : 80
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 8274789..1f26bab 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -125,6 +125,14 @@
         this(context, null, 0);
     }
 
+    @Override
+    protected View getAccessibilityInitialFocusView() {
+        if (mSystemShortcutContainer != null) {
+            return mSystemShortcutContainer.getChildAt(0);
+        }
+        return super.getAccessibilityInitialFocusView();
+    }
+
     public LauncherAccessibilityDelegate getAccessibilityDelegate() {
         return mAccessibilityDelegate;
     }
@@ -242,7 +250,6 @@
                     popupDataProvider.getNotificationKeysForItem(item),
                     systemShortcuts);
         }
-        launcher.tryClearAccessibilityFocus(icon);
         launcher.refreshAndBindWidgetsForPackageUser(PackageUserKey.fromItemInfo(item));
         container.requestFocus();
         return container;
diff --git a/src/com/android/launcher3/util/EventLogArray.kt b/src/com/android/launcher3/util/EventLogArray.kt
new file mode 100644
index 0000000..a17d650
--- /dev/null
+++ b/src/com/android/launcher3/util/EventLogArray.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.launcher3.util
+
+import java.io.PrintWriter
+import java.text.SimpleDateFormat
+import java.util.Date
+import java.util.Locale
+
+/**
+ * A utility class to record and log events. Events are stored in a fixed size array and old logs
+ * are purged as new events come.
+ */
+class EventLogArray(private val name: String, size: Int) {
+
+    companion object {
+        private const val TYPE_ONE_OFF = 0
+        private const val TYPE_FLOAT = 1
+        private const val TYPE_INTEGER = 2
+        private const val TYPE_BOOL_TRUE = 3
+        private const val TYPE_BOOL_FALSE = 4
+        private fun isEntrySame(entry: EventEntry?, type: Int, event: String): Boolean {
+            return entry != null && entry.type == type && entry.event == event
+        }
+    }
+
+    private val logs: Array<EventEntry?>
+    private var nextIndex = 0
+
+    init {
+        logs = arrayOfNulls(size)
+    }
+
+    fun addLog(event: String) {
+        addLog(TYPE_ONE_OFF, event, 0f)
+    }
+
+    fun addLog(event: String, extras: Int) {
+        addLog(TYPE_INTEGER, event, extras.toFloat())
+    }
+
+    fun addLog(event: String, extras: Float) {
+        addLog(TYPE_FLOAT, event, extras)
+    }
+
+    fun addLog(event: String, extras: Boolean) {
+        addLog(if (extras) TYPE_BOOL_TRUE else TYPE_BOOL_FALSE, event, 0f)
+    }
+
+    private fun addLog(type: Int, event: String, extras: Float) {
+        // Merge the logs if it's a duplicate
+        val last = (nextIndex + logs.size - 1) % logs.size
+        val secondLast = (nextIndex + logs.size - 2) % logs.size
+        if (isEntrySame(logs[last], type, event) && isEntrySame(logs[secondLast], type, event)) {
+            logs[last]!!.update(type, event, extras)
+            logs[secondLast]!!.duplicateCount++
+            return
+        }
+        if (logs[nextIndex] == null) {
+            logs[nextIndex] = EventEntry()
+        }
+        logs[nextIndex]!!.update(type, event, extras)
+        nextIndex = (nextIndex + 1) % logs.size
+    }
+
+    fun dump(prefix: String, writer: PrintWriter) {
+        writer.println("$prefix$name event history:")
+        val sdf = SimpleDateFormat("  HH:mm:ss.SSSZ  ", Locale.US)
+        val date = Date()
+        for (i in logs.indices) {
+            val log = logs[(nextIndex + logs.size - i - 1) % logs.size] ?: continue
+            date.time = log.time
+            val msg = StringBuilder(prefix).append(sdf.format(date)).append(log.event)
+            when (log.type) {
+                TYPE_BOOL_FALSE -> msg.append(": false")
+                TYPE_BOOL_TRUE -> msg.append(": true")
+                TYPE_FLOAT -> msg.append(": ").append(log.extras)
+                TYPE_INTEGER -> msg.append(": ").append(log.extras.toInt())
+                else -> {}
+            }
+            if (log.duplicateCount > 0) {
+                msg.append(" & ").append(log.duplicateCount).append(" similar events")
+            }
+            writer.println(msg)
+        }
+    }
+
+    /** A single event entry. */
+    private class EventEntry {
+        var type = 0
+        var event: String? = null
+        var extras = 0f
+        var time: Long = 0
+        var duplicateCount = 0
+        fun update(type: Int, event: String, extras: Float) {
+            this.type = type
+            this.event = event
+            this.extras = extras
+            time = System.currentTimeMillis()
+            duplicateCount = 0
+        }
+    }
+}