Merge "Do not show install shortcut for Settings" into main
diff --git a/go/src/com/android/launcher3/model/WidgetsModel.java b/go/src/com/android/launcher3/model/WidgetsModel.java
index 1aa5d03..3a28444 100644
--- a/go/src/com/android/launcher3/model/WidgetsModel.java
+++ b/go/src/com/android/launcher3/model/WidgetsModel.java
@@ -33,6 +33,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.Predicate;
 
 /**
  * Widgets data model that is used by the adapters of the widget views and controllers.
@@ -48,6 +49,19 @@
     private static final ArrayList<WidgetsListBaseEntry> EMPTY_WIDGET_LIST = new ArrayList<>();
 
     /**
+     * Returns a list of {@link WidgetsListBaseEntry} filtered using given widget item filter. All
+     * {@link WidgetItem}s in a single row are sorted (based on label and user), but the overall
+     * list of {@link WidgetsListBaseEntry}s is not sorted.
+     *
+     * @see com.android.launcher3.widget.picker.WidgetsListAdapter#setWidgets(List)
+     */
+    public synchronized ArrayList<WidgetsListBaseEntry> getFilteredWidgetsListForPicker(
+            Context context,
+            Predicate<WidgetItem> widgetItemFilter) {
+        return EMPTY_WIDGET_LIST;
+    }
+
+    /**
      * Returns a list of {@link WidgetsListBaseEntry}. All {@link WidgetItem} in a single row are
      * sorted (based on label and user), but the overall list of {@link WidgetsListBaseEntry}s is
      * not sorted. This list is sorted at the UI when using
@@ -88,4 +102,4 @@
             Context context, ComponentName provider, UserHandle userHandle) {
         return new PackageItemInfo(provider.getPackageName(), userHandle);
     }
-}
\ No newline at end of file
+}
diff --git a/quickstep/src/com/android/launcher3/WidgetPickerActivity.java b/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
index 575ad9e..b9b4461 100644
--- a/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
+++ b/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
@@ -158,7 +158,10 @@
             LauncherAppState app = LauncherAppState.getInstance(this);
             mModel.update(app, null);
             final ArrayList<WidgetsListBaseEntry> widgets =
-                    mModel.getWidgetsListForPicker(app.getContext());
+                    mModel.getFilteredWidgetsListForPicker(
+                            app.getContext(),
+                            /*widgetItemFilter=*/ item -> item.widgetInfo != null
+                    );
             MAIN_EXECUTOR.execute(() -> mPopupDataProvider.setAllWidgets(widgets));
         });
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
index 9dac89d..2421c94 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
@@ -23,6 +23,7 @@
 import android.app.ActivityOptions;
 import android.view.KeyEvent;
 import android.view.View;
+import android.view.animation.AnimationUtils;
 import android.window.RemoteTransition;
 
 import androidx.annotation.NonNull;
@@ -38,6 +39,7 @@
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.QuickStepContract;
 
 import java.io.PrintWriter;
 import java.util.List;
@@ -148,8 +150,13 @@
             return -1;
         }
 
+        TaskbarActivityContext context = mControllers.taskbarActivityContext;
         RemoteTransition remoteTransition = new RemoteTransition(new SlideInRemoteTransition(
-                Utilities.isRtl(mControllers.taskbarActivityContext.getResources())));
+                Utilities.isRtl(mControllers.taskbarActivityContext.getResources()),
+                context.getDeviceProfile().overviewPageSpacing,
+                QuickStepContract.getWindowCornerRadius(context),
+                AnimationUtils.loadInterpolator(
+                        context, android.R.interpolator.fast_out_extra_slow_in)));
         if (mOnDesktop) {
             UI_HELPER_EXECUTOR.execute(() ->
                     SystemUiProxy.INSTANCE.get(mKeyboardQuickSwitchView.getContext())
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
index 8092582..d834935 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
@@ -16,6 +16,7 @@
 package com.android.launcher3.uioverrides;
 
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SPLIT_WIDGET_ATTEMPT;
 
 import android.app.ActivityOptions;
 import android.app.ActivityTaskManager;
@@ -59,6 +60,9 @@
                     remoteResponse.getLaunchOptions(view));
         }
         if (mLauncher.isSplitSelectionEnabled()) {
+            // Log metric
+            StatsLogManager.StatsLogger logger = mLauncher.getStatsLogManager().logger();
+            logger.log(LAUNCHER_SPLIT_WIDGET_ATTEMPT);
             Toast.makeText(hostView.getContext(), R.string.split_widgets_not_supported,
                     Toast.LENGTH_SHORT).show();
             return true;
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
index 6651c73..0650f9d 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
@@ -110,10 +110,13 @@
                         clampToProgress(LINEAR, 0, 0.33f));
             }
 
