Merge "Make multi-instances split opt-in based" into tm-qpr-dev
This commit is contained in:
@@ -111,4 +111,8 @@
|
|||||||
|
|
||||||
<!-- Whether to dim a split-screen task when the other is the IME target -->
|
<!-- Whether to dim a split-screen task when the other is the IME target -->
|
||||||
<bool name="config_dimNonImeAttachedSide">true</bool>
|
<bool name="config_dimNonImeAttachedSide">true</bool>
|
||||||
|
|
||||||
|
<!-- Components support to launch multiple instances into split-screen -->
|
||||||
|
<string-array name="config_componentsSupportMultiInstancesSplit">
|
||||||
|
</string-array>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -601,7 +601,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
|
|||||||
animator.start();
|
animator.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Swich both surface position with animation. */
|
/** Switch both surface position with animation. */
|
||||||
public void splitSwitching(SurfaceControl.Transaction t, SurfaceControl leash1,
|
public void splitSwitching(SurfaceControl.Transaction t, SurfaceControl leash1,
|
||||||
SurfaceControl leash2, Consumer<Rect> finishCallback) {
|
SurfaceControl leash2, Consumer<Rect> finishCallback) {
|
||||||
final boolean isLandscape = isLandscape();
|
final boolean isLandscape = isLandscape();
|
||||||
|
|||||||
@@ -86,8 +86,8 @@ interface ISplitScreen {
|
|||||||
/**
|
/**
|
||||||
* Starts a pair of intent and task in one transition.
|
* Starts a pair of intent and task in one transition.
|
||||||
*/
|
*/
|
||||||
oneway void startIntentAndTask(in PendingIntent pendingIntent, in Intent fillInIntent,
|
oneway void startIntentAndTask(in PendingIntent pendingIntent, in Bundle options1, int taskId,
|
||||||
in Bundle options1, int taskId, in Bundle options2, int sidePosition, float splitRatio,
|
in Bundle options2, int sidePosition, float splitRatio,
|
||||||
in RemoteTransition remoteTransition, in InstanceId instanceId) = 16;
|
in RemoteTransition remoteTransition, in InstanceId instanceId) = 16;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -108,9 +108,8 @@ interface ISplitScreen {
|
|||||||
* Starts a pair of intent and task using legacy transition system.
|
* Starts a pair of intent and task using legacy transition system.
|
||||||
*/
|
*/
|
||||||
oneway void startIntentAndTaskWithLegacyTransition(in PendingIntent pendingIntent,
|
oneway void startIntentAndTaskWithLegacyTransition(in PendingIntent pendingIntent,
|
||||||
in Intent fillInIntent, in Bundle options1, int taskId, in Bundle options2,
|
in Bundle options1, int taskId, in Bundle options2, int splitPosition, float splitRatio,
|
||||||
int splitPosition, float splitRatio, in RemoteAnimationAdapter adapter,
|
in RemoteAnimationAdapter adapter, in InstanceId instanceId) = 12;
|
||||||
in InstanceId instanceId) = 12;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts a pair of shortcut and task using legacy transition system.
|
* Starts a pair of shortcut and task using legacy transition system.
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package com.android.wm.shell.splitscreen;
|
|||||||
|
|
||||||
import static android.app.ActivityManager.START_SUCCESS;
|
import static android.app.ActivityManager.START_SUCCESS;
|
||||||
import static android.app.ActivityManager.START_TASK_TO_FRONT;
|
import static android.app.ActivityManager.START_TASK_TO_FRONT;
|
||||||
|
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
|
||||||
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
|
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
|
||||||
import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
|
import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
|
||||||
import static android.view.Display.DEFAULT_DISPLAY;
|
import static android.view.Display.DEFAULT_DISPLAY;
|
||||||
@@ -32,6 +33,8 @@ 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;
|
||||||
import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
|
import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
|
||||||
|
|
||||||
|
import android.annotation.NonNull;
|
||||||
|
import android.annotation.Nullable;
|
||||||
import android.app.ActivityManager;
|
import android.app.ActivityManager;
|
||||||
import android.app.ActivityOptions;
|
import android.app.ActivityOptions;
|
||||||
import android.app.ActivityTaskManager;
|
import android.app.ActivityTaskManager;
|
||||||
@@ -60,13 +63,12 @@ import android.window.WindowContainerTransaction;
|
|||||||
|
|
||||||
import androidx.annotation.BinderThread;
|
import androidx.annotation.BinderThread;
|
||||||
import androidx.annotation.IntDef;
|
import androidx.annotation.IntDef;
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.internal.logging.InstanceId;
|
import com.android.internal.logging.InstanceId;
|
||||||
import com.android.internal.protolog.common.ProtoLog;
|
import com.android.internal.protolog.common.ProtoLog;
|
||||||
import com.android.launcher3.icons.IconProvider;
|
import com.android.launcher3.icons.IconProvider;
|
||||||
|
import com.android.wm.shell.R;
|
||||||
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
|
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
|
||||||
import com.android.wm.shell.ShellTaskOrganizer;
|
import com.android.wm.shell.ShellTaskOrganizer;
|
||||||
import com.android.wm.shell.common.DisplayController;
|
import com.android.wm.shell.common.DisplayController;
|
||||||
@@ -166,8 +168,11 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
|||||||
private final IconProvider mIconProvider;
|
private final IconProvider mIconProvider;
|
||||||
private final Optional<RecentTasksController> mRecentTasksOptional;
|
private final Optional<RecentTasksController> mRecentTasksOptional;
|
||||||
private final SplitScreenShellCommandHandler mSplitScreenShellCommandHandler;
|
private final SplitScreenShellCommandHandler mSplitScreenShellCommandHandler;
|
||||||
|
private final String[] mMultiInstancesComponents;
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
StageCoordinator mStageCoordinator;
|
||||||
|
|
||||||
private StageCoordinator mStageCoordinator;
|
|
||||||
// Only used for the legacy recents animation from splitscreen to allow the tasks to be animated
|
// Only used for the legacy recents animation from splitscreen to allow the tasks to be animated
|
||||||
// outside the bounds of the roots by being reparented into a higher level fullscreen container
|
// outside the bounds of the roots by being reparented into a higher level fullscreen container
|
||||||
private SurfaceControl mGoingToRecentsTasksLayer;
|
private SurfaceControl mGoingToRecentsTasksLayer;
|
||||||
@@ -210,6 +215,51 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
|||||||
if (ActivityTaskManager.supportsSplitScreenMultiWindow(context)) {
|
if (ActivityTaskManager.supportsSplitScreenMultiWindow(context)) {
|
||||||
shellInit.addInitCallback(this::onInit, this);
|
shellInit.addInitCallback(this::onInit, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(255224696): Remove the config once having a way for client apps to opt-in
|
||||||
|
// multi-instances split.
|
||||||
|
mMultiInstancesComponents = mContext.getResources()
|
||||||
|
.getStringArray(R.array.config_componentsSupportMultiInstancesSplit);
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
SplitScreenController(Context context,
|
||||||
|
ShellInit shellInit,
|
||||||
|
ShellCommandHandler shellCommandHandler,
|
||||||
|
ShellController shellController,
|
||||||
|
ShellTaskOrganizer shellTaskOrganizer,
|
||||||
|
SyncTransactionQueue syncQueue,
|
||||||
|
RootTaskDisplayAreaOrganizer rootTDAOrganizer,
|
||||||
|
DisplayController displayController,
|
||||||
|
DisplayImeController displayImeController,
|
||||||
|
DisplayInsetsController displayInsetsController,
|
||||||
|
DragAndDropController dragAndDropController,
|
||||||
|
Transitions transitions,
|
||||||
|
TransactionPool transactionPool,
|
||||||
|
IconProvider iconProvider,
|
||||||
|
RecentTasksController recentTasks,
|
||||||
|
ShellExecutor mainExecutor,
|
||||||
|
StageCoordinator stageCoordinator) {
|
||||||
|
mShellCommandHandler = shellCommandHandler;
|
||||||
|
mShellController = shellController;
|
||||||
|
mTaskOrganizer = shellTaskOrganizer;
|
||||||
|
mSyncQueue = syncQueue;
|
||||||
|
mContext = context;
|
||||||
|
mRootTDAOrganizer = rootTDAOrganizer;
|
||||||
|
mMainExecutor = mainExecutor;
|
||||||
|
mDisplayController = displayController;
|
||||||
|
mDisplayImeController = displayImeController;
|
||||||
|
mDisplayInsetsController = displayInsetsController;
|
||||||
|
mDragAndDropController = dragAndDropController;
|
||||||
|
mTransitions = transitions;
|
||||||
|
mTransactionPool = transactionPool;
|
||||||
|
mIconProvider = iconProvider;
|
||||||
|
mRecentTasksOptional = Optional.of(recentTasks);
|
||||||
|
mStageCoordinator = stageCoordinator;
|
||||||
|
mSplitScreenShellCommandHandler = new SplitScreenShellCommandHandler(this);
|
||||||
|
shellInit.addInitCallback(this::onInit, this);
|
||||||
|
mMultiInstancesComponents = mContext.getResources()
|
||||||
|
.getStringArray(R.array.config_componentsSupportMultiInstancesSplit);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SplitScreen asSplitScreen() {
|
public SplitScreen asSplitScreen() {
|
||||||
@@ -471,72 +521,116 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
|||||||
startIntent(intent, fillInIntent, position, options);
|
startIntent(intent, fillInIntent, position, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent,
|
||||||
|
@Nullable Bundle options1, int taskId, @Nullable Bundle options2,
|
||||||
|
@SplitPosition int splitPosition, float splitRatio, RemoteAnimationAdapter adapter,
|
||||||
|
InstanceId instanceId) {
|
||||||
|
Intent fillInIntent = null;
|
||||||
|
if (launchSameComponentAdjacently(pendingIntent, splitPosition, taskId)
|
||||||
|
&& supportMultiInstancesSplit(pendingIntent.getIntent().getComponent())) {
|
||||||
|
fillInIntent = new Intent();
|
||||||
|
fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
|
||||||
|
}
|
||||||
|
mStageCoordinator.startIntentAndTaskWithLegacyTransition(pendingIntent, fillInIntent,
|
||||||
|
options1, taskId, options2, splitPosition, splitRatio, adapter, instanceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startIntentAndTask(PendingIntent pendingIntent, @Nullable Bundle options1,
|
||||||
|
int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition,
|
||||||
|
float splitRatio, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
|
||||||
|
Intent fillInIntent = null;
|
||||||
|
if (launchSameComponentAdjacently(pendingIntent, splitPosition, taskId)
|
||||||
|
&& supportMultiInstancesSplit(pendingIntent.getIntent().getComponent())) {
|
||||||
|
fillInIntent = new Intent();
|
||||||
|
fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
|
||||||
|
}
|
||||||
|
mStageCoordinator.startIntentAndTask(pendingIntent, fillInIntent, options1, taskId,
|
||||||
|
options2, splitPosition, splitRatio, remoteTransition, instanceId);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void startIntent(PendingIntent intent, @Nullable Intent fillInIntent,
|
public void startIntent(PendingIntent intent, @Nullable Intent fillInIntent,
|
||||||
@SplitPosition int position, @Nullable Bundle options) {
|
@SplitPosition int position, @Nullable Bundle options) {
|
||||||
if (fillInIntent == null) {
|
// Flag this as a no-user-action launch to prevent sending user leaving event to the current
|
||||||
fillInIntent = new Intent();
|
// top activity since it's going to be put into another side of the split. This prevents the
|
||||||
}
|
// current top activity from going into pip mode due to user leaving event.
|
||||||
// Flag this as a no-user-action launch to prevent sending user leaving event to the
|
if (fillInIntent == null) fillInIntent = new Intent();
|
||||||
// current top activity since it's going to be put into another side of the split. This
|
|
||||||
// prevents the current top activity from going into pip mode due to user leaving event.
|
|
||||||
fillInIntent.addFlags(FLAG_ACTIVITY_NO_USER_ACTION);
|
fillInIntent.addFlags(FLAG_ACTIVITY_NO_USER_ACTION);
|
||||||
|
|
||||||
// Flag with MULTIPLE_TASK if this is launching the same activity into both sides of the
|
if (launchSameComponentAdjacently(intent, position, INVALID_TASK_ID)) {
|
||||||
// split and there is no reusable background task.
|
final ComponentName launching = intent.getIntent().getComponent();
|
||||||
if (shouldAddMultipleTaskFlag(intent.getIntent(), position)) {
|
if (supportMultiInstancesSplit(launching)) {
|
||||||
final ActivityManager.RecentTaskInfo taskInfo = mRecentTasksOptional.isPresent()
|
// To prevent accumulating large number of instances in the background, reuse task
|
||||||
? mRecentTasksOptional.get().findTaskInBackground(
|
// in the background with priority.
|
||||||
intent.getIntent().getComponent())
|
final ActivityManager.RecentTaskInfo taskInfo = mRecentTasksOptional
|
||||||
: null;
|
.map(recentTasks -> recentTasks.findTaskInBackground(launching))
|
||||||
if (taskInfo != null) {
|
.orElse(null);
|
||||||
startTask(taskInfo.taskId, position, options);
|
if (taskInfo != null) {
|
||||||
|
startTask(taskInfo.taskId, position, options);
|
||||||
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
|
||||||
|
"Start task in background");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flag with MULTIPLE_TASK if this is launching the same activity into both sides of
|
||||||
|
// the split and there is no reusable background task.
|
||||||
|
fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
|
||||||
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
|
||||||
|
} else if (isSplitScreenVisible()) {
|
||||||
|
mStageCoordinator.switchSplitPosition("startIntent");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
|
|
||||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ENABLE_SHELL_TRANSITIONS) {
|
|
||||||
mStageCoordinator.startIntentLegacy(intent, fillInIntent, position, options);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mStageCoordinator.startIntent(intent, fillInIntent, position, options);
|
mStageCoordinator.startIntent(intent, fillInIntent, position, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns {@code true} if it's launching the same component on both sides of the split. */
|
/** Returns {@code true} if it's launching the same component on both sides of the split. */
|
||||||
@VisibleForTesting
|
private boolean launchSameComponentAdjacently(@Nullable PendingIntent pendingIntent,
|
||||||
boolean shouldAddMultipleTaskFlag(@Nullable Intent startIntent, @SplitPosition int position) {
|
@SplitPosition int position, int taskId) {
|
||||||
if (startIntent == null) {
|
if (pendingIntent == null || pendingIntent.getIntent() == null) return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final ComponentName launchingActivity = startIntent.getComponent();
|
final ComponentName launchingActivity = pendingIntent.getIntent().getComponent();
|
||||||
if (launchingActivity == null) {
|
if (launchingActivity == null) return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isSplitScreenVisible()) {
|
if (taskId != INVALID_TASK_ID) {
|
||||||
// To prevent users from constantly dropping the same app to the same side resulting in
|
final ActivityManager.RunningTaskInfo taskInfo =
|
||||||
// a large number of instances in the background.
|
mTaskOrganizer.getRunningTaskInfo(taskId);
|
||||||
final ActivityManager.RunningTaskInfo targetTaskInfo = getTaskInfo(position);
|
if (taskInfo != null) {
|
||||||
final ComponentName targetActivity = targetTaskInfo != null
|
return Objects.equals(taskInfo.baseIntent.getComponent(), launchingActivity);
|
||||||
? targetTaskInfo.baseIntent.getComponent() : null;
|
|
||||||
if (Objects.equals(launchingActivity, targetActivity)) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
// Allow users to start a new instance the same to adjacent side.
|
|
||||||
final ActivityManager.RunningTaskInfo pairedTaskInfo =
|
|
||||||
getTaskInfo(SplitLayout.reversePosition(position));
|
|
||||||
final ComponentName pairedActivity = pairedTaskInfo != null
|
|
||||||
? pairedTaskInfo.baseIntent.getComponent() : null;
|
|
||||||
return Objects.equals(launchingActivity, pairedActivity);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final ActivityManager.RunningTaskInfo taskInfo = getFocusingTaskInfo();
|
if (!isSplitScreenVisible()) {
|
||||||
if (taskInfo != null && isValidToEnterSplitScreen(taskInfo)) {
|
// Split screen is not yet activated, check if the current top running task is valid to
|
||||||
return Objects.equals(taskInfo.baseIntent.getComponent(), launchingActivity);
|
// split together.
|
||||||
|
final ActivityManager.RunningTaskInfo taskInfo = getFocusingTaskInfo();
|
||||||
|
if (taskInfo != null && isValidToEnterSplitScreen(taskInfo)) {
|
||||||
|
return Objects.equals(taskInfo.baseIntent.getComponent(), launchingActivity);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare to the adjacent side of the split to determine if this is launching the same
|
||||||
|
// component adjacently.
|
||||||
|
final ActivityManager.RunningTaskInfo pairedTaskInfo =
|
||||||
|
getTaskInfo(SplitLayout.reversePosition(position));
|
||||||
|
final ComponentName pairedActivity = pairedTaskInfo != null
|
||||||
|
? pairedTaskInfo.baseIntent.getComponent() : null;
|
||||||
|
return Objects.equals(launchingActivity, pairedActivity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
/** Returns {@code true} if the component supports multi-instances split. */
|
||||||
|
boolean supportMultiInstancesSplit(@Nullable ComponentName launching) {
|
||||||
|
if (launching == null) return false;
|
||||||
|
|
||||||
|
final String componentName = launching.flattenToString();
|
||||||
|
for (int i = 0; i < mMultiInstancesComponents.length; i++) {
|
||||||
|
if (mMultiInstancesComponents[i].equals(componentName)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -839,14 +933,13 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent,
|
public void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent,
|
||||||
Intent fillInIntent, Bundle options1, int taskId, Bundle options2,
|
Bundle options1, int taskId, Bundle options2, int splitPosition, float splitRatio,
|
||||||
int splitPosition, float splitRatio, RemoteAnimationAdapter adapter,
|
RemoteAnimationAdapter adapter, InstanceId instanceId) {
|
||||||
InstanceId instanceId) {
|
|
||||||
executeRemoteCallWithTaskPermission(mController,
|
executeRemoteCallWithTaskPermission(mController,
|
||||||
"startIntentAndTaskWithLegacyTransition", (controller) ->
|
"startIntentAndTaskWithLegacyTransition", (controller) ->
|
||||||
controller.mStageCoordinator.startIntentAndTaskWithLegacyTransition(
|
controller.startIntentAndTaskWithLegacyTransition(pendingIntent,
|
||||||
pendingIntent, fillInIntent, options1, taskId, options2,
|
options1, taskId, options2, splitPosition, splitRatio, adapter,
|
||||||
splitPosition, splitRatio, adapter, instanceId));
|
instanceId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -872,14 +965,13 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void startIntentAndTask(PendingIntent pendingIntent, Intent fillInIntent,
|
public void startIntentAndTask(PendingIntent pendingIntent, @Nullable Bundle options1,
|
||||||
@Nullable Bundle options1, int taskId, @Nullable Bundle options2,
|
int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition,
|
||||||
@SplitPosition int splitPosition, float splitRatio,
|
float splitRatio, @Nullable RemoteTransition remoteTransition,
|
||||||
@Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
|
InstanceId instanceId) {
|
||||||
executeRemoteCallWithTaskPermission(mController, "startIntentAndTask",
|
executeRemoteCallWithTaskPermission(mController, "startIntentAndTask",
|
||||||
(controller) -> controller.mStageCoordinator.startIntentAndTask(pendingIntent,
|
(controller) -> controller.startIntentAndTask(pendingIntent, options1, taskId,
|
||||||
fillInIntent, options1, taskId, options2, splitPosition, splitRatio,
|
options2, splitPosition, splitRatio, remoteTransition, instanceId));
|
||||||
remoteTransition, instanceId));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
|
|||||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
|
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
|
||||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
|
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
|
||||||
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
|
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
|
||||||
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
|
|
||||||
import static android.content.res.Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
|
import static android.content.res.Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
|
||||||
import static android.view.Display.DEFAULT_DISPLAY;
|
import static android.view.Display.DEFAULT_DISPLAY;
|
||||||
import static android.view.RemoteAnimationTarget.MODE_OPENING;
|
import static android.view.RemoteAnimationTarget.MODE_OPENING;
|
||||||
@@ -428,6 +427,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
|||||||
/** 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) {
|
||||||
|
if (!ENABLE_SHELL_TRANSITIONS) {
|
||||||
|
startIntentLegacy(intent, fillInIntent, position, options);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||||
final WindowContainerTransaction evictWct = new WindowContainerTransaction();
|
final WindowContainerTransaction evictWct = new WindowContainerTransaction();
|
||||||
prepareEvictChildTasks(position, evictWct);
|
prepareEvictChildTasks(position, evictWct);
|
||||||
@@ -441,13 +445,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
|||||||
prepareEnterSplitScreen(wct, null /* taskInfo */, position);
|
prepareEnterSplitScreen(wct, null /* taskInfo */, position);
|
||||||
|
|
||||||
mSplitTransitions.startEnterTransition(transitType, wct, null, this,
|
mSplitTransitions.startEnterTransition(transitType, wct, null, this,
|
||||||
aborted -> {
|
null /* consumedCallback */,
|
||||||
// Switch the split position if launching as MULTIPLE_TASK failed.
|
|
||||||
if (aborted && (fillInIntent.getFlags() & FLAG_ACTIVITY_MULTIPLE_TASK) != 0) {
|
|
||||||
setSideStagePositionAnimated(
|
|
||||||
SplitLayout.reversePosition(mSideStagePosition));
|
|
||||||
}
|
|
||||||
} /* consumedCallback */,
|
|
||||||
(finishWct, finishT) -> {
|
(finishWct, finishT) -> {
|
||||||
if (!evictWct.isEmpty()) {
|
if (!evictWct.isEmpty()) {
|
||||||
finishWct.merge(evictWct, true);
|
finishWct.merge(evictWct, true);
|
||||||
@@ -457,7 +455,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
|||||||
|
|
||||||
/** Launches an activity into split by legacy transition. */
|
/** Launches an activity into split by legacy transition. */
|
||||||
void startIntentLegacy(PendingIntent intent, Intent fillInIntent,
|
void startIntentLegacy(PendingIntent intent, Intent fillInIntent,
|
||||||
@SplitPosition int position, @androidx.annotation.Nullable Bundle options) {
|
@SplitPosition int position, @Nullable Bundle options) {
|
||||||
final WindowContainerTransaction evictWct = new WindowContainerTransaction();
|
final WindowContainerTransaction evictWct = new WindowContainerTransaction();
|
||||||
prepareEvictChildTasks(position, evictWct);
|
prepareEvictChildTasks(position, evictWct);
|
||||||
|
|
||||||
@@ -473,12 +471,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
|||||||
exitSplitScreen(mMainStage.getChildCount() == 0
|
exitSplitScreen(mMainStage.getChildCount() == 0
|
||||||
? mSideStage : mMainStage, EXIT_REASON_UNKNOWN));
|
? mSideStage : mMainStage, EXIT_REASON_UNKNOWN));
|
||||||
mSplitUnsupportedToast.show();
|
mSplitUnsupportedToast.show();
|
||||||
} else {
|
|
||||||
// Switch the split position if launching as MULTIPLE_TASK failed.
|
|
||||||
if ((fillInIntent.getFlags() & FLAG_ACTIVITY_MULTIPLE_TASK) != 0) {
|
|
||||||
setSideStagePosition(SplitLayout.reversePosition(
|
|
||||||
getSideStagePosition()), null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do nothing when the animation was cancelled.
|
// Do nothing when the animation was cancelled.
|
||||||
@@ -771,9 +763,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
|||||||
mSideStage.evictInvisibleChildren(wct);
|
mSideStage.evictInvisibleChildren(wct);
|
||||||
}
|
}
|
||||||
|
|
||||||
Bundle resolveStartStage(@StageType int stage,
|
Bundle resolveStartStage(@StageType int stage, @SplitPosition int position,
|
||||||
@SplitPosition int position, @androidx.annotation.Nullable Bundle options,
|
@Nullable Bundle options, @Nullable WindowContainerTransaction wct) {
|
||||||
@androidx.annotation.Nullable WindowContainerTransaction wct) {
|
|
||||||
switch (stage) {
|
switch (stage) {
|
||||||
case STAGE_TYPE_UNDEFINED: {
|
case STAGE_TYPE_UNDEFINED: {
|
||||||
if (position != SPLIT_POSITION_UNDEFINED) {
|
if (position != SPLIT_POSITION_UNDEFINED) {
|
||||||
@@ -844,9 +835,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
|||||||
: mMainStage.getTopVisibleChildTaskId();
|
: mMainStage.getTopVisibleChildTaskId();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setSideStagePositionAnimated(@SplitPosition int sideStagePosition) {
|
void switchSplitPosition(String reason) {
|
||||||
if (mSideStagePosition == sideStagePosition) return;
|
final SurfaceControl.Transaction t = mTransactionPool.acquire();
|
||||||
SurfaceControl.Transaction t = mTransactionPool.acquire();
|
|
||||||
mTempRect1.setEmpty();
|
mTempRect1.setEmpty();
|
||||||
final StageTaskListener topLeftStage =
|
final StageTaskListener topLeftStage =
|
||||||
mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage;
|
mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage;
|
||||||
@@ -886,6 +876,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
|||||||
va.start();
|
va.start();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Switch split position: %s", reason);
|
||||||
|
mLogger.logSwap(getMainStagePosition(), mMainStage.getTopChildTaskUid(),
|
||||||
|
getSideStagePosition(), mSideStage.getTopChildTaskUid(),
|
||||||
|
mSplitLayout.isLandscape());
|
||||||
}
|
}
|
||||||
|
|
||||||
void setSideStagePosition(@SplitPosition int sideStagePosition,
|
void setSideStagePosition(@SplitPosition int sideStagePosition,
|
||||||
@@ -1617,10 +1612,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDoubleTappedDivider() {
|
public void onDoubleTappedDivider() {
|
||||||
setSideStagePositionAnimated(SplitLayout.reversePosition(mSideStagePosition));
|
switchSplitPosition("double tap");
|
||||||
mLogger.logSwap(getMainStagePosition(), mMainStage.getTopChildTaskUid(),
|
|
||||||
getSideStagePosition(), mSideStage.getTopChildTaskUid(),
|
|
||||||
mSplitLayout.isLandscape());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -16,18 +16,24 @@
|
|||||||
|
|
||||||
package com.android.wm.shell.splitscreen;
|
package com.android.wm.shell.splitscreen;
|
||||||
|
|
||||||
|
import static android.app.PendingIntent.FLAG_IMMUTABLE;
|
||||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
|
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
|
||||||
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
|
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
|
||||||
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
|
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
|
||||||
|
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
|
||||||
|
import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
|
||||||
|
|
||||||
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 org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
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;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.ArgumentMatchers.isNull;
|
||||||
|
import static org.mockito.Mockito.doNothing;
|
||||||
import static org.mockito.Mockito.doReturn;
|
import static org.mockito.Mockito.doReturn;
|
||||||
import static org.mockito.Mockito.spy;
|
import static org.mockito.Mockito.spy;
|
||||||
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.times;
|
||||||
@@ -35,6 +41,8 @@ import static org.mockito.Mockito.verify;
|
|||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import android.app.ActivityManager;
|
import android.app.ActivityManager;
|
||||||
|
import android.app.ActivityTaskManager;
|
||||||
|
import android.app.PendingIntent;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.ActivityInfo;
|
import android.content.pm.ActivityInfo;
|
||||||
@@ -65,11 +73,11 @@ import com.android.wm.shell.transition.Transitions;
|
|||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.Captor;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link SplitScreenController}
|
* Tests for {@link SplitScreenController}
|
||||||
*/
|
*/
|
||||||
@@ -91,18 +99,21 @@ public class SplitScreenControllerTests extends ShellTestCase {
|
|||||||
@Mock Transitions mTransitions;
|
@Mock Transitions mTransitions;
|
||||||
@Mock TransactionPool mTransactionPool;
|
@Mock TransactionPool mTransactionPool;
|
||||||
@Mock IconProvider mIconProvider;
|
@Mock IconProvider mIconProvider;
|
||||||
@Mock Optional<RecentTasksController> mRecentTasks;
|
@Mock StageCoordinator mStageCoordinator;
|
||||||
|
@Mock RecentTasksController mRecentTasks;
|
||||||
|
@Captor ArgumentCaptor<Intent> mIntentCaptor;
|
||||||
|
|
||||||
private SplitScreenController mSplitScreenController;
|
private SplitScreenController mSplitScreenController;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setup() {
|
public void setup() {
|
||||||
|
assumeTrue(ActivityTaskManager.supportsSplitScreenMultiWindow(mContext));
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
mSplitScreenController = spy(new SplitScreenController(mContext, mShellInit,
|
mSplitScreenController = spy(new SplitScreenController(mContext, mShellInit,
|
||||||
mShellCommandHandler, mShellController, mTaskOrganizer, mSyncQueue,
|
mShellCommandHandler, mShellController, mTaskOrganizer, mSyncQueue,
|
||||||
mRootTDAOrganizer, mDisplayController, mDisplayImeController,
|
mRootTDAOrganizer, mDisplayController, mDisplayImeController,
|
||||||
mDisplayInsetsController, mDragAndDropController, mTransitions, mTransactionPool,
|
mDisplayInsetsController, mDragAndDropController, mTransitions, mTransactionPool,
|
||||||
mIconProvider, mRecentTasks, mMainExecutor));
|
mIconProvider, mRecentTasks, mMainExecutor, mStageCoordinator));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -148,58 +159,100 @@ public class SplitScreenControllerTests extends ShellTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testShouldAddMultipleTaskFlag_notInSplitScreen() {
|
public void testStartIntent_appendsNoUserActionFlag() {
|
||||||
doReturn(false).when(mSplitScreenController).isSplitScreenVisible();
|
|
||||||
doReturn(true).when(mSplitScreenController).isValidToEnterSplitScreen(any());
|
|
||||||
|
|
||||||
// Verify launching the same activity returns true.
|
|
||||||
Intent startIntent = createStartIntent("startActivity");
|
Intent startIntent = createStartIntent("startActivity");
|
||||||
ActivityManager.RunningTaskInfo focusTaskInfo =
|
PendingIntent pendingIntent =
|
||||||
createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, startIntent);
|
PendingIntent.getActivity(mContext, 0, startIntent, FLAG_IMMUTABLE);
|
||||||
doReturn(focusTaskInfo).when(mSplitScreenController).getFocusingTaskInfo();
|
|
||||||
assertTrue(mSplitScreenController.shouldAddMultipleTaskFlag(
|
|
||||||
startIntent, SPLIT_POSITION_TOP_OR_LEFT));
|
|
||||||
|
|
||||||
// Verify launching different activity returns false.
|
mSplitScreenController.startIntent(pendingIntent, null, SPLIT_POSITION_TOP_OR_LEFT, null);
|
||||||
Intent diffIntent = createStartIntent("diffActivity");
|
|
||||||
focusTaskInfo =
|
verify(mStageCoordinator).startIntent(eq(pendingIntent), mIntentCaptor.capture(),
|
||||||
createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, diffIntent);
|
eq(SPLIT_POSITION_TOP_OR_LEFT), isNull());
|
||||||
doReturn(focusTaskInfo).when(mSplitScreenController).getFocusingTaskInfo();
|
assertEquals(FLAG_ACTIVITY_NO_USER_ACTION,
|
||||||
assertFalse(mSplitScreenController.shouldAddMultipleTaskFlag(
|
mIntentCaptor.getValue().getFlags() & FLAG_ACTIVITY_NO_USER_ACTION);
|
||||||
startIntent, SPLIT_POSITION_TOP_OR_LEFT));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testShouldAddMultipleTaskFlag_inSplitScreen() {
|
public void startIntent_multiInstancesSupported_appendsMultipleTaskFag() {
|
||||||
doReturn(true).when(mSplitScreenController).isSplitScreenVisible();
|
doReturn(true).when(mSplitScreenController).supportMultiInstancesSplit(any());
|
||||||
Intent startIntent = createStartIntent("startActivity");
|
Intent startIntent = createStartIntent("startActivity");
|
||||||
|
PendingIntent pendingIntent =
|
||||||
|
PendingIntent.getActivity(mContext, 0, startIntent, FLAG_IMMUTABLE);
|
||||||
|
// Put the same component into focus task
|
||||||
|
ActivityManager.RunningTaskInfo focusTaskInfo =
|
||||||
|
createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, startIntent);
|
||||||
|
doReturn(focusTaskInfo).when(mStageCoordinator).getFocusingTaskInfo();
|
||||||
|
doReturn(true).when(mStageCoordinator).isValidToEnterSplitScreen(any());
|
||||||
|
|
||||||
|
mSplitScreenController.startIntent(pendingIntent, null, SPLIT_POSITION_TOP_OR_LEFT, null);
|
||||||
|
|
||||||
|
verify(mStageCoordinator).startIntent(eq(pendingIntent), mIntentCaptor.capture(),
|
||||||
|
eq(SPLIT_POSITION_TOP_OR_LEFT), isNull());
|
||||||
|
assertEquals(FLAG_ACTIVITY_MULTIPLE_TASK,
|
||||||
|
mIntentCaptor.getValue().getFlags() & FLAG_ACTIVITY_MULTIPLE_TASK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void startIntent_multiInstancesSupported_startTaskInBackgroundBeforeSplitActivated() {
|
||||||
|
doReturn(true).when(mSplitScreenController).supportMultiInstancesSplit(any());
|
||||||
|
doNothing().when(mSplitScreenController).startTask(anyInt(), anyInt(), any());
|
||||||
|
Intent startIntent = createStartIntent("startActivity");
|
||||||
|
PendingIntent pendingIntent =
|
||||||
|
PendingIntent.getActivity(mContext, 0, startIntent, FLAG_IMMUTABLE);
|
||||||
|
// Put the same component into focus task
|
||||||
|
ActivityManager.RunningTaskInfo focusTaskInfo =
|
||||||
|
createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, startIntent);
|
||||||
|
doReturn(focusTaskInfo).when(mStageCoordinator).getFocusingTaskInfo();
|
||||||
|
doReturn(true).when(mStageCoordinator).isValidToEnterSplitScreen(any());
|
||||||
|
// Put the same component into a task in the background
|
||||||
|
ActivityManager.RecentTaskInfo sameTaskInfo = new ActivityManager.RecentTaskInfo();
|
||||||
|
doReturn(sameTaskInfo).when(mRecentTasks).findTaskInBackground(any());
|
||||||
|
|
||||||
|
mSplitScreenController.startIntent(pendingIntent, null, SPLIT_POSITION_TOP_OR_LEFT, null);
|
||||||
|
|
||||||
|
verify(mSplitScreenController).startTask(anyInt(), eq(SPLIT_POSITION_TOP_OR_LEFT),
|
||||||
|
isNull());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void startIntent_multiInstancesSupported_startTaskInBackgroundAfterSplitActivated() {
|
||||||
|
doReturn(true).when(mSplitScreenController).supportMultiInstancesSplit(any());
|
||||||
|
doNothing().when(mSplitScreenController).startTask(anyInt(), anyInt(), any());
|
||||||
|
Intent startIntent = createStartIntent("startActivity");
|
||||||
|
PendingIntent pendingIntent =
|
||||||
|
PendingIntent.getActivity(mContext, 0, startIntent, FLAG_IMMUTABLE);
|
||||||
|
// Put the same component into another side of the split
|
||||||
|
doReturn(true).when(mSplitScreenController).isSplitScreenVisible();
|
||||||
ActivityManager.RunningTaskInfo sameTaskInfo =
|
ActivityManager.RunningTaskInfo sameTaskInfo =
|
||||||
createTaskInfo(WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, startIntent);
|
createTaskInfo(WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, startIntent);
|
||||||
Intent diffIntent = createStartIntent("diffActivity");
|
doReturn(sameTaskInfo).when(mSplitScreenController).getTaskInfo(
|
||||||
ActivityManager.RunningTaskInfo differentTaskInfo =
|
SPLIT_POSITION_BOTTOM_OR_RIGHT);
|
||||||
createTaskInfo(WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, diffIntent);
|
// Put the same component into a task in the background
|
||||||
|
doReturn(new ActivityManager.RecentTaskInfo()).when(mRecentTasks)
|
||||||
|
.findTaskInBackground(any());
|
||||||
|
|
||||||
// Verify launching the same activity return false.
|
mSplitScreenController.startIntent(pendingIntent, null, SPLIT_POSITION_TOP_OR_LEFT, null);
|
||||||
doReturn(sameTaskInfo).when(mSplitScreenController)
|
|
||||||
.getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT);
|
|
||||||
assertFalse(mSplitScreenController.shouldAddMultipleTaskFlag(
|
|
||||||
startIntent, SPLIT_POSITION_TOP_OR_LEFT));
|
|
||||||
|
|
||||||
// Verify launching the same activity as adjacent returns true.
|
verify(mSplitScreenController).startTask(anyInt(), eq(SPLIT_POSITION_TOP_OR_LEFT),
|
||||||
doReturn(differentTaskInfo).when(mSplitScreenController)
|
isNull());
|
||||||
.getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT);
|
}
|
||||||
doReturn(sameTaskInfo).when(mSplitScreenController)
|
|
||||||
.getTaskInfo(SPLIT_POSITION_BOTTOM_OR_RIGHT);
|
|
||||||
assertTrue(mSplitScreenController.shouldAddMultipleTaskFlag(
|
|
||||||
startIntent, SPLIT_POSITION_TOP_OR_LEFT));
|
|
||||||
|
|
||||||
// Verify launching different activity from adjacent returns false.
|
@Test
|
||||||
doReturn(differentTaskInfo).when(mSplitScreenController)
|
public void startIntent_multiInstancesNotSupported_switchesPositionAfterSplitActivated() {
|
||||||
.getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT);
|
doReturn(false).when(mSplitScreenController).supportMultiInstancesSplit(any());
|
||||||
doReturn(differentTaskInfo).when(mSplitScreenController)
|
Intent startIntent = createStartIntent("startActivity");
|
||||||
.getTaskInfo(SPLIT_POSITION_BOTTOM_OR_RIGHT);
|
PendingIntent pendingIntent =
|
||||||
assertFalse(mSplitScreenController.shouldAddMultipleTaskFlag(
|
PendingIntent.getActivity(mContext, 0, startIntent, FLAG_IMMUTABLE);
|
||||||
startIntent, SPLIT_POSITION_TOP_OR_LEFT));
|
// Put the same component into another side of the split
|
||||||
|
doReturn(true).when(mSplitScreenController).isSplitScreenVisible();
|
||||||
|
ActivityManager.RunningTaskInfo sameTaskInfo =
|
||||||
|
createTaskInfo(WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, startIntent);
|
||||||
|
doReturn(sameTaskInfo).when(mSplitScreenController).getTaskInfo(
|
||||||
|
SPLIT_POSITION_BOTTOM_OR_RIGHT);
|
||||||
|
|
||||||
|
mSplitScreenController.startIntent(pendingIntent, null, SPLIT_POSITION_TOP_OR_LEFT, null);
|
||||||
|
|
||||||
|
verify(mStageCoordinator).switchSplitPosition(anyString());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Intent createStartIntent(String activityName) {
|
private Intent createStartIntent(String activityName) {
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import static android.app.ActivityManager.START_FLAG_DEBUG;
|
|||||||
import static android.app.ActivityManager.START_FLAG_NATIVE_DEBUGGING;
|
import static android.app.ActivityManager.START_FLAG_NATIVE_DEBUGGING;
|
||||||
import static android.app.ActivityManager.START_FLAG_TRACK_ALLOCATION;
|
import static android.app.ActivityManager.START_FLAG_TRACK_ALLOCATION;
|
||||||
import static android.app.ActivityManager.START_TASK_TO_FRONT;
|
import static android.app.ActivityManager.START_TASK_TO_FRONT;
|
||||||
|
import static android.app.ActivityOptions.ANIM_REMOTE_ANIMATION;
|
||||||
import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY;
|
import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY;
|
||||||
import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
|
import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN;
|
||||||
import static android.app.WaitResult.INVALID_DELAY;
|
import static android.app.WaitResult.INVALID_DELAY;
|
||||||
@@ -2592,6 +2593,10 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
|
|||||||
// Apply options to prevent pendingOptions be taken when scheduling
|
// Apply options to prevent pendingOptions be taken when scheduling
|
||||||
// activity lifecycle transaction to make sure the override pending app
|
// activity lifecycle transaction to make sure the override pending app
|
||||||
// transition will be applied immediately.
|
// transition will be applied immediately.
|
||||||
|
if (activityOptions.getAnimationType() == ANIM_REMOTE_ANIMATION) {
|
||||||
|
targetActivity.mPendingRemoteAnimation =
|
||||||
|
activityOptions.getRemoteAnimationAdapter();
|
||||||
|
}
|
||||||
targetActivity.applyOptionsAnimation();
|
targetActivity.applyOptionsAnimation();
|
||||||
if (activityOptions != null && activityOptions.getLaunchCookie() != null) {
|
if (activityOptions != null && activityOptions.getLaunchCookie() != null) {
|
||||||
targetActivity.mLaunchCookie = activityOptions.getLaunchCookie();
|
targetActivity.mLaunchCookie = activityOptions.getLaunchCookie();
|
||||||
|
|||||||
Reference in New Issue
Block a user