Merge "Support to launch multi-instance when long press to split on an app icon" into tm-qpr-dev
This commit is contained in:
@@ -45,12 +45,10 @@ import android.app.WindowConfiguration;
|
|||||||
import android.content.ActivityNotFoundException;
|
import android.content.ActivityNotFoundException;
|
||||||
import android.content.ClipData;
|
import android.content.ClipData;
|
||||||
import android.content.ClipDescription;
|
import android.content.ClipDescription;
|
||||||
import android.content.ComponentName;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.ActivityInfo;
|
import android.content.pm.ActivityInfo;
|
||||||
import android.content.pm.LauncherApps;
|
import android.content.pm.LauncherApps;
|
||||||
import android.content.pm.ResolveInfo;
|
|
||||||
import android.graphics.Insets;
|
import android.graphics.Insets;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@@ -64,19 +62,15 @@ import androidx.annotation.Nullable;
|
|||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
import com.android.internal.logging.InstanceId;
|
import com.android.internal.logging.InstanceId;
|
||||||
import com.android.internal.protolog.common.ProtoLog;
|
|
||||||
import com.android.wm.shell.R;
|
import com.android.wm.shell.R;
|
||||||
import com.android.wm.shell.common.DisplayLayout;
|
import com.android.wm.shell.common.DisplayLayout;
|
||||||
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.protolog.ShellProtoLogGroup;
|
|
||||||
import com.android.wm.shell.splitscreen.SplitScreenController;
|
import com.android.wm.shell.splitscreen.SplitScreenController;
|
||||||
|
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The policy for handling drag and drop operations to shell.
|
* The policy for handling drag and drop operations to shell.
|
||||||
@@ -269,46 +263,10 @@ public class DragAndDropPolicy {
|
|||||||
mStarter.startShortcut(packageName, id, position, opts, user);
|
mStarter.startShortcut(packageName, id, position, opts, user);
|
||||||
} else {
|
} else {
|
||||||
final PendingIntent launchIntent = intent.getParcelableExtra(EXTRA_PENDING_INTENT);
|
final PendingIntent launchIntent = intent.getParcelableExtra(EXTRA_PENDING_INTENT);
|
||||||
mStarter.startIntent(launchIntent, getStartIntentFillInIntent(launchIntent, position),
|
mStarter.startIntent(launchIntent, null /* fillIntent */, position, opts);
|
||||||
position, opts);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the fill-in intent to use when starting an app from a drop.
|
|
||||||
*/
|
|
||||||
@VisibleForTesting
|
|
||||||
Intent getStartIntentFillInIntent(PendingIntent launchIntent, @SplitPosition int position) {
|
|
||||||
// Get the drag app
|
|
||||||
final List<ResolveInfo> infos = launchIntent.queryIntentComponents(0 /* flags */);
|
|
||||||
final ComponentName dragIntentActivity = !infos.isEmpty()
|
|
||||||
? infos.get(0).activityInfo.getComponentName()
|
|
||||||
: null;
|
|
||||||
|
|
||||||
// Get the current app (either fullscreen or the remaining app post-drop if in splitscreen)
|
|
||||||
final boolean inSplitScreen = mSplitScreen != null
|
|
||||||
&& mSplitScreen.isSplitScreenVisible();
|
|
||||||
final ComponentName currentActivity;
|
|
||||||
if (!inSplitScreen) {
|
|
||||||
currentActivity = mSession.runningTaskInfo != null
|
|
||||||
? mSession.runningTaskInfo.baseIntent.getComponent()
|
|
||||||
: null;
|
|
||||||
} else {
|
|
||||||
final ActivityManager.RunningTaskInfo nonReplacedTaskInfo =
|
|
||||||
mSplitScreen.getTaskInfo(SplitLayout.reversePosition(position));
|
|
||||||
currentActivity = nonReplacedTaskInfo.baseIntent.getComponent();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Objects.equals(currentActivity, dragIntentActivity)) {
|
|
||||||
// Only apply MULTIPLE_TASK if we are dragging the same activity
|
|
||||||
final Intent fillInIntent = new Intent();
|
|
||||||
fillInIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
|
|
||||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Adding MULTIPLE_TASK");
|
|
||||||
return fillInIntent;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Per-drag session data.
|
* Per-drag session data.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ 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.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
|
||||||
|
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
|
||||||
|
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;
|
||||||
import static android.view.RemoteAnimationTarget.MODE_OPENING;
|
import static android.view.RemoteAnimationTarget.MODE_OPENING;
|
||||||
@@ -58,7 +61,9 @@ import androidx.annotation.IntDef;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
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.launcher3.icons.IconProvider;
|
import com.android.launcher3.icons.IconProvider;
|
||||||
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
|
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
|
||||||
import com.android.wm.shell.ShellTaskOrganizer;
|
import com.android.wm.shell.ShellTaskOrganizer;
|
||||||
@@ -74,6 +79,7 @@ import com.android.wm.shell.common.annotations.ExternalThread;
|
|||||||
import com.android.wm.shell.common.split.SplitLayout;
|
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.draganddrop.DragAndDropPolicy;
|
import com.android.wm.shell.draganddrop.DragAndDropPolicy;
|
||||||
|
import com.android.wm.shell.protolog.ShellProtoLogGroup;
|
||||||
import com.android.wm.shell.recents.RecentTasksController;
|
import com.android.wm.shell.recents.RecentTasksController;
|
||||||
import com.android.wm.shell.splitscreen.SplitScreen.StageType;
|
import com.android.wm.shell.splitscreen.SplitScreen.StageType;
|
||||||
import com.android.wm.shell.transition.LegacyTransitions;
|
import com.android.wm.shell.transition.LegacyTransitions;
|
||||||
@@ -95,7 +101,7 @@ import java.util.concurrent.Executor;
|
|||||||
*/
|
*/
|
||||||
// TODO(b/198577848): Implement split screen flicker test to consolidate CUJ of split screen.
|
// TODO(b/198577848): Implement split screen flicker test to consolidate CUJ of split screen.
|
||||||
public class SplitScreenController implements DragAndDropPolicy.Starter,
|
public class SplitScreenController implements DragAndDropPolicy.Starter,
|
||||||
RemoteCallable<SplitScreenController> {
|
RemoteCallable<SplitScreenController>, ShellTaskOrganizer.FocusListener {
|
||||||
private static final String TAG = SplitScreenController.class.getSimpleName();
|
private static final String TAG = SplitScreenController.class.getSimpleName();
|
||||||
|
|
||||||
static final int EXIT_REASON_UNKNOWN = 0;
|
static final int EXIT_REASON_UNKNOWN = 0;
|
||||||
@@ -143,6 +149,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
|||||||
// 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 mSplitTasksContainerLayer;
|
private SurfaceControl mSplitTasksContainerLayer;
|
||||||
|
|
||||||
|
private ActivityManager.RunningTaskInfo mFocusingTaskInfo;
|
||||||
|
|
||||||
public SplitScreenController(ShellTaskOrganizer shellTaskOrganizer,
|
public SplitScreenController(ShellTaskOrganizer shellTaskOrganizer,
|
||||||
SyncTransactionQueue syncQueue, Context context,
|
SyncTransactionQueue syncQueue, Context context,
|
||||||
RootTaskDisplayAreaOrganizer rootTDAOrganizer,
|
RootTaskDisplayAreaOrganizer rootTDAOrganizer,
|
||||||
@@ -164,6 +172,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
|||||||
mLogger = new SplitscreenEventLogger();
|
mLogger = new SplitscreenEventLogger();
|
||||||
mIconProvider = iconProvider;
|
mIconProvider = iconProvider;
|
||||||
mRecentTasksOptional = recentTasks;
|
mRecentTasksOptional = recentTasks;
|
||||||
|
mTaskOrganizer.addFocusListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SplitScreen asSplitScreen() {
|
public SplitScreen asSplitScreen() {
|
||||||
@@ -180,6 +189,11 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
|||||||
return mMainExecutor;
|
return mMainExecutor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFocusTaskChanged(ActivityManager.RunningTaskInfo taskInfo) {
|
||||||
|
mFocusingTaskInfo = taskInfo;
|
||||||
|
}
|
||||||
|
|
||||||
public void onOrganizerRegistered() {
|
public void onOrganizerRegistered() {
|
||||||
if (mStageCoordinator == null) {
|
if (mStageCoordinator == null) {
|
||||||
// TODO: Multi-display
|
// TODO: Multi-display
|
||||||
@@ -341,14 +355,21 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
|||||||
options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options,
|
options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options,
|
||||||
null /* wct */);
|
null /* wct */);
|
||||||
|
|
||||||
// Flag this as a no-user-action launch to prevent sending user leaving event to the
|
|
||||||
// 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.
|
|
||||||
if (fillInIntent == null) {
|
if (fillInIntent == null) {
|
||||||
fillInIntent = new Intent();
|
fillInIntent = new Intent();
|
||||||
}
|
}
|
||||||
|
// Flag this as a no-user-action launch to prevent sending user leaving event to the
|
||||||
|
// 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
|
||||||
|
// split.
|
||||||
|
if (isLaunchingAdjacently(intent.getIntent(), position)) {
|
||||||
|
fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
|
||||||
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
|
||||||
|
}
|
||||||
|
|
||||||
intent.send(mContext, 0, fillInIntent, null /* onFinished */, null /* handler */,
|
intent.send(mContext, 0, fillInIntent, null /* onFinished */, null /* handler */,
|
||||||
null /* requiredPermission */, options);
|
null /* requiredPermission */, options);
|
||||||
} catch (PendingIntent.CanceledException e) {
|
} catch (PendingIntent.CanceledException e) {
|
||||||
@@ -358,6 +379,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
|||||||
|
|
||||||
private void startIntentLegacy(PendingIntent intent, @Nullable Intent fillInIntent,
|
private void startIntentLegacy(PendingIntent intent, @Nullable Intent fillInIntent,
|
||||||
@SplitPosition int position, @Nullable Bundle options) {
|
@SplitPosition int position, @Nullable Bundle options) {
|
||||||
|
boolean startSameActivityAdjacently = isLaunchingAdjacently(intent.getIntent(), position);
|
||||||
|
|
||||||
final WindowContainerTransaction evictWct = new WindowContainerTransaction();
|
final WindowContainerTransaction evictWct = new WindowContainerTransaction();
|
||||||
mStageCoordinator.prepareEvictChildTasks(position, evictWct);
|
mStageCoordinator.prepareEvictChildTasks(position, evictWct);
|
||||||
|
|
||||||
@@ -368,14 +391,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
|||||||
IRemoteAnimationFinishedCallback finishedCallback,
|
IRemoteAnimationFinishedCallback finishedCallback,
|
||||||
SurfaceControl.Transaction t) {
|
SurfaceControl.Transaction t) {
|
||||||
if (apps == null || apps.length == 0) {
|
if (apps == null || apps.length == 0) {
|
||||||
final ActivityManager.RunningTaskInfo pairedTaskInfo =
|
if (startSameActivityAdjacently) {
|
||||||
getTaskInfo(SplitLayout.reversePosition(position));
|
|
||||||
final ComponentName pairedActivity = pairedTaskInfo != null
|
|
||||||
? pairedTaskInfo.baseIntent.getComponent() : null;
|
|
||||||
final ComponentName intentActivity = intent.getIntent() != null
|
|
||||||
? intent.getIntent().getComponent() : null;
|
|
||||||
|
|
||||||
if (Objects.equals(pairedActivity, intentActivity)) {
|
|
||||||
// Switch split position if dragging the same activity to another side.
|
// Switch split position if dragging the same activity to another side.
|
||||||
setSideStagePosition(SplitLayout.reversePosition(
|
setSideStagePosition(SplitLayout.reversePosition(
|
||||||
mStageCoordinator.getSideStagePosition()));
|
mStageCoordinator.getSideStagePosition()));
|
||||||
@@ -417,11 +433,47 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
|||||||
fillInIntent = new Intent();
|
fillInIntent = new Intent();
|
||||||
}
|
}
|
||||||
fillInIntent.addFlags(FLAG_ACTIVITY_NO_USER_ACTION);
|
fillInIntent.addFlags(FLAG_ACTIVITY_NO_USER_ACTION);
|
||||||
|
if (startSameActivityAdjacently) {
|
||||||
|
fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
|
||||||
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
|
||||||
|
}
|
||||||
|
|
||||||
wct.sendPendingIntent(intent, fillInIntent, options);
|
wct.sendPendingIntent(intent, fillInIntent, options);
|
||||||
mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct);
|
mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns {@code true} if it's launching the same component on both sides of the split. */
|
||||||
|
@VisibleForTesting
|
||||||
|
boolean isLaunchingAdjacently(@Nullable Intent startIntent,
|
||||||
|
@SplitPosition int position) {
|
||||||
|
if (startIntent == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final ComponentName launchingActivity = startIntent.getComponent();
|
||||||
|
if (launchingActivity == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSplitScreenVisible()) {
|
||||||
|
final ActivityManager.RunningTaskInfo pairedTaskInfo =
|
||||||
|
getTaskInfo(SplitLayout.reversePosition(position));
|
||||||
|
final ComponentName pairedActivity = pairedTaskInfo != null
|
||||||
|
? pairedTaskInfo.baseIntent.getComponent() : null;
|
||||||
|
return Objects.equals(launchingActivity, pairedActivity);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mFocusingTaskInfo != null
|
||||||
|
// TODO (b/238032411): have an API to determine whether an activity is valid for
|
||||||
|
// split screen or not.
|
||||||
|
&& mFocusingTaskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
|
||||||
|
&& mFocusingTaskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD) {
|
||||||
|
return Objects.equals(mFocusingTaskInfo.baseIntent.getComponent(), launchingActivity);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
RemoteAnimationTarget[] onGoingToRecentsLegacy(RemoteAnimationTarget[] apps) {
|
RemoteAnimationTarget[] onGoingToRecentsLegacy(RemoteAnimationTarget[] apps) {
|
||||||
if (isSplitScreenVisible()) {
|
if (isSplitScreenVisible()) {
|
||||||
// Evict child tasks except the top visible one under split root to ensure it could be
|
// Evict child tasks except the top visible one under split root to ensure it could be
|
||||||
|
|||||||
@@ -200,11 +200,6 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
|
|||||||
@Override
|
@Override
|
||||||
@CallSuper
|
@CallSuper
|
||||||
public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
|
public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
|
||||||
if (!taskInfo.supportsMultiWindow) {
|
|
||||||
// Leave split screen if the task no longer supports multi window.
|
|
||||||
mCallbacks.onNoLongerSupportMultiWindow();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (mRootTaskInfo.taskId == taskInfo.taskId) {
|
if (mRootTaskInfo.taskId == taskInfo.taskId) {
|
||||||
// Inflates split decor view only when the root task is visible.
|
// Inflates split decor view only when the root task is visible.
|
||||||
if (mRootTaskInfo.isVisible != taskInfo.isVisible) {
|
if (mRootTaskInfo.isVisible != taskInfo.isVisible) {
|
||||||
@@ -217,6 +212,12 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
|
|||||||
}
|
}
|
||||||
mRootTaskInfo = taskInfo;
|
mRootTaskInfo = taskInfo;
|
||||||
} else if (taskInfo.parentTaskId == mRootTaskInfo.taskId) {
|
} else if (taskInfo.parentTaskId == mRootTaskInfo.taskId) {
|
||||||
|
if (!taskInfo.supportsMultiWindow) {
|
||||||
|
// Leave split screen if the task no longer supports multi window.
|
||||||
|
mCallbacks.onNoLongerSupportMultiWindow();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
mChildrenTaskInfo.put(taskInfo.taskId, taskInfo);
|
mChildrenTaskInfo.put(taskInfo.taskId, taskInfo);
|
||||||
mCallbacks.onChildTaskStatusChanged(taskInfo.taskId, true /* present */,
|
mCallbacks.onChildTaskStatusChanged(taskInfo.taskId, true /* present */,
|
||||||
taskInfo.isVisible);
|
taskInfo.isVisible);
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPL
|
|||||||
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_RIGHT;
|
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_RIGHT;
|
||||||
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_TOP;
|
import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_TOP;
|
||||||
|
|
||||||
import static junit.framework.Assert.assertNull;
|
|
||||||
import static junit.framework.Assert.assertTrue;
|
import static junit.framework.Assert.assertTrue;
|
||||||
import static junit.framework.Assert.fail;
|
import static junit.framework.Assert.fail;
|
||||||
|
|
||||||
@@ -57,7 +56,6 @@ import android.content.ComponentName;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.ActivityInfo;
|
import android.content.pm.ActivityInfo;
|
||||||
import android.content.pm.ResolveInfo;
|
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.graphics.Insets;
|
import android.graphics.Insets;
|
||||||
@@ -265,62 +263,6 @@ public class DragAndDropPolicyTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testLaunchMultipleTask_differentActivity() {
|
|
||||||
setRunningTask(mFullscreenAppTask);
|
|
||||||
mPolicy.start(mLandscapeDisplayLayout, mActivityClipData, mLoggerSessionId);
|
|
||||||
Intent fillInIntent = mPolicy.getStartIntentFillInIntent(mock(PendingIntent.class), 0);
|
|
||||||
assertNull(fillInIntent);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testLaunchMultipleTask_differentActivity_inSplitscreen() {
|
|
||||||
setRunningTask(mFullscreenAppTask);
|
|
||||||
doReturn(true).when(mSplitScreenStarter).isSplitScreenVisible();
|
|
||||||
doReturn(mFullscreenAppTask).when(mSplitScreenStarter).getTaskInfo(anyInt());
|
|
||||||
mPolicy.start(mLandscapeDisplayLayout, mActivityClipData, mLoggerSessionId);
|
|
||||||
Intent fillInIntent = mPolicy.getStartIntentFillInIntent(mock(PendingIntent.class), 0);
|
|
||||||
assertNull(fillInIntent);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testLaunchMultipleTask_sameActivity() {
|
|
||||||
setRunningTask(mFullscreenAppTask);
|
|
||||||
|
|
||||||
// Replace the mocked drag pending intent and ensure it resolves to the same activity
|
|
||||||
PendingIntent launchIntent = mock(PendingIntent.class);
|
|
||||||
ResolveInfo launchInfo = new ResolveInfo();
|
|
||||||
launchInfo.activityInfo = mFullscreenAppTask.topActivityInfo;
|
|
||||||
doReturn(Collections.singletonList(launchInfo))
|
|
||||||
.when(launchIntent).queryIntentComponents(anyInt());
|
|
||||||
mActivityClipData.getItemAt(0).getIntent().putExtra(ClipDescription.EXTRA_PENDING_INTENT,
|
|
||||||
launchIntent);
|
|
||||||
|
|
||||||
mPolicy.start(mLandscapeDisplayLayout, mActivityClipData, mLoggerSessionId);
|
|
||||||
Intent fillInIntent = mPolicy.getStartIntentFillInIntent(launchIntent, 0);
|
|
||||||
assertTrue((fillInIntent.getFlags() & Intent.FLAG_ACTIVITY_MULTIPLE_TASK) != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testLaunchMultipleTask_sameActivity_inSplitScreen() {
|
|
||||||
setRunningTask(mFullscreenAppTask);
|
|
||||||
|
|
||||||
// Replace the mocked drag pending intent and ensure it resolves to the same activity
|
|
||||||
PendingIntent launchIntent = mock(PendingIntent.class);
|
|
||||||
ResolveInfo launchInfo = new ResolveInfo();
|
|
||||||
launchInfo.activityInfo = mFullscreenAppTask.topActivityInfo;
|
|
||||||
doReturn(Collections.singletonList(launchInfo))
|
|
||||||
.when(launchIntent).queryIntentComponents(anyInt());
|
|
||||||
mActivityClipData.getItemAt(0).getIntent().putExtra(ClipDescription.EXTRA_PENDING_INTENT,
|
|
||||||
launchIntent);
|
|
||||||
|
|
||||||
doReturn(true).when(mSplitScreenStarter).isSplitScreenVisible();
|
|
||||||
doReturn(mFullscreenAppTask).when(mSplitScreenStarter).getTaskInfo(anyInt());
|
|
||||||
mPolicy.start(mLandscapeDisplayLayout, mActivityClipData, mLoggerSessionId);
|
|
||||||
Intent fillInIntent = mPolicy.getStartIntentFillInIntent(launchIntent, 0);
|
|
||||||
assertTrue((fillInIntent.getFlags() & Intent.FLAG_ACTIVITY_MULTIPLE_TASK) != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Target filterTargetByType(ArrayList<Target> targets, int type) {
|
private Target filterTargetByType(ArrayList<Target> targets, int type) {
|
||||||
for (Target t : targets) {
|
for (Target t : targets) {
|
||||||
if (type == t.type) {
|
if (type == t.type) {
|
||||||
|
|||||||
@@ -0,0 +1,152 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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.splitscreen;
|
||||||
|
|
||||||
|
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
|
||||||
|
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
|
||||||
|
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
|
||||||
|
|
||||||
|
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.assertTrue;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
|
import static org.mockito.Mockito.doReturn;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
|
||||||
|
import android.app.ActivityManager;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.ActivityInfo;
|
||||||
|
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
import androidx.test.filters.SmallTest;
|
||||||
|
|
||||||
|
import com.android.launcher3.icons.IconProvider;
|
||||||
|
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
|
||||||
|
import com.android.wm.shell.ShellTaskOrganizer;
|
||||||
|
import com.android.wm.shell.ShellTestCase;
|
||||||
|
import com.android.wm.shell.common.DisplayController;
|
||||||
|
import com.android.wm.shell.common.DisplayImeController;
|
||||||
|
import com.android.wm.shell.common.DisplayInsetsController;
|
||||||
|
import com.android.wm.shell.common.ShellExecutor;
|
||||||
|
import com.android.wm.shell.common.SyncTransactionQueue;
|
||||||
|
import com.android.wm.shell.common.TransactionPool;
|
||||||
|
import com.android.wm.shell.recents.RecentTasksController;
|
||||||
|
import com.android.wm.shell.transition.Transitions;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link SplitScreenController}
|
||||||
|
*/
|
||||||
|
@SmallTest
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class SplitScreenControllerTests extends ShellTestCase {
|
||||||
|
|
||||||
|
@Mock ShellTaskOrganizer mTaskOrganizer;
|
||||||
|
@Mock SyncTransactionQueue mSyncQueue;
|
||||||
|
@Mock RootTaskDisplayAreaOrganizer mRootTDAOrganizer;
|
||||||
|
@Mock ShellExecutor mMainExecutor;
|
||||||
|
@Mock DisplayController mDisplayController;
|
||||||
|
@Mock DisplayImeController mDisplayImeController;
|
||||||
|
@Mock DisplayInsetsController mDisplayInsetsController;
|
||||||
|
@Mock Transitions mTransitions;
|
||||||
|
@Mock TransactionPool mTransactionPool;
|
||||||
|
@Mock IconProvider mIconProvider;
|
||||||
|
@Mock Optional<RecentTasksController> mRecentTasks;
|
||||||
|
|
||||||
|
private SplitScreenController mSplitScreenController;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
MockitoAnnotations.initMocks(this);
|
||||||
|
mSplitScreenController = spy(new SplitScreenController(mTaskOrganizer, mSyncQueue, mContext,
|
||||||
|
mRootTDAOrganizer, mMainExecutor, mDisplayController, mDisplayImeController,
|
||||||
|
mDisplayInsetsController, mTransitions, mTransactionPool, mIconProvider,
|
||||||
|
mRecentTasks));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIsLaunchingAdjacently_notInSplitScreen() {
|
||||||
|
doReturn(false).when(mSplitScreenController).isSplitScreenVisible();
|
||||||
|
|
||||||
|
// Verify launching the same activity returns true.
|
||||||
|
Intent startIntent = createStartIntent("startActivity");
|
||||||
|
ActivityManager.RunningTaskInfo focusTaskInfo =
|
||||||
|
createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, startIntent);
|
||||||
|
mSplitScreenController.onFocusTaskChanged(focusTaskInfo);
|
||||||
|
assertTrue(mSplitScreenController.isLaunchingAdjacently(
|
||||||
|
startIntent, SPLIT_POSITION_TOP_OR_LEFT));
|
||||||
|
|
||||||
|
// Verify launching different activity returns false.
|
||||||
|
Intent diffIntent = createStartIntent("diffActivity");
|
||||||
|
focusTaskInfo =
|
||||||
|
createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, diffIntent);
|
||||||
|
mSplitScreenController.onFocusTaskChanged(focusTaskInfo);
|
||||||
|
assertFalse(mSplitScreenController.isLaunchingAdjacently(
|
||||||
|
startIntent, SPLIT_POSITION_TOP_OR_LEFT));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIsLaunchingAdjacently_inSplitScreen() {
|
||||||
|
doReturn(true).when(mSplitScreenController).isSplitScreenVisible();
|
||||||
|
|
||||||
|
// Verify launching the same activity returns true.
|
||||||
|
Intent startIntent = createStartIntent("startActivity");
|
||||||
|
ActivityManager.RunningTaskInfo pairingTaskInfo =
|
||||||
|
createTaskInfo(WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, startIntent);
|
||||||
|
doReturn(pairingTaskInfo).when(mSplitScreenController).getTaskInfo(anyInt());
|
||||||
|
assertTrue(mSplitScreenController.isLaunchingAdjacently(
|
||||||
|
startIntent, SPLIT_POSITION_TOP_OR_LEFT));
|
||||||
|
|
||||||
|
// Verify launching different activity returns false.
|
||||||
|
Intent diffIntent = createStartIntent("diffActivity");
|
||||||
|
pairingTaskInfo =
|
||||||
|
createTaskInfo(WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, diffIntent);
|
||||||
|
doReturn(pairingTaskInfo).when(mSplitScreenController).getTaskInfo(anyInt());
|
||||||
|
assertFalse(mSplitScreenController.isLaunchingAdjacently(
|
||||||
|
startIntent, SPLIT_POSITION_TOP_OR_LEFT));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Intent createStartIntent(String activityName) {
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.setComponent(new ComponentName(mContext, activityName));
|
||||||
|
return intent;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ActivityManager.RunningTaskInfo createTaskInfo(int winMode, int actType,
|
||||||
|
Intent strIntent) {
|
||||||
|
ActivityManager.RunningTaskInfo info = new ActivityManager.RunningTaskInfo();
|
||||||
|
info.configuration.windowConfiguration.setActivityType(actType);
|
||||||
|
info.configuration.windowConfiguration.setWindowingMode(winMode);
|
||||||
|
info.supportsMultiWindow = true;
|
||||||
|
info.baseIntent = strIntent;
|
||||||
|
info.baseActivity = strIntent.getComponent();
|
||||||
|
ActivityInfo activityInfo = new ActivityInfo();
|
||||||
|
activityInfo.packageName = info.baseActivity.getPackageName();
|
||||||
|
activityInfo.name = info.baseActivity.getClassName();
|
||||||
|
info.topActivityInfo = activityInfo;
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user