+            // We sync the scrim fade with the taskbar animation duration to avoid any flickers for
+            // taskbar icons disappearing before hotseat icons show up.
+            float scrimUpperBoundFromSplit = TASKBAR_TO_HOME_DURATION / (float) config.duration;
             config.setInterpolator(ANIM_OVERVIEW_ACTIONS_FADE, clampToProgress(LINEAR, 0, 0.25f));
             config.setInterpolator(ANIM_SCRIM_FADE,
                     fromState == OVERVIEW_SPLIT_SELECT
-                            ? clampToProgress(LINEAR, 0.33f, 1)
+                            ? clampToProgress(LINEAR, 0.33f, scrimUpperBoundFromSplit)
                             : LINEAR);
             config.setInterpolator(ANIM_WORKSPACE_SCALE, DECELERATE);
             config.setInterpolator(ANIM_WORKSPACE_FADE, ACCELERATE);
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 9972e59..7982606 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -23,6 +23,7 @@
 import static android.view.MotionEvent.ACTION_POINTER_UP;
 import static android.view.MotionEvent.ACTION_UP;
 
+import static com.android.launcher3.Flags.enableCursorHoverStates;
 import static com.android.launcher3.Launcher.INTENT_ACTION_ALL_APPS_TOGGLE;
 import static com.android.launcher3.LauncherPrefs.backedUpItem;
 import static com.android.launcher3.MotionEventsUtils.isTrackpadMotionEvent;
@@ -734,7 +735,8 @@
         final int action = event.getActionMasked();
         // Note this will create a new consumer every mouse click, as after ACTION_UP from the click
         // an ACTION_HOVER_ENTER will fire as well.
