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/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java index dc61345708b10..e6d088e6537de 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,11 +20,15 @@ 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; import android.graphics.Rect; import android.view.SurfaceControl; +import android.view.SurfaceSession; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; @@ -35,6 +39,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; @@ -45,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; @@ -54,6 +59,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; @@ -101,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; @@ -153,9 +162,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,13 +225,32 @@ 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()) { // 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)); } } @@ -264,40 +296,16 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.LayoutChan @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/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/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 d318a5aaef5cd..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 @@ -25,16 +25,22 @@ 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.app.ActivityManager; 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 android.window.WindowContainerTransaction; 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; @@ -42,7 +48,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; @@ -51,8 +82,11 @@ public 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; @@ -60,18 +94,21 @@ public 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; @@ -82,17 +119,17 @@ public 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. */ @@ -153,6 +190,7 @@ public class SplitLayout { if (mInitialized) return; mInitialized = true; mSplitWindowManager.init(this); + mDisplayImeController.addPositionProcessor(mImePositionProcessor); } /** Releases the surface holding the current {@link DividerView}. */ @@ -160,6 +198,8 @@ public class SplitLayout { if (!mInitialized) return; mInitialized = false; mSplitWindowManager.release(); + mDisplayImeController.removePositionProcessor(mImePositionProcessor); + mImePositionProcessor.reset(); } /** @@ -168,14 +208,14 @@ public 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); } @@ -192,11 +232,11 @@ public 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: @@ -206,7 +246,7 @@ public class SplitLayout { } void onDoubleTappedDivider() { - mLayoutChangeListener.onDoubleTappedDivider(); + mSplitLayoutHandler.onDoubleTappedDivider(); } /** @@ -265,8 +305,38 @@ public class SplitLayout { return bounds.width() > bounds.height(); } - /** Listens layout change event. */ - public interface LayoutChangeListener { + /** 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, 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, bounds1.left, bounds1.top) + .setWindowCrop(leash1, bounds1.width(), bounds1.height()); + + t.setPosition(leash2, bounds2.left, bounds2.top) + .setWindowCrop(leash2, bounds2.width(), bounds2.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, mImePositionProcessor.adjustForIme(mBounds1)) + .setBounds(task2.token, mImePositionProcessor.adjustForIme(mBounds2)); + } + + /** Handles layout change event. */ + public interface SplitLayoutHandler { + /** Calls when dismissing split. */ void onSnappedToDismiss(boolean snappedToEnd); @@ -279,5 +349,133 @@ public 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 { + /** + * 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 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; + private float mLastDim2; + + 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; + 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; + + // 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 + // re-inflated. + mSplitWindowManager.setInteractive( + !showing || imeTargetPosition == SPLIT_POSITION_UNDEFINED); + + 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); + } + + 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); + 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); + mYOffsetForIme = + (int) getProgressValue((float) mLastYOffset, (float) mTargetYOffset, progress); + } + + private float getProgressValue(float start, float end, float progress) { + return start + (end - start) * progress; + } + + 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); + t.setAlpha(dimLayer2, mDimValue2).setVisibility(dimLayer2, mDimValue2 > 0.001f); + } } } 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/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/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 13596ea5e8860..86bf3ff1cea98 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/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 eb60dc25ccd26..0264c5a1c55aa 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,9 @@ 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.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; @@ -47,14 +48,15 @@ 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; import android.window.TransitionInfo; import android.window.TransitionRequestInfo; +import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; - import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; @@ -64,6 +66,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; @@ -84,19 +87,22 @@ 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(); /** 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; 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; @@ -115,8 +121,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()) { @@ -137,8 +143,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, @@ -176,7 +192,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 +217,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 +241,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 +307,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 +505,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; @@ -497,55 +515,49 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener, } @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())); - + public void onDoubleTappedDivider() { + setSideStagePosition(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT + ? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT); } @Override - public void onDoubleTappedDivider() { - setSideStagePosition(mSideStagePosition == STAGE_POSITION_TOP_OR_LEFT - ? STAGE_POSITION_BOTTOM_OR_RIGHT : STAGE_POSITION_TOP_OR_LEFT); + 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 -> layout.applySurfaceChanges(t, topLeftStage.mRootLeash, + bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer)); } @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); + 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)); + 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 + 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 @@ -555,7 +567,7 @@ class StageCoordinator implements SplitLayout.LayoutChangeListener, mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext, mDisplayAreaInfo.configuration, this, b -> mRootTDAOrganizer.attachToDisplayArea(mDisplayId, b), - mDisplayImeController); + mDisplayImeController, mTaskOrganizer); } } @@ -574,12 +586,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 +754,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/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java index 147a9df7fefe6..0fd8eca6290e6 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,23 +61,31 @@ 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(); void onNoLongerSupportMultiWindow(); } + 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); } @@ -94,6 +105,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); @@ -146,6 +159,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/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())); 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/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/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), 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 b3d2be99db147..90b5b37694c6e 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; @@ -52,6 +55,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; @@ -62,12 +67,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);