Merge "Adds supportsCommitHaptic" into main
diff --git a/aconfig/launcher.aconfig b/aconfig/launcher.aconfig
index 760d8ac..9080284 100644
--- a/aconfig/launcher.aconfig
+++ b/aconfig/launcher.aconfig
@@ -76,3 +76,10 @@
     description: "Enables logging of Launcher restore metrics to the Backup & Restore team"
     bug: "307527314"
 }
+
+flag {
+    name: "enable_unfolded_two_pane_picker"
+    namespace: "launcher"
+    description: "Enables two pane widget picker for unfolded foldables"
+    bug: "313922374"
+}
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
index cbb991d..b29ce6b 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
@@ -133,11 +133,20 @@
         GroupTask task = mControllerCallbacks.getTaskAt(index);
         if (task == null) {
             return Math.max(0, index);
-        } else if (mOnDesktop) {
+        }
+        Task task2 = task.task2;
+        int runningTaskId = ActivityManagerWrapper.getInstance().getRunningTask().taskId;
+        if (runningTaskId == task.task1.key.id
+                || (task2 != null && runningTaskId == task2.key.id)) {
+            // Ignore attempts to run the selected task if it is already running.
+            return -1;
+        }
+
+        if (mOnDesktop) {
             UI_HELPER_EXECUTOR.execute(() ->
                     SystemUiProxy.INSTANCE.get(mKeyboardQuickSwitchView.getContext())
                             .showDesktopApp(task.task1.key.id));
-        } else if (task.task2 == null) {
+        } else if (task2 == null) {
             UI_HELPER_EXECUTOR.execute(() ->
                     ActivityManagerWrapper.getInstance().startActivityFromRecents(
                             task.task1.key,
@@ -145,8 +154,7 @@
                                     taskView == null ? mKeyboardQuickSwitchView : taskView, null)
                                     .options));
         } else {
-            mControllers.uiController.launchSplitTasks(
-                    taskView == null ? mKeyboardQuickSwitchView : taskView, task);
+            mControllers.uiController.launchSplitTasks(task);
         }
         return -1;
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index bbe73ff..b4754c6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -26,7 +26,6 @@
 import android.os.RemoteException;
 import android.util.Log;
 import android.view.TaskTransitionSpec;
-import android.view.View;
 import android.view.WindowManagerGlobal;
 
 import androidx.annotation.NonNull;
@@ -386,8 +385,8 @@
     }
 
     @Override
