Loading libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java +33 −0 Original line number Diff line number Diff line Loading @@ -26,15 +26,22 @@ import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSIT import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Rect; import android.window.DesktopExperienceFlags; import android.window.DisplayAreaInfo; import android.window.WindowContainerToken; import com.android.internal.util.ArrayUtils; import com.android.wm.shell.Flags; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.shared.split.SplitScreenConstants; import com.android.wm.shell.splitscreen.StageTaskListener; /** Helper utility class for split screen components to use. */ public class SplitScreenUtils { Loading Loading @@ -152,4 +159,30 @@ public class SplitScreenUtils { return false; } } /** * Retrieves the new parent WindowContainerToken for tasks in the main or stage display area * after the split pair is dismissed. This token will be used for reparenting tasks. The * specific stage (main or side) from which the display ID is obtained does not alter the * resulting parent token, as it's based on the display area of the display itself. * * @param stage The StageTaskListener representing the current stage. * @param rootTDAOrganizer The RootTaskDisplayAreaOrganizer to query for DisplayAreaInfo. * @return The WindowContainerToken of the parent display area if * DesktopExperienceFlags.ENABLE_NON_DEFAULT_DISPLAY_SPLIT is true and a valid * DisplayAreaInfo is found for the main stage's display; otherwise, returns null. */ @Nullable public static WindowContainerToken getNewParentTokenForStage( @Nullable StageTaskListener stage, @NonNull RootTaskDisplayAreaOrganizer rootTDAOrganizer) { if (!DesktopExperienceFlags.ENABLE_NON_DEFAULT_DISPLAY_SPLIT.isTrue() || stage == null || stage.getRunningTaskInfo() == null) { return null; } final int displayId = stage.getRunningTaskInfo().displayId; final DisplayAreaInfo displayAreaInfo = rootTDAOrganizer.getDisplayAreaInfo(displayId); return displayAreaInfo != null ? displayAreaInfo.token : null; } } libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +25 −16 Original line number Diff line number Diff line Loading @@ -47,6 +47,7 @@ import static com.android.wm.shell.common.split.SplitLayout.PARALLAX_FLEX_HYBRID import static com.android.wm.shell.common.split.SplitLayout.RESTING_DIM_LAYER; import static com.android.wm.shell.common.split.SplitScreenUtils.reverseSplitPosition; import static com.android.wm.shell.common.split.SplitScreenUtils.splitFailureMessage; import static com.android.wm.shell.common.split.SplitScreenUtils.getNewParentTokenForStage; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN; import static com.android.wm.shell.shared.TransitionUtil.isClosingType; import static com.android.wm.shell.shared.TransitionUtil.isOpeningMode; Loading Loading @@ -329,15 +330,12 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } // Find the current split-screen root task info. RunningTaskInfo currentRootTaskInfo = null; for (int id : mSplitMultiDisplayHelper.getCachedOrSystemDisplayIds()) { final RunningTaskInfo rootTaskInfo = mSplitMultiDisplayHelper.getDisplayRootTaskInfo(id); if (rootTaskInfo != null) { currentRootTaskInfo = rootTaskInfo; break; } } RunningTaskInfo currentRootTaskInfo = mSplitMultiDisplayHelper.getCachedOrSystemDisplayIds() .stream() .map(id -> mSplitMultiDisplayHelper.getDisplayRootTaskInfo(id)) .filter(Objects::nonNull) .findFirst() .orElse(null); if (currentRootTaskInfo == null) { throw new IllegalStateException("Failed to find current split screen root task info."); Loading Loading @@ -662,16 +660,19 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, .filter(stage -> stage.getId() == stageToTop) .findFirst().orElse(null); if (stageToDeactivate != null) { stageToDeactivate.deactivate(wct, true /*toTop*/); stageToDeactivate.deactivate(wct, true /*reparentTasksToTop*/, getNewParentTokenForStage(stageToDeactivate, mRootTDAOrganizer)); } else { // If no one stage is meant to go to the top, deactivate all stages to move any // child tasks out from under their respective stage root tasks. mStageOrderOperator.getAllStages().forEach(stage -> stage.deactivate(wct, false /*reparentTasksToTop*/)); stage.deactivate(wct, false /*reparentTasksToTop*/, null)); } mStageOrderOperator.onExitingSplit(); } else { mMainStage.deactivate(wct, stageToTop == STAGE_TYPE_MAIN); mMainStage.deactivate( wct, stageToTop == STAGE_TYPE_MAIN, getNewParentTokenForStage(mMainStage, mRootTDAOrganizer)); } } Loading Loading @@ -1790,8 +1791,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // TODO: b/393217881 - replace DEFAULT DISPLAY with the current display id RunningTaskInfo rootTaskInfo = mSplitMultiDisplayHelper.getDisplayRootTaskInfo(DEFAULT_DISPLAY); if (childrenToTop == null || childrenToTop.getTopVisibleChildTaskId() == INVALID_TASK_ID) { mSideStage.removeAllTasks(wct, false /* toTop */); mSideStage.removeAllTasks( wct, false /* toTop */, getNewParentTokenForStage(mSideStage, mRootTDAOrganizer)); deactivateSplit(wct, STAGE_TYPE_UNDEFINED); wct.reorder(rootTaskInfo.token, false /* onTop */); setRootForceTranslucent(true, wct); Loading Loading @@ -1822,7 +1826,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, WindowContainerTransaction finishedWCT = new WindowContainerTransaction(); mIsExiting = false; deactivateSplit(finishedWCT, childrenToTop.getId()); mSideStage.removeAllTasks(finishedWCT, childrenToTop == mSideStage /* toTop */); mSideStage.removeAllTasks( finishedWCT, childrenToTop == mSideStage /* toTop */, getNewParentTokenForStage(mSideStage, mRootTDAOrganizer)); finishedWCT.reorder(rootTaskInfo.token, false /* toTop */); setRootForceTranslucent(true, finishedWCT); finishedWCT.setBounds(mSideStage.mRootTaskInfo.token, mTempRect1); Loading Loading @@ -1981,9 +1987,12 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (enableFlexibleSplit()) { mStageOrderOperator.getActiveStages().stream() .filter(stage -> stage.getId() != stageToTop) .forEach(stage -> stage.removeAllTasks(wct, false /*toTop*/)); .forEach(stage -> stage.removeAllTasks(wct, false /*toTop*/, getNewParentTokenForStage(stage, mRootTDAOrganizer))); } else { mSideStage.removeAllTasks(wct, stageToTop == STAGE_TYPE_SIDE); mSideStage.removeAllTasks(wct, stageToTop == STAGE_TYPE_SIDE, getNewParentTokenForStage(mSideStage, mRootTDAOrganizer)); } if (exitReason != EXIT_REASON_DESKTOP_MODE) { Loading libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java +7 −5 Original line number Diff line number Diff line Loading @@ -533,10 +533,11 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener { } void deactivate(WindowContainerTransaction wct) { deactivate(wct, false /* toTop */); deactivate(wct, false /* toTop */, null /* newParent */); } void deactivate(WindowContainerTransaction wct, boolean reparentTasksToTop) { void deactivate(WindowContainerTransaction wct, boolean reparentTasksToTop, @Nullable WindowContainerToken newParent) { if (!mIsActive && !enableFlexibleSplit()) return; ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "deactivate: reparentTasksToTop=%b " + "rootTaskInfo=%s stage=%s", Loading @@ -549,7 +550,7 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener { final WindowContainerToken rootToken = mRootTaskInfo.token; wct.reparentTasks( rootToken, null /* newParent */, newParent, null /* windowingModes */, null /* activityTypes */, reparentTasksToTop); Loading @@ -557,14 +558,15 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener { // -------- // Previously only used in SideStage. With flexible split this is called for all stages boolean removeAllTasks(WindowContainerTransaction wct, boolean toTop) { boolean removeAllTasks(WindowContainerTransaction wct, boolean toTop, @Nullable WindowContainerToken newParent) { ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "remove all side stage tasks: childCount=%d toTop=%b " + " stageI=%s", mChildrenTaskInfo.size(), toTop, stageTypeToString(mId)); if (mChildrenTaskInfo.size() == 0) return false; wct.reparentTasks( mRootTaskInfo.token, null /* newParent */, newParent, null /* windowingModes */, null /* activityTypes */, toTop); Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java +7 −2 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER; import static com.android.wm.shell.common.split.SplitScreenUtils.getNewParentTokenForStage; import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_10_90; import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_50_50; import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_90_10; Loading Loading @@ -624,11 +625,15 @@ public class SplitTransitionTests extends ShellTestCase { for (int i = 0; i < wct.getHierarchyOps().size(); ++i) { WindowContainerTransaction.HierarchyOp op = wct.getHierarchyOps().get(i); if (op.getType() == HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT) { IBinder expectedNewParentBinder = Optional.ofNullable( getNewParentTokenForStage(mMainStage, mRootTDAOrganizer)) .map(WindowContainerToken::asBinder) .orElse(null); if (op.getContainer() == mMainStage.mRootTaskInfo.token.asBinder() && op.getNewParent() == null) { && op.getNewParent() == expectedNewParentBinder) { reparentedMain = true; } else if (op.getContainer() == mSideStage.mRootTaskInfo.token.asBinder() && op.getNewParent() == null) { && op.getNewParent() == expectedNewParentBinder) { reparentedSide = true; } } Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java +33 −0 Original line number Diff line number Diff line Loading @@ -26,15 +26,22 @@ import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSIT import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Rect; import android.window.DesktopExperienceFlags; import android.window.DisplayAreaInfo; import android.window.WindowContainerToken; import com.android.internal.util.ArrayUtils; import com.android.wm.shell.Flags; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.shared.split.SplitScreenConstants; import com.android.wm.shell.splitscreen.StageTaskListener; /** Helper utility class for split screen components to use. */ public class SplitScreenUtils { Loading Loading @@ -152,4 +159,30 @@ public class SplitScreenUtils { return false; } } /** * Retrieves the new parent WindowContainerToken for tasks in the main or stage display area * after the split pair is dismissed. This token will be used for reparenting tasks. The * specific stage (main or side) from which the display ID is obtained does not alter the * resulting parent token, as it's based on the display area of the display itself. * * @param stage The StageTaskListener representing the current stage. * @param rootTDAOrganizer The RootTaskDisplayAreaOrganizer to query for DisplayAreaInfo. * @return The WindowContainerToken of the parent display area if * DesktopExperienceFlags.ENABLE_NON_DEFAULT_DISPLAY_SPLIT is true and a valid * DisplayAreaInfo is found for the main stage's display; otherwise, returns null. */ @Nullable public static WindowContainerToken getNewParentTokenForStage( @Nullable StageTaskListener stage, @NonNull RootTaskDisplayAreaOrganizer rootTDAOrganizer) { if (!DesktopExperienceFlags.ENABLE_NON_DEFAULT_DISPLAY_SPLIT.isTrue() || stage == null || stage.getRunningTaskInfo() == null) { return null; } final int displayId = stage.getRunningTaskInfo().displayId; final DisplayAreaInfo displayAreaInfo = rootTDAOrganizer.getDisplayAreaInfo(displayId); return displayAreaInfo != null ? displayAreaInfo.token : null; } }
libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +25 −16 Original line number Diff line number Diff line Loading @@ -47,6 +47,7 @@ import static com.android.wm.shell.common.split.SplitLayout.PARALLAX_FLEX_HYBRID import static com.android.wm.shell.common.split.SplitLayout.RESTING_DIM_LAYER; import static com.android.wm.shell.common.split.SplitScreenUtils.reverseSplitPosition; import static com.android.wm.shell.common.split.SplitScreenUtils.splitFailureMessage; import static com.android.wm.shell.common.split.SplitScreenUtils.getNewParentTokenForStage; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN; import static com.android.wm.shell.shared.TransitionUtil.isClosingType; import static com.android.wm.shell.shared.TransitionUtil.isOpeningMode; Loading Loading @@ -329,15 +330,12 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } // Find the current split-screen root task info. RunningTaskInfo currentRootTaskInfo = null; for (int id : mSplitMultiDisplayHelper.getCachedOrSystemDisplayIds()) { final RunningTaskInfo rootTaskInfo = mSplitMultiDisplayHelper.getDisplayRootTaskInfo(id); if (rootTaskInfo != null) { currentRootTaskInfo = rootTaskInfo; break; } } RunningTaskInfo currentRootTaskInfo = mSplitMultiDisplayHelper.getCachedOrSystemDisplayIds() .stream() .map(id -> mSplitMultiDisplayHelper.getDisplayRootTaskInfo(id)) .filter(Objects::nonNull) .findFirst() .orElse(null); if (currentRootTaskInfo == null) { throw new IllegalStateException("Failed to find current split screen root task info."); Loading Loading @@ -662,16 +660,19 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, .filter(stage -> stage.getId() == stageToTop) .findFirst().orElse(null); if (stageToDeactivate != null) { stageToDeactivate.deactivate(wct, true /*toTop*/); stageToDeactivate.deactivate(wct, true /*reparentTasksToTop*/, getNewParentTokenForStage(stageToDeactivate, mRootTDAOrganizer)); } else { // If no one stage is meant to go to the top, deactivate all stages to move any // child tasks out from under their respective stage root tasks. mStageOrderOperator.getAllStages().forEach(stage -> stage.deactivate(wct, false /*reparentTasksToTop*/)); stage.deactivate(wct, false /*reparentTasksToTop*/, null)); } mStageOrderOperator.onExitingSplit(); } else { mMainStage.deactivate(wct, stageToTop == STAGE_TYPE_MAIN); mMainStage.deactivate( wct, stageToTop == STAGE_TYPE_MAIN, getNewParentTokenForStage(mMainStage, mRootTDAOrganizer)); } } Loading Loading @@ -1790,8 +1791,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // TODO: b/393217881 - replace DEFAULT DISPLAY with the current display id RunningTaskInfo rootTaskInfo = mSplitMultiDisplayHelper.getDisplayRootTaskInfo(DEFAULT_DISPLAY); if (childrenToTop == null || childrenToTop.getTopVisibleChildTaskId() == INVALID_TASK_ID) { mSideStage.removeAllTasks(wct, false /* toTop */); mSideStage.removeAllTasks( wct, false /* toTop */, getNewParentTokenForStage(mSideStage, mRootTDAOrganizer)); deactivateSplit(wct, STAGE_TYPE_UNDEFINED); wct.reorder(rootTaskInfo.token, false /* onTop */); setRootForceTranslucent(true, wct); Loading Loading @@ -1822,7 +1826,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, WindowContainerTransaction finishedWCT = new WindowContainerTransaction(); mIsExiting = false; deactivateSplit(finishedWCT, childrenToTop.getId()); mSideStage.removeAllTasks(finishedWCT, childrenToTop == mSideStage /* toTop */); mSideStage.removeAllTasks( finishedWCT, childrenToTop == mSideStage /* toTop */, getNewParentTokenForStage(mSideStage, mRootTDAOrganizer)); finishedWCT.reorder(rootTaskInfo.token, false /* toTop */); setRootForceTranslucent(true, finishedWCT); finishedWCT.setBounds(mSideStage.mRootTaskInfo.token, mTempRect1); Loading Loading @@ -1981,9 +1987,12 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (enableFlexibleSplit()) { mStageOrderOperator.getActiveStages().stream() .filter(stage -> stage.getId() != stageToTop) .forEach(stage -> stage.removeAllTasks(wct, false /*toTop*/)); .forEach(stage -> stage.removeAllTasks(wct, false /*toTop*/, getNewParentTokenForStage(stage, mRootTDAOrganizer))); } else { mSideStage.removeAllTasks(wct, stageToTop == STAGE_TYPE_SIDE); mSideStage.removeAllTasks(wct, stageToTop == STAGE_TYPE_SIDE, getNewParentTokenForStage(mSideStage, mRootTDAOrganizer)); } if (exitReason != EXIT_REASON_DESKTOP_MODE) { Loading
libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java +7 −5 Original line number Diff line number Diff line Loading @@ -533,10 +533,11 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener { } void deactivate(WindowContainerTransaction wct) { deactivate(wct, false /* toTop */); deactivate(wct, false /* toTop */, null /* newParent */); } void deactivate(WindowContainerTransaction wct, boolean reparentTasksToTop) { void deactivate(WindowContainerTransaction wct, boolean reparentTasksToTop, @Nullable WindowContainerToken newParent) { if (!mIsActive && !enableFlexibleSplit()) return; ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "deactivate: reparentTasksToTop=%b " + "rootTaskInfo=%s stage=%s", Loading @@ -549,7 +550,7 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener { final WindowContainerToken rootToken = mRootTaskInfo.token; wct.reparentTasks( rootToken, null /* newParent */, newParent, null /* windowingModes */, null /* activityTypes */, reparentTasksToTop); Loading @@ -557,14 +558,15 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener { // -------- // Previously only used in SideStage. With flexible split this is called for all stages boolean removeAllTasks(WindowContainerTransaction wct, boolean toTop) { boolean removeAllTasks(WindowContainerTransaction wct, boolean toTop, @Nullable WindowContainerToken newParent) { ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "remove all side stage tasks: childCount=%d toTop=%b " + " stageI=%s", mChildrenTaskInfo.size(), toTop, stageTypeToString(mId)); if (mChildrenTaskInfo.size() == 0) return false; wct.reparentTasks( mRootTaskInfo.token, null /* newParent */, newParent, null /* windowingModes */, null /* activityTypes */, toTop); Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java +7 −2 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER; import static com.android.wm.shell.common.split.SplitScreenUtils.getNewParentTokenForStage; import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_10_90; import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_50_50; import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_90_10; Loading Loading @@ -624,11 +625,15 @@ public class SplitTransitionTests extends ShellTestCase { for (int i = 0; i < wct.getHierarchyOps().size(); ++i) { WindowContainerTransaction.HierarchyOp op = wct.getHierarchyOps().get(i); if (op.getType() == HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT) { IBinder expectedNewParentBinder = Optional.ofNullable( getNewParentTokenForStage(mMainStage, mRootTDAOrganizer)) .map(WindowContainerToken::asBinder) .orElse(null); if (op.getContainer() == mMainStage.mRootTaskInfo.token.asBinder() && op.getNewParent() == null) { && op.getNewParent() == expectedNewParentBinder) { reparentedMain = true; } else if (op.getContainer() == mSideStage.mRootTaskInfo.token.asBinder() && op.getNewParent() == null) { && op.getNewParent() == expectedNewParentBinder) { reparentedSide = true; } } Loading