Merge "Support moving task to an activated split stage in a specific position" into sc-v2-dev
This commit is contained in:
@@ -34,6 +34,7 @@ import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.app.ActivityManager;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
@@ -61,6 +62,8 @@ import com.android.wm.shell.animation.Interpolators;
|
||||
import com.android.wm.shell.common.DisplayImeController;
|
||||
import com.android.wm.shell.common.DisplayInsetsController;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
* Records and handles layout of splits. Helps to calculate proper bounds when configuration or
|
||||
* divide position changes.
|
||||
@@ -415,6 +418,19 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
|
||||
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.
|
||||
*/
|
||||
@@ -502,6 +518,13 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
|
||||
}
|
||||
}
|
||||
|
||||
/** Dumps the current split bounds recorded in this layout. */
|
||||
public void dump(@NonNull PrintWriter pw, String prefix) {
|
||||
pw.println(prefix + "bounds1=" + mBounds1.toShortString());
|
||||
pw.println(prefix + "dividerBounds=" + mDividerBounds.toShortString());
|
||||
pw.println(prefix + "bounds2=" + mBounds2.toShortString());
|
||||
}
|
||||
|
||||
/** Handles layout change event. */
|
||||
public interface SplitLayoutHandler {
|
||||
|
||||
|
||||
@@ -50,10 +50,6 @@ class SideStage extends StageTaskListener {
|
||||
wct.setBounds(rootToken, rootBounds).reorder(rootToken, true /* onTop */);
|
||||
}
|
||||
|
||||
void addTask(ActivityManager.RunningTaskInfo task, WindowContainerTransaction wct) {
|
||||
wct.reparent(task.token, mRootTaskInfo.token, true /* onTop*/);
|
||||
}
|
||||
|
||||
boolean removeAllTasks(WindowContainerTransaction wct, boolean toTop) {
|
||||
// No matter if the root task is empty or not, moving the root to bottom because it no
|
||||
// longer preserves visible child task.
|
||||
|
||||
@@ -24,6 +24,8 @@ import static android.view.RemoteAnimationTarget.MODE_OPENING;
|
||||
import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
|
||||
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT;
|
||||
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityTaskManager;
|
||||
@@ -182,30 +184,17 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
||||
}
|
||||
|
||||
public boolean moveToSideStage(int taskId, @SplitPosition int sideStagePosition) {
|
||||
return moveToStage(taskId, STAGE_TYPE_SIDE, sideStagePosition,
|
||||
new WindowContainerTransaction());
|
||||
}
|
||||
|
||||
private boolean moveToStage(int taskId, @SplitScreen.StageType int stageType,
|
||||
@SplitPosition int stagePosition, WindowContainerTransaction wct) {
|
||||
final ActivityManager.RunningTaskInfo task = mTaskOrganizer.getRunningTaskInfo(taskId);
|
||||
if (task == null) {
|
||||
throw new IllegalArgumentException("Unknown taskId" + taskId);
|
||||
}
|
||||
return moveToSideStage(task, sideStagePosition);
|
||||
}
|
||||
|
||||
public boolean moveToSideStage(int taskId, @SplitPosition int sideStagePosition,
|
||||
WindowContainerTransaction wct) {
|
||||
final ActivityManager.RunningTaskInfo task = mTaskOrganizer.getRunningTaskInfo(taskId);
|
||||
if (task == null) {
|
||||
throw new IllegalArgumentException("Unknown taskId" + taskId);
|
||||
}
|
||||
return moveToSideStage(task, sideStagePosition, wct);
|
||||
}
|
||||
|
||||
public boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
|
||||
@SplitPosition int sideStagePosition) {
|
||||
return mStageCoordinator.moveToSideStage(task, sideStagePosition);
|
||||
}
|
||||
|
||||
public boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
|
||||
@SplitPosition int sideStagePosition, WindowContainerTransaction wct) {
|
||||
return mStageCoordinator.moveToSideStage(task, sideStagePosition, wct);
|
||||
return mStageCoordinator.moveToStage(task, stageType, stagePosition, wct);
|
||||
}
|
||||
|
||||
public boolean removeFromSideStage(int taskId) {
|
||||
@@ -221,13 +210,14 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
|
||||
}
|
||||
|
||||
public void enterSplitScreen(int taskId, boolean leftOrTop) {
|
||||
moveToSideStage(taskId,
|
||||
leftOrTop ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT);
|
||||
enterSplitScreen(taskId, leftOrTop, new WindowContainerTransaction());
|
||||
}
|
||||
|
||||
public void enterSplitScreen(int taskId, boolean leftOrTop, WindowContainerTransaction wct) {
|
||||
moveToSideStage(taskId,
|
||||
leftOrTop ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT, wct);
|
||||
final int stageType = isSplitScreenVisible() ? STAGE_TYPE_UNDEFINED : STAGE_TYPE_SIDE;
|
||||
final int stagePosition =
|
||||
leftOrTop ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT;
|
||||
moveToStage(taskId, stageType, stagePosition, wct);
|
||||
}
|
||||
|
||||
public void exitSplitScreen(int toTopTaskId, @ExitReason int exitReason) {
|
||||
|
||||
@@ -273,18 +273,31 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
return mSideStageListener.mVisible && mMainStageListener.mVisible;
|
||||
}
|
||||
|
||||
boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
|
||||
@SplitPosition int sideStagePosition) {
|
||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
return moveToSideStage(task, sideStagePosition, wct);
|
||||
}
|
||||
boolean moveToStage(ActivityManager.RunningTaskInfo task, @SplitScreen.StageType int stageType,
|
||||
@SplitPosition int stagePosition, WindowContainerTransaction wct) {
|
||||
StageTaskListener targetStage;
|
||||
int sideStagePosition;
|
||||
if (stageType == STAGE_TYPE_MAIN) {
|
||||
targetStage = mMainStage;
|
||||
sideStagePosition = SplitLayout.reversePosition(stagePosition);
|
||||
} else if (stageType == STAGE_TYPE_SIDE) {
|
||||
targetStage = mSideStage;
|
||||
sideStagePosition = stagePosition;
|
||||
} else {
|
||||
if (mMainStage.isActive()) {
|
||||
// If the split screen is activated, retrieves target stage based on position.
|
||||
targetStage = stagePosition == mSideStagePosition ? mSideStage : mMainStage;
|
||||
sideStagePosition = mSideStagePosition;
|
||||
} else {
|
||||
targetStage = mSideStage;
|
||||
sideStagePosition = stagePosition;
|
||||
}
|
||||
}
|
||||
|
||||
boolean moveToSideStage(ActivityManager.RunningTaskInfo task,
|
||||
@SplitPosition int sideStagePosition, WindowContainerTransaction wct) {
|
||||
final WindowContainerTransaction evictWct = new WindowContainerTransaction();
|
||||
setSideStagePosition(sideStagePosition, wct);
|
||||
mSideStage.evictAllChildren(evictWct);
|
||||
mSideStage.addTask(task, wct);
|
||||
final WindowContainerTransaction evictWct = new WindowContainerTransaction();
|
||||
targetStage.evictAllChildren(evictWct);
|
||||
targetStage.addTask(task, wct);
|
||||
if (!evictWct.isEmpty()) {
|
||||
wct.merge(evictWct, true /* transfer */);
|
||||
}
|
||||
@@ -463,9 +476,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
case STAGE_TYPE_MAIN: {
|
||||
if (position != SPLIT_POSITION_UNDEFINED) {
|
||||
// Set the side stage opposite of what we want to the main stage.
|
||||
final int sideStagePosition = position == SPLIT_POSITION_TOP_OR_LEFT
|
||||
? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT;
|
||||
setSideStagePosition(sideStagePosition, wct);
|
||||
setSideStagePosition(SplitLayout.reversePosition(position), wct);
|
||||
} else {
|
||||
position = getMainStagePosition();
|
||||
}
|
||||
@@ -489,8 +500,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
|
||||
@SplitLayout.SplitPosition
|
||||
int getMainStagePosition() {
|
||||
return mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT
|
||||
? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT;
|
||||
return SplitLayout.reversePosition(mSideStagePosition);
|
||||
}
|
||||
|
||||
void setSideStagePosition(@SplitPosition int sideStagePosition,
|
||||
@@ -870,8 +880,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
|
||||
@Override
|
||||
public void onDoubleTappedDivider() {
|
||||
setSideStagePosition(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT
|
||||
? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT, null /* wct */);
|
||||
setSideStagePosition(SplitLayout.reversePosition(mSideStagePosition), null /* wct */);
|
||||
mLogger.logSwap(getMainStagePosition(), mMainStage.getTopChildTaskUid(),
|
||||
getSideStagePosition(), mSideStage.getTopChildTaskUid(),
|
||||
mSplitLayout.isLandscape());
|
||||
@@ -1296,11 +1305,16 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
|
||||
pw.println(prefix + TAG + " mDisplayId=" + mDisplayId);
|
||||
pw.println(innerPrefix + "mDividerVisible=" + mDividerVisible);
|
||||
pw.println(innerPrefix + "MainStage");
|
||||
pw.println(childPrefix + "stagePosition=" + getMainStagePosition());
|
||||
pw.println(childPrefix + "isActive=" + mMainStage.isActive());
|
||||
mMainStageListener.dump(pw, childPrefix);
|
||||
pw.println(innerPrefix + "SideStage");
|
||||
pw.println(childPrefix + "stagePosition=" + getSideStagePosition());
|
||||
mSideStageListener.dump(pw, childPrefix);
|
||||
pw.println(innerPrefix + "mSplitLayout=" + mSplitLayout);
|
||||
if (mMainStage.isActive()) {
|
||||
pw.println(innerPrefix + "SplitLayout");
|
||||
mSplitLayout.dump(pw, childPrefix);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -293,6 +293,10 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
|
||||
}
|
||||
}
|
||||
|
||||
void addTask(ActivityManager.RunningTaskInfo task, WindowContainerTransaction wct) {
|
||||
wct.reparent(task.token, mRootTaskInfo.token, true /* onTop*/);
|
||||
}
|
||||
|
||||
void setBounds(Rect bounds, WindowContainerTransaction wct) {
|
||||
wct.setBounds(mRootTaskInfo.token, bounds);
|
||||
}
|
||||
|
||||
@@ -19,11 +19,14 @@ package com.android.wm.shell.splitscreen;
|
||||
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
|
||||
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME;
|
||||
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT;
|
||||
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT;
|
||||
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_UNDEFINED;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RETURN_HOME;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.clearInvocations;
|
||||
@@ -49,7 +52,6 @@ import com.android.wm.shell.common.DisplayInsetsController;
|
||||
import com.android.wm.shell.common.SyncTransactionQueue;
|
||||
import com.android.wm.shell.common.TransactionPool;
|
||||
import com.android.wm.shell.common.split.SplitLayout;
|
||||
import com.android.wm.shell.recents.RecentTasksController;
|
||||
import com.android.wm.shell.transition.Transitions;
|
||||
|
||||
import org.junit.Before;
|
||||
@@ -110,12 +112,39 @@ public class StageCoordinatorTests extends ShellTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoveToSideStage() {
|
||||
public void testMoveToStage() {
|
||||
final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
|
||||
|
||||
mStageCoordinator.moveToSideStage(task, SPLIT_POSITION_BOTTOM_OR_RIGHT);
|
||||
mStageCoordinator.moveToStage(task, STAGE_TYPE_MAIN, SPLIT_POSITION_BOTTOM_OR_RIGHT,
|
||||
new WindowContainerTransaction());
|
||||
verify(mMainStage).addTask(eq(task), any(WindowContainerTransaction.class));
|
||||
assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getMainStagePosition());
|
||||
|
||||
mStageCoordinator.moveToStage(task, STAGE_TYPE_SIDE, SPLIT_POSITION_BOTTOM_OR_RIGHT,
|
||||
new WindowContainerTransaction());
|
||||
verify(mSideStage).addTask(eq(task), any(WindowContainerTransaction.class));
|
||||
assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getSideStagePosition());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoveToUndefinedStage() {
|
||||
final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build();
|
||||
|
||||
// Verify move to undefined stage while split screen not activated moves task to side stage.
|
||||
when(mMainStage.isActive()).thenReturn(false);
|
||||
mStageCoordinator.setSideStagePosition(SPLIT_POSITION_TOP_OR_LEFT, null);
|
||||
mStageCoordinator.moveToStage(task, STAGE_TYPE_UNDEFINED, SPLIT_POSITION_BOTTOM_OR_RIGHT,
|
||||
new WindowContainerTransaction());
|
||||
verify(mSideStage).addTask(eq(task), any(WindowContainerTransaction.class));
|
||||
assertEquals(SPLIT_POSITION_BOTTOM_OR_RIGHT, mStageCoordinator.getSideStagePosition());
|
||||
|
||||
// Verify move to undefined stage after split screen activated moves task based on position.
|
||||
when(mMainStage.isActive()).thenReturn(true);
|
||||
assertEquals(SPLIT_POSITION_TOP_OR_LEFT, mStageCoordinator.getMainStagePosition());
|
||||
mStageCoordinator.moveToStage(task, STAGE_TYPE_UNDEFINED, SPLIT_POSITION_TOP_OR_LEFT,
|
||||
new WindowContainerTransaction());
|
||||
verify(mMainStage).addTask(eq(task), any(WindowContainerTransaction.class));
|
||||
assertEquals(SPLIT_POSITION_TOP_OR_LEFT, mStageCoordinator.getMainStagePosition());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user