-    public void launchSplitTasks(@NonNull View taskView, @NonNull GroupTask groupTask) {
-        mLauncher.launchSplitTasks(taskView, groupTask);
+    public void launchSplitTasks(@NonNull GroupTask groupTask) {
+        mLauncher.launchSplitTasks(groupTask);
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index 445b312..aee3c6f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -297,11 +297,9 @@
     }
 
     /**
-     * Launches the focused task in splitscreen.
-     *
-     * No-op if the view is not yet open.
+     * Launches the given task in split-screen.
      */
-    public void launchSplitTasks(@NonNull View taskview, @NonNull GroupTask groupTask) { }
+    public void launchSplitTasks(@NonNull GroupTask groupTask) { }
 
     /**
      * Returns the matching view (if any) in the taskbar.
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 5b0c8c3..89b7fa4 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -19,6 +19,7 @@
 import static android.os.Trace.TRACE_TAG_APP;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_OPTIMIZE_MEASURE;
 import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED;
+
 import static com.android.app.animation.Interpolators.EMPHASIZED;
 import static com.android.launcher3.LauncherConstants.SavedInstanceKeys.PENDING_SPLIT_SELECT_INFO;
 import static com.android.launcher3.LauncherConstants.SavedInstanceKeys.RUNTIME_STATE;
@@ -34,8 +35,6 @@
 import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK;
 import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
 import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_HOME_TRANSITION_LISTENER;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
 import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
 import static com.android.launcher3.popup.QuickstepSystemShortcut.getSplitSelectShortcutByPosition;
@@ -1263,24 +1262,19 @@
 
     /**
      * Launches the given {@link GroupTask} in splitscreen.
-     *
-     * If the second split task is missing, launches the first task normally.
      */
-    public void launchSplitTasks(@NonNull View taskView, @NonNull GroupTask groupTask) {
-        if (groupTask.task2 == null) {
-            UI_HELPER_EXECUTOR.execute(() ->
-                    ActivityManagerWrapper.getInstance().startActivityFromRecents(
-                            groupTask.task1.key,
-                            getActivityLaunchOptions(taskView, null).options));
-            return;
-        }
+    public void launchSplitTasks(@NonNull GroupTask groupTask) {
+        // Top/left and bottom/right tasks respectively.
+        Task task1 = groupTask.task1;
+        // task2 should never be null when calling this method. Allow a crash to catch invalid calls
+        Task task2 = groupTask.task2;
         mSplitSelectStateController.launchExistingSplitPair(
                 null /* launchingTaskView */,
-                groupTask.task1.key.id,
-                groupTask.task2.key.id,
+                task1.key.id,
+                task2.key.id,
                 SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT,
                 /* callback= */ success -> mSplitSelectStateController.resetState(),
-                /* freezeTaskList= */ true,
+                /* freezeTaskList= */ false,
                 groupTask.mSplitBounds == null
                         ? SNAP_TO_50_50
                         : groupTask.mSplitBounds.snapPosition);
diff --git a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
index 5568459..9e58160 100644
--- a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
+++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
@@ -55,6 +55,7 @@
 import com.android.launcher3.QuickstepTransitionManager;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.taskbar.LauncherTaskbarUIController;
 import com.android.launcher3.uioverrides.QuickstepLauncher;
 import com.android.quickstep.util.RectFSpringAnim;
 import com.android.systemui.shared.system.QuickStepContract;
@@ -416,6 +417,10 @@
         if (mLauncher.isDestroyed()) {
             return;
         }
+        LauncherTaskbarUIController taskbarUIController = mLauncher.getTaskbarUIController();
+        if (taskbarUIController != null) {
+            taskbarUIController.onLauncherVisibilityChanged(true);
+        }
         // TODO: Catch the moment when launcher becomes visible after the top app un-occludes
         //  launcher and start animating afterwards. Currently we occasionally get a flicker from
         //  animating when launcher is still invisible.
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 39edd70..22163b9 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -18,9 +18,11 @@
 import static android.os.Trace.TRACE_TAG_APP;
 import static android.view.RemoteAnimationTarget.MODE_CLOSING;
 import static android.view.RemoteAnimationTarget.MODE_OPENING;
+
 import static com.android.launcher3.QuickstepTransitionManager.RECENTS_LAUNCH_DURATION;
 import static com.android.launcher3.QuickstepTransitionManager.STATUS_BAR_TRANSITION_DURATION;
 import static com.android.launcher3.QuickstepTransitionManager.STATUS_BAR_TRANSITION_PRE_DELAY;
+import static com.android.launcher3.testing.shared.TestProtocol.LAUNCHER_ACTIVITY_STOPPED_MESSAGE;
 import static com.android.launcher3.testing.shared.TestProtocol.OVERVIEW_STATE_ORDINAL;
 import static com.android.quickstep.OverviewComponentObserver.startHomeIntentSafely;
 import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
@@ -344,6 +346,8 @@
         // Workaround for b/78520668, explicitly trim memory once UI is hidden
         onTrimMemory(TRIM_MEMORY_UI_HIDDEN);
         mFallbackRecentsView.updateLocusId();
+        AccessibilityManagerCompat.sendTestProtocolEventToTest(
+                this, LAUNCHER_ACTIVITY_STOPPED_MESSAGE);
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
deleted file mode 100644
index 27de20c..0000000
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ /dev/null
@@ -1,1515 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.quickstep;
-
-import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
-
-import static com.android.launcher3.testing.shared.TestProtocol.testLogD;
-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.SplitConfigurationOptions.StagePosition;
-import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.RECENT_TASKS_MISSING;
-import static com.android.quickstep.util.LogUtils.splitFailureMessage;
-
-import android.app.ActivityManager;
-import android.app.ActivityOptions;
-import android.app.PendingIntent;
-import android.app.PictureInPictureParams;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ShortcutInfo;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.IBinder.DeathRecipient;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.util.Log;
-import android.view.IRecentsAnimationController;
-import android.view.IRecentsAnimationRunner;
-import android.view.IRemoteAnimationRunner;
-import android.view.MotionEvent;
-import android.view.RemoteAnimationAdapter;
-import android.view.RemoteAnimationTarget;
-import android.view.SurfaceControl;
-import android.window.IOnBackInvokedCallback;
-import android.window.RemoteTransition;
-import android.window.TaskSnapshot;
-import android.window.TransitionFilter;
-
-import androidx.annotation.MainThread;
-import androidx.annotation.Nullable;
-import androidx.annotation.WorkerThread;
-
-import com.android.internal.logging.InstanceId;
-import com.android.internal.util.ScreenshotRequest;
-import com.android.internal.view.AppearanceRegion;
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.util.MainThreadInitializedObject;
-import com.android.launcher3.util.Preconditions;
-import com.android.quickstep.util.ActiveGestureLog;
-import com.android.quickstep.util.AssistUtils;
-import com.android.systemui.shared.recents.ISystemUiProxy;
-import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
-import com.android.systemui.shared.system.RecentsAnimationListener;
-import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController;
-import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController;
-import com.android.systemui.shared.system.smartspace.SmartspaceState;
-import com.android.systemui.unfold.progress.IUnfoldAnimation;
-import com.android.systemui.unfold.progress.IUnfoldTransitionListener;
-import com.android.wm.shell.back.IBackAnimation;
-import com.android.wm.shell.bubbles.IBubbles;
-import com.android.wm.shell.bubbles.IBubblesListener;
-import com.android.wm.shell.common.split.SplitScreenConstants.PersistentSnapPosition;
-import com.android.wm.shell.desktopmode.IDesktopMode;
-import com.android.wm.shell.desktopmode.IDesktopTaskListener;
-import com.android.wm.shell.draganddrop.IDragAndDrop;
-import com.android.wm.shell.onehanded.IOneHanded;
-import com.android.wm.shell.pip.IPip;
-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.splitscreen.ISplitScreen;
-import com.android.wm.shell.splitscreen.ISplitScreenListener;
-import com.android.wm.shell.splitscreen.ISplitSelectListener;
-import com.android.wm.shell.startingsurface.IStartingWindow;
-import com.android.wm.shell.startingsurface.IStartingWindowListener;
-import com.android.wm.shell.transition.IHomeTransitionListener;
-import com.android.wm.shell.transition.IShellTransitions;
-import com.android.wm.shell.util.GroupedRecentTaskInfo;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.LinkedHashMap;
-
-/**
- * Holds the reference to SystemUI.
- */
-public class SystemUiProxy implements ISystemUiProxy {
-    private static final String TAG = SystemUiProxy.class.getSimpleName();
-
-    public static final MainThreadInitializedObject<SystemUiProxy> INSTANCE =
-            new MainThreadInitializedObject<>(SystemUiProxy::new);
-
-    private static final int MSG_SET_SHELF_HEIGHT = 1;
-    private static final int MSG_SET_LAUNCHER_KEEP_CLEAR_AREA_HEIGHT = 2;
-
-    private ISystemUiProxy mSystemUiProxy;
-    private IPip mPip;
-    private IBubbles mBubbles;
-    private ISysuiUnlockAnimationController mSysuiUnlockAnimationController;
-    private ISplitScreen mSplitScreen;
-    private IOneHanded mOneHanded;
-    private IShellTransitions mShellTransitions;
-    private IStartingWindow mStartingWindow;
-    private IRecentTasks mRecentTasks;
-    private IBackAnimation mBackAnimation;
-    private IDesktopMode mDesktopMode;
-    private IUnfoldAnimation mUnfoldAnimation;
-    private final DeathRecipient mSystemUiProxyDeathRecipient = () -> {
-        MAIN_EXECUTOR.execute(() -> clearProxy());
-    };
-
-    // Save the listeners passed into the proxy since OverviewProxyService may not have been bound
-    // yet, and we'll need to set/register these listeners with SysUI when they do.  Note that it is
-    // up to the caller to clear the listeners to prevent leaks as these can be held indefinitely
-    // in case SysUI needs to rebind.
-    private IPipAnimationListener mPipAnimationListener;
-    private IBubblesListener mBubblesListener;
-    private ISplitScreenListener mSplitScreenListener;
-    private ISplitSelectListener mSplitSelectListener;
-    private IStartingWindowListener mStartingWindowListener;
-    private ILauncherUnlockAnimationController mLauncherUnlockAnimationController;
-    private String mLauncherActivityClass;
-    private IRecentTasksListener mRecentTasksListener;
-    private IUnfoldTransitionListener mUnfoldAnimationListener;
-    private IDesktopTaskListener mDesktopTaskListener;
-    private final LinkedHashMap<RemoteTransition, TransitionFilter> mRemoteTransitions =
-            new LinkedHashMap<>();
-    private IBinder mOriginalTransactionToken = null;
-    private IOnBackInvokedCallback mBackToLauncherCallback;
-    private IRemoteAnimationRunner mBackToLauncherRunner;
-    private IDragAndDrop mDragAndDrop;
-    private IHomeTransitionListener mHomeTransitionListener;
-
-    // Used to dedupe calls to SystemUI
-    private int mLastShelfHeight;
-    private boolean mLastShelfVisible;
-
-    // Used to dedupe calls to SystemUI
-    private int mLastLauncherKeepClearAreaHeight;
-    private boolean mLastLauncherKeepClearAreaHeightVisible;
-
-    private final Context mContext;
-    private final Handler mAsyncHandler;
-
-    // TODO(141886704): Find a way to remove this
-    private int mLastSystemUiStateFlags;
-
-    /**
-     * This is a singleton pending intent that is used to start recents via Shell (which is a
-     * different process). It is bare-bones, so it's expected that the component and options will
-     * be provided via fill-in intent.
-     */
-    private final PendingIntent mRecentsPendingIntent;
-
-    public SystemUiProxy(Context context) {
-        mContext = context;
-        mAsyncHandler = new Handler(UI_HELPER_EXECUTOR.getLooper(), this::handleMessageAsync);
-        final Intent baseIntent = new Intent().setPackage(mContext.getPackageName());
-        final ActivityOptions options = ActivityOptions.makeBasic()
-                .setPendingIntentCreatorBackgroundActivityStartMode(
-                        ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
-        mRecentsPendingIntent = PendingIntent.getActivity(mContext, 0, baseIntent,
-                PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT
-                        | Intent.FILL_IN_COMPONENT, options.toBundle());
-    }
-
-    @Override
-    public void onBackPressed() {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.onBackPressed();
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call onBackPressed", e);
-            }
-        }
-    }
-
-    @Override
-    public void onImeSwitcherPressed() {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.onImeSwitcherPressed();
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call onImeSwitcherPressed", e);
-            }
-        }
-    }
-
-    @Override
-    public void setHomeRotationEnabled(boolean enabled) {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.setHomeRotationEnabled(enabled);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call onBackPressed", e);
-            }
-        }
-    }
-
-    @Override
-    public IBinder asBinder() {
-        // Do nothing
-        return null;
-    }
-
-    /**
-     * Sets proxy state, including death linkage, various listeners, and other configuration objects
-     */
-    @MainThread
-    public void setProxy(ISystemUiProxy proxy, IPip pip, IBubbles bubbles, ISplitScreen splitScreen,
-            IOneHanded oneHanded, IShellTransitions shellTransitions,
-            IStartingWindow startingWindow, IRecentTasks recentTasks,
-            ISysuiUnlockAnimationController sysuiUnlockAnimationController,
-            IBackAnimation backAnimation, IDesktopMode desktopMode,
-            IUnfoldAnimation unfoldAnimation, IDragAndDrop dragAndDrop) {
-        Preconditions.assertUIThread();
-        unlinkToDeath();
-        mSystemUiProxy = proxy;
-        mPip = pip;
-        mBubbles = bubbles;
-        mSplitScreen = splitScreen;
-        mOneHanded = oneHanded;
-        mShellTransitions = shellTransitions;
-        mStartingWindow = startingWindow;
-        mSysuiUnlockAnimationController = sysuiUnlockAnimationController;
-        mRecentTasks = recentTasks;
-        mBackAnimation = backAnimation;
-        mDesktopMode = desktopMode;
-        mUnfoldAnimation = unfoldAnimation;
-        mDragAndDrop = dragAndDrop;
-        linkToDeath();
-        // re-attach the listeners once missing due to setProxy has not been initialized yet.
-        setPipAnimationListener(mPipAnimationListener);
-        setBubblesListener(mBubblesListener);
-        registerSplitScreenListener(mSplitScreenListener);
-        registerSplitSelectListener(mSplitSelectListener);
-        setHomeTransitionListener(mHomeTransitionListener);
-        setStartingWindowListener(mStartingWindowListener);
-        setLauncherUnlockAnimationController(
-                mLauncherActivityClass, mLauncherUnlockAnimationController);
-        new LinkedHashMap<>(mRemoteTransitions).forEach(this::registerRemoteTransition);
-        setupTransactionQueue();
-        registerRecentTasksListener(mRecentTasksListener);
-        setBackToLauncherCallback(mBackToLauncherCallback, mBackToLauncherRunner);
-        setUnfoldAnimationListener(mUnfoldAnimationListener);
-        setDesktopTaskListener(mDesktopTaskListener);
-        setAssistantOverridesRequested(
-                AssistUtils.newInstance(mContext).getSysUiAssistOverrideInvocationTypes());
-    }
-
-    /**
-     * Clear the proxy to release held resources and turn the majority of its operations into no-ops
-     */
-    @MainThread
-    public void clearProxy() {
-        setProxy(null, null, null, null, null, null, null, null, null, null, null, null, null);
-    }
-
-    // TODO(141886704): Find a way to remove this
-    public void setLastSystemUiStateFlags(int stateFlags) {
-        mLastSystemUiStateFlags = stateFlags;
-    }
-
-    // TODO(141886704): Find a way to remove this
-    public int getLastSystemUiStateFlags() {
-        return mLastSystemUiStateFlags;
-    }
-
-    public boolean isActive() {
-        return mSystemUiProxy != null;
-    }
-
-    private void linkToDeath() {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.asBinder().linkToDeath(mSystemUiProxyDeathRecipient, 0 /* flags */);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Failed to link sysui proxy death recipient");
-            }
-        }
-    }
-
-    private void unlinkToDeath() {
-        if (mSystemUiProxy != null) {
-            mSystemUiProxy.asBinder().unlinkToDeath(mSystemUiProxyDeathRecipient, 0 /* flags */);
-        }
-    }
-
-    @Override
-    public void startScreenPinning(int taskId) {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.startScreenPinning(taskId);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call startScreenPinning", e);
-            }
-        }
-    }
-
-    @Override
-    public void onOverviewShown(boolean fromHome) {
-        onOverviewShown(fromHome, TAG);
-    }
-
-    public void onOverviewShown(boolean fromHome, String tag) {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.onOverviewShown(fromHome);
-            } catch (RemoteException e) {
-                Log.w(tag, "Failed call onOverviewShown from: " + (fromHome ? "home" : "app"), e);
-            }
-        }
-    }
-
-    @MainThread
-    @Override
-    public void onStatusBarTouchEvent(MotionEvent event) {
-        Preconditions.assertUIThread();
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.onStatusBarTouchEvent(event);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call onStatusBarTouchEvent with arg: " + event, e);
-            }
-        }
-    }
-
-    @Override
-    public void onStatusBarTrackpadEvent(MotionEvent event) {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.onStatusBarTrackpadEvent(event);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call onStatusBarTrackpadEvent with arg: " + event, e);
-            }
-        }
-    }
-
-    @Override
-    public void onAssistantProgress(float progress) {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.onAssistantProgress(progress);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call onAssistantProgress with progress: " + progress, e);
-            }
-        }
-    }
-
-    @Override
-    public void onAssistantGestureCompletion(float velocity) {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.onAssistantGestureCompletion(velocity);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call onAssistantGestureCompletion", e);
-            }
-        }
-    }
-
-    @Override
-    public void startAssistant(Bundle args) {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.startAssistant(args);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call startAssistant", e);
-            }
-        }
-    }
-
-    @Override
-    public void setAssistantOverridesRequested(int[] invocationTypes) {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.setAssistantOverridesRequested(invocationTypes);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call setAssistantOverridesRequested", e);
-            }
-        }
-    }
-
-    @Override
-    public void animateNavBarLongPress(boolean isTouchDown, boolean shrink, long durationMs) {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.animateNavBarLongPress(isTouchDown, shrink, durationMs);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call animateNavBarLongPress", e);
-            }
-        }
-    }
-
-    @Override
-    public void notifyAccessibilityButtonClicked(int displayId) {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.notifyAccessibilityButtonClicked(displayId);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call notifyAccessibilityButtonClicked", e);
-            }
-        }
-    }
-
-    @Override
-    public void notifyAccessibilityButtonLongClicked() {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.notifyAccessibilityButtonLongClicked();
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call notifyAccessibilityButtonLongClicked", e);
-            }
-        }
-    }
-
-    @Override
-    public void stopScreenPinning() {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.stopScreenPinning();
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call stopScreenPinning", e);
-            }
-        }
-    }
-
-    @Override
-    public void notifyPrioritizedRotation(int rotation) {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.notifyPrioritizedRotation(rotation);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call notifyPrioritizedRotation with arg: " + rotation, e);
-            }
-        }
-    }
-
-    @Override
-    public void notifyTaskbarStatus(boolean visible, boolean stashed) {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.notifyTaskbarStatus(visible, stashed);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call notifyTaskbarStatus with arg: " +
-                        visible + ", " + stashed, e);
-            }
-        }
-    }
-
-    /**
-     * NOTE: If called to suspend, caller MUST call this method to also un-suspend
-     * @param suspend should be true to stop auto-hide, false to resume normal behavior
-     */
-    @Override
-    public void notifyTaskbarAutohideSuspend(boolean suspend) {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.notifyTaskbarAutohideSuspend(suspend);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call notifyTaskbarAutohideSuspend with arg: " +
-                        suspend, e);
-            }
-        }
-    }
-
-    @Override
-    public void takeScreenshot(ScreenshotRequest request) {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.takeScreenshot(request);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call takeScreenshot");
-            }
-        }
-    }
-
-    @Override
-    public void expandNotificationPanel() {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.expandNotificationPanel();
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call expandNotificationPanel", e);
-            }
-        }
-    }
-
-    @Override
-    public void toggleNotificationPanel() {
-        if (mSystemUiProxy != null) {
-            try {
-                mSystemUiProxy.toggleNotificationPanel();
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call toggleNotificationPanel", e);
-            }
-        }
-    }
-
-    //
-    // Pip
-    //
-
-    /**
-     * Sets the shelf height.
-     */
-    public void setShelfHeight(boolean visible, int shelfHeight) {
-        Message.obtain(mAsyncHandler, MSG_SET_SHELF_HEIGHT,
-                visible ? 1 : 0 , shelfHeight).sendToTarget();
-    }
-
-    @WorkerThread
-    private void setShelfHeightAsync(int visibleInt, int shelfHeight) {
-        boolean visible = visibleInt != 0;
-        boolean changed = visible != mLastShelfVisible || shelfHeight != mLastShelfHeight;
-        IPip pip = mPip;
-        if (pip != null && changed) {
-            mLastShelfVisible = visible;
-            mLastShelfHeight = shelfHeight;
-            try {
-                pip.setShelfHeight(visible, shelfHeight);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call setShelfHeight visible: " + visible
-                        + " height: " + shelfHeight, e);
-            }
-        }
-    }
-
-    /**
-     * Sets the height of the keep clear area that is going to be reported by
-     * the Launcher for the Hotseat.
-     */
-    public void setLauncherKeepClearAreaHeight(boolean visible, int height) {
-        Message.obtain(mAsyncHandler, MSG_SET_LAUNCHER_KEEP_CLEAR_AREA_HEIGHT,
-                visible ? 1 : 0 , height).sendToTarget();
-    }
-
-    @WorkerThread
-    private void setLauncherKeepClearAreaHeight(int visibleInt, int height) {
-        boolean visible = visibleInt != 0;
-        boolean changed = visible != mLastLauncherKeepClearAreaHeightVisible
-                || height != mLastLauncherKeepClearAreaHeight;
-        IPip pip = mPip;
-        if (pip != null && changed) {
-            mLastLauncherKeepClearAreaHeightVisible = visible;
-            mLastLauncherKeepClearAreaHeight = height;
-            try {
-                pip.setLauncherKeepClearAreaHeight(visible, height);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call setLauncherKeepClearAreaHeight visible: " + visible
-                        + " height: " + height, e);
-            }
-        }
-    }
-
-    /**
-     * Sets listener to get pip animation callbacks.
-     */
-    public void setPipAnimationListener(IPipAnimationListener listener) {
-        if (mPip != null) {
-            try {
-                mPip.setPipAnimationListener(listener);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call setPinnedStackAnimationListener", e);
-            }
-        }
-        mPipAnimationListener = listener;
-    }
-
-    /**
-     * @return Destination bounds of auto-pip animation, {@code null} if the animation is not ready.
-     */
-    @Nullable
-    public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
-            PictureInPictureParams pictureInPictureParams, int launcherRotation,
-            Rect hotseatKeepClearArea) {
-        if (mPip != null) {
-            try {
-                return mPip.startSwipePipToHome(componentName, activityInfo,
-                        pictureInPictureParams, launcherRotation, hotseatKeepClearArea);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call startSwipePipToHome", e);
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Notifies WM Shell that launcher has finished the preparation of the animation for swipe to
-     * home. WM Shell can choose to fade out the overlay when entering PIP is finished, and WM Shell
-     * should be responsible for cleaning up the overlay.
-     */
-    public void stopSwipePipToHome(int taskId, ComponentName componentName, Rect destinationBounds,
-            SurfaceControl overlay) {
-        if (mPip != null) {
-            try {
-                mPip.stopSwipePipToHome(taskId, componentName, destinationBounds, overlay);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call stopSwipePipToHome");
-            }
-        }
-    }
-
-    /**
-     * Notifies WM Shell that launcher has aborted all the animation for swipe to home. WM Shell
-     * can use this callback to clean up its internal states.
-     */
-    public void abortSwipePipToHome(int taskId, ComponentName componentName) {
-        if (mPip != null) {
-            try {
-                mPip.abortSwipePipToHome(taskId, componentName);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call abortSwipePipToHome");
-            }
-        }
-    }
-
-    /**
-     * Sets the next pip animation type to be the alpha animation.
-     */
-    public void setPipAnimationTypeToAlpha() {
-        if (mPip != null) {
-            try {
-                mPip.setPipAnimationTypeToAlpha();
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call setPipAnimationTypeToAlpha", e);
-            }
-        }
-    }
-
-    /**
-     * Sets the app icon size in pixel used by Launcher all apps.
-     */
-    public void setLauncherAppIconSize(int iconSizePx) {
-        if (mPip != null) {
-            try {
-                mPip.setLauncherAppIconSize(iconSizePx);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call setLauncherAppIconSize", e);
-            }
-        }
-    }
-
-    //
-    // Bubbles
-    //
-
-    /**
-     * Sets the listener to be notified of bubble state changes.
-     */
-    public void setBubblesListener(IBubblesListener listener) {
-        if (mBubbles != null) {
-            try {
-                if (mBubblesListener != null) {
-                    // Clear out any previous listener
-                    mBubbles.unregisterBubbleListener(mBubblesListener);
-                }
-                if (listener != null) {
-                    mBubbles.registerBubbleListener(listener);
-                }
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call registerBubblesListener");
-            }
-        }
-        mBubblesListener = listener;
-    }
-
-    /**
-     * Tells SysUI to show the bubble with the provided key.
-     * @param key the key of the bubble to show.
-     * @param bubbleBarOffsetX the offset of the bubble bar from the edge of the screen on the X
-     *                         axis.
-     * @param bubbleBarOffsetY the offset of the bubble bar from the edge of the screen on the Y
-     *                         axis.
-     */
-    public void showBubble(String key, int bubbleBarOffsetX, int bubbleBarOffsetY) {
-        if (mBubbles != null) {
-            try {
-                mBubbles.showBubble(key, bubbleBarOffsetX, bubbleBarOffsetY);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call showBubble");
-            }
-        }
-    }
-
-    /**
-     * Tells SysUI to remove the bubble with the provided key.
-     * @param key the key of the bubble to show.
-     */
-    public void removeBubble(String key) {
-        if (mBubbles == null) return;
-        try {
-            mBubbles.removeBubble(key);
-        } catch (RemoteException e) {
-            Log.w(TAG, "Failed call removeBubble");
-        }
-    }
-
-    /**
-     * Tells SysUI to remove all bubbles.
-     */
-    public void removeAllBubbles() {
-        if (mBubbles == null) return;
-        try {
-            mBubbles.removeAllBubbles();
-        } catch (RemoteException e) {
-            Log.w(TAG, "Failed call removeAllBubbles");
-        }
-    }
-
-    /**
-     * Tells SysUI to collapse the bubbles.
-     */
-    public void collapseBubbles() {
-        if (mBubbles != null) {
-            try {
-                mBubbles.collapseBubbles();
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call collapseBubbles");
-            }
-        }
-    }
-
-    /**
-     * Tells SysUI when the bubble is being dragged.
-     * Should be called only when the bubble bar is expanded.
-     * @param bubbleKey the key of the bubble to collapse/expand
-     * @param isBeingDragged whether the bubble is being dragged
-     */
-    public void onBubbleDrag(@Nullable String bubbleKey, boolean isBeingDragged) {
-        if (mBubbles == null) return;
-        try {
-            mBubbles.onBubbleDrag(bubbleKey, isBeingDragged);
-        } catch (RemoteException e) {
-            Log.w(TAG, "Failed call onBubbleDrag");
-        }
-    }
-
-    /**
-     * Tells SysUI to show user education relative to the reference point provided.
-     * @param position the bubble bar top center position in Screen coordinates.
-     */
-    public void showUserEducation(Point position) {
-        try {
-            mBubbles.showUserEducation(position.x, position.y);
-        } catch (RemoteException e) {
-            Log.w(TAG, "Failed call showUserEducation");
-        }
-    }
-
-    //
-    // Splitscreen
-    //
-
-    public void registerSplitScreenListener(ISplitScreenListener listener) {
-        if (mSplitScreen != null) {
-            try {
-                mSplitScreen.registerSplitScreenListener(listener);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call registerSplitScreenListener");
-            }
-        }
-        mSplitScreenListener = listener;
-    }
-
-    public void unregisterSplitScreenListener(ISplitScreenListener listener) {
-        if (mSplitScreen != null) {
-            try {
-                mSplitScreen.unregisterSplitScreenListener(listener);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call unregisterSplitScreenListener");
-            }
-        }
-        mSplitScreenListener = null;
-    }
-
-    public void registerSplitSelectListener(ISplitSelectListener listener) {
-        if (mSplitScreen != null) {
-            try {
-                mSplitScreen.registerSplitSelectListener(listener);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call registerSplitSelectListener");
-            }
-        }
-        mSplitSelectListener = listener;
-    }
-
-    public void unregisterSplitSelectListener(ISplitSelectListener listener) {
-        if (mSplitScreen != null) {
-            try {
-                mSplitScreen.unregisterSplitSelectListener(listener);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call unregisterSplitSelectListener");
-            }
-        }
-        mSplitSelectListener = null;
-    }
-
-    /** Start multiple tasks in split-screen simultaneously. */
-    public void startTasks(int taskId1, Bundle options1, int taskId2, Bundle options2,
-            @StagePosition int splitPosition, @PersistentSnapPosition int snapPosition,
-            RemoteTransition remoteTransition, InstanceId instanceId) {
-        if (mSystemUiProxy != null) {
-            try {
-                mSplitScreen.startTasks(taskId1, options1, taskId2, options2, splitPosition,
-                        snapPosition, remoteTransition, instanceId);
-            } catch (RemoteException e) {
-                Log.w(TAG, splitFailureMessage("startTasks", "RemoteException"), e);
-            }
-        }
-    }
-
-    public void startIntentAndTask(PendingIntent pendingIntent, int userId1, Bundle options1,
-            int taskId, Bundle options2, @StagePosition int splitPosition,
-            @PersistentSnapPosition int snapPosition, RemoteTransition remoteTransition,
-            InstanceId instanceId) {
-        if (mSystemUiProxy != null) {
-            try {
-                mSplitScreen.startIntentAndTask(pendingIntent, userId1, options1, taskId, options2,
-                        splitPosition, snapPosition, remoteTransition, instanceId);
-            } catch (RemoteException e) {
-                Log.w(TAG, splitFailureMessage("startIntentAndTask", "RemoteException"), e);
-            }
-        }
-    }
-
-    public void startIntents(PendingIntent pendingIntent1, int userId1,
-            @Nullable ShortcutInfo shortcutInfo1, Bundle options1, PendingIntent pendingIntent2,
-            int userId2, @Nullable ShortcutInfo shortcutInfo2, Bundle options2,
-            @StagePosition int splitPosition, @PersistentSnapPosition int snapPosition,
-            RemoteTransition remoteTransition, InstanceId instanceId) {
-        if (mSystemUiProxy != null) {
-            try {
-                mSplitScreen.startIntents(pendingIntent1, userId1, shortcutInfo1, options1,
-                        pendingIntent2, userId2, shortcutInfo2, options2, splitPosition,
-                        snapPosition, remoteTransition, instanceId);
-            } catch (RemoteException e) {
-                Log.w(TAG, splitFailureMessage("startIntents", "RemoteException"), e);
-            }
-        }
-    }
-
-    public void startShortcutAndTask(ShortcutInfo shortcutInfo, Bundle options1, int taskId,
-            Bundle options2, @StagePosition int splitPosition,
-            @PersistentSnapPosition int snapPosition, RemoteTransition remoteTransition,
-            InstanceId instanceId) {
-        if (mSystemUiProxy != null) {
-            try {
-                mSplitScreen.startShortcutAndTask(shortcutInfo, options1, taskId, options2,
-                        splitPosition, snapPosition, remoteTransition, instanceId);
-            } catch (RemoteException e) {
-                Log.w(TAG, splitFailureMessage("startShortcutAndTask", "RemoteException"), e);
-            }
-        }
-    }
-
-    /**
-     * Start multiple tasks in split-screen simultaneously.
-     */
-    public void startTasksWithLegacyTransition(int taskId1, Bundle options1, int taskId2,
-            Bundle options2, @StagePosition int splitPosition,
-            @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter,
-            InstanceId instanceId) {
-        if (mSystemUiProxy != null) {
-            try {
-                mSplitScreen.startTasksWithLegacyTransition(taskId1, options1, taskId2, options2,
-                        splitPosition, snapPosition, adapter, instanceId);
-            } catch (RemoteException e) {
-                Log.w(TAG, splitFailureMessage(
-                        "startTasksWithLegacyTransition", "RemoteException"), e);
-            }
-        }
-    }
-
-    public void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, int userId1,
-            Bundle options1, int taskId, Bundle options2, @StagePosition int splitPosition,
-            @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter,
-            InstanceId instanceId) {
-        if (mSystemUiProxy != null) {
-            try {
-                mSplitScreen.startIntentAndTaskWithLegacyTransition(pendingIntent, userId1,
-                        options1, taskId, options2, splitPosition, snapPosition, adapter,
-                        instanceId);
-            } catch (RemoteException e) {
-                Log.w(TAG, splitFailureMessage(
-                        "startIntentAndTaskWithLegacyTransition", "RemoteException"), e);
-            }
-        }
-    }
-
-    public void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo, Bundle options1,
-            int taskId, Bundle options2, @StagePosition int splitPosition,
-            @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter,
-            InstanceId instanceId) {
-        if (mSystemUiProxy != null) {
-            try {
-                mSplitScreen.startShortcutAndTaskWithLegacyTransition(shortcutInfo, options1,
-                        taskId, options2, splitPosition, snapPosition, adapter, instanceId);
-            } catch (RemoteException e) {
-                Log.w(TAG, splitFailureMessage(
-                        "startShortcutAndTaskWithLegacyTransition", "RemoteException"), e);
-            }
-        }
-    }
-
-    /**
-     * Starts a pair of intents or shortcuts in split-screen using legacy transition. Passing a
-     * non-null shortcut info means to start the app as a shortcut.
-     */
-    public void startIntentsWithLegacyTransition(PendingIntent pendingIntent1, int userId1,
-            @Nullable ShortcutInfo shortcutInfo1, @Nullable Bundle options1,
-            PendingIntent pendingIntent2, int userId2, @Nullable ShortcutInfo shortcutInfo2,
-            @Nullable Bundle options2, @StagePosition int sidePosition,
-            @PersistentSnapPosition int snapPosition, RemoteAnimationAdapter adapter,
-            InstanceId instanceId) {
-        if (mSystemUiProxy != null) {
-            try {
-                mSplitScreen.startIntentsWithLegacyTransition(pendingIntent1, userId1,
-                        shortcutInfo1, options1, pendingIntent2, userId2, shortcutInfo2, options2,
-                        sidePosition, snapPosition, adapter, instanceId);
-            } catch (RemoteException e) {
-                Log.w(TAG, splitFailureMessage(
-                        "startIntentsWithLegacyTransition", "RemoteException"), e);
-            }
-        }
-    }
-
-    public void startShortcut(String packageName, String shortcutId, int position,
-            Bundle options, UserHandle user, InstanceId instanceId) {
-        if (mSplitScreen != null) {
-            try {
-                mSplitScreen.startShortcut(packageName, shortcutId, position, options,
-                        user, instanceId);
-            } catch (RemoteException e) {
-                Log.w(TAG, splitFailureMessage("startShortcut", "RemoteException"), e);
-            }
-        }
-    }
-
-    public void startIntent(PendingIntent intent, int userId, Intent fillInIntent, int position,
-            Bundle options, InstanceId instanceId) {
-        if (mSplitScreen != null) {
-            try {
-                mSplitScreen.startIntent(intent, userId, fillInIntent, position, options,
-                        instanceId);
-            } catch (RemoteException e) {
-                Log.w(TAG, splitFailureMessage("startIntent", "RemoteException"), e);
-            }
-        }
-    }
-
-    public void removeFromSideStage(int taskId) {
-        if (mSplitScreen != null) {
-            try {
-                mSplitScreen.removeFromSideStage(taskId);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call removeFromSideStage");
-            }
-        }
-    }
-
-    /**
-     * Call this when going to recents so that shell can set-up and provide appropriate leashes
-     * for animation (eg. DividerBar).
-     *
-     * @return RemoteAnimationTargets of windows that need to animate but only exist in shell.
-     */
-    @Nullable
-    public RemoteAnimationTarget[] onGoingToRecentsLegacy(RemoteAnimationTarget[] apps) {
-        if (!TaskAnimationManager.ENABLE_SHELL_TRANSITIONS && mSplitScreen != null) {
-            try {
-                return mSplitScreen.onGoingToRecentsLegacy(apps);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call onGoingToRecentsLegacy");
-            }
-        }
-        return null;
-    }
-
-    @Nullable
-    public RemoteAnimationTarget[] onStartingSplitLegacy(RemoteAnimationTarget[] apps) {
-        if (mSplitScreen != null) {
-            try {
-                return mSplitScreen.onStartingSplitLegacy(apps);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call onStartingSplitLegacy");
-            }
-        }
-        return null;
-    }
-
-    //
-    // One handed
-    //
-
-    public void startOneHandedMode() {
-        if (mOneHanded != null) {
-            try {
-                mOneHanded.startOneHanded();
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call startOneHandedMode", e);
-            }
-        }
-    }
-
-    public void stopOneHandedMode() {
-        if (mOneHanded != null) {
-            try {
-                mOneHanded.stopOneHanded();
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call stopOneHandedMode", e);
-            }
-        }
-    }
-
-    //
-    // Remote transitions
-    //
-
-    public void registerRemoteTransition(
-            RemoteTransition remoteTransition, TransitionFilter filter) {
-        if (mShellTransitions != null) {
-            try {
-                mShellTransitions.registerRemote(filter, remoteTransition);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call registerRemoteTransition");
-            }
-        }
-        if (!mRemoteTransitions.containsKey(remoteTransition)) {
-            mRemoteTransitions.put(remoteTransition, filter);
-        }
-    }
-
-    public void unregisterRemoteTransition(RemoteTransition remoteTransition) {
-        if (mShellTransitions != null) {
-            try {
-                mShellTransitions.unregisterRemote(remoteTransition);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call registerRemoteTransition");
-            }
-        }
-        mRemoteTransitions.remove(remoteTransition);
-    }
-
-    public void setHomeTransitionListener(IHomeTransitionListener listener) {
-        if (!FeatureFlags.enableHomeTransitionListener()) {
-            return;
-        }
-
-        mHomeTransitionListener = listener;
-
-        if (mShellTransitions != null) {
-            try {
-                mShellTransitions.setHomeTransitionListener(listener);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call setHomeTransitionListener", e);
-            }
-        } else  {
-            Log.w(TAG, "Unable to call setHomeTransitionListener because ShellTransitions is null");
-        }
-    }
-
-    /**
-     * Use SystemUI's transaction-queue instead of Launcher's independent one. This is necessary
-     * if Launcher and SystemUI need to coordinate transactions (eg. for shell transitions).
-     */
-    public void shareTransactionQueue() {
-        if (mOriginalTransactionToken == null) {
-            mOriginalTransactionToken = SurfaceControl.Transaction.getDefaultApplyToken();
-        }
-        setupTransactionQueue();
-    }
-
-    /**
-     * Switch back to using Launcher's independent transaction queue.
-     */
-    public void unshareTransactionQueue() {
-        if (mOriginalTransactionToken == null) {
-            return;
-        }
-        SurfaceControl.Transaction.setDefaultApplyToken(mOriginalTransactionToken);
-        mOriginalTransactionToken = null;
-    }
-
-    private void setupTransactionQueue() {
-        if (mOriginalTransactionToken == null) {
-            return;
-        }
-        if (mShellTransitions == null) {
-            SurfaceControl.Transaction.setDefaultApplyToken(mOriginalTransactionToken);
-            return;
-        }
-        final IBinder shellApplyToken;
-        try {
-            shellApplyToken = mShellTransitions.getShellApplyToken();
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error getting Shell's apply token", e);
-            return;
-        }
-        if (shellApplyToken == null) {
-            Log.e(TAG, "Didn't receive apply token from Shell");
-            return;
-        }
-        SurfaceControl.Transaction.setDefaultApplyToken(shellApplyToken);
-    }
-
-    //
-    // Starting window
-    //
-
-    /**
-     * Sets listener to get callbacks when launching a task.
-     */
-    public void setStartingWindowListener(IStartingWindowListener listener) {
-        if (mStartingWindow != null) {
-            try {
-                mStartingWindow.setStartingWindowListener(listener);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call setStartingWindowListener", e);
-            }
-        }
-        mStartingWindowListener = listener;
-    }
-
-    //
-    // SmartSpace transitions
-    //
-
-    /**
-     * Sets the instance of {@link ILauncherUnlockAnimationController} that System UI should use to
-     * control the launcher side of the unlock animation. This will also cause us to dispatch the
-     * current state of the smartspace to System UI (this will subsequently happen if the state
-     * changes).
-     */
-    public void setLauncherUnlockAnimationController(
-            String activityClass, ILauncherUnlockAnimationController controller) {
-        if (mSysuiUnlockAnimationController != null) {
-            try {
-                mSysuiUnlockAnimationController.setLauncherUnlockController(
-                        activityClass, controller);
-                if (controller != null) {
-                    controller.dispatchSmartspaceStateToSysui();
-                }
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call setLauncherUnlockAnimationController", e);
-            }
-        }
-        mLauncherActivityClass = activityClass;
-        mLauncherUnlockAnimationController = controller;
-    }
-
-    /**
-     * Tells System UI that the Launcher's smartspace state has been updated, so that it can prepare
-     * the unlock animation accordingly.
-     */
-    public void notifySysuiSmartspaceStateUpdated(SmartspaceState state) {
-        if (mSysuiUnlockAnimationController != null) {
-            try {
-                mSysuiUnlockAnimationController.onLauncherSmartspaceStateUpdated(state);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call notifySysuiSmartspaceStateUpdated", e);
-                e.printStackTrace();
-            }
-        }
-    }
-
-    //
-    // Recents
-    //
-
-    public void registerRecentTasksListener(IRecentTasksListener listener) {
-        if (mRecentTasks != null) {
-            try {
-                mRecentTasks.registerRecentTasksListener(listener);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call registerRecentTasksListener", e);
-            }
-        }
-        mRecentTasksListener = listener;
-    }
-
-    public void unregisterRecentTasksListener(IRecentTasksListener listener) {
-        if (mRecentTasks != null) {
-            try {
-                mRecentTasks.unregisterRecentTasksListener(listener);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call unregisterRecentTasksListener");
-            }
-        }
-        mRecentTasksListener = null;
-    }
-
-    //
-    // Back navigation transitions
-    //
-
-    /** Sets the launcher {@link android.window.IOnBackInvokedCallback} to shell */
-    public void setBackToLauncherCallback(IOnBackInvokedCallback callback,
-            IRemoteAnimationRunner runner) {
-        mBackToLauncherCallback = callback;
-        mBackToLauncherRunner = runner;
-        if (mBackAnimation == null || mBackToLauncherCallback == null) {
-            return;
-        }
-        try {
-            mBackAnimation.setBackToLauncherCallback(callback, runner);
-        } catch (RemoteException | SecurityException e) {
-            Log.e(TAG, "Failed call setBackToLauncherCallback", e);
-        }
-    }
-
-    /** Clears the previously registered {@link IOnBackInvokedCallback}.
-     *
-     * @param callback The previously registered callback instance.
-     */
-    public void clearBackToLauncherCallback(IOnBackInvokedCallback callback) {
-        if (mBackToLauncherCallback != callback) {
-            return;
-        }
-        mBackToLauncherCallback = null;
-        mBackToLauncherRunner = null;
-        if (mBackAnimation == null) {
-            return;
-        }
-        try {
-            mBackAnimation.clearBackToLauncherCallback();
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed call clearBackToLauncherCallback", e);
-        }
-    }
-
-    /**
-     * Called when the status bar color needs to be customized when back navigation.
-     */
-    public void customizeStatusBarAppearance(AppearanceRegion appearance) {
-        if (mBackAnimation == null) {
-            return;
-        }
-        try {
-            mBackAnimation.customizeStatusBarAppearance(appearance);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed call useLauncherSysBarFlags", e);
-        }
-    }
-
-    public ArrayList<GroupedRecentTaskInfo> getRecentTasks(int numTasks, int userId) {
-        if (mRecentTasks == null) {
-            Log.w(TAG, "getRecentTasks() failed due to null mRecentTasks");
-            return new ArrayList<>();
-        }
-        try {
-            final GroupedRecentTaskInfo[] rawTasks = mRecentTasks.getRecentTasks(numTasks,
-                    RECENT_IGNORE_UNAVAILABLE, userId);
-            if (rawTasks == null) {
-                return new ArrayList<>();
-            }
-            return new ArrayList<>(Arrays.asList(rawTasks));
-        } catch (RemoteException e) {
-            Log.w(TAG, "Failed call getRecentTasks", e);
-            return new ArrayList<>();
-        }
-    }
-
-    /**
-     * Gets the set of running tasks.
-     */
-    public ArrayList<ActivityManager.RunningTaskInfo> getRunningTasks(int numTasks) {
-        if (mRecentTasks != null
-                && mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC)) {
-            try {
-                return new ArrayList<>(Arrays.asList(mRecentTasks.getRunningTasks(numTasks)));
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call getRunningTasks", e);
-            }
-        }
-        return new ArrayList<>();
-    }
-
-    private boolean handleMessageAsync(Message msg) {
-        switch (msg.what) {
-            case MSG_SET_SHELF_HEIGHT:
-                setShelfHeightAsync(msg.arg1, msg.arg2);
-                return true;
-            case MSG_SET_LAUNCHER_KEEP_CLEAR_AREA_HEIGHT:
-                setLauncherKeepClearAreaHeight(msg.arg1, msg.arg2);
-                return true;
-        }
-
-        return false;
-    }
-
-    //
-    // Desktop Mode
-    //
-
-    /** Call shell to show all apps active on the desktop */
-    public void showDesktopApps(int displayId, @Nullable RemoteTransition transition) {
-        if (mDesktopMode != null) {
-            try {
-                mDesktopMode.showDesktopApps(displayId, transition);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call showDesktopApps", e);
-            }
-        }
-    }
-
-    /** Call shell to stash desktop apps */
-    public void stashDesktopApps(int displayId) {
-        if (mDesktopMode != null) {
-            try {
-                mDesktopMode.stashDesktopApps(displayId);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call stashDesktopApps", e);
-            }
-        }
-    }
-
-    /** Call shell to hide desktop apps that may be stashed */
-    public void hideStashedDesktopApps(int displayId) {
-        if (mDesktopMode != null) {
-            try {
-                mDesktopMode.hideStashedDesktopApps(displayId);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call hideStashedDesktopApps", e);
-            }
-        }
-    }
-
-    /**
-     * If task with the given id is on the desktop, bring it to front
-     */
-    public void showDesktopApp(int taskId) {
-        if (mDesktopMode != null) {
-            try {
-                mDesktopMode.showDesktopApp(taskId);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call showDesktopApp", e);
-            }
-        }
-    }
-
-    /** Call shell to get number of visible freeform tasks */
-    public int getVisibleDesktopTaskCount(int displayId) {
-        if (mDesktopMode != null) {
-            try {
-                return mDesktopMode.getVisibleTaskCount(displayId);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call getVisibleDesktopTaskCount", e);
-            }
-        }
-        return 0;
-    }
-
-    /** Set a listener on shell to get updates about desktop task state */
-    public void setDesktopTaskListener(@Nullable IDesktopTaskListener listener) {
-        mDesktopTaskListener = listener;
-        if (mDesktopMode != null) {
-            try {
-                mDesktopMode.setTaskListener(listener);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call setDesktopTaskListener", e);
-            }
-        }
-    }
-
-    /** Perform cleanup transactions after animation to split select is complete */
-    public void onDesktopSplitSelectAnimComplete(ActivityManager.RunningTaskInfo taskInfo) {
-        if (mDesktopMode != null) {
-            try {
-                mDesktopMode.onDesktopSplitSelectAnimComplete(taskInfo);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed call onDesktopSplitSelectAnimComplete", e);
-            }
-        }
-    }
-
-    //
-    // Unfold transition
-    //
-
-    /** Sets the unfold animation lister to sysui. */
-    public void setUnfoldAnimationListener(IUnfoldTransitionListener callback) {
-        mUnfoldAnimationListener = callback;
-        if (mUnfoldAnimation == null) {
-            return;
-        }
-        try {
-            Log.d(TAG, "Registering unfold animation receiver");
-            mUnfoldAnimation.setListener(callback);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed call setUnfoldAnimationListener", e);
-        }
-    }
-
-    //
-    // Recents
-    //
-
-    /**
-     * Starts the recents activity. The caller should manage the thread on which this is called.
-     */
-    public boolean startRecentsActivity(Intent intent, ActivityOptions options,
-            RecentsAnimationListener listener) {
-        if (mRecentTasks == null) {
-            ActiveGestureLog.INSTANCE.addLog("Null mRecentTasks", RECENT_TASKS_MISSING);
-            return false;
-        }
-        final IRecentsAnimationRunner runner = new IRecentsAnimationRunner.Stub() {
-            @Override
-            public void onAnimationStart(IRecentsAnimationController controller,
-                    RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
-                    Rect homeContentInsets, Rect minimizedHomeBounds, Bundle extras) {
-                // Aidl bundles need to explicitly set class loader
-                // https://developer.android.com/guide/components/aidl#Bundles
-                if (extras != null) {
-                    extras.setClassLoader(getClass().getClassLoader());
-                }
-                listener.onAnimationStart(new RecentsAnimationControllerCompat(controller), apps,
-                        wallpapers, homeContentInsets, minimizedHomeBounds, extras);
-            }
-
-            @Override
-            public void onAnimationCanceled(int[] taskIds, TaskSnapshot[] taskSnapshots) {
-                listener.onAnimationCanceled(
-                        ThumbnailData.wrap(taskIds, taskSnapshots));
-            }
-
-            @Override
-            public void onTasksAppeared(RemoteAnimationTarget[] apps) {
-                listener.onTasksAppeared(apps);
-            }
-        };
-        final Bundle optsBundle = options.toBundle();
-        try {
-            mRecentTasks.startRecentsTransition(mRecentsPendingIntent, intent, optsBundle,
-                    mContext.getIApplicationThread(), runner);
-            return true;
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error starting recents via shell", e);
-            return false;
-        }
-    }
-
-    //
-    // Drag and drop
-    //
-
-    /**
-     * For testing purposes.  Returns `true` only if the shell drop target has shown and
-     * drawn and is ready to handle drag events and the subsequent drop.
-     */
-    public boolean isDragAndDropReady() {
-        if (mDragAndDrop == null) {
-            return false;
-        }
-        try {
-            return mDragAndDrop.isReadyToHandleDrag();
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error querying drag state", e);
-            return false;
-        }
-    }
-
-    public void dump(PrintWriter pw) {
-        pw.println(TAG + ":");
-
-        pw.println("\tmSystemUiProxy=" + mSystemUiProxy);
-        pw.println("\tmPip=" + mPip);
-        pw.println("\tmPipAnimationListener=" + mPipAnimationListener);
-        pw.println("\tmBubbles=" + mBubbles);
-        pw.println("\tmBubblesListener=" + mBubblesListener);
-        pw.println("\tmSplitScreen=" + mSplitScreen);
-        pw.println("\tmSplitScreenListener=" + mSplitScreenListener);
-        pw.println("\tmSplitSelectListener=" + mSplitSelectListener);
-        pw.println("\tmOneHanded=" + mOneHanded);
-        pw.println("\tmShellTransitions=" + mShellTransitions);
-        pw.println("\tmHomeTransitionListener=" + mHomeTransitionListener);
-        pw.println("\tmStartingWindow=" + mStartingWindow);
-        pw.println("\tmStartingWindowListener=" + mStartingWindowListener);
-        pw.println("\tmSysuiUnlockAnimationController=" + mSysuiUnlockAnimationController);
-        pw.println("\tmLauncherActivityClass=" + mLauncherActivityClass);
-        pw.println("\tmLauncherUnlockAnimationController=" + mLauncherUnlockAnimationController);
-        pw.println("\tmRecentTasks=" + mRecentTasks);
-        pw.println("\tmRecentTasksListener=" + mRecentTasksListener);
-        pw.println("\tmBackAnimation=" + mBackAnimation);
-        pw.println("\tmBackToLauncherCallback=" + mBackToLauncherCallback);
-        pw.println("\tmBackToLauncherRunner=" + mBackToLauncherRunner);
-        pw.println("\tmDesktopMode=" + mDesktopMode);
-        pw.println("\tmDesktopTaskListener=" + mDesktopTaskListener);
-        pw.println("\tmUnfoldAnimation=" + mUnfoldAnimation);
-        pw.println("\tmUnfoldAnimationListener=" + mUnfoldAnimationListener);
-        pw.println("\tmDragAndDrop=" + mDragAndDrop);
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.kt b/quickstep/src/com/android/quickstep/SystemUiProxy.kt
new file mode 100644
index 0000000..c70642a
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.kt
@@ -0,0 +1,1232 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep
+
+import android.app.ActivityManager
+import android.app.ActivityManager.RunningTaskInfo
+import android.app.ActivityOptions
+import android.app.PendingIntent
+import android.app.PictureInPictureParams
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.pm.ActivityInfo
+import android.content.pm.PackageManager
+import android.content.pm.ShortcutInfo
+import android.graphics.Point
+import android.graphics.Rect
+import android.os.Bundle
+import android.os.Handler
+import android.os.IBinder
+import android.os.Message
+import android.os.RemoteException
+import android.os.UserHandle
+import android.util.Log
+import android.view.IRecentsAnimationController
+import android.view.IRecentsAnimationRunner
+import android.view.IRemoteAnimationRunner
+import android.view.MotionEvent
+import android.view.RemoteAnimationAdapter
+import android.view.RemoteAnimationTarget
+import android.view.SurfaceControl
+import android.window.IOnBackInvokedCallback
+import android.window.RemoteTransition
+import android.window.TaskSnapshot
+import android.window.TransitionFilter
+import androidx.annotation.MainThread
+import androidx.annotation.WorkerThread
+import com.android.internal.logging.InstanceId
+import com.android.internal.util.ScreenshotRequest
+import com.android.internal.view.AppearanceRegion
+import com.android.launcher3.config.FeatureFlags
+import com.android.launcher3.util.Executors
+import com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR
+import com.android.launcher3.util.MainThreadInitializedObject
+import com.android.launcher3.util.Preconditions
+import com.android.launcher3.util.SplitConfigurationOptions.StagePosition
+import com.android.quickstep.util.ActiveGestureErrorDetector
+import com.android.quickstep.util.ActiveGestureLog
+import com.android.quickstep.util.AssistUtils
+import com.android.quickstep.util.LogUtils.splitFailureMessage
+import com.android.systemui.shared.recents.ISystemUiProxy
+import com.android.systemui.shared.recents.model.ThumbnailData
+import com.android.systemui.shared.system.RecentsAnimationControllerCompat
+import com.android.systemui.shared.system.RecentsAnimationListener
+import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController
+import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController
+import com.android.systemui.shared.system.smartspace.SmartspaceState
+import com.android.systemui.unfold.progress.IUnfoldAnimation
+import com.android.systemui.unfold.progress.IUnfoldTransitionListener
+import com.android.wm.shell.back.IBackAnimation
+import com.android.wm.shell.bubbles.IBubbles
+import com.android.wm.shell.bubbles.IBubblesListener
+import com.android.wm.shell.common.split.SplitScreenConstants.PersistentSnapPosition
+import com.android.wm.shell.desktopmode.IDesktopMode
+import com.android.wm.shell.desktopmode.IDesktopTaskListener
+import com.android.wm.shell.draganddrop.IDragAndDrop
+import com.android.wm.shell.onehanded.IOneHanded
+import com.android.wm.shell.pip.IPip
+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.splitscreen.ISplitScreen
+import com.android.wm.shell.splitscreen.ISplitScreenListener
+import com.android.wm.shell.splitscreen.ISplitSelectListener
+import com.android.wm.shell.startingsurface.IStartingWindow
+import com.android.wm.shell.startingsurface.IStartingWindowListener
+import com.android.wm.shell.transition.IHomeTransitionListener
+import com.android.wm.shell.transition.IShellTransitions
+import com.android.wm.shell.util.GroupedRecentTaskInfo
+import java.io.PrintWriter
+
+/** Holds the reference to SystemUI. */
+class SystemUiProxy(private val context: Context) {
+
+    private val asyncHandler = Handler(UI_HELPER_EXECUTOR.looper, this::handleMessageAsync)
+
+    /**
+     * This is a singleton pending intent that is used to start recents via Shell (which is a
+     * different process). It is bare-bones, so it's expected that the component and options will be
+     * provided via fill-in intent.
+     */
+    private val recentsPendingIntent =
+        PendingIntent.getActivity(
+            context,
+            0,
+            Intent().setPackage(context.packageName),
+            PendingIntent.FLAG_MUTABLE or
+                PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT or
+                Intent.FILL_IN_COMPONENT,
+            ActivityOptions.makeBasic()
+                .setPendingIntentCreatorBackgroundActivityStartMode(
+                    ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
+                )
+                .toBundle()
+        )
+
+    private val systemUiProxyDeathRecipient =
+        IBinder.DeathRecipient { Executors.MAIN_EXECUTOR.execute { clearProxy() } }
+
+    private var systemUiProxy: ISystemUiProxy? = null
+
+    private var pip: IPip? = null
+    private var bubbles: IBubbles? = null
+    private var sysuiUnlockAnimationController: ISysuiUnlockAnimationController? = null
+    private var splitScreen: ISplitScreen? = null
+    private var oneHanded: IOneHanded? = null
+    private var shellTransitions: IShellTransitions? = null
+    private var startingWindow: IStartingWindow? = null
+    private var recentTasks: IRecentTasks? = null
+    private var backAnimation: IBackAnimation? = null
+    private var desktopMode: IDesktopMode? = null
+    private var unfoldAnimation: IUnfoldAnimation? = null
+
+    // Save the listeners passed into the proxy since OverviewProxyService may not have been bound
+    // yet, and we'll need to set/register these listeners with SysUI when they do.  Note that it is
+    // up to the caller to clear the listeners to prevent leaks as these can be held indefinitely
+    // in case SysUI needs to rebind.
+    private var pipAnimationListener: IPipAnimationListener? = null
+    private var bubblesListener: IBubblesListener? = null
+    private var splitScreenListener: ISplitScreenListener? = null
+    private var splitSelectListener: ISplitSelectListener? = null
+    private var startingWindowListener: IStartingWindowListener? = null
+    private var launcherUnlockAnimationController: ILauncherUnlockAnimationController? = null
+    private var launcherActivityClass: String? = null
+    private var recentTasksListener: IRecentTasksListener? = null
+    private var unfoldAnimationListener: IUnfoldTransitionListener? = null
+    private var desktopTaskListener: IDesktopTaskListener? = null
+    private val remoteTransitions = LinkedHashMap<RemoteTransition, TransitionFilter>()
+    private var originalTransactionToken: IBinder? = null
+    private var backToLauncherCallback: IOnBackInvokedCallback? = null
+    private var backToLauncherRunner: IRemoteAnimationRunner? = null
+    private var dragAndDrop: IDragAndDrop? = null
+    private var homeTransitionListener: IHomeTransitionListener? = null
+
+    // Used to dedupe calls to SystemUI
+    private var lastShelfHeight = 0
+    private var lastShelfVisible = false
+
+    // Used to dedupe calls to SystemUI
+    private var lastLauncherKeepClearAreaHeight = 0
+    private var lastLauncherKeepClearAreaHeightVisible = false
+
+    // TODO(141886704): Find a way to remove this
+    var lastSystemUiStateFlags = 0
+
+    /**
+     * Sets proxy state, including death linkage, various listeners, and other configuration objects
+     */
+    @MainThread
+    fun setProxy(
+        proxy: ISystemUiProxy?,
+        pip: IPip?,
+        bubbles: IBubbles?,
+        splitScreen: ISplitScreen?,
+        oneHanded: IOneHanded?,
+        shellTransitions: IShellTransitions?,
+        startingWindow: IStartingWindow?,
+        recentTasks: IRecentTasks?,
+        sysuiUnlockAnimationController: ISysuiUnlockAnimationController?,
+        backAnimation: IBackAnimation?,
+        desktopMode: IDesktopMode?,
+        unfoldAnimation: IUnfoldAnimation?,
+        dragAndDrop: IDragAndDrop?
+    ) {
+        Preconditions.assertUIThread()
+        unlinkToDeath()
+        systemUiProxy = proxy
+        this.pip = pip
+        this.bubbles = bubbles
+        this.splitScreen = splitScreen
+        this.oneHanded = oneHanded
+        this.shellTransitions = shellTransitions
+        this.startingWindow = startingWindow
+        this.sysuiUnlockAnimationController = sysuiUnlockAnimationController
+        this.recentTasks = recentTasks
+        this.backAnimation = backAnimation
+        this.desktopMode = desktopMode
+        this.unfoldAnimation = unfoldAnimation
+        this.dragAndDrop = dragAndDrop
+        linkToDeath()
+        // re-attach the listeners once missing due to setProxy has not been initialized yet.
+        setPipAnimationListener(pipAnimationListener)
+        setBubblesListener(bubblesListener)
+        registerSplitScreenListener(splitScreenListener)
+        registerSplitSelectListener(splitSelectListener)
+        setHomeTransitionListener(homeTransitionListener)
+        setStartingWindowListener(startingWindowListener)
+        setLauncherUnlockAnimationController(
+            launcherActivityClass,
+            launcherUnlockAnimationController
+        )
+        LinkedHashMap(remoteTransitions).forEach(this::registerRemoteTransition)
+        setupTransactionQueue()
+        registerRecentTasksListener(recentTasksListener)
+        setBackToLauncherCallback(backToLauncherCallback, backToLauncherRunner)
+        setUnfoldAnimationListener(unfoldAnimationListener)
+        setDesktopTaskListener(desktopTaskListener)
+        setAssistantOverridesRequested(
+            AssistUtils.newInstance(context).sysUiAssistOverrideInvocationTypes
+        )
+    }
+
+    /**
+     * Clear the proxy to release held resources and turn the majority of its operations into no-ops
+     */
+    @MainThread
+    fun clearProxy() {
+        setProxy(null, null, null, null, null, null, null, null, null, null, null, null, null)
+    }
+
+    val isActive: Boolean
+        get() = systemUiProxy != null
+
+    private fun linkToDeath() {
+        tryOrLog("Failed to link sysui proxy death recipient") {
+            systemUiProxy?.asBinder()?.linkToDeath(systemUiProxyDeathRecipient, 0 /* flags */)
+        }
+    }
+
+    private fun unlinkToDeath() {
+        systemUiProxy?.asBinder()?.unlinkToDeath(systemUiProxyDeathRecipient, 0 /* flags */)
+    }
+
+    fun onBackPressed() {
+        tryOrLog("Failed call onBackPressed") { systemUiProxy?.onBackPressed() }
+    }
+
+    fun onImeSwitcherPressed() {
+        tryOrLog("Failed call onImeSwitcherPressed") { systemUiProxy?.onImeSwitcherPressed() }
+    }
+
+    fun setHomeRotationEnabled(enabled: Boolean) {
+        tryOrLog("Failed call setHomeRotationEnabled") {
+            systemUiProxy?.setHomeRotationEnabled(enabled)
+        }
+    }
+
+    fun startScreenPinning(taskId: Int) {
+        tryOrLog("Failed call startScreenPinning") { systemUiProxy?.startScreenPinning(taskId) }
+    }
+
+    fun onOverviewShown(fromHome: Boolean, tag: String?) {
+        try {
+            systemUiProxy?.onOverviewShown(fromHome)
+        } catch (e: RemoteException) {
+            Log.w(tag, "Failed call onOverviewShown from: ${if (fromHome) "home" else "app"}", e)
+        }
+    }
+
+    @MainThread
+    fun onStatusBarTouchEvent(event: MotionEvent) {
+        Preconditions.assertUIThread()
+        tryOrLog("Failed call onStatusBarTouchEvent with arg: $event") {
+            systemUiProxy?.onStatusBarTouchEvent(event)
+        }
+    }
+
+    fun onStatusBarTrackpadEvent(event: MotionEvent) {
+        tryOrLog("Failed call onStatusBarTrackpadEvent with arg: $event") {
+            systemUiProxy?.onStatusBarTrackpadEvent(event)
+        }
+    }
+
+    fun onAssistantProgress(progress: Float) {
+        tryOrLog("Failed call onAssistantProgress with progress: $progress") {
+            systemUiProxy?.onAssistantProgress(progress)
+        }
+    }
+
+    fun onAssistantGestureCompletion(velocity: Float) {
+        tryOrLog("Failed call onAssistantGestureCompletion") {
+            systemUiProxy?.onAssistantGestureCompletion(velocity)
+        }
+    }
+
+    fun startAssistant(args: Bundle?) {
+        tryOrLog("Failed call startAssistant") { systemUiProxy?.startAssistant(args) }
+    }
+
+    fun setAssistantOverridesRequested(invocationTypes: IntArray?) {
+        tryOrLog("Failed call setAssistantOverridesRequested") {
+            systemUiProxy?.setAssistantOverridesRequested(invocationTypes)
+        }
+    }
+
+    fun animateNavBarLongPress(isTouchDown: Boolean, shrink:Boolean, durationMs: Long) {
+        tryOrLog("Failed call animateNavBarLongPress") {
+            systemUiProxy?.animateNavBarLongPress(isTouchDown, shrink, durationMs)
+        }
+    }
+
+    fun notifyAccessibilityButtonClicked(displayId: Int) {
+        tryOrLog("Failed call notifyAccessibilityButtonClicked") {
+            systemUiProxy?.notifyAccessibilityButtonClicked(displayId)
+        }
+    }
+
+    fun notifyAccessibilityButtonLongClicked() {
+        tryOrLog("Failed call notifyAccessibilityButtonLongClicked") {
+            systemUiProxy?.notifyAccessibilityButtonLongClicked()
+        }
+    }
+
+    fun stopScreenPinning() {
+        tryOrLog("Failed call stopScreenPinning") { systemUiProxy?.stopScreenPinning() }
+    }
+
+    fun notifyPrioritizedRotation(rotation: Int) {
+        tryOrLog("Failed call notifyPrioritizedRotation with arg: $rotation") {
+            systemUiProxy?.notifyPrioritizedRotation(rotation)
+        }
+    }
+
+    fun notifyTaskbarStatus(visible: Boolean, stashed: Boolean) {
+        tryOrLog("Failed call notifyTaskbarStatus with arg: $visible, $stashed") {
+            systemUiProxy?.notifyTaskbarStatus(visible, stashed)
+        }
+    }
+
+    /**
+     * NOTE: If called to suspend, caller MUST call this method to also un-suspend
+     *
+     * @param suspend should be true to stop auto-hide, false to resume normal behavior
+     */
+    fun notifyTaskbarAutohideSuspend(suspend: Boolean) {
+        tryOrLog("Failed call notifyTaskbarAutohideSuspend with arg: $suspend") {
+            systemUiProxy?.notifyTaskbarAutohideSuspend(suspend)
+        }
+    }
+
+    fun takeScreenshot(request: ScreenshotRequest?) {
+        tryOrLog("Failed call takeScreenshot") { systemUiProxy?.takeScreenshot(request) }
+    }
+
+    fun expandNotificationPanel() {
+        tryOrLog("Failed call expandNotificationPanel") { systemUiProxy?.expandNotificationPanel() }
+    }
+
+    fun toggleNotificationPanel() {
+        tryOrLog("Failed call toggleNotificationPanel") { systemUiProxy?.toggleNotificationPanel() }
+    }
+
+    //
+    // Pip
+    //
+    /** Sets the shelf height. */
+    fun setShelfHeight(visible: Boolean, shelfHeight: Int) {
+        Message.obtain(asyncHandler, MSG_SET_SHELF_HEIGHT, if (visible) 1 else 0, shelfHeight)
+            .sendToTarget()
+    }
+
+    @WorkerThread
+    private fun setShelfHeightAsync(visibleInt: Int, shelfHeight: Int) {
+        val visible = visibleInt != 0
+        if (visible == lastShelfVisible && shelfHeight == lastShelfHeight) return
+
+        pip?.let {
+            tryOrLog("Failed call setShelfHeight visible: $visible height: $shelfHeight") {
+                it.setShelfHeight(visible, shelfHeight)
+                lastShelfVisible = visible
+                lastShelfHeight = shelfHeight
+            }
+        }
+    }
+
+    /**
+     * Sets the height of the keep clear area that is going to be reported by the Launcher for the
+     * Hotseat.
+     */
+    fun setLauncherKeepClearAreaHeight(visible: Boolean, height: Int) {
+        Message.obtain(
+                asyncHandler,
+                MSG_SET_LAUNCHER_KEEP_CLEAR_AREA_HEIGHT,
+                if (visible) 1 else 0,
+                height
+            )
+            .sendToTarget()
+    }
+
+    @WorkerThread
+    private fun setLauncherKeepClearAreaHeight(visibleInt: Int, height: Int) {
+        val visible = visibleInt != 0
+        if (
+            visible == lastLauncherKeepClearAreaHeightVisible &&
+                height == lastLauncherKeepClearAreaHeight
+        )
+            return
+
+        pip?.let {
+            tryOrLog("Failed call setLauncherKeepClearAreaHeight vis: $visible height: $height") {
+                it.setLauncherKeepClearAreaHeight(visible, height)
+                lastLauncherKeepClearAreaHeightVisible = visible
+                lastLauncherKeepClearAreaHeight = height
+            }
+        }
+    }
+
+    /** Sets listener to get pip animation callbacks. */
+    fun setPipAnimationListener(listener: IPipAnimationListener?) {
+        tryOrLog("Failed call setPinnedStackAnimationListener") {
+            pip?.setPipAnimationListener(listener)
+        }
+        pipAnimationListener = listener
+    }
+
+    /** @return Destination bounds of auto-pip animation, `null` if the animation is not ready. */
+    fun startSwipePipToHome(
+        componentName: ComponentName?,
+        activityInfo: ActivityInfo?,
+        pictureInPictureParams: PictureInPictureParams?,
+        launcherRotation: Int,
+        hotseatKeepClearArea: Rect?
+    ): Rect? {
+        return tryOrElse("Failed call startSwipePipToHome", null) {
+            return pip?.startSwipePipToHome(
+                componentName,
+                activityInfo,
+                pictureInPictureParams,
+                launcherRotation,
+                hotseatKeepClearArea
+            )
+        }
+    }
+
+    /**
+     * Notifies WM Shell that launcher has finished the preparation of the animation for swipe to
+     * home. WM Shell can choose to fade out the overlay when entering PIP is finished, and WM Shell
+     * should be responsible for cleaning up the overlay.
+     */
+    fun stopSwipePipToHome(
+        taskId: Int,
+        componentName: ComponentName?,
+        destinationBounds: Rect?,
+        overlay: SurfaceControl?
+    ) {
+        tryOrLog("Failed call stopSwipePipToHome") {
+            pip?.stopSwipePipToHome(taskId, componentName, destinationBounds, overlay)
+        }
+    }
+
+    /**
+     * Notifies WM Shell that launcher has aborted all the animation for swipe to home. WM Shell can
+     * use this callback to clean up its internal states.
+     */
+    fun abortSwipePipToHome(taskId: Int, componentName: ComponentName?) {
+        tryOrLog("Failed call abortSwipePipToHome") {
+            pip?.abortSwipePipToHome(taskId, componentName)
+        }
+    }
+
+    /** Sets the next pip animation type to be the alpha animation. */
+    fun setPipAnimationTypeToAlpha() {
+        tryOrLog("Failed call setPipAnimationTypeToAlpha") { pip?.setPipAnimationTypeToAlpha() }
+    }
+
+    /** Sets the app icon size in pixel used by Launcher all apps. */
+    fun setLauncherAppIconSize(iconSizePx: Int) {
+        tryOrLog("Failed call setLauncherAppIconSize") { pip?.setLauncherAppIconSize(iconSizePx) }
+    }
+
+    //
+    // Bubbles
+    //
+    /** Sets the listener to be notified of bubble state changes. */
+    fun setBubblesListener(listener: IBubblesListener?) {
+        bubbles?.let {
+            tryOrLog("Failed call setLauncherAppIconSize") {
+                bubblesListener?.let(it::unregisterBubbleListener)
+                listener?.let(it::registerBubbleListener)
+            }
+        }
+        bubblesListener = listener
+    }
+
+    /**
+     * Tells SysUI to show the bubble with the provided key.
+     *
+     * @param key the key of the bubble to show.
+     * @param bubbleBarOffsetX the offset of the bubble bar from the edge of the screen on the X
+     *   axis.
+     * @param bubbleBarOffsetY the offset of the bubble bar from the edge of the screen on the Y
+     *   axis.
+     */
+    fun showBubble(key: String?, bubbleBarOffsetX: Int, bubbleBarOffsetY: Int) {
+        tryOrLog("Failed call showBubble") {
+            bubbles?.showBubble(key, bubbleBarOffsetX, bubbleBarOffsetY)
+        }
+    }
+
+    /**
+     * Tells SysUI to remove the bubble with the provided key.
+     *
+     * @param key the key of the bubble to show.
+     */
+    fun removeBubble(key: String?) {
+        tryOrLog("Failed call removeBubble") { bubbles?.removeBubble(key) }
+    }
+
+    /** Tells SysUI to remove all bubbles. */
+    fun removeAllBubbles() {
+        tryOrLog("Failed call removeAllBubbles") { bubbles?.removeAllBubbles() }
+    }
+
+    /** Tells SysUI to collapse the bubbles. */
+    fun collapseBubbles() {
+        tryOrLog("Failed call collapseBubbles") { bubbles?.collapseBubbles() }
+    }
+
+    /**
+     * Tells SysUI when the bubble is being dragged. Should be called only when the bubble bar is
+     * expanded.
+     *
+     * @param bubbleKey the key of the bubble to collapse/expand
+     * @param isBeingDragged whether the bubble is being dragged
+     */
+    fun onBubbleDrag(bubbleKey: String?, isBeingDragged: Boolean) {
+        tryOrLog("Failed call onBubbleDrag") { bubbles?.onBubbleDrag(bubbleKey, isBeingDragged) }
+    }
+
+    /**
+     * Tells SysUI to show user education relative to the reference point provided.
+     *
+     * @param position the bubble bar top center position in Screen coordinates.
+     */
+    fun showUserEducation(position: Point) {
+        tryOrLog("Failed call showUserEducation") {
+            bubbles?.showUserEducation(position.x, position.y)
+        }
+    }
+
+    //
+    // Splitscreen
+    //
+    fun registerSplitScreenListener(listener: ISplitScreenListener?) {
+        tryOrLog("Failed call registerSplitScreenListener") {
+            splitScreen?.registerSplitScreenListener(listener)
+        }
+        splitScreenListener = listener
+    }
+
+    fun registerSplitSelectListener(listener: ISplitSelectListener?) {
+        tryOrLog("Failed call registerSplitScreenListener") {
+            splitScreen?.registerSplitSelectListener(listener)
+        }
+        splitSelectListener = listener
+    }
+
+    /** Start multiple tasks in split-screen simultaneously. */
+    fun startTasks(
+        taskId1: Int,
+        options1: Bundle?,
+        taskId2: Int,
+        options2: Bundle?,
+        @StagePosition splitPosition: Int,
+        @PersistentSnapPosition snapPosition: Int,
+        remoteTransition: RemoteTransition?,
+        instanceId: InstanceId?
+    ) {
+        tryOrLog(splitFailureMessage("startTasks", "RemoteException")) {
+            splitScreen?.startTasks(
+                taskId1,
+                options1,
+                taskId2,
+                options2,
+                splitPosition,
+                snapPosition,
+                remoteTransition,
+                instanceId
+            )
+        }
+    }
+
+    fun startIntentAndTask(
+        pendingIntent: PendingIntent?,
+        userId1: Int,
+        options1: Bundle?,
+        taskId: Int,
+        options2: Bundle?,
+        @StagePosition splitPosition: Int,
+        @PersistentSnapPosition snapPosition: Int,
+        remoteTransition: RemoteTransition?,
+        instanceId: InstanceId?
+    ) {
+        tryOrLog(splitFailureMessage("startIntentAndTask", "RemoteException")) {
+            splitScreen?.startIntentAndTask(
+                pendingIntent,
+                userId1,
+                options1,
+                taskId,
+                options2,
+                splitPosition,
+                snapPosition,
+                remoteTransition,
+                instanceId
+            )
+        }
+    }
+
+    fun startIntents(
+        pendingIntent1: PendingIntent?,
+        userId1: Int,
+        shortcutInfo1: ShortcutInfo?,
+        options1: Bundle?,
+        pendingIntent2: PendingIntent?,
+        userId2: Int,
+        shortcutInfo2: ShortcutInfo?,
+        options2: Bundle?,
+        @StagePosition splitPosition: Int,
+        @PersistentSnapPosition snapPosition: Int,
+        remoteTransition: RemoteTransition?,
+        instanceId: InstanceId?
+    ) {
+        tryOrLog(splitFailureMessage("startIntents", "RemoteException")) {
+            splitScreen?.startIntents(
+                pendingIntent1,
+                userId1,
+                shortcutInfo1,
+                options1,
+                pendingIntent2,
+                userId2,
+                shortcutInfo2,
+                options2,
+                splitPosition,
+                snapPosition,
+                remoteTransition,
+                instanceId
+            )
+        }
+    }
+
+    fun startShortcutAndTask(
+        shortcutInfo: ShortcutInfo?,
+        options1: Bundle?,
+        taskId: Int,
+        options2: Bundle?,
+        @StagePosition splitPosition: Int,
+        @PersistentSnapPosition snapPosition: Int,
+        remoteTransition: RemoteTransition?,
+        instanceId: InstanceId?
+    ) {
+        tryOrLog(splitFailureMessage("startShortcutAndTask", "RemoteException")) {
+            splitScreen?.startShortcutAndTask(
+                shortcutInfo,
+                options1,
+                taskId,
+                options2,
+                splitPosition,
+                snapPosition,
+                remoteTransition,
+                instanceId
+            )
+        }
+    }
+
+    /** Start multiple tasks in split-screen simultaneously. */
+    fun startTasksWithLegacyTransition(
+        taskId1: Int,
+        options1: Bundle?,
+        taskId2: Int,
+        options2: Bundle?,
+        @StagePosition splitPosition: Int,
+        @PersistentSnapPosition snapPosition: Int,
+        adapter: RemoteAnimationAdapter?,
+        instanceId: InstanceId?
+    ) {
+        tryOrLog(splitFailureMessage("startTasksWithLegacyTransition", "RemoteException")) {
+            splitScreen?.startTasksWithLegacyTransition(
+                taskId1,
+                options1,
+                taskId2,
+                options2,
+                splitPosition,
+                snapPosition,
+                adapter,
+                instanceId
+            )
+        }
+    }
+
+    fun startIntentAndTaskWithLegacyTransition(
+        pendingIntent: PendingIntent?,
+        userId1: Int,
+        options1: Bundle?,
+        taskId: Int,
+        options2: Bundle?,
+        @StagePosition splitPosition: Int,
+        @PersistentSnapPosition snapPosition: Int,
+        adapter: RemoteAnimationAdapter?,
+        instanceId: InstanceId?
+    ) {
+        tryOrLog(splitFailureMessage("startIntentAndTaskWithLegacyTransition", "RemoteException")) {
+            splitScreen?.startIntentAndTaskWithLegacyTransition(
+                pendingIntent,
+                userId1,
+                options1,
+                taskId,
+                options2,
+                splitPosition,
+                snapPosition,
+                adapter,
+                instanceId
+            )
+        }
+    }
+
+    fun startShortcutAndTaskWithLegacyTransition(
+        shortcutInfo: ShortcutInfo?,
+        options1: Bundle?,
+        taskId: Int,
+        options2: Bundle?,
+        @StagePosition splitPosition: Int,
+        @PersistentSnapPosition snapPosition: Int,
+        adapter: RemoteAnimationAdapter?,
+        instanceId: InstanceId?
+    ) {
+        tryOrLog(
+            splitFailureMessage("startShortcutAndTaskWithLegacyTransition", "RemoteException")
+        ) {
+            splitScreen?.startShortcutAndTaskWithLegacyTransition(
+                shortcutInfo,
+                options1,
+                taskId,
+                options2,
+                splitPosition,
+                snapPosition,
+                adapter,
+                instanceId
+            )
+        }
+    }
+
+    /**
+     * Starts a pair of intents or shortcuts in split-screen using legacy transition. Passing a
+     * non-null shortcut info means to start the app as a shortcut.
+     */
+    fun startIntentsWithLegacyTransition(
+        pendingIntent1: PendingIntent?,
+        userId1: Int,
+        shortcutInfo1: ShortcutInfo?,
+        options1: Bundle?,
+        pendingIntent2: PendingIntent?,
+        userId2: Int,
+        shortcutInfo2: ShortcutInfo?,
+        options2: Bundle?,
+        @StagePosition sidePosition: Int,
+        @PersistentSnapPosition snapPosition: Int,
+        adapter: RemoteAnimationAdapter?,
+        instanceId: InstanceId?
+    ) {
+        tryOrLog(splitFailureMessage("startIntentsWithLegacyTransition", "RemoteException")) {
+            splitScreen?.startIntentsWithLegacyTransition(
+                pendingIntent1,
+                userId1,
+                shortcutInfo1,
+                options1,
+                pendingIntent2,
+                userId2,
+                shortcutInfo2,
+                options2,
+                sidePosition,
+                snapPosition,
+                adapter,
+                instanceId
+            )
+        }
+    }
+
+    fun startShortcut(
+        packageName: String?,
+        shortcutId: String?,
+        position: Int,
+        options: Bundle?,
+        user: UserHandle?,
+        instanceId: InstanceId?
+    ) {
+        tryOrLog(splitFailureMessage("startShortcut", "RemoteException")) {
+            splitScreen?.startShortcut(packageName, shortcutId, position, options, user, instanceId)
+        }
+    }
+
+    fun startIntent(
+        intent: PendingIntent?,
+        userId: Int,
+        fillInIntent: Intent?,
+        position: Int,
+        options: Bundle?,
+        instanceId: InstanceId?
+    ) {
+        tryOrLog(splitFailureMessage("startIntent", "RemoteException")) {
+            splitScreen?.startIntent(intent, userId, fillInIntent, position, options, instanceId)
+        }
+    }
+
+    /**
+     * Call this when going to recents so that shell can set-up and provide appropriate leashes for
+     * animation (eg. DividerBar).
+     *
+     * @return RemoteAnimationTargets of windows that need to animate but only exist in shell.
+     */
+    fun onGoingToRecentsLegacy(
+        apps: Array<RemoteAnimationTarget?>?
+    ): Array<RemoteAnimationTarget>? {
+        if (TaskAnimationManager.ENABLE_SHELL_TRANSITIONS) return null
+        return tryOrElse("Failed call onGoingToRecentsLegacy", null) {
+            return splitScreen?.onGoingToRecentsLegacy(apps)
+        }
+    }
+
+    fun onStartingSplitLegacy(apps: Array<RemoteAnimationTarget?>?): Array<RemoteAnimationTarget>? {
+        return tryOrElse("Failed call onStartingSplitLegacy", null) {
+            return splitScreen?.onStartingSplitLegacy(apps)
+        }
+    }
+
+    //
+    // One handed
+    //
+    fun startOneHandedMode() {
+        tryOrLog("Failed call startOneHandedMode") { oneHanded?.startOneHanded() }
+    }
+
+    fun stopOneHandedMode() {
+        tryOrLog("Failed call startOneHandedstopOneHandedModeMode") { oneHanded?.stopOneHanded() }
+    }
+
+    //
+    // Remote transitions
+    //
+    fun registerRemoteTransition(remoteTransition: RemoteTransition, filter: TransitionFilter) {
+        tryOrLog("Failed call registerRemoteTransition") {
+            shellTransitions?.registerRemote(filter, remoteTransition)
+        }
+        if (!remoteTransitions.containsKey(remoteTransition)) {
+            remoteTransitions[remoteTransition] = filter
+        }
+    }
+
+    fun unregisterRemoteTransition(remoteTransition: RemoteTransition) {
+        tryOrLog("Failed call unregisterRemoteTransition") {
+            shellTransitions?.unregisterRemote(remoteTransition)
+        }
+        remoteTransitions.remove(remoteTransition)
+    }
+
+    fun setHomeTransitionListener(listener: IHomeTransitionListener?) {
+        if (!FeatureFlags.enableHomeTransitionListener()) return
+
+        homeTransitionListener = listener
+
+        tryOrLog("Failed call unregisterRemoteTransition") {
+            shellTransitions?.setHomeTransitionListener(listener)
+                ?: Log.w(
+                    TAG,
+                    "Unable to call setHomeTransitionListener because ShellTransitions is null"
+                )
+        }
+    }
+
+    /**
+     * Use SystemUI's transaction-queue instead of Launcher's independent one. This is necessary if
+     * Launcher and SystemUI need to coordinate transactions (eg. for shell transitions).
+     */
+    fun shareTransactionQueue() {
+        if (originalTransactionToken == null) {
+            originalTransactionToken = SurfaceControl.Transaction.getDefaultApplyToken()
+        }
+        setupTransactionQueue()
+    }
+
+    /** Switch back to using Launcher's independent transaction queue. */
+    fun unshareTransactionQueue() {
+        originalTransactionToken?.let { SurfaceControl.Transaction.setDefaultApplyToken(it) }
+        originalTransactionToken = null
+    }
+
+    private fun setupTransactionQueue() {
+        originalTransactionToken ?: return
+
+        var transitions = shellTransitions ?: run {
+            SurfaceControl.Transaction.setDefaultApplyToken(originalTransactionToken)
+            return
+        }
+        tryOrLog("Error getting Shell's apply token") {
+            transitions.getShellApplyToken()?.apply {
+                SurfaceControl.Transaction.setDefaultApplyToken(this)
+            } ?: Log.e(TAG, "Didn't receive apply token from Shell")
+        }
+    }
+
+    //
+    // Starting window
+    //
+    /** Sets listener to get callbacks when launching a task. */
+    fun setStartingWindowListener(listener: IStartingWindowListener?) {
+        tryOrLog("Failed call setStartingWindowListener") {
+            startingWindow?.setStartingWindowListener(listener)
+        }
+        startingWindowListener = listener
+    }
+
+    //
+    // SmartSpace transitions
+    //
+    /**
+     * Sets the instance of [ILauncherUnlockAnimationController] that System UI should use to
+     * control the launcher side of the unlock animation. This will also cause us to dispatch the
+     * current state of the smartspace to System UI (this will subsequently happen if the state
+     * changes).
+     */
+    fun setLauncherUnlockAnimationController(
+        activityClass: String?,
+        controller: ILauncherUnlockAnimationController?
+    ) {
+        tryOrLog("Failed call setLauncherUnlockAnimationController") {
+            sysuiUnlockAnimationController?.let {
+                it.setLauncherUnlockController(activityClass, controller)
+                controller?.dispatchSmartspaceStateToSysui()
+            }
+        }
+
+        launcherActivityClass = activityClass
+        launcherUnlockAnimationController = controller
+    }
+
+    /**
+     * Tells System UI that the Launcher's smartspace state has been updated, so that it can prepare
+     * the unlock animation accordingly.
+     */
+    fun notifySysuiSmartspaceStateUpdated(state: SmartspaceState?) {
+        tryOrLog("Failed call notifySysuiSmartspaceStateUpdated") {
+            sysuiUnlockAnimationController?.onLauncherSmartspaceStateUpdated(state)
+        }
+    }
+
+    //
+    // Recents
+    //
+    fun registerRecentTasksListener(listener: IRecentTasksListener?) {
+        tryOrLog("Failed call registerRecentTasksListener") {
+            recentTasks?.registerRecentTasksListener(listener)
+        }
+        recentTasksListener = listener
+    }
+
+    //
+    // Back navigation transitions
+    //
+    /** Sets the launcher [android.window.IOnBackInvokedCallback] to shell */
+    fun setBackToLauncherCallback(
+        callback: IOnBackInvokedCallback?,
+        runner: IRemoteAnimationRunner?
+    ) {
+        backToLauncherCallback = callback
+        backToLauncherRunner = runner
+
+        callback ?: return
+        tryOrLog("Failed call setBackToLauncherCallback") {
+            backAnimation?.setBackToLauncherCallback(callback, runner)
+        }
+    }
+
+    /**
+     * Clears the previously registered [IOnBackInvokedCallback].
+     *
+     * @param callback The previously registered callback instance.
+     */
+    fun clearBackToLauncherCallback(callback: IOnBackInvokedCallback) {
+        if (backToLauncherCallback != callback) {
+            return
+        }
+        backToLauncherCallback = null
+        backToLauncherRunner = null
+        tryOrLog("Failed call clearBackToLauncherCallback") {
+            backAnimation?.clearBackToLauncherCallback()
+        }
+    }
+
+    /** Called when the status bar color needs to be customized when back navigation. */
+    fun customizeStatusBarAppearance(appearance: AppearanceRegion?) {
+        tryOrLog("Failed call customizeStatusBarAppearance") {
+            backAnimation?.customizeStatusBarAppearance(appearance)
+        }
+    }
+
+    fun getRecentTasks(numTasks: Int, userId: Int): ArrayList<GroupedRecentTaskInfo> {
+        return tryOrElse("Failed call getRecentTasks", ArrayList()) {
+            return recentTasks
+                ?.getRecentTasks(numTasks, ActivityManager.RECENT_IGNORE_UNAVAILABLE, userId)
+                ?.let { ArrayList(it.asList()) }
+                ?: ArrayList()
+        }
+    }
+
+    /** Gets the set of running tasks. */
+    fun getRunningTasks(numTasks: Int): ArrayList<RunningTaskInfo> {
+        if (!context.packageManager.hasSystemFeature(PackageManager.FEATURE_PC)) {
+            return ArrayList()
+        }
+
+        return tryOrElse("Failed call getRunningTasks", ArrayList()) {
+            recentTasks?.getRunningTasks(numTasks)?.let { ArrayList(it.asList()) } ?: ArrayList()
+        }
+    }
+
+    private fun handleMessageAsync(msg: Message): Boolean {
+        when (msg.what) {
+            MSG_SET_SHELF_HEIGHT -> {
+                setShelfHeightAsync(msg.arg1, msg.arg2)
+                return true
+            }
+            MSG_SET_LAUNCHER_KEEP_CLEAR_AREA_HEIGHT -> {
+                setLauncherKeepClearAreaHeight(msg.arg1, msg.arg2)
+                return true
+            }
+        }
+        return false
+    }
+
+    //
+    // Desktop Mode
+    //
+    /** Call shell to show all apps active on the desktop */
+    fun showDesktopApps(displayId: Int, transition: RemoteTransition?) {
+        tryOrLog("Failed call showDesktopApps") {
+            desktopMode?.showDesktopApps(displayId, transition)
+        }
+    }
+
+    /** Call shell to stash desktop apps */
+    fun stashDesktopApps(displayId: Int) {
+        tryOrLog("Failed call stashDesktopApps") { desktopMode?.stashDesktopApps(displayId) }
+    }
+
+    /** Call shell to hide desktop apps that may be stashed */
+    fun hideStashedDesktopApps(displayId: Int) {
+        tryOrLog("Failed call hideStashedDesktopApps") {
+            desktopMode?.hideStashedDesktopApps(displayId)
+        }
+    }
+
+    /** If task with the given id is on the desktop, bring it to front */
+    fun showDesktopApp(taskId: Int) {
+        tryOrLog("Failed call showDesktopApp") { desktopMode?.showDesktopApp(taskId) }
+    }
+
+    /** Call shell to get number of visible freeform tasks */
+    fun getVisibleDesktopTaskCount(displayId: Int): Int {
+        return tryOrElse("Failed call getVisibleDesktopTaskCount", 0) {
+            return desktopMode?.getVisibleTaskCount(displayId) ?: 0
+        }
+    }
+
+    /** Set a listener on shell to get updates about desktop task state */
+    fun setDesktopTaskListener(listener: IDesktopTaskListener?) {
+        desktopTaskListener = listener
+        tryOrLog("Failed call setDesktopTaskListener") { desktopMode?.setTaskListener(listener) }
+    }
+
+    /** Perform cleanup transactions after animation to split select is complete */
+    fun onDesktopSplitSelectAnimComplete(taskInfo: RunningTaskInfo?) {
+        tryOrLog("Failed call onDesktopSplitSelectAnimComplete") {
+            desktopMode?.onDesktopSplitSelectAnimComplete(taskInfo)
+        }
+    }
+    //
+    // Unfold transition
+    //
+    /** Sets the unfold animation lister to sysui. */
+    fun setUnfoldAnimationListener(callback: IUnfoldTransitionListener?) {
+        unfoldAnimationListener = callback
+        tryOrLog("Failed call setUnfoldAnimationListener") {
+            unfoldAnimation?.setListener(callback)
+        }
+    }
+
+    //
+    // Recents
+    //
+    /** Starts the recents activity. The caller should manage the thread on which this is called. */
+    fun startRecentsActivity(
+        intent: Intent?,
+        options: ActivityOptions,
+        listener: RecentsAnimationListener
+    ): Boolean {
+
+        val runner: IRecentsAnimationRunner =
+            object : IRecentsAnimationRunner.Stub() {
+                override fun onAnimationStart(
+                    controller: IRecentsAnimationController,
+                    apps: Array<RemoteAnimationTarget>,
+                    wallpapers: Array<RemoteAnimationTarget>,
+                    homeContentInsets: Rect,
+                    minimizedHomeBounds: Rect,
+                    extras: Bundle?
+                ) {
+                    // Aidl bundles need to explicitly set class loader
+                    // https://developer.android.com/guide/components/aidl#Bundles
+                    extras?.classLoader = javaClass.classLoader
+                    listener.onAnimationStart(
+                        RecentsAnimationControllerCompat(controller),
+                        apps,
+                        wallpapers,
+                        homeContentInsets,
+                        minimizedHomeBounds,
+                        extras
+                    )
+                }
+
+                override fun onAnimationCanceled(
+                    taskIds: IntArray,
+                    taskSnapshots: Array<TaskSnapshot>
+                ) {
+                    listener.onAnimationCanceled(ThumbnailData.wrap(taskIds, taskSnapshots))
+                }
+
+                override fun onTasksAppeared(apps: Array<RemoteAnimationTarget>) {
+                    listener.onTasksAppeared(apps)
+                }
+            }
+
+        return tryOrElse("Error starting recents via shell", false) {
+            recentTasks?.let {
+                it.startRecentsTransition(
+                    recentsPendingIntent,
+                    intent,
+                    options.toBundle(),
+                    context.iApplicationThread,
+                    runner
+                )
+                return true
+            }
+                ?: run {
+                    ActiveGestureLog.INSTANCE.addLog(
+                        "Null recentTasks",
+                        ActiveGestureErrorDetector.GestureEvent.RECENT_TASKS_MISSING
+                    )
+                    false
+                }
+        }
+    }
+
+    //
+    // Drag and drop
+    //
+
+    /**
+     * For testing purposes. Returns `true` only if the shell drop target has shown and drawn and is
+     * ready to handle drag events and the subsequent drop.
+     */
+    val isDragAndDropReady: Boolean
+        get() =
+            tryOrElse("Error querying drag state", false) {
+                dragAndDrop?.isReadyToHandleDrag ?: false
+            }
+
+    fun dump(pw: PrintWriter) {
+        pw.println("$TAG:")
+        pw.println("\tsystemUiProxy=$systemUiProxy")
+        pw.println("\tpip=$pip")
+        pw.println("\tpipAnimationListener=$pipAnimationListener")
+        pw.println("\tbubbles=$bubbles")
+        pw.println("\tbubblesListener=$bubblesListener")
+        pw.println("\tsplitScreen=$splitScreen")
+        pw.println("\tsplitScreenListener=$splitScreenListener")
+        pw.println("\tsplitSelectListener=$splitSelectListener")
+        pw.println("\toneHanded=$oneHanded")
+        pw.println("\tshellTransitions=$shellTransitions")
+        pw.println("\thomeTransitionListener=$homeTransitionListener")
+        pw.println("\tstartingWindow=$startingWindow")
+        pw.println("\tstartingWindowListener=$startingWindowListener")
+        pw.println("\tsysuiUnlockAnimationController=$sysuiUnlockAnimationController")
+        pw.println("\tlauncherActivityClass=$launcherActivityClass")
+        pw.println("\tlauncherUnlockAnimationController=$launcherUnlockAnimationController")
+        pw.println("\trecentTasks=$recentTasks")
+        pw.println("\trecentTasksListener=$recentTasksListener")
+        pw.println("\tbackAnimation=$backAnimation")
+        pw.println("\tbackToLauncherCallback=$backToLauncherCallback")
+        pw.println("\tbackToLauncherRunner=$backToLauncherRunner")
+        pw.println("\tdesktopMode=$desktopMode")
+        pw.println("\tdesktopTaskListener=$desktopTaskListener")
+        pw.println("\tunfoldAnimation=$unfoldAnimation")
+        pw.println("\tunfoldAnimationListener=$unfoldAnimationListener")
+        pw.println("\tdragAndDrop=$dragAndDrop")
+    }
+
+    companion object {
+        private const val TAG = "SystemUiProxy"
+        private const val MSG_SET_SHELF_HEIGHT = 1
+        private const val MSG_SET_LAUNCHER_KEEP_CLEAR_AREA_HEIGHT = 2
+
+        @JvmField
+        val INSTANCE = MainThreadInitializedObject { context: Context -> SystemUiProxy(context) }
+
+        private inline fun tryOrLog(msg: String, f: () -> Unit) =
+            try {
+                f()
+            } catch (e: RemoteException) {
+                Log.w(TAG, msg, e)
+            }
+
+        private inline fun <T> tryOrElse(msg: String, fallback: T, f: () -> T): T =
+            try {
+                f()
+            } catch (e: RemoteException) {
+                Log.w(TAG, msg, e)
+                fallback
+            }
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
index e5fca4b..9e21595 100644
--- a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
+++ b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
@@ -195,11 +195,15 @@
             return null;
         }
 
-        CancellableTask<ThumbnailData> request = new CancellableTask<ThumbnailData>() {
+        CancellableTask<ThumbnailData> request = new CancellableTask<>() {
             @Override
             public ThumbnailData getResultOnBg() {
-                return ActivityManagerWrapper.getInstance().getTaskThumbnail(
+                ThumbnailData thumbnailData = ActivityManagerWrapper.getInstance().getTaskThumbnail(
                         key.id, lowResolution);
+                if (thumbnailData.thumbnail != null) {
+                    return thumbnailData;
+                }
+                return ActivityManagerWrapper.getInstance().takeTaskThumbnail(key.id);
             }
 
             @Override
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 137ffe8..025003b 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -432,7 +432,7 @@
     <dimen name="split_instructions_start_margin_cancel">8dp</dimen>
 
     <!-- Workspace grid visualization parameters -->
-    <dimen name="grid_visualization_rounding_radius">28dp</dimen>
+    <dimen name="grid_visualization_rounding_radius">16dp</dimen>
     <dimen name="grid_visualization_horizontal_cell_spacing">6dp</dimen>
     <dimen name="grid_visualization_vertical_cell_spacing">6dp</dimen>
 
diff --git a/src/com/android/launcher3/allapps/PrivateProfileManager.java b/src/com/android/launcher3/allapps/PrivateProfileManager.java
index d8f6689..77eb07e 100644
--- a/src/com/android/launcher3/allapps/PrivateProfileManager.java
+++ b/src/com/android/launcher3/allapps/PrivateProfileManager.java
@@ -92,9 +92,6 @@
         boolean isEnabled = !mAllApps.getAppsStore()
                 .hasModelFlag(FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED);
         int updatedState = isEnabled ? STATE_ENABLED : STATE_DISABLED;
-        if (getCurrentState() == updatedState) {
-            return;
-        }
         setCurrentState(updatedState);
         resetPrivateSpaceDecorator(updatedState);
     }
@@ -126,19 +123,27 @@
 
     @VisibleForTesting
     void resetPrivateSpaceDecorator(int updatedState) {
+        ActivityAllAppsContainerView<?>.AdapterHolder mainAdapterHolder = mAllApps.mAH.get(MAIN);
         if (updatedState == STATE_ENABLED) {
-            // Add Private Space Decorator to the Recycler view.
+            // Create a new decorator instance if not already available.
             if (mPrivateAppsSectionDecorator == null) {
                 mPrivateAppsSectionDecorator = new PrivateAppsSectionDecorator(
                         mAllApps.mActivityContext,
-                        mAllApps.mAH.get(MAIN).mAppsList);
+                        mainAdapterHolder.mAppsList);
             }
-            mAllApps.mAH.get(MAIN).mRecyclerView.addItemDecoration(mPrivateAppsSectionDecorator);
+            for (int i = 0; i < mainAdapterHolder.mRecyclerView.getItemDecorationCount(); i++) {
+                if (mainAdapterHolder.mRecyclerView.getItemDecorationAt(i)
+                        .equals(mPrivateAppsSectionDecorator)) {
+                    // No need to add another decorator if one is already present in recycler view.
+                    return;
+                }
+            }
+            // Add Private Space Decorator to the Recycler view.
+            mainAdapterHolder.mRecyclerView.addItemDecoration(mPrivateAppsSectionDecorator);
         } else {
             // Remove Private Space Decorator from the Recycler view.
             if (mPrivateAppsSectionDecorator != null) {
-                mAllApps.mAH.get(MAIN).mRecyclerView
-                        .removeItemDecoration(mPrivateAppsSectionDecorator);
+                mainAdapterHolder.mRecyclerView.removeItemDecoration(mPrivateAppsSectionDecorator);
             }
         }
     }
diff --git a/src/com/android/launcher3/keyboard/ItemFocusIndicatorHelper.java b/src/com/android/launcher3/keyboard/ItemFocusIndicatorHelper.java
index 57fab2d..2dc8d81 100644
--- a/src/com/android/launcher3/keyboard/ItemFocusIndicatorHelper.java
+++ b/src/com/android/launcher3/keyboard/ItemFocusIndicatorHelper.java
@@ -29,8 +29,7 @@
 import android.util.FloatProperty;
 import android.view.View;
 
-import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.util.Themes;
+import com.android.launcher3.R;
 
 /**
  * A helper class to draw background of a focused item.
@@ -103,9 +102,8 @@
 
         setAlpha(0);
         mShift = 0;
-        if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
-            mRadius = Themes.getDialogCornerRadius(container.getContext());
-        }
+        mRadius = container.getResources().getDimensionPixelSize(
+                R.dimen.grid_visualization_rounding_radius);
     }
 
     protected void setAlpha(float alpha) {
diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java
index 1b1d347..40bb6ad 100644
--- a/src/com/android/launcher3/testing/TestInformationHandler.java
+++ b/src/com/android/launcher3/testing/TestInformationHandler.java
@@ -15,8 +15,8 @@
  */
 package com.android.launcher3.testing;
 
-import static com.android.launcher3.allapps.AllAppsStore.DEFER_UPDATES_TEST;
 import static com.android.launcher3.Flags.enableGridOnlyOverview;
+import static com.android.launcher3.allapps.AllAppsStore.DEFER_UPDATES_TEST;
 import static com.android.launcher3.config.FeatureFlags.FOLDABLE_SINGLE_PAGE;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 
@@ -297,8 +297,10 @@
     }
 
     protected boolean isLauncherInitialized() {
-        return Launcher.ACTIVITY_TRACKER.getCreatedActivity() == null
-                || LauncherAppState.getInstance(mContext).getModel().isModelLoaded();
+        Launcher launcher = Launcher.ACTIVITY_TRACKER.getCreatedActivity();
+        return launcher == null
+                || (LauncherAppState.getInstance(mContext).getModel().isModelLoaded()
+                && !launcher.isBindingItems());
     }
 
     protected Activity getCurrentActivity() {
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index 5ec1022..9855b20 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -105,6 +105,7 @@
             mNavBarScrimPaint.setColor(navBarScrimColor);
             invalidate();
         }
+        setupNavBarColor();
     }
 
     @Override
@@ -218,10 +219,18 @@
     }
 
     protected void setupNavBarColor() {
-        boolean isSheetDark = Themes.getAttrBoolean(getContext(), R.attr.isMainColorDark);
-        getSystemUiController().updateUiState(
-                SystemUiController.UI_STATE_WIDGET_BOTTOM_SHEET,
-                isSheetDark ? SystemUiController.FLAG_DARK_NAV : SystemUiController.FLAG_LIGHT_NAV);
+        boolean isNavBarDark = Themes.getAttrBoolean(getContext(), R.attr.isMainColorDark);
+
+        // In light mode, landscape reverses navbar background color.
+        boolean isPhoneLandscape =
+                !mActivityContext.getDeviceProfile().isTablet && mInsets.bottom == 0;
+        if (!isNavBarDark && isPhoneLandscape) {
+            isNavBarDark = true;
+        }
+
+        getSystemUiController().updateUiState(SystemUiController.UI_STATE_WIDGET_BOTTOM_SHEET,
+                isNavBarDark ? SystemUiController.FLAG_DARK_NAV
+                        : SystemUiController.FLAG_LIGHT_NAV);
     }
 
     protected SystemUiController getSystemUiController() {
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index dbfe5ad..da90f17 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -24,7 +24,6 @@
 
 import android.animation.Animator;
 import android.content.Context;
-import android.content.pm.LauncherApps;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Rect;
@@ -106,9 +105,7 @@
     private final UserHandle mCurrentUser = Process.myUserHandle();
     private final Predicate<WidgetsListBaseEntry> mPrimaryWidgetsFilter =
             entry -> mCurrentUser.equals(entry.mPkgItem.user);
-    private final Predicate<WidgetsListBaseEntry> mWorkWidgetsFilter =
-            entry -> !mCurrentUser.equals(entry.mPkgItem.user)
-                    && !mUserManagerState.isUserQuiet(entry.mPkgItem.user);
+    private final Predicate<WidgetsListBaseEntry> mWorkWidgetsFilter;
     protected final boolean mHasWorkProfile;
     protected boolean mHasRecommendedWidgets;
     protected final SparseArray<AdapterHolder> mAdapters = new SparseArray();
@@ -182,20 +179,23 @@
     public WidgetsFullSheet(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
         mDeviceProfile = mActivityContext.getDeviceProfile();
-        mHasWorkProfile = context.getSystemService(LauncherApps.class).getProfiles().size() > 1;
         mOrientation = context.getResources().getConfiguration().orientation;
+        mUserCache = UserCache.INSTANCE.get(context);
+        mHasWorkProfile = mUserCache.getUserProfiles()
+                .stream()
+                .anyMatch(user -> mUserCache.getUserInfo(user).isWork());
+        mWorkWidgetsFilter = entry -> mHasWorkProfile
+                && mUserCache.getUserInfo(entry.mPkgItem.user).isWork();
         mAdapters.put(AdapterHolder.PRIMARY, new AdapterHolder(AdapterHolder.PRIMARY));
         mAdapters.put(AdapterHolder.WORK, new AdapterHolder(AdapterHolder.WORK));
         mAdapters.put(AdapterHolder.SEARCH, new AdapterHolder(AdapterHolder.SEARCH));
 
         Resources resources = getResources();
+        mUserManagerState.init(UserCache.INSTANCE.get(context),
+                context.getSystemService(UserManager.class));
         mTabsHeight = mHasWorkProfile
                 ? resources.getDimensionPixelSize(R.dimen.all_apps_header_pill_height)
                 : 0;
-
-        mUserCache = UserCache.INSTANCE.get(context);
-        mUserManagerState.init(UserCache.INSTANCE.get(context),
-                context.getSystemService(UserManager.class));
     }
 
     public WidgetsFullSheet(Context context, AttributeSet attrs) {
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index dd8ab81..fb81700 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -585,6 +585,7 @@
         if (hasSystemLauncherObject(OVERVIEW_RES_ID)) return "Overview";
         if (hasLauncherObject(WORKSPACE_RES_ID)) return "Workspace";
         if (hasLauncherObject(APPS_RES_ID)) return "AllApps";
+        if (hasLauncherObject(TASKBAR_RES_ID)) return "Taskbar";
         if (mDevice.hasObject(By.pkg(getLauncherPackageName()).depth(0))) {
             return "<Launcher in invalid state>";
         }
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
index 8a34f0d..068482e 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
@@ -16,8 +16,6 @@
 
 package com.android.launcher3.tapl;
 
-import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
-
 import android.graphics.Rect;
 
 import androidx.annotation.NonNull;
@@ -192,8 +190,8 @@
     private List<Integer> getCurrentTasksCenterXList() {
         return mLauncher.isTablet()
                 ? mOverview.getCurrentTasksForTablet().stream()
-                    .map(OverviewTask::getTaskCenterX)
-                    .collect(Collectors.toList())
+                .map(OverviewTask::getTaskCenterX)
+                .collect(Collectors.toList())
                 : List.of(mOverview.getCurrentTask().getTaskCenterX());
     }
 
@@ -203,11 +201,11 @@
     public LaunchedAppState open() {
         try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
             verifyActiveContainer();
-            mLauncher.executeAndWaitForEvent(
+            mLauncher.executeAndWaitForLauncherEvent(
                     () -> mLauncher.clickLauncherObject(mTask),
-                    event -> event.getEventType() == TYPE_WINDOW_STATE_CHANGED,
-                    () -> "Launching task didn't open a new window: "
-                            + mTask.getParent().getContentDescription(),
+                    event -> TestProtocol.LAUNCHER_ACTIVITY_STOPPED_MESSAGE
+                            .equals(event.getClassName().toString()),
+                    () -> "Launcher activity didn't stop",
                     "clicking an overview task");
             if (mOverview.getContainerType()
                     == LauncherInstrumentation.ContainerType.SPLIT_SCREEN_SELECT) {