Merge "Support moving task to an activated split stage in a specific position" into sc-v2-dev am: dcab13878e

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/16279912

Change-Id: Ic3874630bbae2d7356c5134e3418286aa0f251e8
This commit is contained in:
Jerry Chang
2021-11-19 08:41:38 +00:00
committed by Automerger Merge Worker
6 changed files with 106 additions and 50 deletions

View File

@@ -34,6 +34,7 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator; import android.animation.ValueAnimator;
import android.annotation.IntDef; import android.annotation.IntDef;
import android.annotation.NonNull;
import android.app.ActivityManager; import android.app.ActivityManager;
import android.content.Context; import android.content.Context;
import android.content.res.Configuration; 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.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController; 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 * Records and handles layout of splits. Helps to calculate proper bounds when configuration or
* divide position changes. * divide position changes.
@@ -415,6 +418,19 @@ 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.
*/ */
@@ -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. */ /** Handles layout change event. */
public interface SplitLayoutHandler { public interface SplitLayoutHandler {

View File

@@ -50,10 +50,6 @@ class SideStage extends StageTaskListener {
wct.setBounds(rootToken, rootBounds).reorder(rootToken, true /* onTop */); 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) { boolean removeAllTasks(WindowContainerTransaction wct, boolean toTop) {
// No matter if the root task is empty or not, moving the root to bottom because it no // No matter if the root task is empty or not, moving the root to bottom because it no
// longer preserves visible child task. // longer preserves visible child task.

View File

@@ -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.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_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT; 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.ActivityManager;
import android.app.ActivityTaskManager; import android.app.ActivityTaskManager;
@@ -182,30 +184,17 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
} }
public boolean moveToSideStage(int taskId, @SplitPosition int sideStagePosition) { 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); final ActivityManager.RunningTaskInfo task = mTaskOrganizer.getRunningTaskInfo(taskId);
if (task == null) { if (task == null) {
throw new IllegalArgumentException("Unknown taskId" + taskId); throw new IllegalArgumentException("Unknown taskId" + taskId);
} }
return moveToSideStage(task, sideStagePosition); return mStageCoordinator.moveToStage(task, stageType, stagePosition, wct);
}
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);
} }
public boolean removeFromSideStage(int taskId) { public boolean removeFromSideStage(int taskId) {
@@ -221,13 +210,14 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
} }
public void enterSplitScreen(int taskId, boolean leftOrTop) { public void enterSplitScreen(int taskId, boolean leftOrTop) {
moveToSideStage(taskId, enterSplitScreen(taskId, leftOrTop, new WindowContainerTransaction());
leftOrTop ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT);
} }
public void enterSplitScreen(int taskId, boolean leftOrTop, WindowContainerTransaction wct) { public void enterSplitScreen(int taskId, boolean leftOrTop, WindowContainerTransaction wct) {
moveToSideStage(taskId, final int stageType = isSplitScreenVisible() ? STAGE_TYPE_UNDEFINED : STAGE_TYPE_SIDE;
leftOrTop ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT, wct); 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) { public void exitSplitScreen(int toTopTaskId, @ExitReason int exitReason) {

View File

@@ -273,18 +273,31 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
return mSideStageListener.mVisible && mMainStageListener.mVisible; return mSideStageListener.mVisible && mMainStageListener.mVisible;
} }
boolean moveToSideStage(ActivityManager.RunningTaskInfo task, boolean moveToStage(ActivityManager.RunningTaskInfo task, @SplitScreen.StageType int stageType,
@SplitPosition int sideStagePosition) { @SplitPosition int stagePosition, WindowContainerTransaction wct) {
final WindowContainerTransaction wct = new WindowContainerTransaction(); StageTaskListener targetStage;
return moveToSideStage(task, sideStagePosition, wct); 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); setSideStagePosition(sideStagePosition, wct);
mSideStage.evictAllChildren(evictWct); final WindowContainerTransaction evictWct = new WindowContainerTransaction();
mSideStage.addTask(task, wct); targetStage.evictAllChildren(evictWct);
targetStage.addTask(task, wct);
if (!evictWct.isEmpty()) { if (!evictWct.isEmpty()) {
wct.merge(evictWct, true /* transfer */); wct.merge(evictWct, true /* transfer */);
} }
@@ -463,9 +476,7 @@ 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.
final int sideStagePosition = position == SPLIT_POSITION_TOP_OR_LEFT setSideStagePosition(SplitLayout.reversePosition(position), wct);
? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT;
setSideStagePosition(sideStagePosition, wct);
} else { } else {
position = getMainStagePosition(); position = getMainStagePosition();
} }
@@ -489,8 +500,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@SplitLayout.SplitPosition @SplitLayout.SplitPosition
int getMainStagePosition() { int getMainStagePosition() {
return mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT return SplitLayout.reversePosition(mSideStagePosition);
? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT;
} }
void setSideStagePosition(@SplitPosition int sideStagePosition, void setSideStagePosition(@SplitPosition int sideStagePosition,
@@ -870,8 +880,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@Override @Override
public void onDoubleTappedDivider() { public void onDoubleTappedDivider() {
setSideStagePosition(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT setSideStagePosition(SplitLayout.reversePosition(mSideStagePosition), null /* wct */);
? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT, null /* wct */);
mLogger.logSwap(getMainStagePosition(), mMainStage.getTopChildTaskUid(), mLogger.logSwap(getMainStagePosition(), mMainStage.getTopChildTaskUid(),
getSideStagePosition(), mSideStage.getTopChildTaskUid(), getSideStagePosition(), mSideStage.getTopChildTaskUid(),
mSplitLayout.isLandscape()); mSplitLayout.isLandscape());
@@ -1296,11 +1305,16 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler,
pw.println(prefix + TAG + " mDisplayId=" + mDisplayId); pw.println(prefix + TAG + " mDisplayId=" + mDisplayId);
pw.println(innerPrefix + "mDividerVisible=" + mDividerVisible); pw.println(innerPrefix + "mDividerVisible=" + mDividerVisible);
pw.println(innerPrefix + "MainStage"); pw.println(innerPrefix + "MainStage");
pw.println(childPrefix + "stagePosition=" + getMainStagePosition());
pw.println(childPrefix + "isActive=" + mMainStage.isActive()); pw.println(childPrefix + "isActive=" + mMainStage.isActive());
mMainStageListener.dump(pw, childPrefix); mMainStageListener.dump(pw, childPrefix);
pw.println(innerPrefix + "SideStage"); pw.println(innerPrefix + "SideStage");
pw.println(childPrefix + "stagePosition=" + getSideStagePosition());
mSideStageListener.dump(pw, childPrefix); mSideStageListener.dump(pw, childPrefix);
pw.println(innerPrefix + "mSplitLayout=" + mSplitLayout); if (mMainStage.isActive()) {
pw.println(innerPrefix + "SplitLayout");
mSplitLayout.dump(pw, childPrefix);
}
} }
/** /**

View File

@@ -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) { void setBounds(Rect bounds, WindowContainerTransaction wct) {
wct.setBounds(mRootTaskInfo.token, bounds); wct.setBounds(mRootTaskInfo.token, bounds);
} }

View File

@@ -19,11 +19,14 @@ package com.android.wm.shell.splitscreen;
import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.view.Display.DEFAULT_DISPLAY; 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_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT; 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 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.any;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations; 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.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.split.SplitLayout; import com.android.wm.shell.common.split.SplitLayout;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.transition.Transitions;
import org.junit.Before; import org.junit.Before;
@@ -110,12 +112,39 @@ public class StageCoordinatorTests extends ShellTestCase {
} }
@Test @Test
public void testMoveToSideStage() { public void testMoveToStage() {
final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build(); 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)); 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 @Test