diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java index 1a38fcfba5a08..1b87945ec985c 100644 --- a/core/java/android/app/TaskInfo.java +++ b/core/java/android/app/TaskInfo.java @@ -472,7 +472,8 @@ public class TaskInfo { && isFocused == that.isFocused && isVisible == that.isVisible && isSleeping == that.isSleeping - && Objects.equals(mTopActivityLocusId, that.mTopActivityLocusId); + && Objects.equals(mTopActivityLocusId, that.mTopActivityLocusId) + && parentTaskId == that.parentTaskId; } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java index 8483f070d06ae..06f4367752fb1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java @@ -119,8 +119,6 @@ public final class ShellCommandHandlerImpl { return runRemoveFromSideStage(args, pw); case "setSideStagePosition": return runSetSideStagePosition(args, pw); - case "setSideStageVisibility": - return runSetSideStageVisibility(args, pw); case "help": return runHelp(pw); default: @@ -186,18 +184,6 @@ public final class ShellCommandHandlerImpl { return true; } - private boolean runSetSideStageVisibility(String[] args, PrintWriter pw) { - if (args.length < 3) { - // First arguments are "WMShell" and command name. - pw.println("Error: side stage visibility should be provided as arguments"); - return false; - } - final Boolean visible = new Boolean(args[2]); - - mSplitScreenOptional.ifPresent(split -> split.setSideStageVisibility(visible)); - return true; - } - private boolean runHelp(PrintWriter pw) { pw.println("Window Manager Shell commands:"); pw.println(" help"); @@ -215,8 +201,6 @@ public final class ShellCommandHandlerImpl { pw.println(" Enable/Disable outline on the side-stage."); pw.println(" setSideStagePosition "); pw.println(" Sets the position of the side-stage."); - pw.println(" setSideStageVisibility "); - pw.println(" Show/hide side-stage."); return true; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java index 33eec335bca33..e528df8c89b40 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java @@ -19,7 +19,6 @@ package com.android.wm.shell.apppairs; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; -import static android.view.WindowManagerPolicyConstants.SPLIT_DIVIDER_LAYER; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; @@ -83,7 +82,7 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.SplitLayou public void onLeashReady(SurfaceControl leash) { mSyncQueue.runInSync(t -> t .show(leash) - .setLayer(leash, SPLIT_DIVIDER_LAYER) + .setLayer(leash, Integer.MAX_VALUE) .setPosition(leash, mSplitLayout.getDividerBounds().left, mSplitLayout.getDividerBounds().top)); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java index 3ac7cbfe9c2e6..5dc6bd19853a4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java @@ -21,7 +21,6 @@ import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; -import static android.view.WindowManagerPolicyConstants.SPLIT_DIVIDER_LAYER; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -162,7 +161,7 @@ public class SplitDecorManager extends WindowlessWindowManager { mBackgroundLeash = SurfaceUtils.makeColorLayer(mHostLeash, RESIZING_BACKGROUND_SURFACE_NAME, mSurfaceSession); t.setColor(mBackgroundLeash, getResizingBackgroundColor(resizingTask)) - .setLayer(mBackgroundLeash, SPLIT_DIVIDER_LAYER - 1); + .setLayer(mBackgroundLeash, Integer.MAX_VALUE - 1); } if (mIcon == null && resizingTask.topActivityInfo != null) { @@ -175,7 +174,7 @@ public class SplitDecorManager extends WindowlessWindowManager { lp.width = mIcon.getIntrinsicWidth(); lp.height = mIcon.getIntrinsicHeight(); mViewHost.relayout(lp); - t.setLayer(mIconLeash, SPLIT_DIVIDER_LAYER); + t.setLayer(mIconLeash, Integer.MAX_VALUE); } t.setPosition(mIconLeash, newBounds.width() / 2 - mIcon.getIntrinsicWidth() / 2, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java index d05a4df2a4cce..116d3524e711d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java @@ -23,7 +23,6 @@ import static android.view.WindowManager.DOCKED_INVALID; import static android.view.WindowManager.DOCKED_LEFT; import static android.view.WindowManager.DOCKED_RIGHT; import static android.view.WindowManager.DOCKED_TOP; -import static android.view.WindowManagerPolicyConstants.SPLIT_DIVIDER_LAYER; import static com.android.internal.policy.DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_END; import static com.android.internal.policy.DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_START; @@ -202,24 +201,24 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange /** Applies new configuration, returns {@code false} if there's no effect to the layout. */ public boolean updateConfiguration(Configuration configuration) { + // Always update configuration after orientation changed to make sure to render divider bar + // with proper resources that matching screen orientation. + final int orientation = configuration.orientation; + if (mOrientation != orientation) { + mContext = mContext.createConfigurationContext(configuration); + mSplitWindowManager.setConfiguration(configuration); + mOrientation = orientation; + } + // Update the split bounds when necessary. Besides root bounds changed, split bounds need to // be updated when the rotation changed to cover the case that users rotated the screen 180 // degrees. - // Make sure to render the divider bar with proper resources that matching the screen - // orientation. final int rotation = configuration.windowConfiguration.getRotation(); final Rect rootBounds = configuration.windowConfiguration.getBounds(); - final int orientation = configuration.orientation; - - if (mOrientation == orientation - && rotation == mRotation - && mRootBounds.equals(rootBounds)) { + if (mRotation == rotation && mRootBounds.equals(rootBounds)) { return false; } - mContext = mContext.createConfigurationContext(configuration); - mSplitWindowManager.setConfiguration(configuration); - mOrientation = orientation; mTempRect.set(mRootBounds); mRootBounds.set(rootBounds); mRotation = rotation; @@ -493,7 +492,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange mTempRect.set(getRefDividerBounds()); t.setPosition(dividerLeash, mTempRect.left, mTempRect.top); // Resets layer of divider bar to make sure it is always on top. - t.setLayer(dividerLeash, SPLIT_DIVIDER_LAYER); + t.setLayer(dividerLeash, Integer.MAX_VALUE); } mTempRect.set(getRefBounds1()); t.setPosition(leash1, mTempRect.left, mTempRect.top) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl index 13137faa529fb..9adf1961ebf51 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl @@ -41,11 +41,6 @@ interface ISplitScreen { */ oneway void unregisterSplitScreenListener(in ISplitScreenListener listener) = 2; - /** - * Hides the side-stage if it is currently visible. - */ - oneway void setSideStageVisibility(boolean visible) = 3; - /** * Removes a task from the side stage. */ 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 22dd9b953be7a..ae5e075c4d3f7 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 @@ -52,9 +52,6 @@ class MainStage extends StageTaskListener { if (mIsActive) return; final WindowContainerToken rootToken = mRootTaskInfo.token; - // Moving the root task to top after the child tasks were re-parented , or the root - // task cannot be visible and focused. - wct.reorder(rootToken, true /* onTop */); if (includingTopTask) { wct.reparentTasks( null /* currentParent */, @@ -83,9 +80,6 @@ class MainStage extends StageTaskListener { null /* newParent */, CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE, CONTROLLED_ACTIVITY_TYPES, - toTop) - // We want this re-order to the bottom regardless since we are re-parenting - // all its tasks. - .reorder(rootToken, false /* onTop */); + toTop); } } 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 122fc9f5f780c..d55619f5e5ed2 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 @@ -45,9 +45,6 @@ class SideStage extends StageTaskListener { } 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. - wct.reorder(mRootTaskInfo.token, false /* onTop */); if (mChildrenTaskInfo.size() == 0) return false; wct.reparentTasks( mRootTaskInfo.token, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitDecorManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitDecorManager.java deleted file mode 100644 index 8e5cc6d47edd2..0000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitDecorManager.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (C) 2021 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 android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; -import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; -import static android.view.WindowManagerPolicyConstants.SPLIT_DIVIDER_LAYER; - -import android.app.ActivityManager; -import android.content.Context; -import android.content.res.Configuration; -import android.graphics.Color; -import android.graphics.PixelFormat; -import android.graphics.Rect; -import android.graphics.drawable.Drawable; -import android.os.Binder; -import android.view.IWindow; -import android.view.LayoutInflater; -import android.view.SurfaceControl; -import android.view.SurfaceControlViewHost; -import android.view.SurfaceSession; -import android.view.View; -import android.view.WindowManager; -import android.view.WindowlessWindowManager; -import android.widget.FrameLayout; -import android.widget.ImageView; - -import com.android.launcher3.icons.IconProvider; -import com.android.wm.shell.R; -import com.android.wm.shell.common.SurfaceUtils; - -/** - * Handles split decor like showing resizing hint for a specific split. - */ -class SplitDecorManager extends WindowlessWindowManager { - private static final String TAG = SplitDecorManager.class.getSimpleName(); - private static final String RESIZING_BACKGROUND_SURFACE_NAME = "ResizingBackground"; - - private final IconProvider mIconProvider; - private final SurfaceSession mSurfaceSession; - - private Drawable mIcon; - private ImageView mResizingIconView; - private SurfaceControlViewHost mViewHost; - private SurfaceControl mHostLeash; - private SurfaceControl mIconLeash; - private SurfaceControl mBackgroundLeash; - - SplitDecorManager(Configuration configuration, IconProvider iconProvider, - SurfaceSession surfaceSession) { - super(configuration, null /* rootSurface */, null /* hostInputToken */); - mIconProvider = iconProvider; - mSurfaceSession = surfaceSession; - } - - @Override - protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) { - // Can't set position for the ViewRootImpl SC directly. Create a leash to manipulate later. - final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession()) - .setContainerLayer() - .setName(TAG) - .setHidden(true) - .setParent(mHostLeash) - .setCallsite("SplitDecorManager#attachToParentSurface"); - mIconLeash = builder.build(); - b.setParent(mIconLeash); - } - - void inflate(Context context, SurfaceControl rootLeash, Rect rootBounds) { - if (mIconLeash != null && mViewHost != null) { - return; - } - - context = context.createWindowContext(context.getDisplay(), TYPE_APPLICATION_OVERLAY, - null /* options */); - mHostLeash = rootLeash; - mViewHost = new SurfaceControlViewHost(context, context.getDisplay(), this); - - final FrameLayout rootLayout = (FrameLayout) LayoutInflater.from(context) - .inflate(R.layout.split_decor, null); - mResizingIconView = rootLayout.findViewById(R.id.split_resizing_icon); - - final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( - 0 /* width */, 0 /* height */, TYPE_APPLICATION_OVERLAY, - FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCHABLE, PixelFormat.TRANSLUCENT); - lp.width = rootBounds.width(); - lp.height = rootBounds.height(); - lp.token = new Binder(); - lp.setTitle(TAG); - lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY; - // TODO(b/189839391): Set INPUT_FEATURE_NO_INPUT_CHANNEL after WM supports - // TRUSTED_OVERLAY for windowless window without input channel. - mViewHost.setView(rootLayout, lp); - } - - void release(SurfaceControl.Transaction t) { - if (mViewHost != null) { - mViewHost.release(); - mViewHost = null; - } - if (mIconLeash != null) { - t.remove(mIconLeash); - mIconLeash = null; - } - if (mBackgroundLeash != null) { - t.remove(mBackgroundLeash); - mBackgroundLeash = null; - } - mHostLeash = null; - mIcon = null; - mResizingIconView = null; - } - - /** Showing resizing hint. */ - void onResizing(ActivityManager.RunningTaskInfo resizingTask, Rect newBounds, - SurfaceControl.Transaction t) { - if (mResizingIconView == null) { - return; - } - - if (mIcon == null) { - // TODO: add fade-in animation. - mBackgroundLeash = SurfaceUtils.makeColorLayer(mHostLeash, - RESIZING_BACKGROUND_SURFACE_NAME, mSurfaceSession); - t.setColor(mBackgroundLeash, getResizingBackgroundColor(resizingTask)) - .setLayer(mBackgroundLeash, SPLIT_DIVIDER_LAYER - 1) - .show(mBackgroundLeash); - - mIcon = mIconProvider.getIcon(resizingTask.topActivityInfo); - mResizingIconView.setImageDrawable(mIcon); - mResizingIconView.setVisibility(View.VISIBLE); - - WindowManager.LayoutParams lp = - (WindowManager.LayoutParams) mViewHost.getView().getLayoutParams(); - lp.width = mIcon.getIntrinsicWidth(); - lp.height = mIcon.getIntrinsicHeight(); - mViewHost.relayout(lp); - t.show(mIconLeash).setLayer(mIconLeash, SPLIT_DIVIDER_LAYER); - } - - t.setPosition(mIconLeash, - newBounds.width() / 2 - mIcon.getIntrinsicWidth() / 2, - newBounds.height() / 2 - mIcon.getIntrinsicWidth() / 2); - } - - /** Stops showing resizing hint. */ - void onResized(Rect newBounds, SurfaceControl.Transaction t) { - if (mResizingIconView == null) { - return; - } - - if (mIcon != null) { - mResizingIconView.setVisibility(View.GONE); - mResizingIconView.setImageDrawable(null); - t.remove(mBackgroundLeash).hide(mIconLeash); - mIcon = null; - mBackgroundLeash = null; - } - } - - private static float[] getResizingBackgroundColor(ActivityManager.RunningTaskInfo taskInfo) { - final int taskBgColor = taskInfo.taskDescription.getBackgroundColor(); - return Color.valueOf(taskBgColor == -1 ? Color.WHITE : taskBgColor).getComponents(); - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index 2da5becae8f5c..f20870ff0b2d9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -188,7 +188,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, if (mStageCoordinator == null) { // TODO: Multi-display mStageCoordinator = new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue, - mRootTDAOrganizer, mTaskOrganizer, mDisplayController, mDisplayImeController, + mTaskOrganizer, mDisplayController, mDisplayImeController, mDisplayInsetsController, mTransitions, mTransactionPool, mLogger, mIconProvider, mRecentTasksOptional, mUnfoldControllerProvider); } @@ -239,10 +239,6 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, mStageCoordinator.setSideStagePosition(sideStagePosition, null /* wct */); } - public void setSideStageVisibility(boolean visible) { - mStageCoordinator.setSideStageVisibility(visible); - } - public void enterSplitScreen(int taskId, boolean leftOrTop) { enterSplitScreen(taskId, leftOrTop, new WindowContainerTransaction()); } @@ -644,14 +640,6 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, }); } - @Override - public void setSideStageVisibility(boolean visible) { - executeRemoteCallWithTaskPermission(mController, "setSideStageVisibility", - (controller) -> { - controller.setSideStageVisibility(visible); - }); - } - @Override public void removeFromSideStage(int taskId) { executeRemoteCallWithTaskPermission(mController, "removeFromSideStage", 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 880693de78919..b10d50da10bf3 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 @@ -28,7 +28,6 @@ import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.view.WindowManager.transitTypeToString; -import static android.view.WindowManagerPolicyConstants.SPLIT_DIVIDER_LAYER; import static android.window.TransitionInfo.FLAG_IS_DISPLAY; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; @@ -43,6 +42,7 @@ import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DEVICE_FOLDED; import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER; import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RETURN_HOME; +import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_ROOT_TASK_VANISHED; import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP; import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_UNKNOWN; import static com.android.wm.shell.splitscreen.SplitScreenController.exitReasonToString; @@ -53,6 +53,7 @@ import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_P import static com.android.wm.shell.transition.Transitions.isClosingType; import static com.android.wm.shell.transition.Transitions.isOpeningType; +import android.annotation.CallSuper; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; @@ -77,7 +78,6 @@ import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; import android.view.SurfaceSession; import android.view.WindowManager; -import android.window.DisplayAreaInfo; import android.window.RemoteTransition; import android.window.TransitionInfo; import android.window.TransitionRequestInfo; @@ -88,7 +88,6 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.InstanceId; import com.android.internal.protolog.common.ProtoLog; import com.android.launcher3.icons.IconProvider; -import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; @@ -122,13 +121,13 @@ import javax.inject.Provider; * - 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. - * - The {@link MainStage} configuration is fullscreen when the {@link SideStage} isn't visible. + * - Both stages are put under a single-top root task. * This rules are mostly implemented in {@link #onStageVisibilityChanged(StageListenerImpl)} and * {@link #onStageHasChildrenChanged(StageListenerImpl).} */ class StageCoordinator implements SplitLayout.SplitLayoutHandler, - RootTaskDisplayAreaOrganizer.RootTaskDisplayAreaListener, - DisplayController.OnDisplaysChangedListener, Transitions.TransitionHandler { + DisplayController.OnDisplaysChangedListener, Transitions.TransitionHandler, + ShellTaskOrganizer.TaskListener { private static final String TAG = StageCoordinator.class.getSimpleName(); @@ -148,9 +147,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, private SplitLayout mSplitLayout; private boolean mDividerVisible; private final SyncTransactionQueue mSyncQueue; - private final RootTaskDisplayAreaOrganizer mRootTDAOrganizer; private final ShellTaskOrganizer mTaskOrganizer; - private DisplayAreaInfo mDisplayAreaInfo; private final Context mContext; private final List mListeners = new ArrayList<>(); private final DisplayController mDisplayController; @@ -160,6 +157,15 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, private final SplitScreenTransitions mSplitTransitions; private final SplitscreenEventLogger mLogger; private final Optional mRecentTasks; + + /** + * A single-top root task which the split divider attached to. + */ + @VisibleForTesting + ActivityManager.RunningTaskInfo mRootTaskInfo; + + private SurfaceControl mRootTaskLeash; + // Tracks whether we should update the recent tasks. Only allow this to happen in between enter // and exit, since exit itself can trigger a number of changes that update the stages. private boolean mShouldUpdateRecents; @@ -174,7 +180,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, new SplitWindowManager.ParentContainerCallbacks() { @Override public void attachToParentSurface(SurfaceControl.Builder b) { - mRootTDAOrganizer.attachToDisplayArea(mDisplayId, b); + b.setParent(mRootTaskLeash); } @Override @@ -184,23 +190,21 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, }; StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue, - RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer, - DisplayController displayController, + ShellTaskOrganizer taskOrganizer, DisplayController displayController, DisplayImeController displayImeController, DisplayInsetsController displayInsetsController, Transitions transitions, TransactionPool transactionPool, SplitscreenEventLogger logger, - IconProvider iconProvider, - Optional recentTasks, + IconProvider iconProvider, Optional recentTasks, Provider> unfoldControllerProvider) { mContext = context; mDisplayId = displayId; mSyncQueue = syncQueue; - mRootTDAOrganizer = rootTDAOrganizer; mTaskOrganizer = taskOrganizer; mLogger = logger; mRecentTasks = recentTasks; mMainUnfoldController = unfoldControllerProvider.get().orElse(null); mSideUnfoldController = unfoldControllerProvider.get().orElse(null); + taskOrganizer.createRootTask(displayId, WINDOWING_MODE_FULLSCREEN, this /* listener */); mMainStage = new MainStage( mContext, @@ -224,7 +228,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mDisplayImeController = displayImeController; mDisplayInsetsController = displayInsetsController; mTransactionPool = transactionPool; - mRootTDAOrganizer.registerListener(displayId, this); final DeviceStateManager deviceStateManager = mContext.getSystemService(DeviceStateManager.class); deviceStateManager.registerCallback(taskOrganizer.getExecutor(), @@ -238,9 +241,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, @VisibleForTesting StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue, - RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer, - MainStage mainStage, SideStage sideStage, DisplayController displayController, - DisplayImeController displayImeController, + ShellTaskOrganizer taskOrganizer, MainStage mainStage, SideStage sideStage, + DisplayController displayController, DisplayImeController displayImeController, DisplayInsetsController displayInsetsController, SplitLayout splitLayout, Transitions transitions, TransactionPool transactionPool, SplitscreenEventLogger logger, @@ -249,7 +251,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mContext = context; mDisplayId = displayId; mSyncQueue = syncQueue; - mRootTDAOrganizer = rootTDAOrganizer; mTaskOrganizer = taskOrganizer; mMainStage = mainStage; mSideStage = sideStage; @@ -257,7 +258,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mDisplayImeController = displayImeController; mDisplayInsetsController = displayInsetsController; mTransactionPool = transactionPool; - mRootTDAOrganizer.registerListener(displayId, this); mSplitLayout = splitLayout; mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions, this::onTransitionAnimationComplete, this); @@ -357,6 +357,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, // while task 1 is on the side stage. mMainStage.activate(wct, false /* reparent */); updateWindowBounds(mSplitLayout, wct); + wct.reorder(mRootTaskInfo.token, true); // Make sure the launch options will put tasks in the corresponding split roots addActivityOptions(mainOptions, mMainStage); @@ -470,15 +471,13 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, setSideStagePosition(sidePosition, wct); mSplitLayout.setDivideRatio(splitRatio); - if (mMainStage.isActive()) { - mMainStage.moveToTop(wct); - } else { + if (!mMainStage.isActive()) { // 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 */); } - mSideStage.moveToTop(wct); updateWindowBounds(mSplitLayout, wct); + wct.reorder(mRootTaskInfo.token, true); // Make sure the launch options will put tasks in the corresponding split roots addActivityOptions(mainOptions, mMainStage); @@ -603,14 +602,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, } } - void setSideStageVisibility(boolean visible) { - if (mSideStageListener.mVisible == visible) return; - - final WindowContainerTransaction wct = new WindowContainerTransaction(); - mSideStage.setVisibility(visible, wct); - mTaskOrganizer.applyTransaction(wct); - } - void onKeyguardVisibilityChanged(boolean showing) { if (!mMainStage.isActive()) { return; @@ -682,15 +673,18 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, applyExitSplitScreen(childrenToTop, wct, exitReason); } - private void exitSplitScreen(StageTaskListener childrenToTop, @ExitReason int exitReason) { + private void exitSplitScreen(@Nullable StageTaskListener childrenToTop, + @ExitReason int exitReason) { if (!mMainStage.isActive()) return; final WindowContainerTransaction wct = new WindowContainerTransaction(); applyExitSplitScreen(childrenToTop, wct, exitReason); } - private void applyExitSplitScreen(StageTaskListener childrenToTop, + private void applyExitSplitScreen(@Nullable StageTaskListener childrenToTop, WindowContainerTransaction wct, @ExitReason int exitReason) { + if (!mMainStage.isActive()) return; + mRecentTasks.ifPresent(recentTasks -> { // Notify recents if we are exiting in a way that breaks the pair, and disable further // updates to splits in the recents until we enter split again @@ -705,8 +699,9 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, // we want the tasks to be put to bottom instead of top, otherwise it will end up // a fullscreen plus a pinned task instead of pinned only at the end of the transition. final boolean fromEnteringPip = exitReason == EXIT_REASON_CHILD_TASK_ENTER_PIP; - mSideStage.removeAllTasks(wct, !fromEnteringPip && childrenToTop == mSideStage); - mMainStage.deactivate(wct, !fromEnteringPip && childrenToTop == mMainStage); + mSideStage.removeAllTasks(wct, !fromEnteringPip && mSideStage == childrenToTop); + mMainStage.deactivate(wct, !fromEnteringPip && mMainStage == childrenToTop); + wct.reorder(mRootTaskInfo.token, false /* onTop */); mTaskOrganizer.applyTransaction(wct); mSyncQueue.runInSync(t -> t .setWindowCrop(mMainStage.mRootLeash, null) @@ -777,8 +772,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSideStage.addTask(taskInfo, wct); } mMainStage.activate(wct, true /* includingTopTask */); - mSideStage.moveToTop(wct); updateWindowBounds(mSplitLayout, wct); + wct.reorder(mRootTaskInfo.token, true); } void finishEnterSplitScreen(SurfaceControl.Transaction t) { @@ -916,25 +911,97 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, } } - private void onStageRootTaskAppeared(StageListenerImpl stageListener) { - if (mMainStageListener.mHasRootTask && mSideStageListener.mHasRootTask) { - final WindowContainerTransaction wct = new WindowContainerTransaction(); - // Make the stages adjacent to each other so they occlude what's behind them. - wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token, - true /* moveTogether */); - wct.setLaunchAdjacentFlagRoot(mSideStage.mRootTaskInfo.token); - mTaskOrganizer.applyTransaction(wct); + @Override + @CallSuper + public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { + if (mRootTaskInfo != null || taskInfo.hasParentTask()) { + throw new IllegalArgumentException(this + "\n Unknown task appeared: " + taskInfo); + } + + mRootTaskInfo = taskInfo; + mRootTaskLeash = leash; + + if (mSplitLayout == null) { + mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext, + mRootTaskInfo.configuration, this, mParentContainerCallbacks, + mDisplayImeController, mTaskOrganizer, false /* applyDismissingParallax */); + mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSplitLayout); + } + + if (mMainUnfoldController != null && mSideUnfoldController != null) { + mMainUnfoldController.init(); + mSideUnfoldController.init(); + } + + onRootTaskAppeared(); + } + + @Override + @CallSuper + public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) { + if (mRootTaskInfo == null || mRootTaskInfo.taskId != taskInfo.taskId) { + throw new IllegalArgumentException(this + "\n Unknown task info changed: " + taskInfo); + } + + mRootTaskInfo = taskInfo; + if (mSplitLayout != null + && mSplitLayout.updateConfiguration(mRootTaskInfo.configuration) + && mMainStage.isActive()) { + // TODO(b/204925795): With Shell transition, We are handling split bounds rotation at + // onRotateDisplay. But still need to handle unfold case. + if (ENABLE_SHELL_TRANSITIONS) { + updateUnfoldBounds(); + return; + } + mSplitLayout.update(null /* t */); + onLayoutSizeChanged(mSplitLayout); } } - private void onStageRootTaskVanished(StageListenerImpl stageListener) { - if (stageListener == mMainStageListener || stageListener == mSideStageListener) { - final WindowContainerTransaction wct = new WindowContainerTransaction(); - wct.clearLaunchAdjacentFlagRoot(mSideStage.mRootTaskInfo.token); - // Deactivate the main stage if it no longer has a root task. - mMainStage.deactivate(wct); - mTaskOrganizer.applyTransaction(wct); + @Override + @CallSuper + public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) { + if (mRootTaskInfo == null) { + throw new IllegalArgumentException(this + "\n Unknown task vanished: " + taskInfo); } + + onRootTaskVanished(); + + if (mSplitLayout != null) { + mSplitLayout.release(); + mSplitLayout = null; + } + + mRootTaskInfo = null; + } + + + @VisibleForTesting + void onRootTaskAppeared() { + // Wait unit all root tasks appeared. + if (mRootTaskInfo == null + || !mMainStageListener.mHasRootTask + || !mSideStageListener.mHasRootTask) { + return; + } + + final WindowContainerTransaction wct = new WindowContainerTransaction(); + wct.reparent(mMainStage.mRootTaskInfo.token, mRootTaskInfo.token, true); + wct.reparent(mSideStage.mRootTaskInfo.token, mRootTaskInfo.token, true); + // Make the stages adjacent to each other so they occlude what's behind them. + wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token, + true /* moveTogether */); + wct.setLaunchAdjacentFlagRoot(mSideStage.mRootTaskInfo.token); + mTaskOrganizer.applyTransaction(wct); + } + + private void onRootTaskVanished() { + final WindowContainerTransaction wct = new WindowContainerTransaction(); + if (mRootTaskInfo != null) { + wct.clearLaunchAdjacentFlagRoot(mRootTaskInfo.token); + } + applyExitSplitScreen(null /* childrenToTop */, wct, EXIT_REASON_ROOT_TASK_VANISHED); + mDisplayInsetsController.removeInsetsChangedListener(mDisplayId, mSplitLayout); } private void onStageVisibilityChanged(StageListenerImpl stageListener) { @@ -981,7 +1048,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (mDividerVisible) { t.show(dividerLeash); t.setAlpha(dividerLeash, 1); - t.setLayer(dividerLeash, SPLIT_DIVIDER_LAYER); + t.setLayer(dividerLeash, Integer.MAX_VALUE); t.setPosition(dividerLeash, mSplitLayout.getRefDividerBounds().left, mSplitLayout.getRefDividerBounds().top); @@ -1139,45 +1206,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mTaskOrganizer.applyTransaction(wct); } - @Override - public void onDisplayAreaAppeared(DisplayAreaInfo displayAreaInfo) { - mDisplayAreaInfo = displayAreaInfo; - if (mSplitLayout == null) { - mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext, - mDisplayAreaInfo.configuration, this, mParentContainerCallbacks, - mDisplayImeController, mTaskOrganizer, false /* applyDismissingParallax */); - mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSplitLayout); - - if (mMainUnfoldController != null && mSideUnfoldController != null) { - mMainUnfoldController.init(); - mSideUnfoldController.init(); - } - } - } - - @Override - public void onDisplayAreaVanished(DisplayAreaInfo displayAreaInfo) { - throw new IllegalStateException("Well that was unexpected..."); - } - - @Override - public void onDisplayAreaInfoChanged(DisplayAreaInfo displayAreaInfo) { - mDisplayAreaInfo = displayAreaInfo; - if (mSplitLayout != null - && mSplitLayout.updateConfiguration(mDisplayAreaInfo.configuration) - && mMainStage.isActive()) { - // TODO(b/204925795): With Shell transition, We are handle roation case for apply split - // bounds at onRotateDisplay. But still need to handle unfold case. - if (ENABLE_SHELL_TRANSITIONS) { - updateUnfoldBounds(); - return; - } - - mSplitLayout.update(null /* t */); - onLayoutSizeChanged(mSplitLayout); - } - } - @Override public void onDisplayAdded(int displayId) { if (displayId != DEFAULT_DISPLAY) { @@ -1200,13 +1228,10 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, // Only do this when shell transition if (!ENABLE_SHELL_TRANSITIONS) return; - final SurfaceControl.Transaction t = mTransactionPool.acquire(); mDisplayLayout.rotateTo(mContext.getResources(), toRotation); mSplitLayout.rotateTo(toRotation, mDisplayLayout.stableInsets()); updateWindowBounds(mSplitLayout, wct); updateUnfoldBounds(); - t.apply(); - mTransactionPool.release(t); } private void onFoldedStateChanged(boolean folded) { @@ -1258,7 +1283,10 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask(); if (triggerTask == null) { if (mMainStage.isActive()) { - if (request.getType() == TRANSIT_CHANGE && request.getDisplayChange() != null) { + final TransitionRequestInfo.DisplayChange displayChange = + request.getDisplayChange(); + if (request.getType() == TRANSIT_CHANGE && displayChange != null + && displayChange.getStartRotation() != displayChange.getEndRotation()) { mSplitLayout.setFreezeDividerWindow(true); } // Still want to monitor everything while in split-screen, so return non-null. @@ -1602,7 +1630,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, // Be default, make it visible. The remote animator can adjust alpha if it plans to animate. if (show) { t.setAlpha(leash, 1.f); - t.setLayer(leash, SPLIT_DIVIDER_LAYER); + t.setLayer(leash, Integer.MAX_VALUE); t.setPosition(leash, bounds.left, bounds.top); t.show(leash); } @@ -1685,7 +1713,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, @Override public void onRootTaskAppeared() { mHasRootTask = true; - StageCoordinator.this.onStageRootTaskAppeared(this); + StageCoordinator.this.onRootTaskAppeared(); } @Override @@ -1715,7 +1743,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, @Override public void onRootTaskVanished() { reset(); - StageCoordinator.this.onStageRootTaskVanished(this); + StageCoordinator.this.onRootTaskVanished(); } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java index fb0eebdf0ac92..9fd5d20038733 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java @@ -39,7 +39,6 @@ import android.window.WindowContainerTransaction; import androidx.annotation.NonNull; -import com.android.internal.R; import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.SurfaceUtils; @@ -108,12 +107,7 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { mSurfaceSession = surfaceSession; mIconProvider = iconProvider; mStageTaskUnfoldController = stageTaskUnfoldController; - - // No need to create root task if the device is using legacy split screen. - // TODO(b/199236198): Remove this check after totally deprecated legacy split. - if (!context.getResources().getBoolean(R.bool.config_useLegacySplit)) { - taskOrganizer.createRootTask(displayId, WINDOWING_MODE_MULTI_WINDOW, this); - } + taskOrganizer.createRootTask(displayId, WINDOWING_MODE_MULTI_WINDOW, this); } int getChildCount() { @@ -187,7 +181,7 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { @Override @CallSuper public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { - if (mRootTaskInfo == null && !taskInfo.hasParentTask()) { + if (mRootTaskInfo == null) { mRootLeash = leash; mRootTaskInfo = taskInfo; mSplitDecorManager = new SplitDecorManager( @@ -334,11 +328,6 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { wct.reparent(task.token, mRootTaskInfo.token, true /* onTop*/); } - void moveToTop(WindowContainerTransaction wct) { - final WindowContainerToken rootToken = mRootTaskInfo.token; - wct.reorder(rootToken, true /* onTop */); - } - void reorderChild(int taskId, boolean onTop, WindowContainerTransaction wct) { if (!containsTask(taskId)) { return; @@ -354,10 +343,6 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { } } - void setVisibility(boolean visible, WindowContainerTransaction wct) { - wct.reorder(mRootTaskInfo.token, visible /* onTop */); - } - void onSplitScreenListenerRegistered(SplitScreen.SplitScreenListener listener, @StageType int stage) { for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java index a17942ff7cff7..6ef20e37d5bc4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java @@ -23,7 +23,6 @@ import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.view.WindowManager.transitTypeToString; -import static android.view.WindowManagerPolicyConstants.SPLIT_DIVIDER_LAYER; import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__APP_DOES_NOT_SUPPORT_MULTIWINDOW; import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__APP_FINISHED; @@ -722,7 +721,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (mDividerVisible) { t.show(dividerLeash) - .setLayer(dividerLeash, SPLIT_DIVIDER_LAYER) + .setLayer(dividerLeash, Integer.MAX_VALUE) .setPosition(dividerLeash, mSplitLayout.getDividerBounds().left, mSplitLayout.getDividerBounds().top); @@ -738,7 +737,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, } if (mDividerVisible) { - t.show(outlineLeash).setLayer(outlineLeash, SPLIT_DIVIDER_LAYER); + t.show(outlineLeash).setLayer(outlineLeash, Integer.MAX_VALUE); } else { t.hide(outlineLeash); } @@ -1190,7 +1189,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, // Be default, make it visible. The remote animator can adjust alpha if it plans to animate. if (show) { t.setAlpha(leash, 1.f); - t.setLayer(leash, SPLIT_DIVIDER_LAYER); + t.setLayer(leash, Integer.MAX_VALUE); t.setPosition(leash, bounds.left, bounds.top); t.show(leash); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java index b0e44a15b0dab..542ddeea769c0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java @@ -369,12 +369,20 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { } if (change.getMode() == TRANSIT_CHANGE) { - // If task is child task, only set position in parent. + // If task is child task, only set position in parent and update crop when needed. if (isTask && change.getParent() != null && info.getChange(change.getParent()).getTaskInfo() != null) { final Point positionInParent = change.getTaskInfo().positionInParent; startTransaction.setPosition(change.getLeash(), positionInParent.x, positionInParent.y); + + if (!change.getEndAbsBounds().equals( + info.getChange(change.getParent()).getEndAbsBounds())) { + startTransaction.setWindowCrop(change.getLeash(), + change.getEndAbsBounds().width(), + change.getEndAbsBounds().height()); + } + continue; } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java index daec336fc71ee..8b4e1f8bfdb74 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java @@ -16,7 +16,6 @@ package com.android.wm.shell.common.split; -import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static com.google.common.truth.Truth.assertThat; @@ -85,10 +84,6 @@ public class SplitLayoutTests extends ShellTestCase { // Verify it returns true if new config won't affect split layout. assertThat(mSplitLayout.updateConfiguration(config)).isFalse(); - // Verify updateConfiguration returns true if the orientation changed. - config.orientation = ORIENTATION_LANDSCAPE; - assertThat(mSplitLayout.updateConfiguration(config)).isTrue(); - // Verify updateConfiguration returns true if it rotated. config.windowConfiguration.setRotation(1); assertThat(mSplitLayout.updateConfiguration(config)).isTrue(); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java index 85527c81ad9ec..49f36a4be35fc 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java @@ -16,22 +16,18 @@ package com.android.wm.shell.splitscreen; -import static android.view.Display.DEFAULT_DISPLAY; -import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER; - import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import android.app.ActivityManager; import android.content.Context; import android.graphics.Rect; import android.view.SurfaceControl; -import android.window.DisplayAreaInfo; -import android.window.IWindowContainerToken; -import android.window.WindowContainerToken; +import android.view.SurfaceSession; import com.android.dx.mockito.inline.extended.ExtendedMockito; -import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.TestRunningTaskInfoBuilder; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.DisplayInsetsController; @@ -68,26 +64,24 @@ public class SplitTestUtils { } static class TestStageCoordinator extends StageCoordinator { - final DisplayAreaInfo mDisplayAreaInfo; + final ActivityManager.RunningTaskInfo mRootTask; + final SurfaceControl mRootLeash; TestStageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue, - RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer, - MainStage mainStage, SideStage sideStage, DisplayController displayController, - DisplayImeController imeController, DisplayInsetsController insetsController, - SplitLayout splitLayout, Transitions transitions, TransactionPool transactionPool, - SplitscreenEventLogger logger, - Optional recentTasks, + ShellTaskOrganizer taskOrganizer, MainStage mainStage, SideStage sideStage, + DisplayController displayController, DisplayImeController imeController, + DisplayInsetsController insetsController, SplitLayout splitLayout, + Transitions transitions, TransactionPool transactionPool, + SplitscreenEventLogger logger, Optional recentTasks, Provider> unfoldController) { - super(context, displayId, syncQueue, rootTDAOrganizer, taskOrganizer, mainStage, + super(context, displayId, syncQueue, taskOrganizer, mainStage, sideStage, displayController, imeController, insetsController, splitLayout, transitions, transactionPool, logger, recentTasks, unfoldController); - // Prepare default TaskDisplayArea for testing. - mDisplayAreaInfo = new DisplayAreaInfo( - new WindowContainerToken(new IWindowContainerToken.Default()), - DEFAULT_DISPLAY, - FEATURE_DEFAULT_TASK_CONTAINER); - this.onDisplayAreaAppeared(mDisplayAreaInfo); + // Prepare root task for testing. + mRootTask = new TestRunningTaskInfoBuilder().build(); + mRootLeash = new SurfaceControl.Builder(new SurfaceSession()).setName("test").build(); + onTaskAppeared(mRootTask, mRootLeash); } } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java index 19d2a7ef6f452..f847e6eb5e968 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java @@ -124,9 +124,9 @@ public class SplitTransitionTests extends ShellTestCase { mIconProvider, null); mSideStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface()); mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY, - mSyncQueue, mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage, - mDisplayController, mDisplayImeController, mDisplayInsetsController, mSplitLayout, - mTransitions, mTransactionPool, mLogger, Optional.empty(), Optional::empty); + mSyncQueue, mTaskOrganizer, mMainStage, mSideStage, mDisplayController, + mDisplayImeController, mDisplayInsetsController, mSplitLayout, mTransitions, + mTransactionPool, mLogger, Optional.empty(), Optional::empty); mSplitScreenTransitions = mStageCoordinator.getSplitTransitions(); doAnswer((Answer) invocation -> mock(IBinder.class)) .when(mTransitions).startTransition(anyInt(), any(), any()); @@ -424,19 +424,14 @@ public class SplitTransitionTests extends ShellTestCase { } private boolean containsSplitEnter(@NonNull WindowContainerTransaction wct) { - boolean movedMainToFront = false; - boolean movedSideToFront = false; for (int i = 0; i < wct.getHierarchyOps().size(); ++i) { WindowContainerTransaction.HierarchyOp op = wct.getHierarchyOps().get(i); - if (op.getType() == HIERARCHY_OP_TYPE_REORDER) { - if (op.getContainer() == mMainStage.mRootTaskInfo.token.asBinder()) { - movedMainToFront = true; - } else if (op.getContainer() == mSideStage.mRootTaskInfo.token.asBinder()) { - movedSideToFront = true; - } + if (op.getType() == HIERARCHY_OP_TYPE_REORDER + && op.getContainer() == mStageCoordinator.mRootTaskInfo.token.asBinder()) { + return true; } } - return movedMainToFront && movedSideToFront; + return false; } private boolean containsSplitExit(@NonNull WindowContainerTransaction wct) { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java index a6ee8ff147db6..061136c65daf5 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java @@ -34,21 +34,20 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.ActivityManager; +import android.content.res.Configuration; import android.graphics.Rect; import android.view.SurfaceControl; -import android.window.DisplayAreaInfo; +import android.view.SurfaceSession; import android.window.WindowContainerTransaction; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; -import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestRunningTaskInfoBuilder; @@ -81,8 +80,6 @@ public class StageCoordinatorTests extends ShellTestCase { @Mock private SyncTransactionQueue mSyncQueue; @Mock - private RootTaskDisplayAreaOrganizer mRootTDAOrganizer; - @Mock private MainStage mMainStage; @Mock private SideStage mSideStage; @@ -108,17 +105,27 @@ public class StageCoordinatorTests extends ShellTestCase { private final Rect mBounds1 = new Rect(10, 20, 30, 40); private final Rect mBounds2 = new Rect(5, 10, 15, 20); + private SurfaceSession mSurfaceSession = new SurfaceSession(); + private SurfaceControl mRootLeash; + private ActivityManager.RunningTaskInfo mRootTask; private StageCoordinator mStageCoordinator; @Before public void setup() { MockitoAnnotations.initMocks(this); - mStageCoordinator = spy(createStageCoordinator(mSplitLayout)); + mStageCoordinator = spy(new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue, + mTaskOrganizer, mMainStage, mSideStage, mDisplayController, mDisplayImeController, + mDisplayInsetsController, mSplitLayout, mTransitions, mTransactionPool, mLogger, + Optional.empty(), new UnfoldControllerProvider())); doNothing().when(mStageCoordinator).updateActivityOptions(any(), anyInt()); when(mSplitLayout.getBounds1()).thenReturn(mBounds1); when(mSplitLayout.getBounds2()).thenReturn(mBounds2); when(mSplitLayout.isLandscape()).thenReturn(false); + + mRootTask = new TestRunningTaskInfoBuilder().build(); + mRootLeash = new SurfaceControl.Builder(mSurfaceSession).setName("test").build(); + mStageCoordinator.onTaskAppeared(mRootTask, mRootLeash); } @Test @@ -158,14 +165,17 @@ public class StageCoordinatorTests extends ShellTestCase { } @Test - public void testDisplayAreaAppeared_initializesUnfoldControllers() { - // Create a stage coordinator with null split layout to test layout init flow. - mStageCoordinator = createStageCoordinator(null /* splitLayout */); - - mStageCoordinator.onDisplayAreaAppeared(mock(DisplayAreaInfo.class)); - + public void testRootTaskAppeared_initializesUnfoldControllers() { verify(mMainUnfoldController).init(); verify(mSideUnfoldController).init(); + verify(mStageCoordinator).onRootTaskAppeared(); + } + + @Test + public void testRootTaskInfoChanged_updatesSplitLayout() { + mStageCoordinator.onTaskInfoChanged(mRootTask); + + verify(mSplitLayout).updateConfiguration(any(Configuration.class)); } @Test @@ -302,14 +312,6 @@ public class StageCoordinatorTests extends ShellTestCase { verify(mSplitLayout).applySurfaceChanges(any(), any(), any(), any(), any()); } - private StageCoordinator createStageCoordinator(SplitLayout splitLayout) { - return new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY, - mSyncQueue, mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage, - mDisplayController, mDisplayImeController, mDisplayInsetsController, splitLayout, - mTransitions, mTransactionPool, mLogger, Optional.empty(), - new UnfoldControllerProvider()); - } - private class UnfoldControllerProvider implements Provider> { diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 731a04500b57a..0542c0be57901 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -2701,7 +2701,7 @@ class Task extends TaskFragment { @Override void onDisplayChanged(DisplayContent dc) { final boolean isRootTask = isRootTask(); - if (!isRootTask) { + if (!isRootTask && !mCreatedByOrganizer) { adjustBoundsForDisplayChangeIfNeeded(dc); } super.onDisplayChanged(dc); @@ -3468,9 +3468,9 @@ class Task extends TaskFragment { forAllActivities(r -> { info.addLaunchCookie(r.mLaunchCookie); }); - final Task rootTask = getRootTask(); - info.parentTaskId = rootTask == getParent() && rootTask.mCreatedByOrganizer - ? rootTask.mTaskId + final Task parentTask = getParent() != null ? getParent().asTask() : null; + info.parentTaskId = parentTask != null && parentTask.mCreatedByOrganizer + ? parentTask.mTaskId : INVALID_TASK_ID; info.isFocused = isFocused(); info.isVisible = hasVisibleChildren(); @@ -4146,13 +4146,13 @@ class Task extends TaskFragment { private boolean canBeOrganized() { // All root tasks can be organized - if (isRootTask()) { + if (isRootTask() || mCreatedByOrganizer) { return true; } - // Task could be organized if it's the direct child of the root created by organizer. - final Task rootTask = getRootTask(); - return rootTask == getParent() && rootTask.mCreatedByOrganizer; + // Task could be organized if it's the direct child of a task created by organizer. + final Task parentTask = getParent().asTask(); + return parentTask != null && parentTask.mCreatedByOrganizer; } @Override