Merge "Support starting a shortcut into multi-instances split" into tm-qpr-dev

This commit is contained in:
Jerry Chang
2023-02-06 03:47:54 +00:00
committed by Android (Google) Code Review
5 changed files with 245 additions and 127 deletions

View File

@@ -699,19 +699,6 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
return bounds.width() > bounds.height(); return bounds.width() > bounds.height();
} }
/** Reverse the split position. */
@SplitPosition
public static int reversePosition(@SplitPosition int position) {
switch (position) {
case SPLIT_POSITION_TOP_OR_LEFT:
return SPLIT_POSITION_BOTTOM_OR_RIGHT;
case SPLIT_POSITION_BOTTOM_OR_RIGHT:
return SPLIT_POSITION_TOP_OR_LEFT;
default:
return SPLIT_POSITION_UNDEFINED;
}
}
/** /**
* Return if this layout is landscape. * Return if this layout is landscape.
*/ */

View File

@@ -0,0 +1,85 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.wm.shell.common.split;
import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_ACTIVITY_TYPES;
import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.PendingIntent;
import android.content.Intent;
import com.android.internal.util.ArrayUtils;
import com.android.wm.shell.ShellTaskOrganizer;
/** Helper utility class for split screen components to use. */
public class SplitScreenUtils {
/** Reverse the split position. */
@SplitScreenConstants.SplitPosition
public static int reverseSplitPosition(@SplitScreenConstants.SplitPosition int position) {
switch (position) {
case SPLIT_POSITION_TOP_OR_LEFT:
return SPLIT_POSITION_BOTTOM_OR_RIGHT;
case SPLIT_POSITION_BOTTOM_OR_RIGHT:
return SPLIT_POSITION_TOP_OR_LEFT;
case SPLIT_POSITION_UNDEFINED:
default:
return SPLIT_POSITION_UNDEFINED;
}
}
/** Returns true if the task is valid for split screen. */
public static boolean isValidToSplit(ActivityManager.RunningTaskInfo taskInfo) {
return taskInfo != null && taskInfo.supportsMultiWindow
&& ArrayUtils.contains(CONTROLLED_ACTIVITY_TYPES, taskInfo.getActivityType())
&& ArrayUtils.contains(CONTROLLED_WINDOWING_MODES, taskInfo.getWindowingMode());
}
/** Retrieve package name from an intent */
@Nullable
public static String getPackageName(Intent intent) {
if (intent == null || intent.getComponent() == null) {
return null;
}
return intent.getComponent().getPackageName();
}
/** Retrieve package name from a PendingIntent */
@Nullable
public static String getPackageName(PendingIntent pendingIntent) {
if (pendingIntent == null || pendingIntent.getIntent() == null) {
return null;
}
return getPackageName(pendingIntent.getIntent());
}
/** Retrieve package name from a taskId */
@Nullable
public static String getPackageName(int taskId, ShellTaskOrganizer taskOrganizer) {
final ActivityManager.RunningTaskInfo taskInfo = taskOrganizer.getRunningTaskInfo(taskId);
return taskInfo != null ? getPackageName(taskInfo.baseIntent) : null;
}
/** Returns true if they are the same package. */
public static boolean samePackage(String packageName1, String packageName2) {
return packageName1 != null && packageName1.equals(packageName2);
}
}

View File

