Merge "Making sure the reorder animation goes back to the initial position." into main
diff --git a/OWNERS b/OWNERS
index b8aae78..4409b33 100644
--- a/OWNERS
+++ b/OWNERS
@@ -25,5 +25,10 @@
 andonian@google.com
 sihua@google.com
 
+# Multitasking eng team
+tracyzhou@google.com
+peanutbutter@google.com
+jeremysim@google.com
+
 per-file FeatureFlags.java, globs = set noparent
 per-file FeatureFlags.java = sunnygoyal@google.com, winsonc@google.com, adamcohen@google.com, hyunyoungs@google.com, captaincole@google.com
diff --git a/quickstep/src/com/android/launcher3/proxy/ProxyActivityStarter.java b/quickstep/src/com/android/launcher3/proxy/ProxyActivityStarter.java
index 6d90b035..212a5ff 100644
--- a/quickstep/src/com/android/launcher3/proxy/ProxyActivityStarter.java
+++ b/quickstep/src/com/android/launcher3/proxy/ProxyActivityStarter.java
@@ -53,13 +53,10 @@
 
         try {
             if (mParams.intent != null) {
-                startActivityForResult(mParams.intent, mParams.requestCode, mParams.options);
+                startActivity();
                 return;
             } else if (mParams.intentSender != null) {
-                startIntentSenderForResult(mParams.intentSender, mParams.requestCode,
-                        mParams.fillInIntent, mParams.flagsMask, mParams.flagsValues,
-                        mParams.extraFlags,
-                        mParams.options);
+                startIntentSender();
                 return;
             }
         } catch (NullPointerException | ActivityNotFoundException | SecurityException
@@ -83,4 +80,26 @@
                 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
                         | Intent.FLAG_ACTIVITY_CLEAR_TASK);
     }