-        boolean isHoverActionWithoutConsumer = isHoverActionWithoutConsumer(event);
+        boolean isHoverActionWithoutConsumer = enableCursorHoverStates()
+                && isHoverActionWithoutConsumer(event);
         CompoundString reasonString = action == ACTION_DOWN
                 ? new CompoundString("onMotionEvent: ") : CompoundString.NO_OP;
         if (action == ACTION_DOWN || isHoverActionWithoutConsumer) {
diff --git a/quickstep/src/com/android/quickstep/util/SlideInRemoteTransition.kt b/quickstep/src/com/android/quickstep/util/SlideInRemoteTransition.kt
index c1fa2f3..6544ba7 100644
--- a/quickstep/src/com/android/quickstep/util/SlideInRemoteTransition.kt
+++ b/quickstep/src/com/android/quickstep/util/SlideInRemoteTransition.kt
@@ -15,6 +15,7 @@
  */
 package com.android.quickstep.util
 
+import android.animation.TimeInterpolator
 import android.animation.ValueAnimator
 import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME
 import android.graphics.Rect
@@ -30,7 +31,13 @@
 import com.android.wm.shell.util.TransitionUtil
 
 /** Remote animation which slides the opening targets in and the closing targets out */
-class SlideInRemoteTransition(val isRtl: Boolean) : IRemoteTransition.Stub() {
+class SlideInRemoteTransition(
+    private val isRtl: Boolean,
+    private val pageSpacing: Int,
+    private val cornerRadius: Float,
+    private val interpolator: TimeInterpolator,
+) : IRemoteTransition.Stub() {
+    private val animationDurationMs = 500L
 
     override fun mergeAnimation(
         iBinder: IBinder,
@@ -54,6 +61,8 @@
         finishCB: IRemoteTransitionFinishedCallback
     ) {
         val anim = ValueAnimator.ofFloat(0f, 1f)
+        anim.interpolator = interpolator
+        anim.duration = animationDurationMs
 
         val closingStartBounds: HashMap<SurfaceControl, Rect> = HashMap()
         val openingEndBounds: HashMap<SurfaceControl, Rect> = HashMap()
@@ -67,9 +76,11 @@
             }
             if (TransitionUtil.isClosingType(chg.mode)) {
                 closingStartBounds[leash] = chg.startAbsBounds
+                startT.setCrop(leash, chg.startAbsBounds).setCornerRadius(leash, cornerRadius)
             }
             if (TransitionUtil.isOpeningType(chg.mode)) {
                 openingEndBounds[leash] = chg.endAbsBounds
+                startT.setCrop(leash, chg.endAbsBounds).setCornerRadius(leash, cornerRadius)
             }
         }
         startT.apply()
@@ -80,7 +91,7 @@
                 // Translate the surface from its original position on-screen to off-screen on the
                 // right (or left in RTL)
                 val startBounds = closingStartBounds[it]
-                val targetX = (if (isRtl) -1 else 1) * startBounds!!.right
+                val targetX = (if (isRtl) -1 else 1) * (startBounds!!.right + pageSpacing)
                 t.setPosition(it, anim.animatedValue as Float * targetX, 0f)
             }
             openingEndBounds.keys.forEach {
@@ -90,7 +101,7 @@
                 // Translate the surface from off-screen on the left (or left in RTL) to its final
                 // position on-screen
                 val endBounds = openingEndBounds[it]
-                val targetX = (if (isRtl) -1 else 1) * endBounds!!.right
+                val targetX = (if (isRtl) -1 else 1) * (endBounds!!.right + pageSpacing)
                 t.setPosition(it, (1f - anim.animatedValue as Float) * -targetX, 0f)
             }
             t.apply()
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsPersistentTaskbar.java b/quickstep/tests/src/com/android/quickstep/TaplTestsPersistentTaskbar.java
index b165876..a71d74a 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsPersistentTaskbar.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsPersistentTaskbar.java
@@ -15,6 +15,8 @@
  */
 package com.android.quickstep;
 
+import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL;
+import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT;
 import static com.android.quickstep.TaskbarModeSwitchRule.Mode.PERSISTENT;
 
 import android.graphics.Rect;
@@ -24,6 +26,7 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
+import com.android.launcher3.util.rule.TestStabilityRule;
 import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
 import com.android.quickstep.TaskbarModeSwitchRule.TaskbarModeSwitch;
 
@@ -48,6 +51,7 @@
     }
 
     @Test