@@ -28,6 +28,9 @@ import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTas
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
import static com.android.wm.shell.common.split.SplitScreenUtils.isValidToSplit;
import static com.android.wm.shell.common.split.SplitScreenUtils.reverseSplitPosition;
import static com.android.wm.shell.common.split.SplitScreenUtils.samePackage;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_SPLIT_SCREEN; import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_SPLIT_SCREEN;
@@ -39,11 +42,8 @@ import android.app.ActivityManager;
import android.app.ActivityOptions; import android.app.ActivityOptions;
import android.app.ActivityTaskManager; import android.app.ActivityTaskManager;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.LauncherApps;
import android.content.pm.ShortcutInfo; import android.content.pm.ShortcutInfo;
import android.graphics.Rect; import android.graphics.Rect;
import android.os.Bundle; import android.os.Bundle;
@@ -82,8 +82,8 @@ import com.android.wm.shell.common.SingleInstanceRemoteListener;
import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.annotations.ExternalThread; import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.common.split.SplitLayout;
import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition; import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition;
import com.android.wm.shell.common.split.SplitScreenUtils;
import com.android.wm.shell.draganddrop.DragAndDropController; import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.draganddrop.DragAndDropPolicy; import com.android.wm.shell.draganddrop.DragAndDropPolicy;
import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.protolog.ShellProtoLogGroup;
@@ -318,10 +318,6 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
return mStageCoordinator; return mStageCoordinator;
} }
public boolean isValidToEnterSplitScreen(@NonNull ActivityManager.RunningTaskInfo taskInfo) {
return mStageCoordinator.isValidToEnterSplitScreen(taskInfo);
}
@Nullable @Nullable
public ActivityManager.RunningTaskInfo getTaskInfo(@SplitPosition int splitPosition) { public ActivityManager.RunningTaskInfo getTaskInfo(@SplitPosition int splitPosition) {
if (!isSplitScreenVisible() || splitPosition == SPLIT_POSITION_UNDEFINED) { if (!isSplitScreenVisible() || splitPosition == SPLIT_POSITION_UNDEFINED) {
@@ -480,39 +476,54 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
@Override @Override
public void startShortcut(String packageName, String shortcutId, @SplitPosition int position, public void startShortcut(String packageName, String shortcutId, @SplitPosition int position,
@Nullable Bundle options, UserHandle user) { @Nullable Bundle options, UserHandle user) {
IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() { if (options == null) options = new Bundle();
@Override final ActivityOptions activityOptions = ActivityOptions.fromBundle(options);
public void onAnimationStart(@WindowManager.TransitionOldType int transit,
RemoteAnimationTarget[] apps, if (samePackage(packageName, getPackageName(reverseSplitPosition(position)))) {
RemoteAnimationTarget[] wallpapers, if (supportMultiInstancesSplit(packageName)) {
RemoteAnimationTarget[] nonApps, activityOptions.setApplyMultipleTaskFlagForShortcut(true);
final IRemoteAnimationFinishedCallback finishedCallback) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
try { } else if (isSplitScreenVisible()) {
finishedCallback.onAnimationFinished(); mStageCoordinator.switchSplitPosition("startShortcut");
} catch (RemoteException e) { return;
Slog.e(TAG, "Failed to invoke onAnimationFinished", e); } else {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
"Cancel entering split as not supporting multi-instances");
Toast.makeText(mContext, R.string.dock_multi_instances_not_supported_text,
Toast.LENGTH_SHORT).show();
return;
} }
final WindowContainerTransaction evictWct = new WindowContainerTransaction();
mStageCoordinator.prepareEvictNonOpeningChildTasks(position, apps, evictWct);
mSyncQueue.queue(evictWct);
} }
@Override
public void onAnimationCancelled(boolean isKeyguardOccluded) { mStageCoordinator.startShortcut(packageName, shortcutId, position,
}
};
options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options,
null /* wct */);
RemoteAnimationAdapter wrappedAdapter = new RemoteAnimationAdapter(wrapper,
0 /* duration */, 0 /* statusBarTransitionDelay */);
ActivityOptions activityOptions = ActivityOptions.fromBundle(options);
activityOptions.update(ActivityOptions.makeRemoteAnimation(wrappedAdapter));
try {
LauncherApps launcherApps = mContext.getSystemService(LauncherApps.class);
launcherApps.startShortcut(packageName, shortcutId, null /* sourceBounds */,
activityOptions.toBundle(), user); activityOptions.toBundle(), user);
} catch (ActivityNotFoundException e) {
Slog.e(TAG, "Failed to launch shortcut", e);
} }
void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo,
@Nullable Bundle options1, int taskId, @Nullable Bundle options2,
@SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter,
InstanceId instanceId) {
if (options1 == null) options1 = new Bundle();
final ActivityOptions activityOptions = ActivityOptions.fromBundle(options1);
final String packageName1 = shortcutInfo.getPackage();
final String packageName2 = SplitScreenUtils.getPackageName(taskId, mTaskOrganizer);
if (samePackage(packageName1, packageName2)) {
if (supportMultiInstancesSplit(shortcutInfo.getPackage())) {
activityOptions.setApplyMultipleTaskFlagForShortcut(true);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
} else {
taskId = INVALID_TASK_ID;
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
"Cancel entering split as not supporting multi-instances");
Toast.makeText(mContext, R.string.dock_multi_instances_not_supported_text,
Toast.LENGTH_SHORT).show();
}
}
mStageCoordinator.startShortcutAndTaskWithLegacyTransition(shortcutInfo,
activityOptions.toBundle(), taskId, options2, splitPosition, splitRatio, adapter,
instanceId);
} }
/** /**
@@ -530,8 +541,10 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
@SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter, @SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter,
InstanceId instanceId) { InstanceId instanceId) {
Intent fillInIntent = null; Intent fillInIntent = null;
if (launchSameAppAdjacently(pendingIntent, taskId)) { final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent);
if (supportMultiInstancesSplit(pendingIntent.getIntent().getComponent())) { final String packageName2 = SplitScreenUtils.getPackageName(taskId, mTaskOrganizer);
if (samePackage(packageName1, packageName2)) {
if (supportMultiInstancesSplit(packageName1)) {
fillInIntent = new Intent(); fillInIntent = new Intent();
fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK); fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK"); ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
@@ -551,8 +564,10 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition, int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition,
float splitRatio, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) { float splitRatio, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
Intent fillInIntent = null; Intent fillInIntent = null;
if (launchSameAppAdjacently(pendingIntent, taskId)) { final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent);
if (supportMultiInstancesSplit(pendingIntent.getIntent().getComponent())) { final String packageName2 = SplitScreenUtils.getPackageName(taskId, mTaskOrganizer);
if (samePackage(packageName1, packageName2)) {
if (supportMultiInstancesSplit(packageName1)) {
fillInIntent = new Intent(); fillInIntent = new Intent();
fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK); fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK"); ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
@@ -573,8 +588,10 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId) { float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId) {
Intent fillInIntent1 = null; Intent fillInIntent1 = null;
Intent fillInIntent2 = null; Intent fillInIntent2 = null;
if (launchSameAppAdjacently(pendingIntent1, pendingIntent2)) { final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent1);
if (supportMultiInstancesSplit(pendingIntent1.getIntent().getComponent())) { final String packageName2 = SplitScreenUtils.getPackageName(pendingIntent2);
if (samePackage(packageName1, packageName2)) {
if (supportMultiInstancesSplit(packageName1)) {
fillInIntent1 = new Intent(); fillInIntent1 = new Intent();
fillInIntent1.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK); fillInIntent1.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
fillInIntent2 = new Intent(); fillInIntent2 = new Intent();
@@ -602,13 +619,15 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
if (fillInIntent == null) fillInIntent = new Intent(); if (fillInIntent == null) fillInIntent = new Intent();
fillInIntent.addFlags(FLAG_ACTIVITY_NO_USER_ACTION); fillInIntent.addFlags(FLAG_ACTIVITY_NO_USER_ACTION);
if (launchSameAppAdjacently(position, intent)) { final String packageName1 = SplitScreenUtils.getPackageName(intent);
final ComponentName launching = intent.getIntent().getComponent(); final String packageName2 = getPackageName(reverseSplitPosition(position));
if (supportMultiInstancesSplit(launching)) { if (SplitScreenUtils.samePackage(packageName1, packageName2)) {
if (supportMultiInstancesSplit(packageName1)) {
// To prevent accumulating large number of instances in the background, reuse task // To prevent accumulating large number of instances in the background, reuse task
// in the background with priority. // in the background with priority.
final ActivityManager.RecentTaskInfo taskInfo = mRecentTasksOptional final ActivityManager.RecentTaskInfo taskInfo = mRecentTasksOptional
.map(recentTasks -> recentTasks.findTaskInBackground(launching)) .map(recentTasks -> recentTasks.findTaskInBackground(
intent.getIntent().getComponent()))
.orElse(null); .orElse(null);
if (taskInfo != null) { if (taskInfo != null) {
startTask(taskInfo.taskId, position, options); startTask(taskInfo.taskId, position, options);
@@ -636,65 +655,34 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
mStageCoordinator.startIntent(intent, fillInIntent, position, options); mStageCoordinator.startIntent(intent, fillInIntent, position, options);
} }
/** Retrieve package name of a specific split position if split screen is activated, otherwise
* returns the package name of the top running task. */
@Nullable @Nullable
private String getPackageName(Intent intent) { private String getPackageName(@SplitPosition int position) {
if (intent == null || intent.getComponent() == null) { ActivityManager.RunningTaskInfo taskInfo;
if (isSplitScreenVisible()) {
taskInfo = getTaskInfo(position);
} else {
taskInfo = mRecentTasksOptional
.map(recentTasks -> recentTasks.getTopRunningTask())
.orElse(null);
if (!isValidToSplit(taskInfo)) {
return null; return null;
} }
return intent.getComponent().getPackageName();
} }
private boolean launchSameAppAdjacently(@SplitPosition int position, return taskInfo != null ? SplitScreenUtils.getPackageName(taskInfo.baseIntent) : null;
PendingIntent pendingIntent) {
ActivityManager.RunningTaskInfo adjacentTaskInfo = null;
if (isSplitScreenVisible()) {
adjacentTaskInfo = getTaskInfo(SplitLayout.reversePosition(position));
} else {
adjacentTaskInfo = mRecentTasksOptional
.map(recentTasks -> recentTasks.getTopRunningTask()).orElse(null);
if (!isValidToEnterSplitScreen(adjacentTaskInfo)) {
return false;
}
}
if (adjacentTaskInfo == null) {
return false;
}
final String targetPackageName = getPackageName(pendingIntent.getIntent());
final String adjacentPackageName = getPackageName(adjacentTaskInfo.baseIntent);
return targetPackageName != null && targetPackageName.equals(adjacentPackageName);
}
private boolean launchSameAppAdjacently(PendingIntent pendingIntent, int taskId) {
final ActivityManager.RunningTaskInfo adjacentTaskInfo =
mTaskOrganizer.getRunningTaskInfo(taskId);
if (adjacentTaskInfo == null) {
return false;
}
final String targetPackageName = getPackageName(pendingIntent.getIntent());
final String adjacentPackageName = getPackageName(adjacentTaskInfo.baseIntent);
return targetPackageName != null && targetPackageName.equals(adjacentPackageName);
}
private boolean launchSameAppAdjacently(PendingIntent pendingIntent1,
PendingIntent pendingIntent2) {
final String targetPackageName = getPackageName(pendingIntent1.getIntent());
final String adjacentPackageName = getPackageName(pendingIntent2.getIntent());
return targetPackageName != null && targetPackageName.equals(adjacentPackageName);
} }
@VisibleForTesting @VisibleForTesting
/** Returns {@code true} if the component supports multi-instances split. */ boolean supportMultiInstancesSplit(String packageName) {
boolean supportMultiInstancesSplit(@Nullable ComponentName launching) { if (packageName != null) {
if (launching == null) return false;
final String packageName = launching.getPackageName();
for (int i = 0; i < mAppsSupportMultiInstances.length; i++) { for (int i = 0; i < mAppsSupportMultiInstances.length; i++) {
if (mAppsSupportMultiInstances[i].equals(packageName)) { if (mAppsSupportMultiInstances[i].equals(packageName)) {
return true; return true;
} }
} }
}
return false; return false;
} }
@@ -1011,7 +999,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
InstanceId instanceId) { InstanceId instanceId) {
executeRemoteCallWithTaskPermission(mController, executeRemoteCallWithTaskPermission(mController,
"startShortcutAndTaskWithLegacyTransition", (controller) -> "startShortcutAndTaskWithLegacyTransition", (controller) ->
controller.mStageCoordinator.startShortcutAndTaskWithLegacyTransition( controller.startShortcutAndTaskWithLegacyTransition(
shortcutInfo, options1, taskId, options2, splitPosition, shortcutInfo, options1, taskId, options2, splitPosition,
splitRatio, adapter, instanceId)); splitRatio, adapter, instanceId));
} }