+
+    private void startActivity() throws SendIntentException {
+        if (mParams.requireActivityResult) {
+            startActivityForResult(mParams.intent, mParams.requestCode, mParams.options);
+        } else {
+            startActivity(mParams.intent, mParams.options);
+            finishAndRemoveTask();
+        }
+    }
+
+    private void startIntentSender() throws SendIntentException {
+        if (mParams.requireActivityResult) {
+            startIntentSenderForResult(mParams.intentSender, mParams.requestCode,
+                    mParams.fillInIntent, mParams.flagsMask, mParams.flagsValues,
+                    mParams.extraFlags,
+                    mParams.options);
+        } else {
+            startIntentSender(mParams.intentSender, mParams.fillInIntent, mParams.flagsMask,
+                    mParams.flagsValues, mParams.extraFlags, mParams.options);
+            finishAndRemoveTask();
+        }
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
index a850680..8e4a78f 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java
@@ -207,7 +207,7 @@
                     applyState();
                     boolean disallowLongClick =
                             FeatureFlags.enableSplitContextually()
-                                    ? mLauncher.isSplitSelectionEnabled()
+                                    ? mLauncher.isSplitSelectionActive()
                                     : finalState == LauncherState.OVERVIEW_SPLIT_SELECT;
                     com.android.launcher3.taskbar.Utilities.setOverviewDragState(
                             mControllers, finalState.disallowTaskbarGlobalDrag(),
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt
index 1e3f4f1..aa2b29d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt
@@ -50,6 +50,22 @@
 
     var width: Float = 0f
 
+    /**
+     * Set whether the drawable is anchored to the left or right edge of the container.
+     *
+     * When `anchorLeft` is set to `true`, drawable left edge aligns up with the container left
+     * edge. Drawable can be drawn outside container bounds on the right edge. When it is set to
+     * `false` (the default), drawable right edge aligns up with the container right edge. Drawable
+     * can be drawn outside container bounds on the left edge.
+     */
+    var anchorLeft: Boolean = false
+        set(value) {
+            if (field != value) {
+                field = value
+                invalidateSelf()
+            }
+        }
+
     init {
         paint.color = context.getColor(R.color.taskbar_background)
         paint.flags = Paint.ANTI_ALIAS_FLAG
@@ -106,15 +122,9 @@
 
         // Draw background.
         val radius = backgroundHeight / 2f
-        canvas.drawRoundRect(
-            canvas.width.toFloat() - width,
-            0f,
-            canvas.width.toFloat(),
-            canvas.height.toFloat(),
-            radius,
-            radius,
-            paint
-        )
+        val left = if (anchorLeft) 0f else canvas.width.toFloat() - width
+        val right = if (anchorLeft) width else canvas.width.toFloat()
+        canvas.drawRoundRect(left, 0f, right, canvas.height.toFloat(), radius, radius, paint)
 
         if (showingArrow) {
             // Draw arrow.
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
index ec9f4e5..1e7b3e2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
@@ -197,6 +197,16 @@
         updateChildrenRenderNodeProperties();
     }
 
+    @Override
+    public void onRtlPropertiesChanged(int layoutDirection) {
+        // TODO(b/273310265): set this based on bubble bar position and not LTR or RTL
+        mBubbleBarBackground.setAnchorLeft(layoutDirection == LAYOUT_DIRECTION_RTL);
+    }
+
+    private boolean isOnLeft() {
+        return getLayoutDirection() == LAYOUT_DIRECTION_RTL;
+    }
+
     /**
      * Updates the bounds with translation that may have been applied and returns the result.
      */
@@ -275,18 +285,31 @@
         int bubbleCount = getChildCount();
         final float ty = (mBubbleBarBounds.height() - mIconSize) / 2f;
         final boolean animate = getVisibility() == VISIBLE;
+        final boolean onLeft = isOnLeft();
         for (int i = 0; i < bubbleCount; i++) {
             BubbleView bv = (BubbleView) getChildAt(i);
             bv.setTranslationY(ty);
 
             // the position of the bubble when the bar is fully expanded
-            final float expandedX = i * (mIconSize + mIconSpacing);
+            final float expandedX;
             // the position of the bubble when the bar is fully collapsed
-            final float collapsedX = i == 0 ? 0 : mIconOverlapAmount;
+            final float collapsedX;
+            if (onLeft) {
+                // If bar is on the left, bubbles are ordered right to left
+                expandedX = (bubbleCount - i - 1) * (mIconSize + mIconSpacing);
+                // Shift the first bubble only if there are more bubbles in addition to overflow
+                collapsedX = i == 0 && bubbleCount > 2 ? mIconOverlapAmount : 0;
+            } else {
+                // Bubbles ordered left to right, don't move the first bubble
+                expandedX = i * (mIconSize + mIconSpacing);
+                collapsedX = i == 0 ? 0 : mIconOverlapAmount;
+            }
 
             if (mIsBarExpanded) {
+                // If bar is on the right, account for bubble bar expanding and shifting left
+                final float expandedBarShift = onLeft ? 0 : currentWidth - expandedWidth;
                 // where the bubble will end up when the animation ends
-                final float targetX = currentWidth - expandedWidth + expandedX;
+                final float targetX = expandedX + expandedBarShift;
                 bv.setTranslationX(widthState * (targetX - collapsedX) + collapsedX);
                 // if we're fully expanded, set the z level to 0 or to bubble elevation if dragged
                 if (widthState == 1f) {
@@ -296,7 +319,9 @@
                 bv.setBehindStack(false, animate);
                 bv.setAlpha(1);
             } else {
-                final float targetX = currentWidth - collapsedWidth + collapsedX;
+                // If bar is on the right, account for bubble bar expanding and shifting left
+                final float collapsedBarShift = onLeft ? 0 : currentWidth - collapsedWidth;
+                final float targetX = collapsedX + collapsedBarShift;
                 bv.setTranslationX(widthState * (expandedX - targetX) + targetX);
                 bv.setZ((MAX_BUBBLES * mBubbleElevation) - i);
                 // If we're not the first bubble we're behind the stack
@@ -318,18 +343,22 @@
         final float expandedArrowPosition = arrowPositionForSelectedWhenExpanded();
         final float interpolatedWidth =
                 widthState * (expandedWidth - collapsedWidth) + collapsedWidth;
-        if (mIsBarExpanded) {
-            // when the bar is expanding, the selected bubble is always the first, so the arrow
-            // always shifts with the interpolated width.
-            final float arrowPosition = currentWidth - interpolatedWidth + collapsedArrowPosition;
-            mBubbleBarBackground.setArrowPosition(arrowPosition);
+        final float arrowPosition;
+        if (onLeft) {
+            float interpolatedShift = (expandedArrowPosition - collapsedArrowPosition) * widthState;
+            arrowPosition = collapsedArrowPosition + interpolatedShift;
         } else {
-            final float targetPosition = currentWidth - collapsedWidth + collapsedArrowPosition;
-            final float arrowPosition =
-                    targetPosition + widthState * (expandedArrowPosition - targetPosition);
-            mBubbleBarBackground.setArrowPosition(arrowPosition);
+            if (mIsBarExpanded) {
+                // when the bar is expanding, the selected bubble is always the first, so the arrow
+                // always shifts with the interpolated width.
+                arrowPosition = currentWidth - interpolatedWidth + collapsedArrowPosition;
+            } else {
+                final float targetPosition = currentWidth - collapsedWidth + collapsedArrowPosition;
+                arrowPosition =
+                        targetPosition + widthState * (expandedArrowPosition - targetPosition);
+            }
         }
-
+        mBubbleBarBackground.setArrowPosition(arrowPosition);
         mBubbleBarBackground.setArrowAlpha((int) (255 * widthState));
         mBubbleBarBackground.setWidth(interpolatedWidth);
     }
@@ -394,12 +423,14 @@
             Log.w(TAG, "trying to update selection arrow without a selected view!");
             return;
         }
-        final int index = indexOfChild(mSelectedBubbleView);
         // Find the center of the bubble when it's expanded, set the arrow position to it.
-        final float tx = getPaddingStart() + index * (mIconSize + mIconSpacing) + mIconSize / 2f;
-
+        final float tx = arrowPositionForSelectedWhenExpanded();
+        final float currentArrowPosition = mBubbleBarBackground.getArrowPositionX();
+        if (shouldAnimate && currentArrowPosition > expandedWidth()) {
+            Log.d(TAG, "arrow out of bounds of expanded view, skip animation");
+            shouldAnimate = false;
+        }
         if (shouldAnimate) {
-            final float currentArrowPosition = mBubbleBarBackground.getArrowPositionX();
             ValueAnimator animator = ValueAnimator.ofFloat(currentArrowPosition, tx);
             animator.setDuration(ARROW_POSITION_ANIMATION_DURATION_MS);
             animator.addUpdateListener(animation -> {
@@ -416,12 +447,27 @@
 
     private float arrowPositionForSelectedWhenExpanded() {
         final int index = indexOfChild(mSelectedBubbleView);
-        return getPaddingStart() + index * (mIconSize + mIconSpacing) + mIconSize / 2f;
+        final int bubblePosition;
+        if (isOnLeft()) {
+            // Bubble positions are reversed. First bubble is on the right.
+            bubblePosition = getChildCount() - index - 1;
+        } else {
+            bubblePosition = index;
+        }
+        return getPaddingStart() + bubblePosition * (mIconSize + mIconSpacing) + mIconSize / 2f;
     }
 
     private float arrowPositionForSelectedWhenCollapsed() {
         final int index = indexOfChild(mSelectedBubbleView);
-        return getPaddingStart() + index * (mIconOverlapAmount) + mIconSize / 2f;
+        final int bubblePosition;
+        if (isOnLeft()) {
+            // Bubble positions are reversed. First bubble may be shifted, if there are more
+            // bubbles than the current bubble and overflow.
+            bubblePosition = index == 0 && getChildCount() > 2 ? 1 : 0;
+        } else {
+            bubblePosition = index;
+        }
+        return getPaddingStart() + bubblePosition * (mIconOverlapAmount) + mIconSize / 2f;
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
index 065dd58..6bb7b04 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
@@ -21,6 +21,7 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.util.Log;
+import android.view.Gravity;
 import android.view.MotionEvent;
 import android.view.View;
 import android.widget.FrameLayout;
@@ -289,7 +290,8 @@
      */
     public void addBubble(BubbleBarItem b) {
         if (b != null) {
-            mBarView.addView(b.getView(), 0, new FrameLayout.LayoutParams(mIconSize, mIconSize));
+            mBarView.addView(b.getView(), 0,
+                    new FrameLayout.LayoutParams(mIconSize, mIconSize, Gravity.LEFT));
             b.getView().setOnClickListener(mBubbleClickListener);
             mBubbleDragController.setupBubbleView(b.getView());
         } else {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
index 3ebc8ed..5c4eb9d 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
@@ -138,6 +138,11 @@
                         || Flags.enablePrivateSpaceInstallShortcut())) {
             StartActivityParams params = new StartActivityParams((PendingIntent) null, 0);
             params.intentSender = launcherApps.getAppMarketActivityIntent(packageName, user);
+            ActivityOptions options = ActivityOptions.makeBasic()
+                    .setPendingIntentBackgroundActivityStartMode(ActivityOptions
+                            .MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
+            params.options = options.toBundle();
+            params.requireActivityResult = false;
             return ProxyActivityStarter.getLaunchIntent(context, params);
         } else {
             return new Intent(Intent.ACTION_VIEW)
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
index d834935..22f24f1 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
@@ -59,7 +59,7 @@
             return RemoteViews.startPendingIntent(hostView, pendingIntent,
                     remoteResponse.getLaunchOptions(view));
         }
-        if (mLauncher.isSplitSelectionEnabled()) {
+        if (mLauncher.isSplitSelectionActive()) {
             // Log metric
             StatsLogManager.StatsLogger logger = mLauncher.getStatsLogManager().logger();
             logger.log(LAUNCHER_SPLIT_WIDGET_ATTEMPT);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 2e8e613..9b73bb1 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -725,7 +725,7 @@
     }
 
     @Override
-    public boolean isSplitSelectionEnabled() {
+    public boolean isSplitSelectionActive() {
         return mSplitSelectStateController.isSplitSelectActive();
     }
 
diff --git a/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java b/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
index b663970..1abedb7 100644
--- a/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitWithKeyboardShortcutController.java
@@ -43,6 +43,7 @@
 import com.android.quickstep.RecentsAnimationTargets;
 import com.android.quickstep.RecentsModel;
 import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.TopTaskTracker;
 import com.android.quickstep.views.FloatingTaskView;
 import com.android.quickstep.views.RecentsView;
 import com.android.systemui.shared.recents.model.Task;
@@ -75,7 +76,9 @@
 
     @BinderThread
     public void enterStageSplit(boolean leftOrTop) {
-        if (!enableSplitContextually()) {
+        if (!enableSplitContextually() ||
+                // Do not enter stage split from keyboard shortcuts if the user is already in split
+                TopTaskTracker.INSTANCE.get(mLauncher).getRunningSplitTaskIds().length == 2) {
             return;
         }
         RecentsAnimationCallbacks callbacks = new RecentsAnimationCallbacks(
diff --git a/res/drawable/encrypted_24px.xml b/res/drawable/encrypted_24px.xml
deleted file mode 100644
index cf4d2df..0000000
--- a/res/drawable/encrypted_24px.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="960"
-    android:viewportHeight="960"
-    android:tint="?attr/colorControlNormal">
-    <path
-        android:fillColor="@android:color/white"
-        android:pathData="M420,600L540,600L517,471Q537,461 548.5,442Q560,423 560,400Q560,367 536.5,343.5Q513,320 480,320Q447,320 423.5,343.5Q400,367 400,400Q400,423 411.5,442Q423,461 443,471L420,600ZM480,880Q341,845 250.5,720.5Q160,596 160,444L160,200L480,80L800,200L800,444Q800,596 709.5,720.5Q619,845 480,880ZM480,796Q584,763 652,664Q720,565 720,444L720,255L480,165L240,255L240,444Q240,565 308,664Q376,763 480,796ZM480,480Q480,480 480,480Q480,480 480,480L480,480L480,480L480,480L480,480Q480,480 480,480Q480,480 480,480Z"/>
-</vector>
diff --git a/res/drawable/ic_encrypted_with_background.xml b/res/drawable/ic_encrypted_with_background.xml
new file mode 100644
index 0000000..155e2be
--- /dev/null
+++ b/res/drawable/ic_encrypted_with_background.xml
@@ -0,0 +1,26 @@
+<!-- Copyright (C) 2024 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
+    android:viewportWidth="48"
+    android:viewportHeight="48"
+    android:width="48dp"
+    android:height="48dp">
+    <path
+        android:pathData="M48 24A24 24 0 0 1 0 24A24 24 0 0 1 48 24Z"
+        android:fillColor="?attr/materialColorSurfaceBright" />
+    <path
+        android:pathData="M24.0002 10.667L13.3335 14.667V22.787C13.3335 29.5203 17.8802 35.8003 24.0002 37.3337C30.1202 35.8003 34.6668 29.5203 34.6668 22.787V14.667L24.0002 10.667ZM32.0002 22.787C32.0002 28.1203 28.6002 33.0537 24.0002 34.5603C19.4002 33.0537 16.0002 28.1337 16.0002 22.787V16.5203L24.0002 13.5203L32.0002 16.5203V22.787ZM25.2402 23.6937L26.0002 28.0003H22.0002L22.7602 23.6937C21.9068 23.2537 21.3335 22.3603 21.3335 21.3337C21.3335 19.867 22.5335 18.667 24.0002 18.667C25.4668 18.667 26.6668 19.867 26.6668 21.3337C26.6668 22.3603 26.0935 23.2537 25.2402 23.6937Z"
+        android:fillColor="?attr/materialColorOnSurface" />
+</vector>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 5efe940..b9fb024 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -447,8 +447,10 @@
     <string name="remote_action_failed">Failed: <xliff:g id="what" example="Pause">%1$s</xliff:g></string>
 
     <!-- Strings for Private Space -->
-    <!-- Private space label -->
+    <!-- Private space tile label -->
     <string name="private_space_label">Private space</string>
+    <!-- Private space tile secondary label -->
+    <string name="private_space_secondary_label">Keep private apps locked and hidden</string>
     <!-- Title for Private Space Container shown at the bottom of all apps drawer -->
     <string name="ps_container_title">Private</string>
     <!-- Description for Private Space Settings button -->
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 39b8de1..5b5d4cb 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -2760,7 +2760,7 @@
         }
         LauncherRootView rv = getRootView();
         if (rv != null) {
-            boolean isSplitSelectionEnabled = isSplitSelectionEnabled();
+            boolean isSplitSelectionEnabled = isSplitSelectionActive();
             boolean disableBack = getStateManager().getState() == NORMAL
                     && AbstractFloatingView.getTopOpenView(this) == null
                     && !isSplitSelectionEnabled;
@@ -2769,7 +2769,7 @@
     }
 
     /** To be overridden by subclasses */
-    public boolean isSplitSelectionEnabled() {
+    public boolean isSplitSelectionActive() {
         // Overridden
         return false;
     }
diff --git a/src/com/android/launcher3/allapps/PrivateProfileManager.java b/src/com/android/launcher3/allapps/PrivateProfileManager.java
index aee511c..1c46dac 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.model.data.ItemInfoWithIcon.FLAG_PRIVATE_SPACE_INSTALL_APP;
 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;
@@ -105,6 +106,7 @@
         itemInfo.bitmap = bitmapInfo;
         itemInfo.contentDescription = context.getResources().getString(
                 com.android.launcher3.R.string.ps_add_button_content_description);
+        itemInfo.runtimeStatusFlags |= FLAG_PRIVATE_SPACE_INSTALL_APP;
 
         BaseAllAppsAdapter.AdapterItem item = new BaseAllAppsAdapter.AdapterItem(VIEW_TYPE_ICON);
         item.itemInfo = itemInfo;
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 45ff33b..f9b7495 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -713,6 +713,9 @@
         @UiEvent(doc = "User tapped on install to private space system shortcut.")
         LAUNCHER_PRIVATE_SPACE_INSTALL_SYSTEM_SHORTCUT_TAP(1565),
 
+        @UiEvent(doc = "User tapped private space install app button.")
+        LAUNCHER_PRIVATE_SPACE_INSTALL_APP_BUTTON_TAP(1605),
+
         @UiEvent(doc = "User attempted to create split screen with a widget")
         LAUNCHER_SPLIT_WIDGET_ATTEMPT(1604)
 
diff --git a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
index 58b12b1..c8ab09c 100644
--- a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
+++ b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
@@ -122,6 +122,11 @@
     public static final int FLAG_ARCHIVED = 1 << 14;
 
     /**
+     * Flag indicating it's the Private Space Install App icon.
+     */
+    public static final int FLAG_PRIVATE_SPACE_INSTALL_APP = 1 << 15;
+
+    /**
      * Status associated with the system state of the underlying item. This is calculated every
      * time a new info is created and not persisted on the disk.
      */
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index ff8b381..369008d 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -19,6 +19,7 @@
 import static com.android.launcher3.LauncherConstants.ActivityCodes.REQUEST_BIND_PENDING_APPWIDGET;
 import static com.android.launcher3.LauncherConstants.ActivityCodes.REQUEST_RECONFIGURE_APPWIDGET;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_OPEN;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_INSTALL_APP_BUTTON_TAP;
 import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_BY_PUBLISHER;
 import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER;
 import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_QUIET_USER;
@@ -352,6 +353,12 @@
                     appInfo.getTargetComponent().getPackageName(), Process.myUserHandle());
         } else {
             intent = item.getIntent();
+            if (item instanceof AppInfo
+                    && (((ItemInfoWithIcon) item).runtimeStatusFlags
+                    & ItemInfoWithIcon.FLAG_PRIVATE_SPACE_INSTALL_APP) != 0) {
+                launcher.getStatsLogManager().logger().log(
+                        LAUNCHER_PRIVATE_SPACE_INSTALL_APP_BUTTON_TAP);
+            }
         }
         if (intent == null) {
             throw new IllegalArgumentException("Input must have a valid intent");
diff --git a/src/com/android/launcher3/touch/ItemLongClickListener.java b/src/com/android/launcher3/touch/ItemLongClickListener.java
index 9e7d4dc..116f13a 100644
--- a/src/com/android/launcher3/touch/ItemLongClickListener.java
+++ b/src/com/android/launcher3/touch/ItemLongClickListener.java
@@ -184,7 +184,7 @@
         // Return early if an item is already being dragged (e.g. when long-pressing two shortcuts)
         if (launcher.getDragController().isDragging()) return false;
         // Return early if user is in the middle of selecting split-screen apps
-        if (FeatureFlags.enableSplitContextually() && launcher.isSplitSelectionEnabled()) {
+        if (FeatureFlags.enableSplitContextually() && launcher.isSplitSelectionActive()) {
             return false;
         }
 
diff --git a/src/com/android/launcher3/touch/WorkspaceTouchListener.java b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
index 5b6c9e0..8c43f75 100644
--- a/src/com/android/launcher3/touch/WorkspaceTouchListener.java
+++ b/src/com/android/launcher3/touch/WorkspaceTouchListener.java
@@ -206,7 +206,7 @@
                         HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
                 mLauncher.getStatsLogManager().logger().log(LAUNCHER_WORKSPACE_LONGPRESS);
                 mLauncher.showDefaultOptions(mTouchDownPoint.x, mTouchDownPoint.y);
-                if (FeatureFlags.enableSplitContextually() && mLauncher.isSplitSelectionEnabled()) {
+                if (FeatureFlags.enableSplitContextually() && mLauncher.isSplitSelectionActive()) {
                     mLauncher.dismissSplitSelection();
                 }
             } else {
diff --git a/src/com/android/launcher3/util/StartActivityParams.java b/src/com/android/launcher3/util/StartActivityParams.java
index b48562f..d66b0a0 100644
--- a/src/com/android/launcher3/util/StartActivityParams.java
+++ b/src/com/android/launcher3/util/StartActivityParams.java
@@ -52,6 +52,7 @@
     public int flagsValues;
     public int extraFlags;
     public Bundle options;
+    public boolean requireActivityResult = true;
 
     public StartActivityParams(Activity activity, int requestCode) {
         this(activity.createPendingResult(requestCode, new Intent(),
@@ -74,6 +75,7 @@
         flagsValues = parcel.readInt();
         extraFlags = parcel.readInt();
         options = parcel.readBundle();
+        requireActivityResult = parcel.readInt() != 0;
     }
 
 
@@ -94,6 +96,7 @@
         parcel.writeInt(flagsValues);
         parcel.writeInt(extraFlags);
         parcel.writeBundle(options);
+        parcel.writeInt(requireActivityResult ? 1 : 0);
     }
 
     /** Perform the operation on the pendingIntent. */
diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java
index bef84f7..0cddc97 100644
--- a/src/com/android/launcher3/views/ActivityContext.java
+++ b/src/com/android/launcher3/views/ActivityContext.java
@@ -150,7 +150,7 @@
      * @return {@code true} if user has selected the first split app and is in the process of
      *         selecting the second
      */
-    default boolean isSplitSelectionEnabled() {
+    default boolean isSplitSelectionActive() {
         // Overridden
         return false;
     }
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index 2596b75..daace8e 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -24,7 +24,7 @@
     <application android:debuggable="true">
         <uses-library android:name="android.test.runner" />
 
-        <receiver android:name="com.android.launcher3.compat.PromiseIconUiTest$UnarchiveBroadcastReceiver"
+        <receiver android:name="com.android.launcher3.compat.TaplPromiseIconUiTest$UnarchiveBroadcastReceiver"
                   android:enabled="true"
                   android:exported="true">
             <intent-filter>
diff --git a/tests/multivalentTests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/multivalentTests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index f68e12c..fef93b7 100644
--- a/tests/multivalentTests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/multivalentTests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -1771,6 +1771,7 @@
             }
             endTime = movePointer(
                     start, end, steps, false, downTime, downTime, slowDown, gestureScope);
+        } finally {
             if (mTrackpadGestureType != TrackpadGestureType.NONE) {
                 for (int i = mPointerCount; i >= 2; i--) {
                     sendPointer(downTime, downTime,
@@ -1778,7 +1779,6 @@
                             start, gestureScope);
                 }
             }
-        } finally {
             sendPointer(downTime, endTime, MotionEvent.ACTION_UP, end, gestureScope);
         }
     }
@@ -2055,11 +2055,14 @@
         final long downTime = SystemClock.uptimeMillis();
         sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, targetCenter,
                 GestureScope.DONT_EXPECT_PILFER);
-        expectEvent(TestProtocol.SEQUENCE_MAIN, longClickEvent);
-        final UiObject2 result = waitForLauncherObject(resName);
-        sendPointer(downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, targetCenter,
-                GestureScope.DONT_EXPECT_PILFER);
-        return result;
+        try {
+            expectEvent(TestProtocol.SEQUENCE_MAIN, longClickEvent);
+            final UiObject2 result = waitForLauncherObject(resName);
+            return result;
+        } finally {
+            sendPointer(downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, targetCenter,
+                    GestureScope.DONT_EXPECT_PILFER);
+        }
     }
 
     @NonNull
@@ -2070,12 +2073,15 @@
         sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, targetCenter,
                 GestureScope.DONT_EXPECT_PILFER, InputDevice.SOURCE_MOUSE,
                 /* isRightClick= */ true);
-        expectEvent(TestProtocol.SEQUENCE_MAIN, rightClickEvent);
-        final UiObject2 result = waitForLauncherObject(resName);
-        sendPointer(downTime, SystemClock.uptimeMillis(), ACTION_UP, targetCenter,
-                GestureScope.DONT_EXPECT_PILFER, InputDevice.SOURCE_MOUSE,
-                /* isRightClick= */ true);
-        return result;
+        try {
+            expectEvent(TestProtocol.SEQUENCE_MAIN, rightClickEvent);
+            final UiObject2 result = waitForLauncherObject(resName);
+            return result;
+        } finally {
+            sendPointer(downTime, SystemClock.uptimeMillis(), ACTION_UP, targetCenter,
+                    GestureScope.DONT_EXPECT_PILFER, InputDevice.SOURCE_MOUSE,
+                    /* isRightClick= */ true);
+        }
     }
 
     private static int getSystemIntegerRes(Context context, String resName) {
diff --git a/tests/multivalentTests/tapl/com/android/launcher3/tapl/WidgetResizeFrame.java b/tests/multivalentTests/tapl/com/android/launcher3/tapl/WidgetResizeFrame.java
index ec1cbd8..d0573e0 100644
--- a/tests/multivalentTests/tapl/com/android/launcher3/tapl/WidgetResizeFrame.java
+++ b/tests/multivalentTests/tapl/com/android/launcher3/tapl/WidgetResizeFrame.java
@@ -16,6 +16,7 @@
 package com.android.launcher3.tapl;
 
 import static com.android.launcher3.tapl.Launchable.DEFAULT_DRAG_STEPS;
+
 import static org.junit.Assert.assertTrue;
 
 import android.graphics.Point;
@@ -61,11 +62,14 @@
             final long downTime = SystemClock.uptimeMillis();
             mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, targetStart,
                     LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
-            mLauncher.movePointer(targetStart, targetDest, DEFAULT_DRAG_STEPS,
-                    true, downTime, downTime, true,
-                    LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
-            mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_UP, targetDest,
-                    LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
+            try {
+                mLauncher.movePointer(targetStart, targetDest, DEFAULT_DRAG_STEPS,
+                        true, downTime, downTime, true,
+                        LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
+            } finally {
+                mLauncher.sendPointer(downTime, downTime, MotionEvent.ACTION_UP, targetDest,
+                        LauncherInstrumentation.GestureScope.DONT_EXPECT_PILFER);
+            }
 
             try (LauncherInstrumentation.Closable c2 = mLauncher.addContextLayer(
                          "want to return resized widget resize frame")) {