From dda6e07e4b0e34152e00e1ffcb58a44454efc384 Mon Sep 17 00:00:00 2001 From: Jerry Chang Date: Wed, 5 May 2021 15:06:01 +0800 Subject: [PATCH 1/5] Adjust split layout with IME animation in split (1/N) Update StagePosition to more general SplitPosition so that it could be shared among split implementations. Bug: 179262787 Test: atest WMShellUnitTests Change-Id: I6f461fac62d350f50171136f079cb0254b06d767 --- .../wm/shell/ShellCommandHandlerImpl.java | 6 +-- .../wm/shell/common/split/SplitLayout.java | 28 +++++++++++- .../shell/draganddrop/DragAndDropPolicy.java | 20 ++++----- .../wm/shell/splitscreen/SplitScreen.java | 26 +---------- .../splitscreen/SplitScreenController.java | 35 +++++++-------- .../shell/splitscreen/StageCoordinator.java | 43 ++++++++++--------- .../draganddrop/DragAndDropPolicyTest.java | 26 +++++------ .../splitscreen/StageCoordinatorTests.java | 4 +- 8 files changed, 98 insertions(+), 90 deletions(-) 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 d451f4a0661bb..0b941b59b3db7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java @@ -16,14 +16,14 @@ package com.android.wm.shell; -import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT; +import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT; import com.android.wm.shell.apppairs.AppPairsController; import com.android.wm.shell.common.ShellExecutor; -import com.android.wm.shell.pip.Pip; import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController; import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController; import com.android.wm.shell.onehanded.OneHandedController; +import com.android.wm.shell.pip.Pip; import com.android.wm.shell.splitscreen.SplitScreenController; import java.io.PrintWriter; @@ -145,7 +145,7 @@ public final class ShellCommandHandlerImpl { } final int taskId = new Integer(args[2]); final int sideStagePosition = args.length > 3 - ? new Integer(args[3]) : STAGE_POSITION_BOTTOM_OR_RIGHT; + ? new Integer(args[3]) : SPLIT_POSITION_BOTTOM_OR_RIGHT; mSplitScreenOptional.ifPresent(split -> split.moveToSideStage(taskId, sideStagePosition)); return true; } 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 d318a5aaef5cd..8e8473172597b 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 @@ -25,6 +25,7 @@ import static com.android.internal.policy.DividerSnapAlgorithm.SnapTarget.FLAG_D import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; +import android.annotation.IntDef; import android.content.Context; import android.content.res.Configuration; import android.graphics.Rect; @@ -42,7 +43,32 @@ import com.android.wm.shell.common.DisplayImeController; * Records and handles layout of splits. Helps to calculate proper bounds when configuration or * divide position changes. */ -public class SplitLayout { +public final class SplitLayout { + /** + * Split position isn't specified normally meaning to use what ever it is currently set to. + */ + public static final int SPLIT_POSITION_UNDEFINED = -1; + + /** + * Specifies that a split is positioned at the top half of the screen if + * in portrait mode or at the left half of the screen if in landscape mode. + */ + public static final int SPLIT_POSITION_TOP_OR_LEFT = 0; + + /** + * Specifies that a split is positioned at the bottom half of the screen if + * in portrait mode or at the right half of the screen if in landscape mode. + */ + public static final int SPLIT_POSITION_BOTTOM_OR_RIGHT = 1; + + @IntDef(prefix = {"SPLIT_POSITION_"}, value = { + SPLIT_POSITION_UNDEFINED, + SPLIT_POSITION_TOP_OR_LEFT, + SPLIT_POSITION_BOTTOM_OR_RIGHT + }) + public @interface SplitPosition { + } + private final int mDividerWindowWidth; private final int mDividerInsets; private final int mDividerSize; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java index 9a09ca43d1d7f..9bcc3acf7a571 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java @@ -29,14 +29,14 @@ import static android.content.Intent.EXTRA_SHORTCUT_ID; import static android.content.Intent.EXTRA_TASK_ID; import static android.content.Intent.EXTRA_USER; +import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT; +import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT; +import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_UNDEFINED; import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_FULLSCREEN; import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_BOTTOM; import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_LEFT; import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_RIGHT; import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_TOP; -import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT; -import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_TOP_OR_LEFT; -import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_UNDEFINED; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED; @@ -64,7 +64,7 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import com.android.wm.shell.common.DisplayLayout; -import com.android.wm.shell.splitscreen.SplitScreen.StagePosition; +import com.android.wm.shell.common.split.SplitLayout.SplitPosition; import com.android.wm.shell.splitscreen.SplitScreen.StageType; import com.android.wm.shell.splitscreen.SplitScreenController; @@ -203,10 +203,10 @@ public class DragAndDropPolicy { final boolean leftOrTop = target.type == TYPE_SPLIT_TOP || target.type == TYPE_SPLIT_LEFT; @StageType int stage = STAGE_TYPE_UNDEFINED; - @StagePosition int position = STAGE_POSITION_UNDEFINED; + @SplitPosition int position = SPLIT_POSITION_UNDEFINED; if (target.type != TYPE_FULLSCREEN && mSplitScreen != null) { // Update launch options for the split side we are targeting. - position = leftOrTop ? STAGE_POSITION_TOP_OR_LEFT : STAGE_POSITION_BOTTOM_OR_RIGHT; + position = leftOrTop ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT; if (!inSplitScreen) { // Launch in the side stage if we are not in split-screen already. stage = STAGE_TYPE_SIDE; @@ -219,7 +219,7 @@ public class DragAndDropPolicy { } private void startClipDescription(ClipDescription description, Intent intent, - @StageType int stage, @StagePosition int position) { + @StageType int stage, @SplitPosition int position) { final boolean isTask = description.hasMimeType(MIMETYPE_APPLICATION_TASK); final boolean isShortcut = description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT); final Bundle opts = intent.hasExtra(EXTRA_ACTIVITY_OPTIONS) @@ -291,12 +291,12 @@ public class DragAndDropPolicy { * Interface for actually committing the task launches. */ public interface Starter { - void startTask(int taskId, @StageType int stage, @StagePosition int position, + void startTask(int taskId, @StageType int stage, @SplitPosition int position, @Nullable Bundle options); void startShortcut(String packageName, String shortcutId, @StageType int stage, - @StagePosition int position, @Nullable Bundle options, UserHandle user); + @SplitPosition int position, @Nullable Bundle options, UserHandle user); void startIntent(PendingIntent intent, Intent fillInIntent, - @StageType int stage, @StagePosition int position, + @StageType int stage, @SplitPosition int position, @Nullable Bundle options); void enterSplitScreen(int taskId, boolean leftOrTop); void exitSplitScreen(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java index d4506fd32c86e..002bfb6e429fe 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java @@ -19,6 +19,7 @@ package com.android.wm.shell.splitscreen; import android.annotation.IntDef; import com.android.wm.shell.common.annotations.ExternalThread; +import com.android.wm.shell.common.split.SplitLayout.SplitPosition; /** * Interface to engage split-screen feature. @@ -26,29 +27,6 @@ import com.android.wm.shell.common.annotations.ExternalThread; */ @ExternalThread public interface SplitScreen { - /** - * Stage position isn't specified normally meaning to use what ever it is currently set to. - */ - int STAGE_POSITION_UNDEFINED = -1; - /** - * Specifies that a stage is positioned at the top half of the screen if - * in portrait mode or at the left half of the screen if in landscape mode. - */ - int STAGE_POSITION_TOP_OR_LEFT = 0; - - /** - * Specifies that a stage is positioned at the bottom half of the screen if - * in portrait mode or at the right half of the screen if in landscape mode. - */ - int STAGE_POSITION_BOTTOM_OR_RIGHT = 1; - - @IntDef(prefix = { "STAGE_POSITION_" }, value = { - STAGE_POSITION_UNDEFINED, - STAGE_POSITION_TOP_OR_LEFT, - STAGE_POSITION_BOTTOM_OR_RIGHT - }) - @interface StagePosition {} - /** * Stage type isn't specified normally meaning to use what ever the default is. * E.g. exit split-screen and launch the app in fullscreen. @@ -75,7 +53,7 @@ public interface SplitScreen { /** Callback interface for listening to changes in a split-screen stage. */ interface SplitScreenListener { - void onStagePositionChanged(@StageType int stage, @StagePosition int position); + void onStagePositionChanged(@StageType int stage, @SplitPosition int position); void onTaskStageChanged(int taskId, @StageType int stage, boolean visible); } 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 5aa59f283434b..9a457b5fd88eb 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 @@ -19,9 +19,9 @@ package com.android.wm.shell.splitscreen; import static android.view.Display.DEFAULT_DISPLAY; import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; -import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT; -import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_TOP_OR_LEFT; -import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_UNDEFINED; +import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT; +import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT; +import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_UNDEFINED; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED; @@ -53,6 +53,7 @@ import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.annotations.ExternalThread; +import com.android.wm.shell.common.split.SplitLayout.SplitPosition; import com.android.wm.shell.draganddrop.DragAndDropPolicy; import com.android.wm.shell.splitscreen.ISplitScreenListener; import com.android.wm.shell.transition.Transitions; @@ -122,7 +123,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, return mStageCoordinator.isSplitScreenVisible(); } - public boolean moveToSideStage(int taskId, @SplitScreen.StagePosition int sideStagePosition) { + public boolean moveToSideStage(int taskId, @SplitPosition int sideStagePosition) { final ActivityManager.RunningTaskInfo task = mTaskOrganizer.getRunningTaskInfo(taskId); if (task == null) { throw new IllegalArgumentException("Unknown taskId" + taskId); @@ -131,7 +132,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, } public boolean moveToSideStage(ActivityManager.RunningTaskInfo task, - @SplitScreen.StagePosition int sideStagePosition) { + @SplitPosition int sideStagePosition) { return mStageCoordinator.moveToSideStage(task, sideStagePosition); } @@ -139,7 +140,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, return mStageCoordinator.removeFromSideStage(taskId); } - public void setSideStagePosition(@SplitScreen.StagePosition int sideStagePosition) { + public void setSideStagePosition(@SplitPosition int sideStagePosition) { mStageCoordinator.setSideStagePosition(sideStagePosition); } @@ -149,7 +150,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, public void enterSplitScreen(int taskId, boolean leftOrTop) { moveToSideStage(taskId, - leftOrTop ? STAGE_POSITION_TOP_OR_LEFT : STAGE_POSITION_BOTTOM_OR_RIGHT); + leftOrTop ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT); } public void exitSplitScreen() { @@ -173,7 +174,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, } public void startTask(int taskId, @SplitScreen.StageType int stage, - @SplitScreen.StagePosition int position, @Nullable Bundle options) { + @SplitPosition int position, @Nullable Bundle options) { options = resolveStartStage(stage, position, options); try { @@ -184,7 +185,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, } public void startShortcut(String packageName, String shortcutId, - @SplitScreen.StageType int stage, @SplitScreen.StagePosition int position, + @SplitScreen.StageType int stage, @SplitPosition int position, @Nullable Bundle options, UserHandle user) { options = resolveStartStage(stage, position, options); @@ -199,7 +200,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, } public void startIntent(PendingIntent intent, Intent fillInIntent, - @SplitScreen.StageType int stage, @SplitScreen.StagePosition int position, + @SplitScreen.StageType int stage, @SplitPosition int position, @Nullable Bundle options) { options = resolveStartStage(stage, position, options); @@ -211,11 +212,11 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, } private Bundle resolveStartStage(@SplitScreen.StageType int stage, - @SplitScreen.StagePosition int position, @Nullable Bundle options) { + @SplitPosition int position, @Nullable Bundle options) { switch (stage) { case STAGE_TYPE_UNDEFINED: { // Use the stage of the specified position is valid. - if (position != STAGE_POSITION_UNDEFINED) { + if (position != SPLIT_POSITION_UNDEFINED) { if (position == mStageCoordinator.getSideStagePosition()) { options = resolveStartStage(STAGE_TYPE_SIDE, position, options); } else { @@ -228,7 +229,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, break; } case STAGE_TYPE_SIDE: { - if (position != STAGE_POSITION_UNDEFINED) { + if (position != SPLIT_POSITION_UNDEFINED) { mStageCoordinator.setSideStagePosition(position); } else { position = mStageCoordinator.getSideStagePosition(); @@ -240,10 +241,10 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, break; } case STAGE_TYPE_MAIN: { - if (position != STAGE_POSITION_UNDEFINED) { + if (position != SPLIT_POSITION_UNDEFINED) { // Set the side stage opposite of what we want to the main stage. - final int sideStagePosition = position == STAGE_POSITION_TOP_OR_LEFT - ? STAGE_POSITION_BOTTOM_OR_RIGHT : STAGE_POSITION_TOP_OR_LEFT; + final int sideStagePosition = position == SPLIT_POSITION_TOP_OR_LEFT + ? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT; mStageCoordinator.setSideStagePosition(sideStagePosition); } else { position = mStageCoordinator.getMainStagePosition(); @@ -418,7 +419,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, @Override public void startTasks(int mainTaskId, @Nullable Bundle mainOptions, int sideTaskId, @Nullable Bundle sideOptions, - @SplitScreen.StagePosition int sidePosition, + @SplitPosition int sidePosition, @Nullable IRemoteTransition remoteTransition) { executeRemoteCallWithTaskPermission(mController, "startTasks", (controller) -> controller.mStageCoordinator.startTasks(mainTaskId, mainOptions, 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 4be9e758bf9b8..ea302691af8e1 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 @@ -25,8 +25,8 @@ import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.view.WindowManager.transitTypeToString; -import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT; -import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_TOP_OR_LEFT; +import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT; +import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED; @@ -54,7 +54,6 @@ import android.window.TransitionInfo; import android.window.TransitionRequestInfo; import android.window.WindowContainerTransaction; - import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; @@ -64,6 +63,7 @@ import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.split.SplitLayout; +import com.android.wm.shell.common.split.SplitLayout.SplitPosition; import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.transition.Transitions; @@ -96,7 +96,8 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener, private final StageListenerImpl mMainStageListener = new StageListenerImpl(); private final SideStage mSideStage; private final StageListenerImpl mSideStageListener = new StageListenerImpl(); - private @SplitScreen.StagePosition int mSideStagePosition = STAGE_POSITION_BOTTOM_OR_RIGHT; + @SplitPosition + private int mSideStagePosition = SPLIT_POSITION_BOTTOM_OR_RIGHT; private final int mDisplayId; private SplitLayout mSplitLayout; @@ -176,7 +177,7 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener, } boolean moveToSideStage(ActivityManager.RunningTaskInfo task, - @SplitScreen.StagePosition int sideStagePosition) { + @SplitPosition int sideStagePosition) { final WindowContainerTransaction wct = new WindowContainerTransaction(); setSideStagePosition(sideStagePosition); mMainStage.activate(getMainStageBounds(), wct); @@ -201,7 +202,7 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener, /** Starts 2 tasks in one transition. */ void startTasks(int mainTaskId, @Nullable Bundle mainOptions, int sideTaskId, - @Nullable Bundle sideOptions, @SplitScreen.StagePosition int sidePosition, + @Nullable Bundle sideOptions, @SplitPosition int sidePosition, @Nullable IRemoteTransition remoteTransition) { final WindowContainerTransaction wct = new WindowContainerTransaction(); mainOptions = mainOptions != null ? mainOptions : new Bundle(); @@ -225,20 +226,22 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener, TRANSIT_SPLIT_SCREEN_PAIR_OPEN, wct, remoteTransition, this); } - @SplitScreen.StagePosition int getSideStagePosition() { + @SplitLayout.SplitPosition + int getSideStagePosition() { return mSideStagePosition; } - @SplitScreen.StagePosition int getMainStagePosition() { - return mSideStagePosition == STAGE_POSITION_TOP_OR_LEFT - ? STAGE_POSITION_BOTTOM_OR_RIGHT : STAGE_POSITION_TOP_OR_LEFT; + @SplitLayout.SplitPosition + int getMainStagePosition() { + return mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT + ? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT; } - void setSideStagePosition(@SplitScreen.StagePosition int sideStagePosition) { + void setSideStagePosition(@SplitPosition int sideStagePosition) { setSideStagePosition(sideStagePosition, true /* updateVisibility */); } - private void setSideStagePosition(@SplitScreen.StagePosition int sideStagePosition, + private void setSideStagePosition(@SplitPosition int sideStagePosition, boolean updateVisibility) { if (mSideStagePosition == sideStagePosition) return; mSideStagePosition = sideStagePosition; @@ -289,7 +292,7 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener, opts.putParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN, stage.mRootTaskInfo.token); } - void updateActivityOptions(Bundle opts, @SplitScreen.StagePosition int position) { + void updateActivityOptions(Bundle opts, @SplitPosition int position) { addActivityOptions(opts, position == mSideStagePosition ? mSideStage : mMainStage); if (!mMainStage.isActive()) { @@ -487,8 +490,8 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener, @Override public void onSnappedToDismiss(boolean bottomOrRight) { final boolean mainStageToTop = - bottomOrRight ? mSideStagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT - : mSideStagePosition == STAGE_POSITION_TOP_OR_LEFT; + bottomOrRight ? mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT + : mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT; if (ENABLE_SHELL_TRANSITIONS) { onSnappedToDismissTransition(mainStageToTop); return; @@ -519,8 +522,8 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener, @Override public void onDoubleTappedDivider() { - setSideStagePosition(mSideStagePosition == STAGE_POSITION_TOP_OR_LEFT - ? STAGE_POSITION_BOTTOM_OR_RIGHT : STAGE_POSITION_TOP_OR_LEFT); + setSideStagePosition(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT + ? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT); } @Override @@ -574,12 +577,12 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener, } private Rect getSideStageBounds() { - return mSideStagePosition == STAGE_POSITION_TOP_OR_LEFT + return mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSplitLayout.getBounds1() : mSplitLayout.getBounds2(); } private Rect getMainStageBounds() { - return mSideStagePosition == STAGE_POSITION_TOP_OR_LEFT + return mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSplitLayout.getBounds2() : mSplitLayout.getBounds1(); } @@ -742,7 +745,7 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener, // Update local states (before animating). setDividerVisibility(true); - setSideStagePosition(STAGE_POSITION_BOTTOM_OR_RIGHT, false /* updateVisibility */); + setSideStagePosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, false /* updateVisibility */); setSplitsVisible(true); addDividerBarToTransition(info, t, true /* show */); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java index 2f2bbba11646e..ba73d555e3343 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java @@ -24,13 +24,13 @@ import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY; import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT; import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK; +import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT; +import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_UNDEFINED; import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_FULLSCREEN; import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_BOTTOM; import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_LEFT; import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_RIGHT; import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_TOP; -import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT; -import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_UNDEFINED; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED; @@ -206,7 +206,7 @@ public class DragAndDropPolicyTest { mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData); verify(mSplitScreenStarter).startIntent(any(), any(), - eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any()); + eq(STAGE_TYPE_UNDEFINED), eq(SPLIT_POSITION_UNDEFINED), any()); } @Test @@ -218,12 +218,12 @@ public class DragAndDropPolicyTest { mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData); verify(mSplitScreenStarter).startIntent(any(), any(), - eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any()); + eq(STAGE_TYPE_UNDEFINED), eq(SPLIT_POSITION_UNDEFINED), any()); reset(mSplitScreenStarter); mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData); verify(mSplitScreenStarter).startIntent(any(), any(), - eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any()); + eq(STAGE_TYPE_SIDE), eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any()); } @Test @@ -235,12 +235,12 @@ public class DragAndDropPolicyTest { mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData); verify(mSplitScreenStarter).startIntent(any(), any(), - eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any()); + eq(STAGE_TYPE_UNDEFINED), eq(SPLIT_POSITION_UNDEFINED), any()); reset(mSplitScreenStarter); mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData); verify(mSplitScreenStarter).startIntent(any(), any(), - eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any()); + eq(STAGE_TYPE_SIDE), eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any()); } @Test @@ -252,7 +252,7 @@ public class DragAndDropPolicyTest { mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData); verify(mSplitScreenStarter).startIntent(any(), any(), - eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any()); + eq(STAGE_TYPE_UNDEFINED), eq(SPLIT_POSITION_UNDEFINED), any()); } @Test @@ -264,7 +264,7 @@ public class DragAndDropPolicyTest { mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData); verify(mSplitScreenStarter).startIntent(any(), any(), - eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any()); + eq(STAGE_TYPE_UNDEFINED), eq(SPLIT_POSITION_UNDEFINED), any()); } @Test @@ -277,13 +277,13 @@ public class DragAndDropPolicyTest { mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData); verify(mSplitScreenStarter).startIntent(any(), any(), - eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any()); + eq(STAGE_TYPE_UNDEFINED), eq(SPLIT_POSITION_UNDEFINED), any()); reset(mSplitScreenStarter); // TODO(b/169894807): Just verify starting for the non-docked task until we have app pairs mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData); verify(mSplitScreenStarter).startIntent(any(), any(), - eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any()); + eq(STAGE_TYPE_SIDE), eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any()); } @Test @@ -296,13 +296,13 @@ public class DragAndDropPolicyTest { mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData); verify(mSplitScreenStarter).startIntent(any(), any(), - eq(STAGE_TYPE_UNDEFINED), eq(STAGE_POSITION_UNDEFINED), any()); + eq(STAGE_TYPE_UNDEFINED), eq(SPLIT_POSITION_UNDEFINED), any()); reset(mSplitScreenStarter); // TODO(b/169894807): Just verify starting for the non-docked task until we have app pairs mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData); verify(mSplitScreenStarter).startIntent(any(), any(), - eq(STAGE_TYPE_SIDE), eq(STAGE_POSITION_BOTTOM_OR_RIGHT), any()); + eq(STAGE_TYPE_SIDE), eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any()); } @Test 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 924e946798315..06b08686bf4cb 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 @@ -18,7 +18,7 @@ package com.android.wm.shell.splitscreen; import static android.view.Display.DEFAULT_DISPLAY; -import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_POSITION_BOTTOM_OR_RIGHT; +import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; @@ -73,7 +73,7 @@ public class StageCoordinatorTests extends ShellTestCase { public void testMoveToSideStage() { final ActivityManager.RunningTaskInfo task = new TestRunningTaskInfoBuilder().build(); - mStageCoordinator.moveToSideStage(task, STAGE_POSITION_BOTTOM_OR_RIGHT); + mStageCoordinator.moveToSideStage(task, SPLIT_POSITION_BOTTOM_OR_RIGHT); verify(mMainStage).activate(any(Rect.class), any(WindowContainerTransaction.class)); verify(mSideStage).addTask(eq(task), any(Rect.class), From 4c425b2a1e9e40ad8b04160481b32d1671b1f661 Mon Sep 17 00:00:00 2001 From: Jerry Chang Date: Wed, 5 May 2021 17:35:09 +0800 Subject: [PATCH 2/5] Adjust split layout with IME animation in split (2/N) Add surface utils to help establishing dim surface layer for splits. Bug: 179262787 Test: atest WMShellUnitTests Change-Id: I0c268e0b1891fddcf717e8e54e0d29acbf8c5f24 --- .../android/wm/shell/apppairs/AppPair.java | 15 +++++++- .../android/wm/shell/common/SurfaceUtils.java | 38 +++++++++++++++++++ .../LegacySplitScreenController.java | 2 +- .../LegacySplitScreenTaskListener.java | 25 ++++-------- .../wm/shell/splitscreen/MainStage.java | 6 ++- .../wm/shell/splitscreen/SideStage.java | 6 ++- .../shell/splitscreen/StageCoordinator.java | 19 ++++++++-- .../shell/splitscreen/StageTaskListener.java | 16 +++++++- .../wm/shell/splitscreen/MainStageTests.java | 8 ++-- .../wm/shell/splitscreen/SideStageTests.java | 5 ++- .../splitscreen/SplitTransitionTests.java | 6 ++- .../splitscreen/StageTaskListenerTests.java | 18 ++++++++- 12 files changed, 130 insertions(+), 34 deletions(-) create mode 100644 libs/WindowManager/Shell/src/com/android/wm/shell/common/SurfaceUtils.java 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 eb82c6d597d4e..83af53f962f37 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 @@ -25,6 +25,7 @@ import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG import android.app.ActivityManager; import android.graphics.Rect; import android.view.SurfaceControl; +import android.view.SurfaceSession; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; @@ -35,6 +36,7 @@ import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; +import com.android.wm.shell.common.SurfaceUtils; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.split.SplitLayout; @@ -54,6 +56,9 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.LayoutChan private SurfaceControl mTaskLeash1; private ActivityManager.RunningTaskInfo mTaskInfo2; private SurfaceControl mTaskLeash2; + private SurfaceControl mDimLayer1; + private SurfaceControl mDimLayer2; + private final SurfaceSession mSurfaceSession = new SurfaceSession(); private final AppPairsController mController; private final SyncTransactionQueue mSyncQueue; @@ -153,9 +158,13 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.LayoutChan } else if (taskInfo.taskId == getTaskId1()) { mTaskInfo1 = taskInfo; mTaskLeash1 = leash; + mSyncQueue.runInSync(t -> mDimLayer1 = + SurfaceUtils.makeDimLayer(t, mTaskLeash1, "Dim layer", mSurfaceSession)); } else if (taskInfo.taskId == getTaskId2()) { mTaskInfo2 = taskInfo; mTaskLeash2 = leash; + mSyncQueue.runInSync(t -> mDimLayer2 = + SurfaceUtils.makeDimLayer(t, mTaskLeash2, "Dim layer", mSurfaceSession)); } else { throw new IllegalStateException("Unknown task=" + taskInfo.taskId); } @@ -212,8 +221,12 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.LayoutChan if (taskInfo.taskId == getRootTaskId()) { // We don't want to release this object back to the pool since the root task went away. mController.unpair(mRootTaskInfo.taskId, false /* releaseToPool */); - } else if (taskInfo.taskId == getTaskId1() || taskInfo.taskId == getTaskId2()) { + } else if (taskInfo.taskId == getTaskId1()) { mController.unpair(mRootTaskInfo.taskId); + mSyncQueue.runInSync(t -> t.remove(mDimLayer1)); + } else if (taskInfo.taskId == getTaskId2()) { + mController.unpair(mRootTaskInfo.taskId); + mSyncQueue.runInSync(t -> t.remove(mDimLayer2)); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SurfaceUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SurfaceUtils.java new file mode 100644 index 0000000000000..55c5125f0a00b --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SurfaceUtils.java @@ -0,0 +1,38 @@ +/* + * 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.common; + +import android.view.SurfaceControl; +import android.view.SurfaceSession; + +/** + * Helpers for handling surface. + */ +public class SurfaceUtils { + /** Creates a dim layer above indicated host surface. */ + public static SurfaceControl makeDimLayer(SurfaceControl.Transaction t, SurfaceControl host, + String name, SurfaceSession surfaceSession) { + SurfaceControl dimLayer = new SurfaceControl.Builder(surfaceSession) + .setParent(host) + .setColorLayer() + .setName(name) + .setCallsite("SurfaceUtils.makeDimLayer") + .build(); + t.setLayer(dimLayer, Integer.MAX_VALUE).setColor(dimLayer, new float[]{0f, 0f, 0f}); + return dimLayer; + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java index 9eacaecc4f506..ee2202a48bf2e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java @@ -577,7 +577,7 @@ public class LegacySplitScreenController implements DisplayController.OnDisplays mSplits.getSplitTransitions().dismissSplit( mSplits, mSplitLayout, !toPrimaryTask, snapped); } else { - mWindowManagerProxy.applyDismissSplit(mSplits, mSplitLayout, !toPrimaryTask); + mWindowManagerProxy.applyDismissSplit(mSplits, mSplitLayout, !toPrimaryTask); onDismissSplit(); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java index cf35656a395be..912c9605197f8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java @@ -38,6 +38,7 @@ import androidx.annotation.NonNull; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.common.SurfaceUtils; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.transition.Transitions; @@ -70,9 +71,9 @@ class LegacySplitScreenTaskListener implements ShellTaskOrganizer.TaskListener { private final LegacySplitScreenTransitions mSplitTransitions; LegacySplitScreenTaskListener(LegacySplitScreenController splitScreenController, - ShellTaskOrganizer shellTaskOrganizer, - Transitions transitions, - SyncTransactionQueue syncQueue) { + ShellTaskOrganizer shellTaskOrganizer, + Transitions transitions, + SyncTransactionQueue syncQueue) { mSplitScreenController = splitScreenController; mTaskOrganizer = shellTaskOrganizer; mSplitTransitions = new LegacySplitScreenTransitions(splitScreenController.mTransactionPool, @@ -146,21 +147,11 @@ class LegacySplitScreenTaskListener implements ShellTaskOrganizer.TaskListener { ProtoLog.v(WM_SHELL_TASK_ORG, "%s onTaskAppeared Supported", TAG); // Initialize dim surfaces: - mPrimaryDim = new SurfaceControl.Builder(mSurfaceSession) - .setParent(mPrimarySurface).setColorLayer() - .setName("Primary Divider Dim") - .setCallsite("SplitScreenTaskOrganizer.onTaskAppeared") - .build(); - mSecondaryDim = new SurfaceControl.Builder(mSurfaceSession) - .setParent(mSecondarySurface).setColorLayer() - .setName("Secondary Divider Dim") - .setCallsite("SplitScreenTaskOrganizer.onTaskAppeared") - .build(); SurfaceControl.Transaction t = getTransaction(); - t.setLayer(mPrimaryDim, Integer.MAX_VALUE); - t.setColor(mPrimaryDim, new float[]{0f, 0f, 0f}); - t.setLayer(mSecondaryDim, Integer.MAX_VALUE); - t.setColor(mSecondaryDim, new float[]{0f, 0f, 0f}); + mPrimaryDim = SurfaceUtils.makeDimLayer( + t, mPrimarySurface, "Primary Divider Dim", mSurfaceSession); + mSecondaryDim = SurfaceUtils.makeDimLayer( + t, mSecondarySurface, "Secondary Divider Dim", mSurfaceSession); t.apply(); releaseTransaction(t); } 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 66a4a60d4be6b..d0998eb57633e 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 @@ -19,6 +19,7 @@ package com.android.wm.shell.splitscreen; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import android.graphics.Rect; +import android.view.SurfaceSession; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; @@ -36,8 +37,9 @@ class MainStage extends StageTaskListener { private boolean mIsActive = false; MainStage(ShellTaskOrganizer taskOrganizer, int displayId, - StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue) { - super(taskOrganizer, displayId, callbacks, syncQueue); + StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue, + SurfaceSession surfaceSession) { + super(taskOrganizer, displayId, callbacks, syncQueue, surfaceSession); } boolean isActive() { 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 01a81d2ff5e01..82f95a4f32ea5 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 @@ -18,6 +18,7 @@ package com.android.wm.shell.splitscreen; import android.app.ActivityManager; import android.graphics.Rect; +import android.view.SurfaceSession; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; @@ -33,8 +34,9 @@ class SideStage extends StageTaskListener { private static final String TAG = SideStage.class.getSimpleName(); SideStage(ShellTaskOrganizer taskOrganizer, int displayId, - StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue) { - super(taskOrganizer, displayId, callbacks, syncQueue); + StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue, + SurfaceSession surfaceSession) { + super(taskOrganizer, displayId, callbacks, syncQueue, surfaceSession); } void addTask(ActivityManager.RunningTaskInfo task, Rect rootBounds, 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 ea302691af8e1..e20feae2d522a 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 @@ -47,6 +47,7 @@ import android.os.Bundle; import android.os.IBinder; import android.util.Log; import android.view.SurfaceControl; +import android.view.SurfaceSession; import android.view.WindowManager; import android.window.DisplayAreaInfo; import android.window.IRemoteTransition; @@ -92,6 +93,8 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener, /** internal value for mDismissTop that represents no dismiss */ private static final int NO_DISMISS = -2; + private final SurfaceSession mSurfaceSession = new SurfaceSession(); + private final MainStage mMainStage; private final StageListenerImpl mMainStageListener = new StageListenerImpl(); private final SideStage mSideStage; @@ -116,8 +119,8 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener, /** Whether the device is supporting legacy split or not. */ private boolean mUseLegacySplit; - @SplitScreen.StageType int mDismissTop = NO_DISMISS; + private final Runnable mOnTransitionAnimationComplete = () -> { // If still playing, let it finish. if (!isSplitScreenVisible()) { @@ -138,8 +141,18 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener, mSyncQueue = syncQueue; mRootTDAOrganizer = rootTDAOrganizer; mTaskOrganizer = taskOrganizer; - mMainStage = new MainStage(mTaskOrganizer, mDisplayId, mMainStageListener, mSyncQueue); - mSideStage = new SideStage(mTaskOrganizer, mDisplayId, mSideStageListener, mSyncQueue); + mMainStage = new MainStage( + mTaskOrganizer, + mDisplayId, + mMainStageListener, + mSyncQueue, + mSurfaceSession); + mSideStage = new SideStage( + mTaskOrganizer, + mDisplayId, + mSideStageListener, + mSyncQueue, + mSurfaceSession); mDisplayImeController = displayImeController; mRootTDAOrganizer.registerListener(displayId, this); mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions, 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 1da0a2d827663..a6d8a0b098dfe 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 @@ -29,11 +29,13 @@ import android.graphics.Point; import android.graphics.Rect; import android.util.SparseArray; import android.view.SurfaceControl; +import android.view.SurfaceSession; import android.window.WindowContainerTransaction; import androidx.annotation.NonNull; import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.common.SurfaceUtils; import com.android.wm.shell.common.SyncTransactionQueue; import java.io.PrintWriter; @@ -44,6 +46,7 @@ import java.io.PrintWriter; * They only serve to hold a collection of tasks and provide APIs like * {@link #setBounds(Rect, WindowContainerTransaction)} for the centralized {@link StageCoordinator} * to perform operations in-sync with other containers. + * * @see StageCoordinator */ class StageTaskListener implements ShellTaskOrganizer.TaskListener { @@ -58,22 +61,30 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { /** Callback interface for listening to changes in a split-screen stage. */ public interface StageListenerCallbacks { void onRootTaskAppeared(); + void onStatusChanged(boolean visible, boolean hasChildren); + void onChildTaskStatusChanged(int taskId, boolean present, boolean visible); + void onRootTaskVanished(); } + private final StageListenerCallbacks mCallbacks; private final SyncTransactionQueue mSyncQueue; + private final SurfaceSession mSurfaceSession; protected ActivityManager.RunningTaskInfo mRootTaskInfo; protected SurfaceControl mRootLeash; + protected SurfaceControl mDimLayer; protected SparseArray mChildrenTaskInfo = new SparseArray<>(); private final SparseArray mChildrenLeashes = new SparseArray<>(); StageTaskListener(ShellTaskOrganizer taskOrganizer, int displayId, - StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue) { + StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue, + SurfaceSession surfaceSession) { mCallbacks = callbacks; mSyncQueue = syncQueue; + mSurfaceSession = surfaceSession; taskOrganizer.createRootTask(displayId, WINDOWING_MODE_MULTI_WINDOW, this); } @@ -93,6 +104,8 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { mRootTaskInfo = taskInfo; mCallbacks.onRootTaskAppeared(); sendStatusChanged(); + mSyncQueue.runInSync(t -> mDimLayer = + SurfaceUtils.makeDimLayer(t, mRootLeash, "Dim layer", mSurfaceSession)); } else if (taskInfo.parentTaskId == mRootTaskInfo.taskId) { final int taskId = taskInfo.taskId; mChildrenLeashes.put(taskId, leash); @@ -140,6 +153,7 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { final int taskId = taskInfo.taskId; if (mRootTaskInfo.taskId == taskId) { mCallbacks.onRootTaskVanished(); + mSyncQueue.runInSync(t -> t.remove(mDimLayer)); mRootTaskInfo = null; } else if (mChildrenTaskInfo.contains(taskId)) { mChildrenTaskInfo.remove(taskId); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java index 702e8945de01d..1bb5fd1e49e70 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java @@ -22,6 +22,7 @@ import static com.google.common.truth.Truth.assertThat; import android.app.ActivityManager; import android.view.SurfaceControl; +import android.view.SurfaceSession; import android.window.WindowContainerTransaction; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -36,7 +37,6 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.mockito.Spy; /** Tests for {@link MainStage} */ @SmallTest @@ -47,14 +47,16 @@ public class MainStageTests { @Mock private SyncTransactionQueue mSyncQueue; @Mock private ActivityManager.RunningTaskInfo mRootTaskInfo; @Mock private SurfaceControl mRootLeash; - @Spy private WindowContainerTransaction mWct; + private WindowContainerTransaction mWct = new WindowContainerTransaction(); + private SurfaceSession mSurfaceSession = new SurfaceSession(); private MainStage mMainStage; @Before public void setup() { MockitoAnnotations.initMocks(this); mRootTaskInfo = new TestRunningTaskInfoBuilder().build(); - mMainStage = new MainStage(mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks, mSyncQueue); + mMainStage = new MainStage(mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks, mSyncQueue, + mSurfaceSession); mMainStage.onTaskAppeared(mRootTaskInfo, mRootLeash); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java index 01888b758bf6f..56a005642ce22 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SideStageTests.java @@ -26,6 +26,7 @@ import static org.mockito.Mockito.verify; import android.app.ActivityManager; import android.view.SurfaceControl; +import android.view.SurfaceSession; import android.window.WindowContainerTransaction; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -52,13 +53,15 @@ public class SideStageTests { @Mock private ActivityManager.RunningTaskInfo mRootTask; @Mock private SurfaceControl mRootLeash; @Spy private WindowContainerTransaction mWct; + private SurfaceSession mSurfaceSession = new SurfaceSession(); private SideStage mSideStage; @Before public void setup() { MockitoAnnotations.initMocks(this); mRootTask = new TestRunningTaskInfoBuilder().build(); - mSideStage = new SideStage(mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks, mSyncQueue); + mSideStage = new SideStage(mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks, mSyncQueue, + mSurfaceSession); mSideStage.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 08ac2a6cfa773..aca80f3556b98 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 @@ -43,6 +43,7 @@ import android.graphics.Rect; import android.os.IBinder; import android.os.RemoteException; import android.view.SurfaceControl; +import android.view.SurfaceSession; import android.window.IRemoteTransition; import android.window.IRemoteTransitionFinishedCallback; import android.window.TransitionInfo; @@ -80,6 +81,7 @@ public class SplitTransitionTests extends ShellTestCase { @Mock private DisplayImeController mDisplayImeController; @Mock private TransactionPool mTransactionPool; @Mock private Transitions mTransitions; + @Mock private SurfaceSession mSurfaceSession; private SplitLayout mSplitLayout; private MainStage mMainStage; private SideStage mSideStage; @@ -98,10 +100,10 @@ public class SplitTransitionTests extends ShellTestCase { doReturn(mock(SurfaceControl.Transaction.class)).when(mTransactionPool).acquire(); mSplitLayout = SplitTestUtils.createMockSplitLayout(); mMainStage = new MainStage(mTaskOrganizer, DEFAULT_DISPLAY, mock( - StageTaskListener.StageListenerCallbacks.class), mSyncQueue); + StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession); mMainStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface()); mSideStage = new SideStage(mTaskOrganizer, DEFAULT_DISPLAY, mock( - StageTaskListener.StageListenerCallbacks.class), mSyncQueue); + StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession); mSideStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface()); mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue, mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java index c66e0730422ce..afb26948f194d 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java @@ -27,6 +27,7 @@ import static org.mockito.Mockito.verify; import android.app.ActivityManager; import android.view.SurfaceControl; +import android.view.SurfaceSession; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; @@ -38,6 +39,8 @@ import com.android.wm.shell.common.SyncTransactionQueue; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -48,6 +51,8 @@ public final class StageTaskListenerTests { @Mock private ShellTaskOrganizer mTaskOrganizer; @Mock private StageTaskListener.StageListenerCallbacks mCallbacks; @Mock private SyncTransactionQueue mSyncQueue; + @Captor private ArgumentCaptor mRunnableCaptor; + private SurfaceSession mSurfaceSession = new SurfaceSession(); private ActivityManager.RunningTaskInfo mRootTask; private StageTaskListener mStageTaskListener; @@ -58,12 +63,23 @@ public final class StageTaskListenerTests { mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks, - mSyncQueue); + mSyncQueue, + mSurfaceSession); mRootTask = new TestRunningTaskInfoBuilder().build(); mRootTask.parentTaskId = INVALID_TASK_ID; mStageTaskListener.onTaskAppeared(mRootTask, new SurfaceControl()); } + @Test + public void testInitsDimLayer() { + verify(mSyncQueue).runInSync(mRunnableCaptor.capture()); + final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + mRunnableCaptor.getValue().runWithTransaction(t); + t.apply(); + + assertThat(mStageTaskListener.mDimLayer).isNotNull(); + } + @Test public void testRootTaskAppeared() { assertThat(mStageTaskListener.mRootTaskInfo.taskId).isEqualTo(mRootTask.taskId); From 5d2533cdfc9152204511518fcf5e72738166fd8b Mon Sep 17 00:00:00 2001 From: Jerry Chang Date: Thu, 6 May 2021 13:54:05 +0800 Subject: [PATCH 3/5] Adjust split layout with IME animation in split (3/N) Listen to IME position changes in SplitLayout to make sure it updates divider view status after re-inflated. Also add functions to get IME position of split which is needed for integrating IME animation with new split implementations. Bug: 179262787 Test: atest WMShellUnitTests Test: observed divider bar won't change to interactive after rotating devices with IME shown. Change-Id: Ic01c23e61ffb666cfc292e56dd6dcf4646be749b --- .../android/wm/shell/apppairs/AppPair.java | 23 +++++- .../wm/shell/common/split/DividerView.java | 12 +-- .../wm/shell/common/split/SplitLayout.java | 78 +++++++++++++++---- .../common/split/SplitWindowManager.java | 13 ++-- .../shell/splitscreen/StageCoordinator.java | 23 +++++- .../shell/common/split/SplitLayoutTests.java | 19 +++-- .../common/split/SplitWindowManagerTests.java | 4 +- 7 files changed, 124 insertions(+), 48 deletions(-) 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 83af53f962f37..abcde34ed5bbf 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 @@ -20,6 +20,9 @@ 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 com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT; +import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT; +import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_UNDEFINED; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG; import android.app.ActivityManager; @@ -47,7 +50,7 @@ import java.io.PrintWriter; * {@link #mTaskInfo1} and {@link #mTaskInfo2} in the pair. * Also includes all UI for managing the pair like the divider. */ -class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.LayoutChangeListener { +class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.SplitLayoutHandler { private static final String TAG = AppPair.class.getSimpleName(); private ActivityManager.RunningTaskInfo mRootTaskInfo; @@ -106,7 +109,8 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.LayoutChan mSplitLayout = new SplitLayout(TAG + "SplitDivider", mDisplayController.getDisplayContext(mRootTaskInfo.displayId), mRootTaskInfo.configuration, this /* layoutChangeListener */, - b -> b.setParent(mRootTaskLeash), mDisplayImeController); + b -> b.setParent(mRootTaskLeash), mDisplayImeController, + mController.getTaskOrganizer()); final WindowContainerToken token1 = task1.token; final WindowContainerToken token2 = task2.token; @@ -216,6 +220,21 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.LayoutChan } } + @Override + public int getSplitItemPosition(WindowContainerToken token) { + if (token == null) { + return SPLIT_POSITION_UNDEFINED; + } + + if (token.equals(mTaskInfo1.getToken())) { + return SPLIT_POSITION_TOP_OR_LEFT; + } else if (token.equals(mTaskInfo2.getToken())) { + return SPLIT_POSITION_BOTTOM_OR_RIGHT; + } + + return SPLIT_POSITION_UNDEFINED; + } + @Override public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) { if (taskInfo.taskId == getRootTaskId()) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java index 442e7a4c6796c..a920f9c08de47 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java @@ -36,13 +36,11 @@ import androidx.annotation.Nullable; import com.android.internal.policy.DividerSnapAlgorithm; import com.android.wm.shell.R; import com.android.wm.shell.animation.Interpolators; -import com.android.wm.shell.common.DisplayImeController; /** * Divider for multi window splits. */ -public class DividerView extends FrameLayout implements View.OnTouchListener, - DisplayImeController.ImePositionProcessor { +public class DividerView extends FrameLayout implements View.OnTouchListener { public static final long TOUCH_ANIMATION_DURATION = 150; public static final long TOUCH_RELEASE_ANIMATION_DURATION = 200; @@ -98,12 +96,6 @@ public class DividerView extends FrameLayout implements View.OnTouchListener, setOnTouchListener(this); } - @Override - public void onImeVisibilityChanged(int displayId, boolean isShowing) { - if (displayId != getDisplay().getDisplayId()) return; - setInteractive(!isShowing); - } - @Override public boolean onTouch(View v, MotionEvent event) { if (mSplitLayout == null || !mInteractive) { @@ -217,7 +209,7 @@ public class DividerView extends FrameLayout implements View.OnTouchListener, mViewHost.relayout(lp); } - private void setInteractive(boolean interactive) { + void setInteractive(boolean interactive) { if (interactive == mInteractive) return; mInteractive = interactive; releaseTouching(); 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 8e8473172597b..c1ba6a5356fad 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 @@ -28,14 +28,17 @@ import android.animation.ValueAnimator; import android.annotation.IntDef; import android.content.Context; import android.content.res.Configuration; +import android.content.res.Resources; import android.graphics.Rect; import android.view.SurfaceControl; import android.view.WindowInsets; import android.view.WindowManager; +import android.window.WindowContainerToken; import androidx.annotation.Nullable; import com.android.internal.policy.DividerSnapAlgorithm; +import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.animation.Interpolators; import com.android.wm.shell.common.DisplayImeController; @@ -77,8 +80,11 @@ public final class SplitLayout { private final Rect mDividerBounds = new Rect(); private final Rect mBounds1 = new Rect(); private final Rect mBounds2 = new Rect(); - private final LayoutChangeListener mLayoutChangeListener; + private final SplitLayoutHandler mSplitLayoutHandler; private final SplitWindowManager mSplitWindowManager; + private final DisplayImeController mDisplayImeController; + private final ImePositionProcessor mImePositionProcessor; + private final ShellTaskOrganizer mTaskOrganizer; private Context mContext; private DividerSnapAlgorithm mDividerSnapAlgorithm; @@ -86,18 +92,21 @@ public final class SplitLayout { private boolean mInitialized = false; public SplitLayout(String windowName, Context context, Configuration configuration, - LayoutChangeListener layoutChangeListener, + SplitLayoutHandler splitLayoutHandler, SplitWindowManager.ParentContainerCallbacks parentContainerCallbacks, - DisplayImeController displayImeController) { + DisplayImeController displayImeController, ShellTaskOrganizer taskOrganizer) { mContext = context.createConfigurationContext(configuration); - mLayoutChangeListener = layoutChangeListener; + mSplitLayoutHandler = splitLayoutHandler; + mDisplayImeController = displayImeController; mSplitWindowManager = new SplitWindowManager( - windowName, mContext, configuration, parentContainerCallbacks, - displayImeController); + windowName, mContext, configuration, parentContainerCallbacks); + mTaskOrganizer = taskOrganizer; + mImePositionProcessor = new ImePositionProcessor(mContext.getDisplayId()); - mDividerWindowWidth = context.getResources().getDimensionPixelSize( + final Resources resources = context.getResources(); + mDividerWindowWidth = resources.getDimensionPixelSize( com.android.internal.R.dimen.docked_stack_divider_thickness); - mDividerInsets = context.getResources().getDimensionPixelSize( + mDividerInsets = resources.getDimensionPixelSize( com.android.internal.R.dimen.docked_stack_divider_insets); mDividerSize = mDividerWindowWidth - mDividerInsets * 2; @@ -179,6 +188,7 @@ public final class SplitLayout { if (mInitialized) return; mInitialized = true; mSplitWindowManager.init(this); + mDisplayImeController.addPositionProcessor(mImePositionProcessor); } /** Releases the surface holding the current {@link DividerView}. */ @@ -186,6 +196,7 @@ public final class SplitLayout { if (!mInitialized) return; mInitialized = false; mSplitWindowManager.release(); + mDisplayImeController.removePositionProcessor(mImePositionProcessor); } /** @@ -194,14 +205,14 @@ public final class SplitLayout { */ void updateDivideBounds(int position) { updateBounds(position); - mLayoutChangeListener.onBoundsChanging(this); mSplitWindowManager.setResizingSplits(true); + mSplitLayoutHandler.onBoundsChanging(this); } void setDividePosition(int position) { mDividePosition = position; updateBounds(mDividePosition); - mLayoutChangeListener.onBoundsChanged(this); + mSplitLayoutHandler.onBoundsChanged(this); mSplitWindowManager.setResizingSplits(false); } @@ -218,11 +229,11 @@ public final class SplitLayout { public void snapToTarget(int currentPosition, DividerSnapAlgorithm.SnapTarget snapTarget) { switch (snapTarget.flag) { case FLAG_DISMISS_START: - mLayoutChangeListener.onSnappedToDismiss(false /* bottomOrRight */); + mSplitLayoutHandler.onSnappedToDismiss(false /* bottomOrRight */); mSplitWindowManager.setResizingSplits(false); break; case FLAG_DISMISS_END: - mLayoutChangeListener.onSnappedToDismiss(true /* bottomOrRight */); + mSplitLayoutHandler.onSnappedToDismiss(true /* bottomOrRight */); mSplitWindowManager.setResizingSplits(false); break; default: @@ -232,7 +243,7 @@ public final class SplitLayout { } void onDoubleTappedDivider() { - mLayoutChangeListener.onDoubleTappedDivider(); + mSplitLayoutHandler.onDoubleTappedDivider(); } /** @@ -291,8 +302,8 @@ public final class SplitLayout { return bounds.width() > bounds.height(); } - /** Listens layout change event. */ - public interface LayoutChangeListener { + /** Handles layout change event. */ + public interface SplitLayoutHandler { /** Calls when dismissing split. */ void onSnappedToDismiss(boolean snappedToEnd); @@ -305,5 +316,42 @@ public final class SplitLayout { /** Calls when user double tapped on the divider bar. */ default void onDoubleTappedDivider() { } + + /** Returns split position of the token. */ + @SplitPosition + int getSplitItemPosition(WindowContainerToken token); + } + + /** Records IME top offset changes and updates SplitLayout correspondingly. */ + private class ImePositionProcessor implements DisplayImeController.ImePositionProcessor { + + private final int mDisplayId; + + private ImePositionProcessor(int displayId) { + mDisplayId = displayId; + } + + @Override + public int onImeStartPositioning(int displayId, int hiddenTop, int shownTop, + boolean showing, boolean isFloating, SurfaceControl.Transaction t) { + if (displayId != mDisplayId) return 0; + final int imeTargetPosition = getImeTargetPosition(); + if (!mInitialized || imeTargetPosition == SPLIT_POSITION_UNDEFINED) return 0; + + // Make {@link DividerView} non-interactive while IME showing in split mode. Listen to + // ImePositionProcessor#onImeVisibilityChanged directly in DividerView is not enough + // because DividerView won't receive onImeVisibilityChanged callback after it being + // re-inflated. + mSplitWindowManager.setInteractive( + !showing || imeTargetPosition == SPLIT_POSITION_UNDEFINED); + + return 0; + } + + @SplitPosition + private int getImeTargetPosition() { + final WindowContainerToken token = mTaskOrganizer.getImeTarget(mDisplayId); + return mSplitLayoutHandler.getSplitItemPosition(token); + } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java index f6efb0120dda5..0cea0efc0057b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java @@ -46,7 +46,6 @@ import android.view.WindowlessWindowManager; import androidx.annotation.Nullable; import com.android.wm.shell.R; -import com.android.wm.shell.common.DisplayImeController; /** * Holds view hierarchy of a root surface and helps to inflate {@link DividerView} for a split. @@ -55,7 +54,6 @@ public final class SplitWindowManager extends WindowlessWindowManager { private static final String TAG = SplitWindowManager.class.getSimpleName(); private final String mWindowName; - private final DisplayImeController mDisplayImeController; private final ParentContainerCallbacks mParentContainerCallbacks; private Context mContext; private SurfaceControlViewHost mViewHost; @@ -68,13 +66,11 @@ public final class SplitWindowManager extends WindowlessWindowManager { } public SplitWindowManager(String windowName, Context context, Configuration config, - ParentContainerCallbacks parentContainerCallbacks, - DisplayImeController displayImeController) { + ParentContainerCallbacks parentContainerCallbacks) { super(config, null /* rootSurface */, null /* hostInputToken */); mContext = context.createConfigurationContext(config); mParentContainerCallbacks = parentContainerCallbacks; mWindowName = windowName; - mDisplayImeController = displayImeController; } @Override @@ -128,7 +124,6 @@ public final class SplitWindowManager extends WindowlessWindowManager { lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY; mViewHost.setView(mDividerView, lp); mDividerView.setup(splitLayout, mViewHost); - mDisplayImeController.addPositionProcessor(mDividerView); } /** @@ -137,7 +132,6 @@ public final class SplitWindowManager extends WindowlessWindowManager { */ void release() { if (mDividerView != null) { - mDisplayImeController.removePositionProcessor(mDividerView); mDividerView = null; } @@ -152,6 +146,11 @@ public final class SplitWindowManager extends WindowlessWindowManager { } } + void setInteractive(boolean interactive) { + if (mDividerView == null) return; + mDividerView.setInteractive(interactive); + } + void setResizingSplits(boolean resizing) { if (resizing == mResizingSplits) return; try { 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 e20feae2d522a..e1799d28e4236 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 @@ -27,6 +27,7 @@ import static android.view.WindowManager.transitTypeToString; import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT; import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT; +import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_UNDEFINED; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED; @@ -53,6 +54,7 @@ import android.window.DisplayAreaInfo; import android.window.IRemoteTransition; import android.window.TransitionInfo; import android.window.TransitionRequestInfo; +import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import com.android.internal.R; @@ -85,8 +87,8 @@ import java.util.List; * This rules are mostly implemented in {@link #onStageVisibilityChanged(StageListenerImpl)} and * {@link #onStageHasChildrenChanged(StageListenerImpl).} */ -class StageCoordinator implements SplitLayout.LayoutChangeListener, - RootTaskDisplayAreaOrganizer.RootTaskDisplayAreaListener, Transitions.TransitionHandler { +class StageCoordinator implements SplitLayout.SplitLayoutHandler, + RootTaskDisplayAreaOrganizer.RootTaskDisplayAreaListener, Transitions.TransitionHandler { private static final String TAG = StageCoordinator.class.getSimpleName(); @@ -564,6 +566,21 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener, .setWindowCrop(mSideStage.mRootLeash, null)); } + @Override + public int getSplitItemPosition(WindowContainerToken token) { + if (token == null) { + return SPLIT_POSITION_UNDEFINED; + } + + if (token.equals(mMainStage.mRootTaskInfo.getToken())) { + return getMainStagePosition(); + } else if (token.equals(mSideStage.mRootTaskInfo.getToken())) { + return getSideStagePosition(); + } + + return SPLIT_POSITION_UNDEFINED; + } + @Override public void onDisplayAreaAppeared(DisplayAreaInfo displayAreaInfo) { mDisplayAreaInfo = displayAreaInfo; @@ -571,7 +588,7 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener, mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext, mDisplayAreaInfo.configuration, this, b -> mRootTDAOrganizer.attachToDisplayArea(mDisplayId, b), - mDisplayImeController); + mDisplayImeController, mTaskOrganizer); } } 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 7b0e6b9a5ed7a..952dc31cdaee6 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 @@ -35,6 +35,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.policy.DividerSnapAlgorithm; +import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.DisplayImeController; @@ -48,9 +49,10 @@ import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidJUnit4.class) public class SplitLayoutTests extends ShellTestCase { - @Mock SplitLayout.LayoutChangeListener mLayoutChangeListener; + @Mock SplitLayout.SplitLayoutHandler mSplitLayoutHandler; @Mock SurfaceControl mRootLeash; @Mock DisplayImeController mDisplayImeController; + @Mock ShellTaskOrganizer mTaskOrganizer; private SplitLayout mSplitLayout; @Before @@ -60,9 +62,10 @@ public class SplitLayoutTests extends ShellTestCase { "TestSplitLayout", mContext, getConfiguration(false), - mLayoutChangeListener, + mSplitLayoutHandler, b -> b.setParent(mRootLeash), - mDisplayImeController); + mDisplayImeController, + mTaskOrganizer); } @Test @@ -76,19 +79,19 @@ public class SplitLayoutTests extends ShellTestCase { @Test public void testUpdateDivideBounds() { mSplitLayout.updateDivideBounds(anyInt()); - verify(mLayoutChangeListener).onBoundsChanging(any(SplitLayout.class)); + verify(mSplitLayoutHandler).onBoundsChanging(any(SplitLayout.class)); } @Test public void testSetDividePosition() { mSplitLayout.setDividePosition(anyInt()); - verify(mLayoutChangeListener).onBoundsChanged(any(SplitLayout.class)); + verify(mSplitLayoutHandler).onBoundsChanged(any(SplitLayout.class)); } @Test public void testOnDoubleTappedDivider() { mSplitLayout.onDoubleTappedDivider(); - verify(mLayoutChangeListener).onDoubleTappedDivider(); + verify(mSplitLayoutHandler).onDoubleTappedDivider(); } @Test @@ -98,11 +101,11 @@ public class SplitLayoutTests extends ShellTestCase { DividerSnapAlgorithm.SnapTarget snapTarget = getSnapTarget(0 /* position */, DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_START); mSplitLayout.snapToTarget(0 /* currentPosition */, snapTarget); - verify(mLayoutChangeListener).onSnappedToDismiss(eq(false)); + verify(mSplitLayoutHandler).onSnappedToDismiss(eq(false)); snapTarget = getSnapTarget(0 /* position */, DividerSnapAlgorithm.SnapTarget.FLAG_DISMISS_END); mSplitLayout.snapToTarget(0 /* currentPosition */, snapTarget); - verify(mLayoutChangeListener).onSnappedToDismiss(eq(true)); + verify(mSplitLayoutHandler).onSnappedToDismiss(eq(true)); } private static Configuration getConfiguration(boolean isLandscape) { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java index 86d0d82222e45..698315a77d8e5 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitWindowManagerTests.java @@ -29,7 +29,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.wm.shell.ShellTestCase; -import com.android.wm.shell.common.DisplayImeController; import org.junit.Before; import org.junit.Test; @@ -43,7 +42,6 @@ import org.mockito.MockitoAnnotations; public class SplitWindowManagerTests extends ShellTestCase { @Mock SurfaceControl mSurfaceControl; @Mock SplitLayout mSplitLayout; - @Mock DisplayImeController mDisplayImeController; private SplitWindowManager mSplitWindowManager; @Before @@ -52,7 +50,7 @@ public class SplitWindowManagerTests extends ShellTestCase { final Configuration configuration = new Configuration(); configuration.setToDefaults(); mSplitWindowManager = new SplitWindowManager("TestSplitDivider", mContext, configuration, - b -> b.setParent(mSurfaceControl), mDisplayImeController); + b -> b.setParent(mSurfaceControl)); when(mSplitLayout.getDividerBounds()).thenReturn( new Rect(0, 0, configuration.windowConfiguration.getBounds().width(), configuration.windowConfiguration.getBounds().height())); From 64049c4e0079e2401d7c0a5d21729cfb7e882b99 Mon Sep 17 00:00:00 2001 From: Jerry Chang Date: Thu, 6 May 2021 14:13:30 +0800 Subject: [PATCH 4/5] Adjust split layout with IME animation in split (4/N) Update split surface dim value to match IME animation. Bug: 179262787 Test: atest WMShellUnitTests Test: manual checks split surface dim properly with IME animation. Change-Id: I17c03def1a7e19d6bdbfc44fa846da4b1efa3275 --- .../android/wm/shell/apppairs/AppPair.java | 36 ++----- .../wm/shell/common/split/SplitLayout.java | 95 +++++++++++++++++++ .../shell/splitscreen/StageCoordinator.java | 63 ++++-------- 3 files changed, 122 insertions(+), 72 deletions(-) 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 abcde34ed5bbf..af65d36da473a 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 @@ -291,40 +291,16 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.SplitLayou @Override public void onBoundsChanging(SplitLayout layout) { - final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash(); - if (dividerLeash == null) return; - final Rect dividerBounds = layout.getDividerBounds(); - final Rect bounds1 = layout.getBounds1(); - final Rect bounds2 = layout.getBounds2(); - mSyncQueue.runInSync(t -> t - .setPosition(dividerLeash, dividerBounds.left, dividerBounds.top) - .setPosition(mTaskLeash1, bounds1.left, bounds1.top) - .setPosition(mTaskLeash2, bounds2.left, bounds2.top) - // Sets crop to prevent visible region of tasks overlap with each other when - // re-positioning surfaces while resizing. - .setWindowCrop(mTaskLeash1, bounds1.width(), bounds1.height()) - .setWindowCrop(mTaskLeash2, bounds2.width(), bounds2.height())); + mSyncQueue.runInSync(t -> + layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2)); } @Override public void onBoundsChanged(SplitLayout layout) { - final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash(); - if (dividerLeash == null) return; - final Rect dividerBounds = layout.getDividerBounds(); - final Rect bounds1 = layout.getBounds1(); - final Rect bounds2 = layout.getBounds2(); final WindowContainerTransaction wct = new WindowContainerTransaction(); - wct.setBounds(mTaskInfo1.token, bounds1) - .setBounds(mTaskInfo2.token, bounds2); - mController.getTaskOrganizer().applyTransaction(wct); - mSyncQueue.runInSync(t -> t - // Resets layer of divider bar to make sure it is always on top. - .setLayer(dividerLeash, Integer.MAX_VALUE) - .setPosition(dividerLeash, dividerBounds.left, dividerBounds.top) - .setPosition(mTaskLeash1, bounds1.left, bounds1.top) - .setPosition(mTaskLeash2, bounds2.left, bounds2.top) - // Resets crop to apply new surface bounds directly. - .setWindowCrop(mTaskLeash1, null) - .setWindowCrop(mTaskLeash2, null)); + layout.applyTaskChanges(wct, mTaskInfo1, mTaskInfo2); + mSyncQueue.queue(wct); + mSyncQueue.runInSync(t -> + layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2)); } } 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 c1ba6a5356fad..310e4791ddd3c 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 @@ -26,6 +26,7 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.annotation.IntDef; +import android.app.ActivityManager; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; @@ -34,6 +35,7 @@ import android.view.SurfaceControl; import android.view.WindowInsets; import android.view.WindowManager; import android.window.WindowContainerToken; +import android.window.WindowContainerTransaction; import androidx.annotation.Nullable; @@ -197,6 +199,7 @@ public final class SplitLayout { mInitialized = false; mSplitWindowManager.release(); mDisplayImeController.removePositionProcessor(mImePositionProcessor); + mImePositionProcessor.reset(); } /** @@ -302,8 +305,35 @@ public final class SplitLayout { return bounds.width() > bounds.height(); } + /** Apply recorded surface layout to the {@link SurfaceControl.Transaction}. */ + public void applySurfaceChanges(SurfaceControl.Transaction t, SurfaceControl leash1, + SurfaceControl leash2, SurfaceControl dimLayer1, SurfaceControl dimLayer2) { + final SurfaceControl dividerLeash = getDividerLeash(); + if (dividerLeash != null) { + t.setPosition(dividerLeash, mDividerBounds.left, mDividerBounds.top) + // Resets layer of divider bar to make sure it is always on top. + .setLayer(dividerLeash, Integer.MAX_VALUE); + } + + t.setPosition(leash1, mBounds1.left, mBounds1.top) + .setWindowCrop(leash1, mBounds1.width(), mBounds1.height()); + + t.setPosition(leash2, mBounds2.left, mBounds2.top) + .setWindowCrop(leash2, mBounds2.width(), mBounds2.height()); + + mImePositionProcessor.applySurfaceDimValues(t, dimLayer1, dimLayer2); + } + + /** Apply recorded task layout to the {@link WindowContainerTransaction}. */ + public void applyTaskChanges(WindowContainerTransaction wct, + ActivityManager.RunningTaskInfo task1, ActivityManager.RunningTaskInfo task2) { + wct.setBounds(task1.token, mBounds1) + .setBounds(task2.token, mBounds2); + } + /** Handles layout change event. */ public interface SplitLayoutHandler { + /** Calls when dismissing split. */ void onSnappedToDismiss(boolean snappedToEnd); @@ -324,9 +354,26 @@ public final class SplitLayout { /** Records IME top offset changes and updates SplitLayout correspondingly. */ private class ImePositionProcessor implements DisplayImeController.ImePositionProcessor { + /** + * Maximum size of an adjusted split bounds relative to original stack bounds. Used to + * restrict IME adjustment so that a min portion of top split remains visible. + */ + private static final float ADJUSTED_SPLIT_FRACTION_MAX = 0.7f; + private static final float ADJUSTED_NONFOCUS_DIM = 0.3f; private final int mDisplayId; + private float mDimValue1; + private float mDimValue2; + + private int mStartImeTop; + private int mEndImeTop; + + private float mTargetDim1; + private float mTargetDim2; + private float mLastDim1; + private float mLastDim2; + private ImePositionProcessor(int displayId) { mDisplayId = displayId; } @@ -337,6 +384,16 @@ public final class SplitLayout { if (displayId != mDisplayId) return 0; final int imeTargetPosition = getImeTargetPosition(); if (!mInitialized || imeTargetPosition == SPLIT_POSITION_UNDEFINED) return 0; + mStartImeTop = showing ? hiddenTop : shownTop; + mEndImeTop = showing ? shownTop : hiddenTop; + + // Update target dim values + mLastDim1 = mDimValue1; + mTargetDim1 = imeTargetPosition == SPLIT_POSITION_BOTTOM_OR_RIGHT && showing + ? ADJUSTED_NONFOCUS_DIM : 0.0f; + mLastDim2 = mDimValue2; + mTargetDim2 = imeTargetPosition == SPLIT_POSITION_TOP_OR_LEFT && showing + ? ADJUSTED_NONFOCUS_DIM : 0.0f; // Make {@link DividerView} non-interactive while IME showing in split mode. Listen to // ImePositionProcessor#onImeVisibilityChanged directly in DividerView is not enough @@ -348,10 +405,48 @@ public final class SplitLayout { return 0; } + @Override + public void onImePositionChanged(int displayId, int imeTop, SurfaceControl.Transaction t) { + if (displayId != mDisplayId) return; + onProgress(getProgress(imeTop)); + mSplitLayoutHandler.onBoundsChanging(SplitLayout.this); + } + + @Override + public void onImeEndPositioning(int displayId, boolean cancel, + SurfaceControl.Transaction t) { + if (displayId != mDisplayId || cancel) return; + onProgress(1.0f); + mSplitLayoutHandler.onBoundsChanging(SplitLayout.this); + } + @SplitPosition private int getImeTargetPosition() { final WindowContainerToken token = mTaskOrganizer.getImeTarget(mDisplayId); return mSplitLayoutHandler.getSplitItemPosition(token); } + + private float getProgress(int currImeTop) { + return ((float) currImeTop - mStartImeTop) / (mEndImeTop - mStartImeTop); + } + + private void onProgress(float progress) { + mDimValue1 = getProgressValue(mLastDim1, mTargetDim1, progress); + mDimValue2 = getProgressValue(mLastDim2, mTargetDim2, progress); + } + + private float getProgressValue(float start, float end, float progress) { + return start + (end - start) * progress; + } + + private void reset() { + mDimValue1 = mDimValue2 = 0.0f; + } + + private void applySurfaceDimValues(SurfaceControl.Transaction t, SurfaceControl dimLayer1, + SurfaceControl dimLayer2) { + t.setAlpha(dimLayer1, mDimValue1).setVisibility(dimLayer1, mDimValue1 > 0.001f); + t.setAlpha(dimLayer2, mDimValue2).setVisibility(dimLayer2, mDimValue2 > 0.001f); + } } } 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 e1799d28e4236..7b788543c0b2d 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 @@ -514,27 +514,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, exitSplitScreen(mainStageToTop ? mMainStage : mSideStage); } - @Override - public void onBoundsChanging(SplitLayout layout) { - final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash(); - if (dividerLeash == null) return; - final Rect mainStageBounds = getMainStageBounds(); - final Rect sideStageBounds = getSideStageBounds(); - - mSyncQueue.runInSync(t -> t - .setPosition(dividerLeash, - mSplitLayout.getDividerBounds().left, mSplitLayout.getDividerBounds().top) - .setPosition(mMainStage.mRootLeash, mainStageBounds.left, mainStageBounds.top) - .setPosition(mSideStage.mRootLeash, sideStageBounds.left, sideStageBounds.top) - // Sets crop to prevent visible region of tasks overlap with each other when - // re-positioning surfaces while resizing. - .setWindowCrop(mMainStage.mRootLeash, - mainStageBounds.width(), mainStageBounds.height()) - .setWindowCrop(mSideStage.mRootLeash, - sideStageBounds.width(), sideStageBounds.height())); - - } - @Override public void onDoubleTappedDivider() { setSideStagePosition(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT @@ -542,28 +521,28 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, } @Override - public void onBoundsChanged(SplitLayout layout) { - final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash(); - if (dividerLeash == null) return; - final Rect mainStageBounds = getMainStageBounds(); - final Rect sideStageBounds = getSideStageBounds(); - final WindowContainerTransaction wct = new WindowContainerTransaction(); - mMainStage.setBounds(mainStageBounds, wct); - mSideStage.setBounds(sideStageBounds, wct); - mTaskOrganizer.applyTransaction(wct); + public void onBoundsChanging(SplitLayout layout) { + final StageTaskListener topLeftStage = + mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage; + final StageTaskListener bottomRightStage = + mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage; - mSyncQueue.runInSync(t -> t - // Resets layer of divider bar to make sure it is always on top. - .setLayer(dividerLeash, Integer.MAX_VALUE) - .setPosition(dividerLeash, - mSplitLayout.getDividerBounds().left, mSplitLayout.getDividerBounds().top) - .setPosition(mMainStage.mRootLeash, - mainStageBounds.left, mainStageBounds.top) - .setPosition(mSideStage.mRootLeash, - sideStageBounds.left, sideStageBounds.top) - // Resets crop to apply new surface bounds directly. - .setWindowCrop(mMainStage.mRootLeash, null) - .setWindowCrop(mSideStage.mRootLeash, null)); + mSyncQueue.runInSync(t -> layout.applySurfaceChanges(t, topLeftStage.mRootLeash, + bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer)); + } + + @Override + public void onBoundsChanged(SplitLayout layout) { + final StageTaskListener topLeftStage = + mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage; + final StageTaskListener bottomRightStage = + mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage; + + final WindowContainerTransaction wct = new WindowContainerTransaction(); + layout.applyTaskChanges(wct, topLeftStage.mRootTaskInfo, bottomRightStage.mRootTaskInfo); + mSyncQueue.queue(wct); + mSyncQueue.runInSync(t -> layout.applySurfaceChanges(t, topLeftStage.mRootLeash, + bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer)); } @Override From aebdc015e0a4d36ab16a700cf7252a03038920c0 Mon Sep 17 00:00:00 2001 From: Jerry Chang Date: Fri, 26 Feb 2021 20:53:06 +0800 Subject: [PATCH 5/5] Adjust split layout with IME animation in split (5/N) Adjust split bounds to match the IME animation. Fix: 179262787 Test: atest WMShellUnitTests Test: observed split bounds been offset properly when rotating devices or changing focus while IME showing. Change-Id: I70d8cdf5a019f77873a75bc0881b1c100b372664 --- .../wm/shell/common/split/SplitLayout.java | 49 +++++++++++++++---- 1 file changed, 39 insertions(+), 10 deletions(-) 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 310e4791ddd3c..e42f511eb3914 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 @@ -119,17 +119,17 @@ public final class SplitLayout { /** Gets bounds of the primary split. */ public Rect getBounds1() { - return mBounds1; + return new Rect(mBounds1); } /** Gets bounds of the secondary split. */ public Rect getBounds2() { - return mBounds2; + return new Rect(mBounds2); } /** Gets bounds of divider window. */ public Rect getDividerBounds() { - return mDividerBounds; + return new Rect(mDividerBounds); } /** Returns leash of the current divider bar. */ @@ -308,18 +308,21 @@ public final class SplitLayout { /** Apply recorded surface layout to the {@link SurfaceControl.Transaction}. */ public void applySurfaceChanges(SurfaceControl.Transaction t, SurfaceControl leash1, SurfaceControl leash2, SurfaceControl dimLayer1, SurfaceControl dimLayer2) { + final Rect dividerBounds = mImePositionProcessor.adjustForIme(mDividerBounds); + final Rect bounds1 = mImePositionProcessor.adjustForIme(mBounds1); + final Rect bounds2 = mImePositionProcessor.adjustForIme(mBounds2); final SurfaceControl dividerLeash = getDividerLeash(); if (dividerLeash != null) { - t.setPosition(dividerLeash, mDividerBounds.left, mDividerBounds.top) + t.setPosition(dividerLeash, dividerBounds.left, dividerBounds.top) // Resets layer of divider bar to make sure it is always on top. .setLayer(dividerLeash, Integer.MAX_VALUE); } - t.setPosition(leash1, mBounds1.left, mBounds1.top) - .setWindowCrop(leash1, mBounds1.width(), mBounds1.height()); + t.setPosition(leash1, bounds1.left, bounds1.top) + .setWindowCrop(leash1, bounds1.width(), bounds1.height()); - t.setPosition(leash2, mBounds2.left, mBounds2.top) - .setWindowCrop(leash2, mBounds2.width(), mBounds2.height()); + t.setPosition(leash2, bounds2.left, bounds2.top) + .setWindowCrop(leash2, bounds2.width(), bounds2.height()); mImePositionProcessor.applySurfaceDimValues(t, dimLayer1, dimLayer2); } @@ -327,8 +330,8 @@ public final class SplitLayout { /** Apply recorded task layout to the {@link WindowContainerTransaction}. */ public void applyTaskChanges(WindowContainerTransaction wct, ActivityManager.RunningTaskInfo task1, ActivityManager.RunningTaskInfo task2) { - wct.setBounds(task1.token, mBounds1) - .setBounds(task2.token, mBounds2); + wct.setBounds(task1.token, mImePositionProcessor.adjustForIme(mBounds1)) + .setBounds(task2.token, mImePositionProcessor.adjustForIme(mBounds2)); } /** Handles layout change event. */ @@ -363,12 +366,15 @@ public final class SplitLayout { private final int mDisplayId; + private int mYOffsetForIme; private float mDimValue1; private float mDimValue2; private int mStartImeTop; private int mEndImeTop; + private int mTargetYOffset; + private int mLastYOffset; private float mTargetDim1; private float mTargetDim2; private float mLastDim1; @@ -395,6 +401,12 @@ public final class SplitLayout { mTargetDim2 = imeTargetPosition == SPLIT_POSITION_TOP_OR_LEFT && showing ? ADJUSTED_NONFOCUS_DIM : 0.0f; + // Calculate target bounds offset for IME + mLastYOffset = mYOffsetForIme; + final boolean needOffset = imeTargetPosition == SPLIT_POSITION_BOTTOM_OR_RIGHT + && !isFloating && !isLandscape(mRootBounds) && showing; + mTargetYOffset = needOffset ? getTargetYOffset() : 0; + // Make {@link DividerView} non-interactive while IME showing in split mode. Listen to // ImePositionProcessor#onImeVisibilityChanged directly in DividerView is not enough // because DividerView won't receive onImeVisibilityChanged callback after it being @@ -420,6 +432,13 @@ public final class SplitLayout { mSplitLayoutHandler.onBoundsChanging(SplitLayout.this); } + private int getTargetYOffset() { + final int desireOffset = Math.abs(mEndImeTop - mStartImeTop); + // Make sure to keep at least 30% visible for the top split. + final int maxOffset = (int) (mBounds1.height() * ADJUSTED_SPLIT_FRACTION_MAX); + return -Math.min(desireOffset, maxOffset); + } + @SplitPosition private int getImeTargetPosition() { final WindowContainerToken token = mTaskOrganizer.getImeTarget(mDisplayId); @@ -433,6 +452,8 @@ public final class SplitLayout { private void onProgress(float progress) { mDimValue1 = getProgressValue(mLastDim1, mTargetDim1, progress); mDimValue2 = getProgressValue(mLastDim2, mTargetDim2, progress); + mYOffsetForIme = + (int) getProgressValue((float) mLastYOffset, (float) mTargetYOffset, progress); } private float getProgressValue(float start, float end, float progress) { @@ -440,9 +461,17 @@ public final class SplitLayout { } private void reset() { + mYOffsetForIme = 0; mDimValue1 = mDimValue2 = 0.0f; } + /* Adjust bounds with IME offset. */ + private Rect adjustForIme(Rect bounds) { + final Rect temp = new Rect(bounds); + if (mYOffsetForIme != 0) temp.offset(0, mYOffsetForIme); + return temp; + } + private void applySurfaceDimValues(SurfaceControl.Transaction t, SurfaceControl dimLayer1, SurfaceControl dimLayer2) { t.setAlpha(dimLayer1, mDimValue1).setVisibility(dimLayer1, mDimValue1 > 0.001f);