View File

@@ -36,12 +36,11 @@ import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;
import static com.android.wm.shell.common.split.SplitLayout.PARALLAX_ALIGN_CENTER; import static com.android.wm.shell.common.split.SplitLayout.PARALLAX_ALIGN_CENTER;
import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_ACTIVITY_TYPES;
import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES;
import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR; import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
import static com.android.wm.shell.common.split.SplitScreenUtils.reverseSplitPosition;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
@@ -76,8 +75,10 @@ import android.app.ActivityOptions;
import android.app.IActivityTaskManager; import android.app.IActivityTaskManager;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.app.WindowConfiguration; import android.app.WindowConfiguration;
import android.content.ActivityNotFoundException;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.LauncherApps;
import android.content.pm.ShortcutInfo; import android.content.pm.ShortcutInfo;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.graphics.Rect; import android.graphics.Rect;
@@ -87,6 +88,7 @@ import android.os.Debug;
import android.os.IBinder; import android.os.IBinder;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.ServiceManager; import android.os.ServiceManager;
import android.os.UserHandle;
import android.util.Log; import android.util.Log;
import android.util.Slog; import android.util.Slog;
import android.view.Choreographer; import android.view.Choreographer;
@@ -370,7 +372,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
int sideStagePosition; int sideStagePosition;
if (stageType == STAGE_TYPE_MAIN) { if (stageType == STAGE_TYPE_MAIN) {
targetStage = mMainStage; targetStage = mMainStage;
sideStagePosition = SplitLayout.reversePosition(stagePosition); sideStagePosition = reverseSplitPosition(stagePosition);
} else if (stageType == STAGE_TYPE_SIDE) { } else if (stageType == STAGE_TYPE_SIDE) {
targetStage = mSideStage; targetStage = mSideStage;
sideStagePosition = stagePosition; sideStagePosition = stagePosition;
@@ -428,6 +430,72 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
return mLogger; return mLogger;
} }
void startShortcut(String packageName, String shortcutId, @SplitPosition int position,
Bundle options, UserHandle user) {
final boolean isEnteringSplit = !isSplitActive();
IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() {
@Override
public void onAnimationStart(@WindowManager.TransitionOldType int transit,
RemoteAnimationTarget[] apps,
RemoteAnimationTarget[] wallpapers,
RemoteAnimationTarget[] nonApps,
final IRemoteAnimationFinishedCallback finishedCallback) {
boolean openingToSide = false;
if (apps != null) {
for (int i = 0; i < apps.length; ++i) {
if (apps[i].mode == MODE_OPENING
&& mSideStage.containsTask(apps[i].taskId)) {
openingToSide = true;
break;
}
}
}
if (isEnteringSplit && !openingToSide) {
mMainExecutor.execute(() -> exitSplitScreen(
mSideStage.getChildCount() == 0 ? mMainStage : mSideStage,
EXIT_REASON_UNKNOWN));
}
if (finishedCallback != null) {
try {
finishedCallback.onAnimationFinished();
} catch (RemoteException e) {
Slog.e(TAG, "Error finishing legacy transition: ", e);
}
}
if (!isEnteringSplit && openingToSide) {
final WindowContainerTransaction evictWct = new WindowContainerTransaction();
prepareEvictNonOpeningChildTasks(position, apps, evictWct);
mSyncQueue.queue(evictWct);
}
}
@Override
public void onAnimationCancelled(boolean isKeyguardOccluded) {
if (isEnteringSplit) {
mMainExecutor.execute(() -> exitSplitScreen(
mSideStage.getChildCount() == 0 ? mMainStage : mSideStage,
EXIT_REASON_UNKNOWN));
}
}
};
options = resolveStartStage(STAGE_TYPE_UNDEFINED, position, options,
null /* wct */);
RemoteAnimationAdapter wrappedAdapter = new RemoteAnimationAdapter(wrapper,
0 /* duration */, 0 /* statusBarTransitionDelay */);
ActivityOptions activityOptions = ActivityOptions.fromBundle(options);
activityOptions.update(ActivityOptions.makeRemoteAnimation(wrappedAdapter));
try {
LauncherApps launcherApps = mContext.getSystemService(LauncherApps.class);
launcherApps.startShortcut(packageName, shortcutId, null /* sourceBounds */,
activityOptions.toBundle(), user);
} catch (ActivityNotFoundException e) {
Slog.e(TAG, "Failed to launch shortcut", e);
}
}
/** Launches an activity into split. */ /** Launches an activity into split. */
void startIntent(PendingIntent intent, Intent fillInIntent, @SplitPosition int position, void startIntent(PendingIntent intent, Intent fillInIntent, @SplitPosition int position,
@Nullable Bundle options) { @Nullable Bundle options) {
@@ -899,7 +967,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
case STAGE_TYPE_MAIN: { case STAGE_TYPE_MAIN: {
if (position != SPLIT_POSITION_UNDEFINED) { if (position != SPLIT_POSITION_UNDEFINED) {
// Set the side stage opposite of what we want to the main stage. // Set the side stage opposite of what we want to the main stage.
setSideStagePosition(SplitLayout.reversePosition(position), wct); setSideStagePosition(reverseSplitPosition(position), wct);
} else { } else {
position = getMainStagePosition(); position = getMainStagePosition();
} }
@@ -923,7 +991,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@SplitPosition @SplitPosition
int getMainStagePosition() { int getMainStagePosition() {
return SplitLayout.reversePosition(mSideStagePosition); return reverseSplitPosition(mSideStagePosition);
} }
int getTaskId(@SplitPosition int splitPosition) { int getTaskId(@SplitPosition int splitPosition) {
@@ -950,7 +1018,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSplitLayout.splitSwitching(t, topLeftStage.mRootLeash, bottomRightStage.mRootLeash, mSplitLayout.splitSwitching(t, topLeftStage.mRootLeash, bottomRightStage.mRootLeash,
insets -> { insets -> {
WindowContainerTransaction wct = new WindowContainerTransaction(); WindowContainerTransaction wct = new WindowContainerTransaction();
setSideStagePosition(SplitLayout.reversePosition(mSideStagePosition), wct); setSideStagePosition(reverseSplitPosition(mSideStagePosition), wct);
mSyncQueue.queue(wct); mSyncQueue.queue(wct);
mSyncQueue.runInSync(st -> { mSyncQueue.runInSync(st -> {
updateSurfaceBounds(mSplitLayout, st, false /* applyResizingOffset */); updateSurfaceBounds(mSplitLayout, st, false /* applyResizingOffset */);
@@ -1694,12 +1762,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
} }
} }
boolean isValidToEnterSplitScreen(@NonNull ActivityManager.RunningTaskInfo taskInfo) {
return taskInfo.supportsMultiWindow
&& ArrayUtils.contains(CONTROLLED_ACTIVITY_TYPES, taskInfo.getActivityType())
&& ArrayUtils.contains(CONTROLLED_WINDOWING_MODES, taskInfo.getWindowingMode());
}
@Override @Override
public void onSnappedToDismiss(boolean bottomOrRight, int reason) { public void onSnappedToDismiss(boolean bottomOrRight, int reason) {
final boolean mainStageToTop = final boolean mainStageToTop =

View File

@@ -27,8 +27,6 @@ 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.SPLIT_POSITION_TOP_OR_LEFT;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue; import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyInt;
@@ -201,7 +199,6 @@ public class SplitScreenControllerTests extends ShellTestCase {
ActivityManager.RunningTaskInfo topRunningTask = ActivityManager.RunningTaskInfo topRunningTask =
createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, startIntent); createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, startIntent);
doReturn(topRunningTask).when(mRecentTasks).getTopRunningTask(); doReturn(topRunningTask).when(mRecentTasks).getTopRunningTask();
doReturn(true).when(mStageCoordinator).isValidToEnterSplitScreen(any());
mSplitScreenController.startIntent(pendingIntent, null, SPLIT_POSITION_TOP_OR_LEFT, null); mSplitScreenController.startIntent(pendingIntent, null, SPLIT_POSITION_TOP_OR_LEFT, null);
@@ -222,7 +219,6 @@ public class SplitScreenControllerTests extends ShellTestCase {
ActivityManager.RunningTaskInfo topRunningTask = ActivityManager.RunningTaskInfo topRunningTask =
createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, startIntent); createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, startIntent);
doReturn(topRunningTask).when(mRecentTasks).getTopRunningTask(); doReturn(topRunningTask).when(mRecentTasks).getTopRunningTask();
doReturn(true).when(mStageCoordinator).isValidToEnterSplitScreen(any());
// Put the same component into a task in the background // Put the same component into a task in the background
ActivityManager.RecentTaskInfo sameTaskInfo = new ActivityManager.RecentTaskInfo(); ActivityManager.RecentTaskInfo sameTaskInfo = new ActivityManager.RecentTaskInfo();
doReturn(sameTaskInfo).when(mRecentTasks).findTaskInBackground(any()); doReturn(sameTaskInfo).when(mRecentTasks).findTaskInBackground(any());