+    @TestStabilityRule.Stability(flavors = LOCAL | PLATFORM_POSTSUBMIT) // b/320490387
     @NavigationModeSwitch(mode = NavigationModeSwitchRule.Mode.THREE_BUTTON)
     public void testThreeButtonsTaskbarBoundsAfterConfigChangeDuringIme() {
         // Start off in light mode.
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index e54539f..754f0cb 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -42,6 +42,7 @@
     <attr name="overviewScrimColor" format="color" />
     <attr name="popupNotificationDotColor" format="color" />
     <attr name="notificationDotColor" format="color" />
+    <attr name="focusOutlineColor" format="color" />
 
     <attr name="folderPaginationColor" format="color" />
     <attr name="folderPreviewColor" format="color" />
diff --git a/res/values/colors.xml b/res/values/colors.xml
index db9631a..6a484d7 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -25,7 +25,6 @@
     <color name="uninstall_target_hover_tint">#FFF0592B</color>
 
     <color name="focused_background">#80c6c5c5</color>
-    <color name="focus_outline_color">@color/material_color_on_secondary_container</color>
 
     <color name="default_shadow_color_no_alpha">#FF000000</color>
 
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 36991b1..b83be35 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -52,6 +52,7 @@
         <item name="workspaceAmbientShadowColor">#40000000</item>
         <item name="workspaceKeyShadowColor">#89000000</item>
         <item name="widgetsTheme">@style/WidgetContainerTheme</item>
+        <item name="focusOutlineColor">@color/material_color_on_secondary_container</item>
         <item name="folderPaginationColor">@color/folder_pagination_color_light</item>
         <item name="folderPreviewColor">@color/folder_preview_light</item>
         <item name="folderBackgroundColor">@color/folder_background_light</item>
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 834ba04..e9545c8 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -81,6 +81,9 @@
     private static final float MIN_FOLDER_TEXT_SIZE_SP = 16f;
     private static final float MIN_WIDGET_PADDING_DP = 6f;
 
+    // Minimum aspect ratio beyond which an extra top padding may be applied to a bottom sheet.
+    private static final float MIN_ASPECT_RATIO_FOR_EXTRA_TOP_PADDING = 1.5f;
+
     public static final PointF DEFAULT_SCALE = new PointF(1.0f, 1.0f);
     public static final ViewScaleProvider DEFAULT_PROVIDER = itemInfo -> DEFAULT_SCALE;
     public static final Consumer<DeviceProfile> DEFAULT_DIMENSION_PROVIDER = dp -> {
@@ -414,8 +417,14 @@
         gridVisualizationPaddingY = res.getDimensionPixelSize(
                 R.dimen.grid_visualization_vertical_cell_spacing);
 
+        // Tablet portrait mode uses a single pane widget picker and extra padding may be applied on
+        // top to avoid making it look too elongated.
+        final boolean applyExtraTopPadding = isTablet
+                && !isLandscape
+                && (aspectRatio > MIN_ASPECT_RATIO_FOR_EXTRA_TOP_PADDING);
         bottomSheetTopPadding = mInsets.top // statusbar height
-                + res.getDimensionPixelSize(R.dimen.bottom_sheet_extra_top_padding)
+                + (applyExtraTopPadding ? res.getDimensionPixelSize(
+                R.dimen.bottom_sheet_extra_top_padding) : 0)
                 + (isTablet ? 0 : edgeMarginPx); // phones need edgeMarginPx additional padding
         bottomSheetOpenDuration = res.getInteger(R.integer.config_bottomSheetOpenDuration);
         bottomSheetCloseDuration = res.getInteger(R.integer.config_bottomSheetCloseDuration);
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index ad764e3..55438fe 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -260,7 +260,7 @@
         mMainAdapterProvider = mSearchUiDelegate.createMainAdapterProvider();
         if (Flags.enablePrivateSpace()) {
             mPrivateSpaceHeaderViewController =
-                    new PrivateSpaceHeaderViewController(mPrivateProfileManager);
+                    new PrivateSpaceHeaderViewController(this, mPrivateProfileManager);
         }
 
         mAH.set(AdapterHolder.MAIN, new AdapterHolder(AdapterHolder.MAIN,
@@ -980,6 +980,11 @@
         return mWorkManager;
     }
 
+    /** Returns whether Private Profile has been setup. */
+    public boolean hasPrivateProfile() {
+        return mHasPrivateApps;
+    }
+
     @Override
     public void onDeviceProfileChanged(DeviceProfile dp) {
         for (AdapterHolder holder : mAH) {
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index 35c07c3..ad875e0 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -325,10 +325,6 @@
                     mPrivateProviderManager.addPrivateSpaceInstallAppButton(mAdapterItems);
                     position++;
                     addAppsWithSections(mPrivateApps, position);
-                    if (mActivityContext.getAppsView() != null) {
-                        mActivityContext.getAppsView().getActiveRecyclerView()
-                                .scrollToBottomWithMotion();
-                    }
                     break;
             }
         }
diff --git a/src/com/android/launcher3/allapps/PrivateProfileManager.java b/src/com/android/launcher3/allapps/PrivateProfileManager.java
index c99b69f..aee511c 100644
--- a/src/com/android/launcher3/allapps/PrivateProfileManager.java
+++ b/src/com/android/launcher3/allapps/PrivateProfileManager.java
@@ -21,6 +21,7 @@
 import static com.android.launcher3.allapps.BaseAllAppsAdapter.VIEW_TYPE_PRIVATE_SPACE_HEADER;
 import static com.android.launcher3.allapps.SectionDecorationInfo.ROUND_NOTHING;
 import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED;
+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.SettingsCache.PRIVATE_SPACE_HIDE_WHEN_LOCKED_URI;
 
@@ -34,7 +35,6 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.launcher3.BuildConfig;
-import com.android.launcher3.Flags;
 import com.android.launcher3.R;
 import com.android.launcher3.icons.BitmapInfo;
 import com.android.launcher3.icons.LauncherIcons;
@@ -59,11 +59,11 @@
     private static final String SAFETY_CENTER_INTENT = Intent.ACTION_SAFETY_CENTER;
     private static final String PS_SETTINGS_FRAGMENT_KEY = ":settings:fragment_args_key";
     private static final String PS_SETTINGS_FRAGMENT_VALUE = "AndroidPrivateSpace_personal";
-    private static final int ANIMATION_DURATION = 2000;
     private final ActivityAllAppsContainerView<?> mAllApps;
     private final Predicate<UserHandle> mPrivateProfileMatcher;
     private PrivateAppsSectionDecorator mPrivateAppsSectionDecorator;
     private boolean mPrivateSpaceSettingsAvailable;
+    private Runnable mUnlockRunnable;
 
     public PrivateProfileManager(UserManager userManager,
             ActivityAllAppsContainerView<?> allApps,
@@ -115,9 +115,17 @@
         mAllApps.mAH.get(MAIN).mAdapter.notifyItemInserted(adapterItems.size() - 1);
     }
 
-    /** Disables quiet mode for Private Space User Profile. */
-    public void unlockPrivateProfile() {
+    /**
+     * Disables quiet mode for Private Space User Profile.
+     * The runnable passed will be executed in the {@link #reset()} method,
+     * when Launcher receives update about profile availability.
+     * The runnable passed is only executed once, and reset after execution.
+     * In case the method is called again, before the previously set runnable was executed,
+     * the runnable will be updated.
+     */
+    public void unlockPrivateProfile(Runnable runnable) {
         enableQuietMode(false);
+        mUnlockRunnable = runnable;
     }
 
     /** Enables quiet mode for Private Space User Profile. */
@@ -133,11 +141,15 @@
 
     /** Resets the current state of Private Profile, w.r.t. to Launcher. */
     public void reset() {
+        int previousState = getCurrentState();
         boolean isEnabled = !mAllApps.getAppsStore()
                 .hasModelFlag(FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED);
         int updatedState = isEnabled ? STATE_ENABLED : STATE_DISABLED;
         setCurrentState(updatedState);
         resetPrivateSpaceDecorator(updatedState);
+        if (transitioningFromLockedToUnlocked(previousState, updatedState)) {
+            applyUnlockRunnable();
+        }
     }
 
     /** Opens the Private Space Settings Entry Point. */
@@ -182,13 +194,6 @@
             }
             // Add Private Space Decorator to the Recycler view.
             mainAdapterHolder.mRecyclerView.addItemDecoration(mPrivateAppsSectionDecorator);
-            if (Flags.privateSpaceAnimation() && mAllApps.getActiveRecyclerView()
-                    == mainAdapterHolder.mRecyclerView) {
-                RecyclerViewAnimationController recyclerViewAnimationController =
-                        new RecyclerViewAnimationController(mAllApps);
-                recyclerViewAnimationController.animateToState(true /* expand */,
-                        ANIMATION_DURATION, () -> {});
-            }
         } else {
             // Remove Private Space Decorator from the Recycler view.
             if (mPrivateAppsSectionDecorator != null) {
@@ -202,6 +207,18 @@
         setQuietMode(enable);
     }
 
+    void applyUnlockRunnable() {
+        if (mUnlockRunnable != null) {
+            // reset the runnable to prevent re-execution.
+            MAIN_EXECUTOR.post(mUnlockRunnable);
+            mUnlockRunnable = null;
+        }
+    }
+
+    private boolean transitioningFromLockedToUnlocked(int previousState, int updatedState) {
+        return previousState == STATE_DISABLED && updatedState == STATE_ENABLED;
+    }
+
     @Override
     public Predicate<UserHandle> getUserMatcher() {
         return mPrivateProfileMatcher;
diff --git a/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewController.java b/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewController.java
index 568ce32..bc3269d 100644
--- a/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewController.java
+++ b/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewController.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3.allapps;
 
+import static com.android.launcher3.allapps.ActivityAllAppsContainerView.AdapterHolder.MAIN;
 import static com.android.launcher3.allapps.PrivateProfileManager.STATE_DISABLED;
 import static com.android.launcher3.allapps.PrivateProfileManager.STATE_ENABLED;
 import static com.android.launcher3.allapps.PrivateProfileManager.STATE_TRANSITION;
@@ -28,6 +29,7 @@
 import android.widget.ImageView;
 import android.widget.RelativeLayout;
 
+import com.android.launcher3.Flags;
 import com.android.launcher3.R;
 import com.android.launcher3.allapps.UserProfileManager.UserProfileState;
 
@@ -36,9 +38,13 @@
  * {@link UserProfileState}
  */
 public class PrivateSpaceHeaderViewController {
+    private static final int ANIMATION_DURATION = 2000;
+    private final ActivityAllAppsContainerView mAllApps;
     private final PrivateProfileManager mPrivateProfileManager;
 
-    public PrivateSpaceHeaderViewController(PrivateProfileManager privateProfileManager) {
+    public PrivateSpaceHeaderViewController(ActivityAllAppsContainerView allApps,
+            PrivateProfileManager privateProfileManager) {
+        this.mAllApps = allApps;
         this.mPrivateProfileManager = privateProfileManager;
     }
 
@@ -77,7 +83,8 @@
                 quietModeButton.setOnClickListener(
                         view -> {
                             mPrivateProfileManager.logEvents(LAUNCHER_PRIVATE_SPACE_UNLOCK_TAP);
-                            mPrivateProfileManager.unlockPrivateProfile();
+                            mPrivateProfileManager.unlockPrivateProfile((this::
+                                    onPrivateProfileUnlocked));
                         });
             }
             default -> quietModeButton.setVisibility(View.GONE);
@@ -106,6 +113,21 @@
         }
     }
 
+    private void onPrivateProfileUnlocked() {
+        // If we are on main adapter view, we apply the PS Container expansion animation and
+        // then scroll down to load the entire container, making animation visible.
+        ActivityAllAppsContainerView<?>.AdapterHolder mainAdapterHolder =
+                (ActivityAllAppsContainerView<?>.AdapterHolder) mAllApps.mAH.get(MAIN);
+        if (Flags.enablePrivateSpace() && Flags.privateSpaceAnimation()
+                && mAllApps.getActiveRecyclerView() == mainAdapterHolder.mRecyclerView) {
+            RecyclerViewAnimationController recyclerViewAnimationController =
+                    new RecyclerViewAnimationController(mAllApps);
+            recyclerViewAnimationController.animateToState(true /* expand */,
+                    ANIMATION_DURATION, () -> {});
+            mAllApps.getActiveRecyclerView().scrollToBottomWithMotion();
+        }
+    }
+
     PrivateProfileManager getPrivateProfileManager() {
         return mPrivateProfileManager;
     }
diff --git a/src/com/android/launcher3/allapps/UserProfileManager.java b/src/com/android/launcher3/allapps/UserProfileManager.java
index 6bef725..8894f45 100644
--- a/src/com/android/launcher3/allapps/UserProfileManager.java
+++ b/src/com/android/launcher3/allapps/UserProfileManager.java
@@ -22,7 +22,6 @@
 import android.os.UserManager;
 
 import androidx.annotation.IntDef;
-import androidx.annotation.VisibleForTesting;
 
 import com.android.launcher3.Utilities;
 import com.android.launcher3.logging.StatsLogManager;
@@ -89,7 +88,6 @@
     }
 
     /** Returns current state for the profile type. */
-    @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
     public int getCurrentState() {
         return mCurrentState;
     }
diff --git a/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java b/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java
index 1a7d797..c36f455 100644
--- a/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java
+++ b/src/com/android/launcher3/keyboard/FocusIndicatorHelper.java
@@ -22,6 +22,7 @@
 
 import com.android.launcher3.Flags;
 import com.android.launcher3.R;
+import com.android.launcher3.util.Themes;
 
 /**
  * A helper class to draw background of a focused view.
@@ -30,8 +31,9 @@
         implements OnFocusChangeListener {
 
     public FocusIndicatorHelper(View container) {
-        super(container, container.getResources().getColor(Flags.enableFocusOutline()
-                ? R.color.focus_outline_color : R.color.focused_background));
+        super(container, Flags.enableFocusOutline() ? Themes.getAttrColor(container.getContext(),
+                R.attr.focusOutlineColor)
+                : container.getResources().getColor(R.color.focused_background));
     }
 
     @Override
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 0dc0d02..7cc33cf 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -704,6 +704,9 @@
         @UiEvent(doc = "User tapped on install to private space system shortcut.")
         LAUNCHER_PRIVATE_SPACE_INSTALL_SYSTEM_SHORTCUT_TAP(1565),
 
+        @UiEvent(doc = "User attempted to create split screen with a widget")
+        LAUNCHER_SPLIT_WIDGET_ATTEMPT(1604)
+
         // ADD MORE
         ;
 
diff --git a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
index 2f16065..4c5bfd8 100644
--- a/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
+++ b/src_shortcuts_overrides/com/android/launcher3/model/WidgetsModel.java
@@ -70,6 +70,34 @@
     private final Map<PackageItemInfo, List<WidgetItem>> mWidgetsList = new HashMap<>();
 
     /**
+     * Returns a list of {@link WidgetsListBaseEntry} filtered using given widget item filter. All
+     * {@link WidgetItem}s in a single row are sorted (based on label and user), but the overall
+     * list of {@link WidgetsListBaseEntry}s is not sorted.
+     *
+     * @see com.android.launcher3.widget.picker.WidgetsListAdapter#setWidgets(List)
+     */
+    public synchronized ArrayList<WidgetsListBaseEntry> getFilteredWidgetsListForPicker(
+            Context context,
+            Predicate<WidgetItem> widgetItemFilter) {
+        ArrayList<WidgetsListBaseEntry> result = new ArrayList<>();
+        AlphabeticIndexCompat indexer = new AlphabeticIndexCompat(context);
+
+        for (Map.Entry<PackageItemInfo, List<WidgetItem>> entry : mWidgetsList.entrySet()) {
+            PackageItemInfo pkgItem = entry.getKey();
+            List<WidgetItem> widgetItems = entry.getValue()
+                    .stream()
+                    .filter(widgetItemFilter).toList();
+            if (!widgetItems.isEmpty()) {
+                String sectionName = (pkgItem.title == null) ? "" :
+                        indexer.computeSectionName(pkgItem.title);
+                result.add(WidgetsListHeaderEntry.create(pkgItem, sectionName, widgetItems));
+                result.add(new WidgetsListContentEntry(pkgItem, sectionName, widgetItems));
+            }
+        }
+        return result;
+    }
+
+    /**
      * Returns a list of {@link WidgetsListBaseEntry}. All {@link WidgetItem} in a single row
      * are sorted (based on label and user), but the overall list of
      * {@link WidgetsListBaseEntry}s is not sorted.
@@ -77,18 +105,8 @@
      * @see com.android.launcher3.widget.picker.WidgetsListAdapter#setWidgets(List)
      */
     public synchronized ArrayList<WidgetsListBaseEntry> getWidgetsListForPicker(Context context) {
-        ArrayList<WidgetsListBaseEntry> result = new ArrayList<>();
-        AlphabeticIndexCompat indexer = new AlphabeticIndexCompat(context);
-
-        for (Map.Entry<PackageItemInfo, List<WidgetItem>> entry : mWidgetsList.entrySet()) {
-            PackageItemInfo pkgItem = entry.getKey();
-            List<WidgetItem> widgetItems = entry.getValue();
-            String sectionName = (pkgItem.title == null) ? "" :
-                    indexer.computeSectionName(pkgItem.title);
-            result.add(WidgetsListHeaderEntry.create(pkgItem, sectionName, widgetItems));
-            result.add(new WidgetsListContentEntry(pkgItem, sectionName, widgetItems));
-        }
-        return result;
+        // return all items
+        return getFilteredWidgetsListForPicker(context, /*widgetItemFilter=*/ item -> true);
     }
 
     /** Returns a mapping of packages to their widgets without static shortcuts. */
diff --git a/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java b/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java
index 79d00c9..24f9acd 100644
--- a/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java
+++ b/tests/src/com/android/launcher3/allapps/PrivateProfileManagerTest.java
@@ -22,6 +22,7 @@
 import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doNothing;
@@ -81,6 +82,8 @@
     @Mock
     private PackageManager mPackageManager;
 
+    private boolean mRunnableCalled = false;
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
@@ -110,7 +113,7 @@
     public void unlockPrivateProfile_requestsQuietModeAsFalse() throws Exception {
         when(mAllAppsStore.hasModelFlag(FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED)).thenReturn(true);
 
-        mPrivateProfileManager.unlockPrivateProfile();
+        mPrivateProfileManager.unlockPrivateProfile(() -> {});
 
         awaitTasksCompleted();
         Mockito.verify(mUserManager).requestQuietModeEnabled(false, PRIVATE_HANDLE);
@@ -133,6 +136,23 @@
     }
 
     @Test
+    public void transitioningToUnlocked_resetCallsPendingRunnable() throws Exception {
+        PrivateProfileManager privateProfileManager = spy(mPrivateProfileManager);
+        doNothing().when(privateProfileManager).resetPrivateSpaceDecorator(anyInt());
+        when(mAllAppsStore.hasModelFlag(FLAG_PRIVATE_PROFILE_QUIET_MODE_ENABLED))
+                .thenReturn(false);
+        when(privateProfileManager.getCurrentState()).thenReturn(STATE_DISABLED);
+        mRunnableCalled = false;
+
+        privateProfileManager.unlockPrivateProfile(this::testRunnable);
+        privateProfileManager.reset();
+
+        awaitTasksCompleted();
+        Mockito.verify(privateProfileManager).applyUnlockRunnable();
+        assertTrue(mRunnableCalled);
+    }
+
+    @Test
     public void openPrivateSpaceSettings_triggersSecurityAndPrivacyIntent() {
         Intent expectedIntent = new Intent(SAFETY_CENTER_INTENT);
         expectedIntent.putExtra(PS_SETTINGS_FRAGMENT_KEY, PS_SETTINGS_FRAGMENT_VALUE);
@@ -150,4 +170,8 @@
     private static void awaitTasksCompleted() throws Exception {
         UI_HELPER_EXECUTOR.submit(() -> null).get();
     }
+
+    private void testRunnable() {
+        mRunnableCalled = true;
+    }
 }
diff --git a/tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewControllerTest.java b/tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewControllerTest.java
index bc09cdd..92fff49 100644
--- a/tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewControllerTest.java
+++ b/tests/src/com/android/launcher3/allapps/PrivateSpaceHeaderViewControllerTest.java
@@ -64,13 +64,16 @@
     private RelativeLayout mPsHeaderLayout;
     @Mock
     private PrivateProfileManager mPrivateProfileManager;
+    @Mock
+    private ActivityAllAppsContainerView mAllApps;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mContext = new ActivityContextWrapper(getApplicationContext());
         mLayoutInflater = LayoutInflater.from(getApplicationContext());
-        mPsHeaderViewController = new PrivateSpaceHeaderViewController(mPrivateProfileManager);
+        mPsHeaderViewController = new PrivateSpaceHeaderViewController(mAllApps,
+                mPrivateProfileManager);
         mPsHeaderLayout = (RelativeLayout) mLayoutInflater.inflate(R.layout.private_space_header,
                 null);
     }
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIcon.java b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
index 867a1a8..156568b 100644
--- a/tests/tapl/com/android/launcher3/tapl/AppIcon.java
+++ b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
@@ -42,7 +42,8 @@
      * @param appName app icon to look for
      */
     static BySelector getAppIconSelector(String appName) {
-        return By.clazz(TextView.class).text(makeMultilinePattern(appName));
+        // focusable=true to avoid matching folder labels
+        return By.clazz(TextView.class).text(makeMultilinePattern(appName)).focusable(true);
     }
 
     /**
diff --git a/tests/tapl/com/android/launcher3/tapl/Launchable.java b/tests/tapl/com/android/launcher3/tapl/Launchable.java
index 28e2590..ed47334 100644
--- a/tests/tapl/com/android/launcher3/tapl/Launchable.java
+++ b/tests/tapl/com/android/launcher3/tapl/Launchable.java
@@ -56,8 +56,8 @@
      */
     public LaunchedAppState launch(String expectedPackageName) {
         try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
-            try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
-                    "want to launch an app from " + launchableType())) {
+            try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(String.format(
+                    "want to launch an app (%s) from %s", expectedPackageName, launchableType()))) {
                 LauncherInstrumentation.log("Launchable.launch before click "
                         + mObject.getVisibleCenter() + " in "
                         + mLauncher.getVisibleBounds(mObject));