diff --git a/aconfig/launcher.aconfig b/aconfig/launcher.aconfig
index a62f809805..f5afd88f15 100644
--- a/aconfig/launcher.aconfig
+++ b/aconfig/launcher.aconfig
@@ -97,3 +97,10 @@ flag {
description: "Enables support for archived apps in Launcher3, such as empty progress bar etc."
bug: "210590852"
}
+
+flag {
+ name: "enable_private_space_install_shortcut"
+ namespace: "launcher"
+ description: "Enables long-press shortcut to install a copy of an app to Private space"
+ bug: "316118005"
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
index 784c560b04..7113e495e0 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/ApiWrapper.java
@@ -129,8 +129,10 @@ public class ApiWrapper {
public static Intent getAppMarketActivityIntent(Context context, String packageName,
UserHandle user) {
LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
- if (android.os.Flags.allowPrivateProfile() && Flags.enablePrivateSpace()
- && Flags.privateSpaceAppInstallerButton()) {
+ if (android.os.Flags.allowPrivateProfile()
+ && Flags.enablePrivateSpace()
+ && (Flags.privateSpaceAppInstallerButton()
+ || Flags.enablePrivateSpaceInstallShortcut())) {
StartActivityParams params = new StartActivityParams((PendingIntent) null, 0);
params.intentSender = launcherApps.getAppMarketActivityIntent(packageName, user);
return ProxyActivityStarter.getLaunchIntent(context, params);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 71f4faf56c..0d4a7f0e50 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -40,6 +40,7 @@ import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID;
import static com.android.launcher3.popup.QuickstepSystemShortcut.getSplitSelectShortcutByPosition;
import static com.android.launcher3.popup.SystemShortcut.APP_INFO;
import static com.android.launcher3.popup.SystemShortcut.INSTALL;
+import static com.android.launcher3.popup.SystemShortcut.PRIVATE_PROFILE_INSTALL;
import static com.android.launcher3.popup.SystemShortcut.WIDGETS;
import static com.android.launcher3.taskbar.LauncherTaskbarUIController.ALL_APPS_PAGE_PROGRESS_INDEX;
import static com.android.launcher3.taskbar.LauncherTaskbarUIController.MINUS_ONE_PAGE_PROGRESS_INDEX;
@@ -95,6 +96,7 @@ import androidx.annotation.RequiresApi;
import com.android.app.viewcapture.SettingsAwareViewCapture;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Flags;
import com.android.launcher3.HomeTransitionController;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherSettings.Favorites;
@@ -415,6 +417,10 @@ public class QuickstepLauncher extends Launcher {
shortcuts.addAll(getSplitShortcuts());
shortcuts.add(WIDGETS);
shortcuts.add(INSTALL);
+
+ if (Flags.enablePrivateSpaceInstallShortcut()) {
+ shortcuts.add(PRIVATE_PROFILE_INSTALL);
+ }
return shortcuts.stream();
}
diff --git a/res/drawable/ic_install_to_private.xml b/res/drawable/ic_install_to_private.xml
new file mode 100644
index 0000000000..7f00f8d6b2
--- /dev/null
+++ b/res/drawable/ic_install_to_private.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 956d24daa2..34677f6ae7 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -171,6 +171,8 @@
Uninstall
App info
+
+ Install in private
Install
diff --git a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
index 4ad4c71a55..ad764e3867 100644
--- a/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/ActivityAllAppsContainerView.java
@@ -1315,6 +1315,10 @@ public class ActivityAllAppsContainerView
: mViewPager == null ? AdapterHolder.MAIN : mViewPager.getNextPage();
}
+ public PrivateProfileManager getPrivateProfileManager() {
+ return mPrivateProfileManager;
+ }
+
/**
* Adds an update listener to animator that adds springs to the animation.
*/
diff --git a/src/com/android/launcher3/allapps/UserProfileManager.java b/src/com/android/launcher3/allapps/UserProfileManager.java
index 0261010d8b..6bef7256ff 100644
--- a/src/com/android/launcher3/allapps/UserProfileManager.java
+++ b/src/com/android/launcher3/allapps/UserProfileManager.java
@@ -94,6 +94,19 @@ public abstract class UserProfileManager {
return mCurrentState;
}
+ /** Returns if user profile is enabled. */
+ public boolean isEnabled() {
+ return mCurrentState == STATE_ENABLED;
+ }
+
+ /** Returns the UserHandle corresponding to the profile type, null in case no matches found. */
+ public UserHandle getProfileUser() {
+ return mUserCache.getUserProfiles().stream()
+ .filter(getUserMatcher())
+ .findAny()
+ .orElse(null);
+ }
+
/** Logs Event to StatsLogManager. */
protected void logEvents(StatsLogManager.EventEnum event) {
mStatsLogManager.logger().log(event);
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 6651fa030e..0dc0d0239a 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -701,6 +701,9 @@ public class StatsLogManager implements ResourceBasedOverride {
@UiEvent(doc = "User tapped private space settings button")
LAUNCHER_PRIVATE_SPACE_SETTINGS_TAP(1550),
+ @UiEvent(doc = "User tapped on install to private space system shortcut.")
+ LAUNCHER_PRIVATE_SPACE_INSTALL_SYSTEM_SHORTCUT_TAP(1565),
+
// ADD MORE
;
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index f39f8060ba..8463361eef 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -1,5 +1,6 @@
package com.android.launcher3.popup;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_PRIVATE_SPACE_INSTALL_SYSTEM_SHORTCUT_TAP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_APP_INFO_TAP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_WIDGETS_TAP;
@@ -8,6 +9,7 @@ import android.content.Context;
import android.content.Intent;
import android.graphics.Rect;
import android.os.Process;
+import android.os.UserHandle;
import android.view.View;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.ImageView;
@@ -20,10 +22,12 @@ import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.allapps.PrivateProfileManager;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.uioverrides.ApiWrapper;
+import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.InstantAppResolver;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.PackageUserKey;
@@ -212,6 +216,69 @@ public abstract class SystemShortcut extend
}
}
+ public static final Factory PRIVATE_PROFILE_INSTALL =
+ (launcher, itemInfo, originalView) -> {
+ if (itemInfo.getTargetComponent() == null
+ || !(itemInfo instanceof com.android.launcher3.model.data.AppInfo)
+ || !itemInfo.getContainerInfo().hasAllAppsContainer()
+ || !Process.myUserHandle().equals(itemInfo.user)) {
+ return null;
+ }
+
+ PrivateProfileManager privateProfileManager =
+ launcher.getAppsView().getPrivateProfileManager();
+ if (privateProfileManager == null || !privateProfileManager.isEnabled()) {
+ return null;
+ }
+
+ UserHandle privateProfileUser = privateProfileManager.getProfileUser();
+ if (privateProfileUser == null) {
+ return null;
+ }
+ // Do not show shortcut if an app is already installed to the space
+ ComponentKey targetKey =
+ new ComponentKey(itemInfo.getTargetComponent(), privateProfileUser);
+ if (launcher.getAppsView().getAppsStore().getApp(targetKey) != null) {
+ return null;
+ }
+
+ // TODO(b/302666597): do not install app if it's in deny list (e.g. settings)
+
+ return new InstallToPrivateProfile(
+ launcher, itemInfo, originalView, privateProfileUser);
+ };
+
+ static class InstallToPrivateProfile extends SystemShortcut {
+ UserHandle mSpaceUser;
+
+ InstallToPrivateProfile(
+ Launcher target, ItemInfo itemInfo, View originalView, UserHandle spaceUser) {
+ // TODO(b/302666597): update icon once available
+ super(
+ R.drawable.ic_install_to_private,
+ R.string.install_private_system_shortcut_label,
+ target,
+ itemInfo,
+ originalView);
+ mSpaceUser = spaceUser;
+ }
+
+ @Override
+ public void onClick(View view) {
+ Intent intent =
+ ApiWrapper.getAppMarketActivityIntent(
+ view.getContext(),
+ mItemInfo.getTargetComponent().getPackageName(),
+ mSpaceUser);
+ mTarget.startActivitySafely(view, intent, mItemInfo);
+ AbstractFloatingView.closeAllOpenViews(mTarget);
+ mTarget.getStatsLogManager()
+ .logger()
+ .withItemInfo(mItemInfo)
+ .log(LAUNCHER_PRIVATE_SPACE_INSTALL_SYSTEM_SHORTCUT_TAP);
+ }
+ }
+
public static final Factory INSTALL =
(activity, itemInfo, originalView) -> {
boolean supportsWebUI = (itemInfo instanceof WorkspaceItemInfo)