Merge "Add multi-instance state to item infos" into 24D1-dev

This commit is contained in:
Winson Chung
2024-05-22 22:21:24 +00:00
committed by Android (Google) Code Review
5 changed files with 137 additions and 37 deletions
@@ -322,14 +322,13 @@ public interface TaskShortcutFactory {
isLargeTileFocusedTask && isInExpectedScrollPosition;
// No "save app pair" menu item if:
// - app pairs feature is not enabled
// - we are in 3p launcher
// - the task in question is a single task
// - the Overview Actions Button should be visible
if (!FeatureFlags.enableAppPairs()
|| !recentsView.supportsAppPairs()
|| !taskView.containsMultipleTasks()
|| shouldShowActionsButtonInstead) {
// - the task view is not a valid save-able split pair
if (!recentsView.supportsAppPairs()
|| shouldShowActionsButtonInstead
|| !recentsView.getSplitSelectController().getAppPairsController()
.canSaveAppPair(taskView)) {
return null;
}
@@ -30,6 +30,7 @@ import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSIT
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.common.split.SplitScreenConstants.isPersistentSnapPosition;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -38,6 +39,7 @@ import android.content.pm.PackageManager;
import android.util.Log;
import android.util.Pair;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
@@ -45,20 +47,25 @@ import com.android.internal.jank.Cuj;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.allapps.AllAppsStore;
import com.android.launcher3.apppairs.AppPairIcon;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.AppPairInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskUtils;
import com.android.quickstep.TopTaskTracker;
import com.android.quickstep.views.GroupedTaskView;
import com.android.quickstep.views.TaskView;
@@ -87,18 +94,88 @@ public class AppPairsController {
private Context mContext;
private final SplitSelectStateController mSplitSelectStateController;
private final StatsLogManager mStatsLogManager;
private final String[] mLegacyMultiInstanceSupportedApps;
public AppPairsController(Context context,
SplitSelectStateController splitSelectStateController,
StatsLogManager statsLogManager) {
mContext = context;
mSplitSelectStateController = splitSelectStateController;
mStatsLogManager = statsLogManager;
mLegacyMultiInstanceSupportedApps = mContext.getResources().getStringArray(
R.array.config_appsSupportMultiInstancesSplit);
}
void onDestroy() {
mContext = null;
}
/**
* Returns whether the given component or its application supports multi-instance.
*/
private boolean supportsMultiInstance(@NonNull ComponentName component) {
// Check the legacy hardcoded allowlist first
for (String pkg : mLegacyMultiInstanceSupportedApps) {
if (pkg.equals(component.getPackageName())) {
return true;
}
}
return false;
}
/**
* Returns whether two apps should be considered the same for multi-instance purposes, which
* requires additional checks to ensure they can be started as multiple instances.
*/
private boolean isSameAppForMultiInstance(@NonNull ItemInfo app1,
@NonNull ItemInfo app2) {
return app1.getTargetPackage().equals(app2.getTargetPackage())
&& app1.user.equals(app2.user);
}
/**
* Returns whether the specified GroupedTaskView can be saved as an app pair.
*/
public boolean canSaveAppPair(TaskView taskView) {
if (mContext == null) {
// Can ignore as the activity is already destroyed
return false;
}
// Disallow saving app pairs if:
// - app pairs feature is not enabled
// - the task in question is a single task
// - at least one app in app pair is unpinnable
// - the task is not a GroupedTaskView
// - both tasks in the GroupedTaskView are from the same app and the app does not
// support multi-instance
if (!FeatureFlags.enableAppPairs()
|| !taskView.containsMultipleTasks()
|| !(taskView instanceof GroupedTaskView)) {
return false;
}
GroupedTaskView gtv = (GroupedTaskView) taskView;
TaskView.TaskIdAttributeContainer[] attributes = gtv.getTaskIdAttributeContainers();
WorkspaceItemInfo info1 = attributes[0].getItemInfo();
WorkspaceItemInfo info2 = attributes[1].getItemInfo();
AppInfo app1 = resolveAppInfoByComponent(info1.getComponentKey());
AppInfo app2 = resolveAppInfoByComponent(info2.getComponentKey());
if (app1 == null || app2 == null) {
// Disallow saving app pairs for apps that don't have a front-door in Launcher
return false;
}
if (isSameAppForMultiInstance(app1, app2)) {
if (!supportsMultiInstance(app1.getTargetComponent())
|| !supportsMultiInstance(app2.getTargetComponent())) {
return false;
}
}
return true;
}
/**
* Creates a new app pair ItemInfo and adds it to the workspace.
* <br>
@@ -118,20 +195,16 @@ public class AppPairsController {
TaskView.TaskIdAttributeContainer[] attributes = gtv.getTaskIdAttributeContainers();
WorkspaceItemInfo recentsInfo1 = attributes[0].getItemInfo();
WorkspaceItemInfo recentsInfo2 = attributes[1].getItemInfo();
WorkspaceItemInfo app1 = lookupLaunchableItem(recentsInfo1.getComponentKey());
WorkspaceItemInfo app2 = lookupLaunchableItem(recentsInfo2.getComponentKey());
WorkspaceItemInfo app1 = resolveAppPairWorkspaceInfo(recentsInfo1);
WorkspaceItemInfo app2 = resolveAppPairWorkspaceInfo(recentsInfo2);
// If app lookup fails, use the WorkspaceItemInfo that we have, but try to override default
// intent with one from PackageManager.
if (app1 == null) {
Log.w(TAG, "Creating an app pair, but app lookup for " + recentsInfo1.title
+ " failed. Falling back to the WorkspaceItemInfo from Recents.");
app1 = convertRecentsItemToAppItem(recentsInfo1);
}
if (app2 == null) {
Log.w(TAG, "Creating an app pair, but app lookup for " + recentsInfo2.title
+ " failed. Falling back to the WorkspaceItemInfo from Recents.");
app2 = convertRecentsItemToAppItem(recentsInfo2);
if (app1 == null || app2 == null) {
// This shouldn't happen if canSaveAppPair() is called above, but log an error and do
// not create the app pair if the workspace items can't be resolved
Log.w(TAG, "Failed to save app pair due to invalid apps ("
+ "app1=" + recentsInfo1.getComponentKey().componentName
+ " app2=" + recentsInfo2.getComponentKey().componentName + ")");
return;
}
// WorkspaceItemProcessor won't process these new ItemInfos until the next launcher restart,
@@ -141,8 +214,9 @@ public class AppPairsController {
@PersistentSnapPosition int snapPosition = gtv.getSnapPosition();
if (!isPersistentSnapPosition(snapPosition)) {
// if we received an illegal snap position, log an error and do not create the app pair.
Log.wtf(TAG, "tried to save an app pair with illegal snapPosition " + snapPosition);
// If we received an illegal snap position, log an error and do not create the app pair
Log.wtf(TAG, "Tried to save an app pair with illegal snapPosition "
+ snapPosition);
return;
}
@@ -227,26 +301,37 @@ public class AppPairsController {
);
}
/**
* Returns an AppInfo associated with the app for the given ComponentKey, or null if no such
* package exists in the AllAppsStore.
*/
@Nullable
private AppInfo resolveAppInfoByComponent(@NonNull ComponentKey key) {
AllAppsStore appsStore = Launcher.getLauncher(mContext).getAppsView().getAppsStore();
// First look up the app info in order of:
// - The exact activity for the recent task
// - The first(?) loaded activity from the package
AppInfo appInfo = appsStore.getApp(key);
if (appInfo == null) {
appInfo = appsStore.getApp(key, PACKAGE_KEY_COMPARATOR);
}
return appInfo;
}
/**
* Creates a new launchable WorkspaceItemInfo of itemType=ITEM_TYPE_APPLICATION by looking the
* ComponentKey up in the AllAppsStore. If no app is found, attempts a lookup by package
* instead. If that lookup fails, returns null.
*/
@Nullable
private WorkspaceItemInfo lookupLaunchableItem(@Nullable ComponentKey key) {
if (key == null) {
private WorkspaceItemInfo resolveAppPairWorkspaceInfo(
@NonNull WorkspaceItemInfo recentTaskInfo) {
// ComponentKey should never be null (see TaskView#getItemInfo)
AppInfo appInfo = resolveAppInfoByComponent(recentTaskInfo.getComponentKey());
if (appInfo == null) {
return null;
}
AllAppsStore appsStore = Launcher.getLauncher(mContext).getAppsView().getAppsStore();
// Lookup by ComponentKey
AppInfo appInfo = appsStore.getApp(key);
if (appInfo == null) {
// Lookup by package
appInfo = appsStore.getApp(key, PACKAGE_KEY_COMPARATOR);
}
return appInfo != null ? appInfo.makeWorkspaceItem(mContext) : null;
}
@@ -39,6 +39,7 @@ import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.launcher3.util.NavigationMode;
import com.android.quickstep.TaskOverlayFactory.OverlayUICallbacks;
import com.android.quickstep.util.AppPairsController;
import com.android.quickstep.util.LayoutUtils;
import java.lang.annotation.Retention;
@@ -133,6 +134,7 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo
protected DeviceProfile mDp;
private final Rect mTaskSize = new Rect();
private boolean mIsGroupedTask = false;
private boolean mCanSaveAppPair = false;
public OverviewActionsView(Context context) {
this(context, null);
@@ -249,9 +251,12 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo
* Updates a batch of flags to hide and show actions buttons when a grouped task (split screen)
* is focused.
* @param isGroupedTask True if the focused task is a grouped task.
* @param canSaveAppPair True if the focused task is a grouped task and can be saved as an app
* pair.
*/
public void updateForGroupedTask(boolean isGroupedTask) {
public void updateForGroupedTask(boolean isGroupedTask, boolean canSaveAppPair) {
mIsGroupedTask = isGroupedTask;
mCanSaveAppPair = canSaveAppPair;
updateActionButtonsVisibility();
}
@@ -268,7 +273,7 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo
private void updateActionButtonsVisibility() {
assert mDp != null;
boolean showSingleTaskActions = !mIsGroupedTask;
boolean showGroupActions = mIsGroupedTask && mDp.isTablet;
boolean showGroupActions = mIsGroupedTask && mDp.isTablet && mCanSaveAppPair;
getActionsAlphas().get(INDEX_GROUPED_ALPHA).setValue(showSingleTaskActions ? 1 : 0);
getGroupActionsAlphas().get(INDEX_GROUPED_ALPHA).setValue(showGroupActions ? 1 : 0);
}
@@ -4023,7 +4023,9 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
* * Device is large screen
*/
private void updateCurrentTaskActionsVisibility() {
boolean isCurrentSplit = getCurrentPageTaskView() instanceof GroupedTaskView;
TaskView taskView = getCurrentPageTaskView();
boolean isCurrentSplit = taskView instanceof GroupedTaskView;
GroupedTaskView groupedTaskView = isCurrentSplit ? (GroupedTaskView) taskView : null;
// Update flags to see if entire actions bar should be hidden.
if (!FeatureFlags.enableAppPairs()) {
mActionsView.updateHiddenFlags(HIDDEN_SPLIT_SCREEN, isCurrentSplit);
@@ -4031,7 +4033,9 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
mActionsView.updateHiddenFlags(HIDDEN_SPLIT_SELECT_ACTIVE, isSplitSelectionActive());
// Update flags to see if actions bar should show buttons for a single task or a pair of
// tasks.
mActionsView.updateForGroupedTask(isCurrentSplit);
boolean canSaveAppPair = isCurrentSplit && supportsAppPairs() &&
getSplitSelectController().getAppPairsController().canSaveAppPair(groupedTaskView);
mActionsView.updateForGroupedTask(isCurrentSplit, canSaveAppPair);
if (isDesktopModeSupported()) {
boolean isCurrentDesktop = getCurrentPageTaskView() instanceof DesktopTaskView;
+7
View File
@@ -274,4 +274,11 @@
<string-array name="skip_private_profile_shortcut_packages" translatable="false">
<item>com.android.settings</item>
</string-array>
<!-- Legacy list of components supporting multiple instances.
DO NOT ADD TO THIS LIST. Apps should use the PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI
property to declare multi-instance support in V+. This resource should match the resource
of the same name in SystemUI. -->
<string-array name="config_appsSupportMultiInstancesSplit">
</string-array>
</resources>