Loading libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java +1 −1 Original line number Diff line number Diff line Loading @@ -33,7 +33,7 @@ import java.util.Optional; /** * Main stage for split-screen mode. When split-screen is active all standard activity types launch * on the main stage, except for task that are explicitly pinned to the {@link SideStage}. * on the main stage, except for task that are explicitly pinned to the {@link StageTaskListener}. * @see StageCoordinator */ class MainStage extends StageTaskListener { Loading libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.javadeleted 100644 → 0 +0 −73 Original line number Diff line number Diff line /* * Copyright (C) 2020 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 com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN; import android.app.ActivityManager; import android.content.Context; import android.view.SurfaceSession; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import com.android.internal.protolog.ProtoLog; import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.windowdecor.WindowDecorViewModel; import java.util.Optional; /** * Side stage for split-screen mode. Only tasks that are explicitly pinned to this stage show up * here. All other task are launch in the {@link MainStage}. * * @see StageCoordinator */ class SideStage extends StageTaskListener { private static final String TAG = SideStage.class.getSimpleName(); SideStage(Context context, ShellTaskOrganizer taskOrganizer, int displayId, StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue, SurfaceSession surfaceSession, IconProvider iconProvider, Optional<WindowDecorViewModel> windowDecorViewModel) { super(context, taskOrganizer, displayId, callbacks, syncQueue, surfaceSession, iconProvider, windowDecorViewModel); } boolean removeAllTasks(WindowContainerTransaction wct, boolean toTop) { ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "remove all side stage tasks: childCount=%d toTop=%b", mChildrenTaskInfo.size(), toTop); if (mChildrenTaskInfo.size() == 0) return false; wct.reparentTasks( mRootTaskInfo.token, null /* newParent */, null /* windowingModes */, null /* activityTypes */, toTop); return true; } boolean removeTask(int taskId, WindowContainerToken newParent, WindowContainerTransaction wct) { final ActivityManager.RunningTaskInfo task = mChildrenTaskInfo.get(taskId); ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "remove side stage task: task=%d exists=%b", taskId, task != null); if (task == null) return false; wct.reparent(task.token, newParent, false /* onTop */); return true; } } libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java +1 −1 Original line number Diff line number Diff line Loading @@ -50,7 +50,7 @@ public interface SplitScreen { /** * The side stage type. * @see SideStage * @see StageTaskListener */ int STAGE_TYPE_SIDE = 1; Loading libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +39 −30 Original line number Diff line number Diff line Loading @@ -155,13 +155,13 @@ import java.util.concurrent.Executor; /** * Coordinates the staging (visibility, sizing, ...) of the split-screen {@link MainStage} and * {@link SideStage} stages. * other stages. * Some high-level rules: * - The {@link StageCoordinator} is only considered active if the {@link SideStage} contains at * - The {@link StageCoordinator} is only considered active if the other stages contain at * least one child task. * - The {@link MainStage} should only have children if the coordinator is active. * - The {@link SplitLayout} divider is only visible if both the {@link MainStage} * and {@link SideStage} are visible. * and other stages are visible. * - Both stages are put under a single-top root task. * This rules are mostly implemented in {@link #onStageVisibilityChanged(StageListenerImpl)} and * {@link #onStageHasChildrenChanged(StageListenerImpl).} Loading @@ -176,7 +176,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private final MainStage mMainStage; private final StageListenerImpl mMainStageListener = new StageListenerImpl(); private final SideStage mSideStage; private final StageTaskListener mSideStage; private final StageListenerImpl mSideStageListener = new StageListenerImpl(); @SplitPosition private int mSideStagePosition = SPLIT_POSITION_BOTTOM_OR_RIGHT; Loading Loading @@ -338,7 +338,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSurfaceSession, iconProvider, mWindowDecorViewModel); mSideStage = new SideStage( mSideStage = new StageTaskListener( mContext, mTaskOrganizer, mDisplayId, Loading Loading @@ -367,7 +367,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, @VisibleForTesting StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue, ShellTaskOrganizer taskOrganizer, MainStage mainStage, SideStage sideStage, ShellTaskOrganizer taskOrganizer, MainStage mainStage, StageTaskListener sideStage, DisplayController displayController, DisplayImeController displayImeController, DisplayInsetsController displayInsetsController, SplitLayout splitLayout, Transitions transitions, TransactionPool transactionPool, ShellExecutor mainExecutor, Loading Loading @@ -424,6 +424,15 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, return mMainStage.isActive(); } /** * Deactivates main stage by removing the stage from the top level split root (usually when a * task underneath gets removed from the stage root). * @param reparentToTop whether we want to put the stage root back on top */ private void deactivateSplit(WindowContainerTransaction wct, boolean reparentToTop) { mMainStage.deactivate(wct, reparentToTop); } /** @return whether this transition-request has the launch-adjacent flag. */ public boolean requestHasLaunchAdjacentFlag(TransitionRequestInfo request) { final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask(); Loading Loading @@ -498,10 +507,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, /** * {@link MainStage} will be deactivated in {@link #onStageHasChildrenChanged} if the * {@link SideStage} no longer has children. * other stages no longer have children. */ final boolean result = mSideStage.removeTask(taskId, mMainStage.isActive() ? mMainStage.mRootTaskInfo.token : null, isSplitActive() ? mMainStage.mRootTaskInfo.token : null, wct); mTaskOrganizer.applyTransaction(wct); return result; Loading Loading @@ -618,7 +627,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } // If split screen is not activated, we're expecting to open a pair of apps to split. final int extraTransitType = mMainStage.isActive() final int extraTransitType = isSplitActive() ? TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE : TRANSIT_SPLIT_SCREEN_PAIR_OPEN; prepareEnterSplitScreen(wct, null /* taskInfo */, position, !mIsDropEntering); Loading Loading @@ -661,7 +670,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } // If split screen is not activated, we're expecting to open a pair of apps to split. final int extraTransitType = mMainStage.isActive() final int extraTransitType = isSplitActive() ? TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE : TRANSIT_SPLIT_SCREEN_PAIR_OPEN; prepareEnterSplitScreen(wct, null /* taskInfo */, position, !mIsDropEntering); Loading Loading @@ -793,7 +802,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private void startWithTask(WindowContainerTransaction wct, int mainTaskId, @Nullable Bundle mainOptions, @PersistentSnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) { if (!mMainStage.isActive()) { if (!isSplitActive()) { // Build a request WCT that will launch both apps such that task 0 is on the main stage // while task 1 is on the side stage. mMainStage.activate(wct, false /* reparent */); Loading Loading @@ -860,7 +869,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, return; } if (!mMainStage.isActive()) { if (!isSplitActive()) { // Build a request WCT that will launch both apps such that task 0 is on the main stage // while task 1 is on the side stage. mMainStage.activate(wct, false /* reparent */); Loading Loading @@ -1110,7 +1119,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, */ void onKeyguardStateChanged(boolean active, boolean occludingTaskRunning) { mKeyguardActive = active; if (!mMainStage.isActive()) { if (!isSplitActive()) { return; } ProtoLog.d(WM_SHELL_SPLIT_SCREEN, Loading Loading @@ -1154,7 +1163,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, * will do a no-op. */ void dismissSplitKeepingLastActiveStage(@ExitReason int reason) { if (!mMainStage.isActive() || mLastActiveStage == STAGE_TYPE_UNDEFINED) { if (!isSplitActive() || mLastActiveStage == STAGE_TYPE_UNDEFINED) { // no-op return; } Loading @@ -1177,8 +1186,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private void exitSplitScreen(@Nullable StageTaskListener childrenToTop, @ExitReason int exitReason) { ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "exitSplitScreen: mainStageToTop=%b reason=%s active=%b", childrenToTop == mMainStage, exitReasonToString(exitReason), mMainStage.isActive()); if (!mMainStage.isActive()) return; childrenToTop == mMainStage, exitReasonToString(exitReason), isSplitActive()); if (!isSplitActive()) return; final WindowContainerTransaction wct = new WindowContainerTransaction(); applyExitSplitScreen(childrenToTop, wct, exitReason); Loading @@ -1188,7 +1197,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, WindowContainerTransaction wct, @ExitReason int exitReason) { ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "applyExitSplitScreen: reason=%s", exitReasonToString(exitReason)); if (!mMainStage.isActive() || mIsExiting) return; if (!isSplitActive() || mIsExiting) return; onSplitScreenExit(); clearSplitPairedInRecents(exitReason); Loading @@ -1200,7 +1209,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitLayout.getInvisibleBounds(mTempRect1); if (childrenToTop == null || childrenToTop.getTopVisibleChildTaskId() == INVALID_TASK_ID) { mSideStage.removeAllTasks(wct, false /* toTop */); mMainStage.deactivate(wct, false /* toTop */); deactivateSplit(wct, false /* reparentToTop */); wct.reorder(mRootTaskInfo.token, false /* onTop */); setRootForceTranslucent(true, wct); wct.setBounds(mSideStage.mRootTaskInfo.token, mTempRect1); Loading Loading @@ -1229,7 +1238,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, childrenToTop.fadeOutDecor(() -> { WindowContainerTransaction finishedWCT = new WindowContainerTransaction(); mIsExiting = false; mMainStage.deactivate(finishedWCT, childrenToTop == mMainStage /* toTop */); deactivateSplit(finishedWCT, childrenToTop == mMainStage /* reparentToTop */); mSideStage.removeAllTasks(finishedWCT, childrenToTop == mSideStage /* toTop */); finishedWCT.reorder(mRootTaskInfo.token, false /* toTop */); setRootForceTranslucent(true, finishedWCT); Loading @@ -1252,7 +1261,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } void dismissSplitScreen(int toTopTaskId, @ExitReason int exitReason) { if (!mMainStage.isActive()) return; if (!isSplitActive()) return; final int stage = getStageOfTask(toTopTaskId); final WindowContainerTransaction wct = new WindowContainerTransaction(); prepareExitSplitScreen(stage, wct); Loading Loading @@ -1362,10 +1371,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, */ void prepareExitSplitScreen(@StageType int stageToTop, @NonNull WindowContainerTransaction wct) { if (!mMainStage.isActive()) return; if (!isSplitActive()) return; ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "prepareExitSplitScreen: stageToTop=%d", stageToTop); mSideStage.removeAllTasks(wct, stageToTop == STAGE_TYPE_SIDE); mMainStage.deactivate(wct, stageToTop == STAGE_TYPE_MAIN); deactivateSplit(wct, stageToTop == STAGE_TYPE_MAIN); } private void prepareEnterSplitScreen(WindowContainerTransaction wct) { Loading Loading @@ -1662,7 +1671,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mRootTaskInfo = taskInfo; if (mSplitLayout != null && mSplitLayout.updateConfiguration(mRootTaskInfo.configuration) && mMainStage.isActive()) { && isSplitActive()) { ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskInfoChanged: task=%d updating", taskInfo.taskId); // Clear the divider remote animating flag as the divider will be re-rendered to apply Loading Loading @@ -1916,7 +1925,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, stageListener == mMainStageListener); final boolean hasChildren = stageListener.mHasChildren; final boolean isSideStage = stageListener == mSideStageListener; if (!hasChildren && !mIsExiting && mMainStage.isActive()) { if (!hasChildren && !mIsExiting && isSplitActive()) { if (isSideStage && mMainStageListener.mVisible) { // Exit to main stage if side stage no longer has children. mSplitLayout.flingDividerToDismiss( Loading @@ -1931,7 +1940,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // Dismiss split screen in the background once any sides of the split become empty. exitSplitScreen(null /* childrenToTop */, EXIT_REASON_APP_FINISHED); } } else if (isSideStage && hasChildren && !mMainStage.isActive()) { } else if (isSideStage && hasChildren && !isSplitActive()) { final WindowContainerTransaction wct = new WindowContainerTransaction(); prepareEnterSplitScreen(wct); Loading Loading @@ -2146,7 +2155,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private void onDisplayChange(int displayId, int fromRotation, int toRotation, @Nullable DisplayAreaInfo newDisplayAreaInfo, WindowContainerTransaction wct) { if (displayId != DEFAULT_DISPLAY || !mMainStage.isActive()) { if (displayId != DEFAULT_DISPLAY || !isSplitActive()) { return; } Loading Loading @@ -2441,7 +2450,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // Not entering or exiting, so just do some house-keeping and validation. // If we're not in split-mode, just abort so something else can handle it. if (!mMainStage.isActive()) return false; if (!isSplitActive()) return false; ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startAnimation: transition=%d", info.getDebugId()); mSplitLayout.setFreezeDividerWindow(false); Loading Loading @@ -2683,7 +2692,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, public void onTransitionAnimationComplete() { ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTransitionAnimationComplete"); // If still playing, let it finish. if (!mMainStage.isActive() && !mIsExiting) { if (!isSplitActive() && !mIsExiting) { // Update divider state after animation so that it is still around and positioned // properly for the animation itself. mSplitLayout.release(); Loading Loading @@ -3147,7 +3156,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, + (mSplitLayout != null ? mSplitLayout.isLeftRightSplit() : "null")); pw.println(innerPrefix + "MainStage"); pw.println(childPrefix + "stagePosition=" + splitPositionToString(getMainStagePosition())); pw.println(childPrefix + "isActive=" + mMainStage.isActive()); pw.println(childPrefix + "isActive=" + isSplitActive()); mMainStage.dump(pw, childPrefix); pw.println(innerPrefix + "MainStageListener"); mMainStageListener.dump(pw, childPrefix); Loading Loading @@ -3267,7 +3276,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, @Override public void onNoLongerSupportMultiWindow(ActivityManager.RunningTaskInfo taskInfo) { ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onNoLongerSupportMultiWindow: task=%s", taskInfo); if (mMainStage.isActive()) { if (isSplitActive()) { final boolean isMainStage = mMainStageListener == this; // If visible, we preserve the app and keep it running. If an app becomes Loading libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java +24 −0 Original line number Diff line number Diff line Loading @@ -475,6 +475,30 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener { }); } // -------- // Previously only used in SideStage boolean removeAllTasks(WindowContainerTransaction wct, boolean toTop) { ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "remove all side stage tasks: childCount=%d toTop=%b", mChildrenTaskInfo.size(), toTop); if (mChildrenTaskInfo.size() == 0) return false; wct.reparentTasks( mRootTaskInfo.token, null /* newParent */, null /* windowingModes */, null /* activityTypes */, toTop); return true; } boolean removeTask(int taskId, WindowContainerToken newParent, WindowContainerTransaction wct) { final ActivityManager.RunningTaskInfo task = mChildrenTaskInfo.get(taskId); ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "remove side stage task: task=%d exists=%b", taskId, task != null); if (task == null) return false; wct.reparent(task.token, newParent, false /* onTop */); return true; } private void sendStatusChanged() { mCallbacks.onStatusChanged(mRootTaskInfo.isVisible, mChildrenTaskInfo.size() > 0); } Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java +1 −1 Original line number Diff line number Diff line Loading @@ -33,7 +33,7 @@ import java.util.Optional; /** * Main stage for split-screen mode. When split-screen is active all standard activity types launch * on the main stage, except for task that are explicitly pinned to the {@link SideStage}. * on the main stage, except for task that are explicitly pinned to the {@link StageTaskListener}. * @see StageCoordinator */ class MainStage extends StageTaskListener { Loading
libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.javadeleted 100644 → 0 +0 −73 Original line number Diff line number Diff line /* * Copyright (C) 2020 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 com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN; import android.app.ActivityManager; import android.content.Context; import android.view.SurfaceSession; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import com.android.internal.protolog.ProtoLog; import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.windowdecor.WindowDecorViewModel; import java.util.Optional; /** * Side stage for split-screen mode. Only tasks that are explicitly pinned to this stage show up * here. All other task are launch in the {@link MainStage}. * * @see StageCoordinator */ class SideStage extends StageTaskListener { private static final String TAG = SideStage.class.getSimpleName(); SideStage(Context context, ShellTaskOrganizer taskOrganizer, int displayId, StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue, SurfaceSession surfaceSession, IconProvider iconProvider, Optional<WindowDecorViewModel> windowDecorViewModel) { super(context, taskOrganizer, displayId, callbacks, syncQueue, surfaceSession, iconProvider, windowDecorViewModel); } boolean removeAllTasks(WindowContainerTransaction wct, boolean toTop) { ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "remove all side stage tasks: childCount=%d toTop=%b", mChildrenTaskInfo.size(), toTop); if (mChildrenTaskInfo.size() == 0) return false; wct.reparentTasks( mRootTaskInfo.token, null /* newParent */, null /* windowingModes */, null /* activityTypes */, toTop); return true; } boolean removeTask(int taskId, WindowContainerToken newParent, WindowContainerTransaction wct) { final ActivityManager.RunningTaskInfo task = mChildrenTaskInfo.get(taskId); ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "remove side stage task: task=%d exists=%b", taskId, task != null); if (task == null) return false; wct.reparent(task.token, newParent, false /* onTop */); return true; } }
libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java +1 −1 Original line number Diff line number Diff line Loading @@ -50,7 +50,7 @@ public interface SplitScreen { /** * The side stage type. * @see SideStage * @see StageTaskListener */ int STAGE_TYPE_SIDE = 1; Loading
libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +39 −30 Original line number Diff line number Diff line Loading @@ -155,13 +155,13 @@ import java.util.concurrent.Executor; /** * Coordinates the staging (visibility, sizing, ...) of the split-screen {@link MainStage} and * {@link SideStage} stages. * other stages. * Some high-level rules: * - The {@link StageCoordinator} is only considered active if the {@link SideStage} contains at * - The {@link StageCoordinator} is only considered active if the other stages contain at * least one child task. * - The {@link MainStage} should only have children if the coordinator is active. * - The {@link SplitLayout} divider is only visible if both the {@link MainStage} * and {@link SideStage} are visible. * and other stages are visible. * - Both stages are put under a single-top root task. * This rules are mostly implemented in {@link #onStageVisibilityChanged(StageListenerImpl)} and * {@link #onStageHasChildrenChanged(StageListenerImpl).} Loading @@ -176,7 +176,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private final MainStage mMainStage; private final StageListenerImpl mMainStageListener = new StageListenerImpl(); private final SideStage mSideStage; private final StageTaskListener mSideStage; private final StageListenerImpl mSideStageListener = new StageListenerImpl(); @SplitPosition private int mSideStagePosition = SPLIT_POSITION_BOTTOM_OR_RIGHT; Loading Loading @@ -338,7 +338,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSurfaceSession, iconProvider, mWindowDecorViewModel); mSideStage = new SideStage( mSideStage = new StageTaskListener( mContext, mTaskOrganizer, mDisplayId, Loading Loading @@ -367,7 +367,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, @VisibleForTesting StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue, ShellTaskOrganizer taskOrganizer, MainStage mainStage, SideStage sideStage, ShellTaskOrganizer taskOrganizer, MainStage mainStage, StageTaskListener sideStage, DisplayController displayController, DisplayImeController displayImeController, DisplayInsetsController displayInsetsController, SplitLayout splitLayout, Transitions transitions, TransactionPool transactionPool, ShellExecutor mainExecutor, Loading Loading @@ -424,6 +424,15 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, return mMainStage.isActive(); } /** * Deactivates main stage by removing the stage from the top level split root (usually when a * task underneath gets removed from the stage root). * @param reparentToTop whether we want to put the stage root back on top */ private void deactivateSplit(WindowContainerTransaction wct, boolean reparentToTop) { mMainStage.deactivate(wct, reparentToTop); } /** @return whether this transition-request has the launch-adjacent flag. */ public boolean requestHasLaunchAdjacentFlag(TransitionRequestInfo request) { final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask(); Loading Loading @@ -498,10 +507,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, /** * {@link MainStage} will be deactivated in {@link #onStageHasChildrenChanged} if the * {@link SideStage} no longer has children. * other stages no longer have children. */ final boolean result = mSideStage.removeTask(taskId, mMainStage.isActive() ? mMainStage.mRootTaskInfo.token : null, isSplitActive() ? mMainStage.mRootTaskInfo.token : null, wct); mTaskOrganizer.applyTransaction(wct); return result; Loading Loading @@ -618,7 +627,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } // If split screen is not activated, we're expecting to open a pair of apps to split. final int extraTransitType = mMainStage.isActive() final int extraTransitType = isSplitActive() ? TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE : TRANSIT_SPLIT_SCREEN_PAIR_OPEN; prepareEnterSplitScreen(wct, null /* taskInfo */, position, !mIsDropEntering); Loading Loading @@ -661,7 +670,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } // If split screen is not activated, we're expecting to open a pair of apps to split. final int extraTransitType = mMainStage.isActive() final int extraTransitType = isSplitActive() ? TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE : TRANSIT_SPLIT_SCREEN_PAIR_OPEN; prepareEnterSplitScreen(wct, null /* taskInfo */, position, !mIsDropEntering); Loading Loading @@ -793,7 +802,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private void startWithTask(WindowContainerTransaction wct, int mainTaskId, @Nullable Bundle mainOptions, @PersistentSnapPosition int snapPosition, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) { if (!mMainStage.isActive()) { if (!isSplitActive()) { // Build a request WCT that will launch both apps such that task 0 is on the main stage // while task 1 is on the side stage. mMainStage.activate(wct, false /* reparent */); Loading Loading @@ -860,7 +869,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, return; } if (!mMainStage.isActive()) { if (!isSplitActive()) { // Build a request WCT that will launch both apps such that task 0 is on the main stage // while task 1 is on the side stage. mMainStage.activate(wct, false /* reparent */); Loading Loading @@ -1110,7 +1119,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, */ void onKeyguardStateChanged(boolean active, boolean occludingTaskRunning) { mKeyguardActive = active; if (!mMainStage.isActive()) { if (!isSplitActive()) { return; } ProtoLog.d(WM_SHELL_SPLIT_SCREEN, Loading Loading @@ -1154,7 +1163,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, * will do a no-op. */ void dismissSplitKeepingLastActiveStage(@ExitReason int reason) { if (!mMainStage.isActive() || mLastActiveStage == STAGE_TYPE_UNDEFINED) { if (!isSplitActive() || mLastActiveStage == STAGE_TYPE_UNDEFINED) { // no-op return; } Loading @@ -1177,8 +1186,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private void exitSplitScreen(@Nullable StageTaskListener childrenToTop, @ExitReason int exitReason) { ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "exitSplitScreen: mainStageToTop=%b reason=%s active=%b", childrenToTop == mMainStage, exitReasonToString(exitReason), mMainStage.isActive()); if (!mMainStage.isActive()) return; childrenToTop == mMainStage, exitReasonToString(exitReason), isSplitActive()); if (!isSplitActive()) return; final WindowContainerTransaction wct = new WindowContainerTransaction(); applyExitSplitScreen(childrenToTop, wct, exitReason); Loading @@ -1188,7 +1197,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, WindowContainerTransaction wct, @ExitReason int exitReason) { ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "applyExitSplitScreen: reason=%s", exitReasonToString(exitReason)); if (!mMainStage.isActive() || mIsExiting) return; if (!isSplitActive() || mIsExiting) return; onSplitScreenExit(); clearSplitPairedInRecents(exitReason); Loading @@ -1200,7 +1209,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitLayout.getInvisibleBounds(mTempRect1); if (childrenToTop == null || childrenToTop.getTopVisibleChildTaskId() == INVALID_TASK_ID) { mSideStage.removeAllTasks(wct, false /* toTop */); mMainStage.deactivate(wct, false /* toTop */); deactivateSplit(wct, false /* reparentToTop */); wct.reorder(mRootTaskInfo.token, false /* onTop */); setRootForceTranslucent(true, wct); wct.setBounds(mSideStage.mRootTaskInfo.token, mTempRect1); Loading Loading @@ -1229,7 +1238,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, childrenToTop.fadeOutDecor(() -> { WindowContainerTransaction finishedWCT = new WindowContainerTransaction(); mIsExiting = false; mMainStage.deactivate(finishedWCT, childrenToTop == mMainStage /* toTop */); deactivateSplit(finishedWCT, childrenToTop == mMainStage /* reparentToTop */); mSideStage.removeAllTasks(finishedWCT, childrenToTop == mSideStage /* toTop */); finishedWCT.reorder(mRootTaskInfo.token, false /* toTop */); setRootForceTranslucent(true, finishedWCT); Loading @@ -1252,7 +1261,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } void dismissSplitScreen(int toTopTaskId, @ExitReason int exitReason) { if (!mMainStage.isActive()) return; if (!isSplitActive()) return; final int stage = getStageOfTask(toTopTaskId); final WindowContainerTransaction wct = new WindowContainerTransaction(); prepareExitSplitScreen(stage, wct); Loading Loading @@ -1362,10 +1371,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, */ void prepareExitSplitScreen(@StageType int stageToTop, @NonNull WindowContainerTransaction wct) { if (!mMainStage.isActive()) return; if (!isSplitActive()) return; ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "prepareExitSplitScreen: stageToTop=%d", stageToTop); mSideStage.removeAllTasks(wct, stageToTop == STAGE_TYPE_SIDE); mMainStage.deactivate(wct, stageToTop == STAGE_TYPE_MAIN); deactivateSplit(wct, stageToTop == STAGE_TYPE_MAIN); } private void prepareEnterSplitScreen(WindowContainerTransaction wct) { Loading Loading @@ -1662,7 +1671,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mRootTaskInfo = taskInfo; if (mSplitLayout != null && mSplitLayout.updateConfiguration(mRootTaskInfo.configuration) && mMainStage.isActive()) { && isSplitActive()) { ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskInfoChanged: task=%d updating", taskInfo.taskId); // Clear the divider remote animating flag as the divider will be re-rendered to apply Loading Loading @@ -1916,7 +1925,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, stageListener == mMainStageListener); final boolean hasChildren = stageListener.mHasChildren; final boolean isSideStage = stageListener == mSideStageListener; if (!hasChildren && !mIsExiting && mMainStage.isActive()) { if (!hasChildren && !mIsExiting && isSplitActive()) { if (isSideStage && mMainStageListener.mVisible) { // Exit to main stage if side stage no longer has children. mSplitLayout.flingDividerToDismiss( Loading @@ -1931,7 +1940,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // Dismiss split screen in the background once any sides of the split become empty. exitSplitScreen(null /* childrenToTop */, EXIT_REASON_APP_FINISHED); } } else if (isSideStage && hasChildren && !mMainStage.isActive()) { } else if (isSideStage && hasChildren && !isSplitActive()) { final WindowContainerTransaction wct = new WindowContainerTransaction(); prepareEnterSplitScreen(wct); Loading Loading @@ -2146,7 +2155,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private void onDisplayChange(int displayId, int fromRotation, int toRotation, @Nullable DisplayAreaInfo newDisplayAreaInfo, WindowContainerTransaction wct) { if (displayId != DEFAULT_DISPLAY || !mMainStage.isActive()) { if (displayId != DEFAULT_DISPLAY || !isSplitActive()) { return; } Loading Loading @@ -2441,7 +2450,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // Not entering or exiting, so just do some house-keeping and validation. // If we're not in split-mode, just abort so something else can handle it. if (!mMainStage.isActive()) return false; if (!isSplitActive()) return false; ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "startAnimation: transition=%d", info.getDebugId()); mSplitLayout.setFreezeDividerWindow(false); Loading Loading @@ -2683,7 +2692,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, public void onTransitionAnimationComplete() { ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTransitionAnimationComplete"); // If still playing, let it finish. if (!mMainStage.isActive() && !mIsExiting) { if (!isSplitActive() && !mIsExiting) { // Update divider state after animation so that it is still around and positioned // properly for the animation itself. mSplitLayout.release(); Loading Loading @@ -3147,7 +3156,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, + (mSplitLayout != null ? mSplitLayout.isLeftRightSplit() : "null")); pw.println(innerPrefix + "MainStage"); pw.println(childPrefix + "stagePosition=" + splitPositionToString(getMainStagePosition())); pw.println(childPrefix + "isActive=" + mMainStage.isActive()); pw.println(childPrefix + "isActive=" + isSplitActive()); mMainStage.dump(pw, childPrefix); pw.println(innerPrefix + "MainStageListener"); mMainStageListener.dump(pw, childPrefix); Loading Loading @@ -3267,7 +3276,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, @Override public void onNoLongerSupportMultiWindow(ActivityManager.RunningTaskInfo taskInfo) { ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onNoLongerSupportMultiWindow: task=%s", taskInfo); if (mMainStage.isActive()) { if (isSplitActive()) { final boolean isMainStage = mMainStageListener == this; // If visible, we preserve the app and keep it running. If an app becomes Loading
libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java +24 −0 Original line number Diff line number Diff line Loading @@ -475,6 +475,30 @@ public class StageTaskListener implements ShellTaskOrganizer.TaskListener { }); } // -------- // Previously only used in SideStage boolean removeAllTasks(WindowContainerTransaction wct, boolean toTop) { ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "remove all side stage tasks: childCount=%d toTop=%b", mChildrenTaskInfo.size(), toTop); if (mChildrenTaskInfo.size() == 0) return false; wct.reparentTasks( mRootTaskInfo.token, null /* newParent */, null /* windowingModes */, null /* activityTypes */, toTop); return true; } boolean removeTask(int taskId, WindowContainerToken newParent, WindowContainerTransaction wct) { final ActivityManager.RunningTaskInfo task = mChildrenTaskInfo.get(taskId); ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "remove side stage task: task=%d exists=%b", taskId, task != null); if (task == null) return false; wct.reparent(task.token, newParent, false /* onTop */); return true; } private void sendStatusChanged() { mCallbacks.onStatusChanged(mRootTaskInfo.isVisible, mChildrenTaskInfo.size() > 0); } Loading