diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java index f6cd7e240bd2c..c62ea335de944 100644 --- a/core/java/android/window/WindowContainerTransaction.java +++ b/core/java/android/window/WindowContainerTransaction.java @@ -360,6 +360,11 @@ public final class WindowContainerTransaction implements Parcelable { } } + /** @hide */ + public boolean isEmpty() { + return mChanges.isEmpty() && mHierarchyOps.isEmpty(); + } + /** @hide */ public Map getChanges() { return mChanges; diff --git a/core/java/android/window/WindowOrganizer.java b/core/java/android/window/WindowOrganizer.java index bb03e28297151..bcd0e8ddf2cd3 100644 --- a/core/java/android/window/WindowOrganizer.java +++ b/core/java/android/window/WindowOrganizer.java @@ -42,7 +42,9 @@ public class WindowOrganizer { @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void applyTransaction(@NonNull WindowContainerTransaction t) { try { - getWindowOrganizerController().applyTransaction(t); + if (!t.isEmpty()) { + getWindowOrganizerController().applyTransaction(t); + } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java index 7f98965d7f1fb..2f2e325aafad5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java @@ -51,8 +51,7 @@ class MainStage extends StageTaskListener { if (mIsActive) return; final WindowContainerToken rootToken = mRootTaskInfo.token; - wct.setHidden(rootToken, false) - .setBounds(rootToken, rootBounds) + wct.setBounds(rootToken, rootBounds) .setLaunchRoot( rootToken, CONTROLLED_WINDOWING_MODES, @@ -63,7 +62,7 @@ class MainStage extends StageTaskListener { CONTROLLED_WINDOWING_MODES, CONTROLLED_ACTIVITY_TYPES, true /* onTop */) - // Moving the root task to top after the child tasks were repareted , or the root + // Moving the root task to top after the child tasks were re-parented , or the root // task cannot be visible and focused. .reorder(rootToken, true /* onTop */); @@ -71,13 +70,16 @@ class MainStage extends StageTaskListener { } void deactivate(WindowContainerTransaction wct) { + deactivate(wct, false /* toTop */); + } + + void deactivate(WindowContainerTransaction wct, boolean toTop) { if (!mIsActive) return; mIsActive = false; if (mRootTaskInfo == null) return; final WindowContainerToken rootToken = mRootTaskInfo.token; - wct.setHidden(rootToken, true) - .setLaunchRoot( + wct.setLaunchRoot( rootToken, null, null) @@ -86,7 +88,9 @@ class MainStage extends StageTaskListener { null /* newParent */, CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE, CONTROLLED_ACTIVITY_TYPES, - false /* onTop */) + toTop) + // We want this re-order to the bottom regardless since we are re-parenting + // all its tasks. .reorder(rootToken, false /* onTop */); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java index cd44e4b7cebf5..e7cd38fb4bca3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java @@ -16,8 +16,6 @@ package com.android.wm.shell.splitscreen; -import static android.app.ActivityTaskManager.INVALID_TASK_ID; - import android.app.ActivityManager; import android.graphics.Rect; import android.window.WindowContainerToken; @@ -50,14 +48,14 @@ class SideStage extends StageTaskListener { .reorder(rootToken, true); } - boolean removeAllTasks(WindowContainerTransaction wct) { + boolean removeAllTasks(WindowContainerTransaction wct, boolean toTop) { if (mChildrenTaskInfo.size() == 0) return false; wct.reparentTasks( mRootTaskInfo.token, null /* newParent */, CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE, CONTROLLED_ACTIVITY_TYPES, - false /* onTop */); + toTop); return true; } @@ -70,12 +68,4 @@ class SideStage extends StageTaskListener { .reparent(task.token, newParent, false /* onTop */); return true; } - - int getTopVisibleTaskId() { - for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) { - final ActivityManager.RunningTaskInfo task = mChildrenTaskInfo.valueAt(i); - if (task.isVisible) return task.taskId; - } - return INVALID_TASK_ID; - } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index dd41957935d7a..d571e75145423 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -146,10 +146,16 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener, } void exitSplitScreen() { + exitSplitScreen(null /* childrenToTop */); + } + + private void exitSplitScreen(StageTaskListener childrenToTop) { final WindowContainerTransaction wct = new WindowContainerTransaction(); - mSideStage.removeAllTasks(wct); - mMainStage.deactivate(wct); + mSideStage.removeAllTasks(wct, childrenToTop == mSideStage); + mMainStage.deactivate(wct, childrenToTop == mMainStage); mTaskOrganizer.applyTransaction(wct); + // Reset divider position. + mSplitLayout.resetDividerPosition(); } void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) { @@ -272,41 +278,29 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener, } private void onStageHasChildrenChanged(StageListenerImpl stageListener) { - if (stageListener == mSideStageListener) { - final WindowContainerTransaction wct = new WindowContainerTransaction(); - if (mSideStageListener.mHasChildren) { - // Make sure the main stage is active. - mMainStage.activate(getMainStageBounds(), wct); - } else { - // The side stage no long has children so we can deactivate the main stage. - mMainStage.deactivate(wct); + final boolean hasChildren = stageListener.mHasChildren; + final boolean isSideStage = stageListener == mSideStageListener; + if (!hasChildren) { + if (isSideStage && mMainStageListener.mVisible) { + // Exit to main stage if side stage no longer has children. + exitSplitScreen(mMainStage); + } else if (!isSideStage && mSideStageListener.mVisible) { + // Exit to side stage if main stage no longer has children. + exitSplitScreen(mSideStage); } + } else if (isSideStage) { + final WindowContainerTransaction wct = new WindowContainerTransaction(); + // Make sure the main stage is active. + mMainStage.activate(getMainStageBounds(), wct); mTaskOrganizer.applyTransaction(wct); } } @Override public void onSnappedToDismiss(boolean bottomOrRight) { - if (mSideStagePosition == SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT && bottomOrRight) { - // Main stage was fully expanded...Just side side-stage. - setSideStageVisibility(false); - } else { - // Side stage was fully expanded...Move its top task to the main stage - // and hide side-stage. - // TODO: Would UX prefer the side-stage go into fullscreen mode here? - final int taskId = mSideStage.getTopVisibleTaskId(); - if (taskId == INVALID_TASK_ID) { - throw new IllegalStateException("Side stage doesn't have visible task? " - + mSideStage); - } - final WindowContainerTransaction wct = new WindowContainerTransaction(); - mSideStage.removeTask(taskId, mMainStage.mRootTaskInfo.getToken(), wct); - mSideStage.setVisibility(false, wct); - mTaskOrganizer.applyTransaction(wct); - } - - // Reset divider position. - mSplitLayout.resetDividerPosition(); + final boolean mainStageToTop = bottomOrRight + && mSideStagePosition == SIDE_STAGE_POSITION_BOTTOM_OR_RIGHT; + exitSplitScreen(mainStageToTop ? mMainStage : mSideStage); } @Override diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index 4b551842c907f..23ae122fcd6c7 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -2522,6 +2522,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { final ActivityOptions activityOptions = options != null ? options.getOptions(this) : null; + boolean moveHomeTaskForward = true; if (activityOptions != null) { activityType = activityOptions.getLaunchActivityType(); windowingMode = activityOptions.getLaunchWindowingMode(); @@ -2529,6 +2530,12 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { && mRecentTasks.isCallerRecents(callingUid)) { mRecentTasks.setFreezeTaskListReordering(); } + if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY + || activityOptions.getLaunchRootTask() != null) { + // Don't move home activity forward if we are launching into primary split or there + // is a launch root set. + moveHomeTaskForward = false; + } } if (activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS) { throw new IllegalArgumentException("startActivityFromRecents: Task " @@ -2545,7 +2552,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { "startActivityFromRecents: Task " + taskId + " not found."); } - if (windowingMode != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { + if (moveHomeTaskForward) { // We always want to return to the home activity instead of the recents activity // from whatever is started from the recents activity, so move the home stack // forward.