diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineManager.java index 5d5a6e50341af..f74cb03424d67 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineManager.java @@ -22,6 +22,7 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMA import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; +import android.annotation.Nullable; import android.content.Context; import android.content.res.Configuration; import android.graphics.PixelFormat; @@ -51,7 +52,8 @@ class OutlineManager extends WindowlessWindowManager { private final Rect mOutlineBounds = new Rect(); private final Rect mTmpBounds = new Rect(); private final Supplier mOutlineSurfaceSupplier; - private SurfaceControlViewHost mViewHost; + private final SurfaceControlViewHost mViewHost; + private final SurfaceControl mLeash; /** * Constructs {@link #OutlineManager} with indicated outline color for the provided root @@ -60,9 +62,27 @@ class OutlineManager extends WindowlessWindowManager { OutlineManager(Context context, Configuration configuration, Supplier outlineSurfaceSupplier, int color) { super(configuration, null /* rootSurface */, null /* hostInputToken */); - mContext = context.createDisplayContext(context.getDisplay()); + mContext = context.createWindowContext(context.getDisplay(), TYPE_APPLICATION_OVERLAY, + null /* options */); mOutlineSurfaceSupplier = outlineSurfaceSupplier; mOutlineColor = color; + + mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this); + final OutlineRoot rootView = (OutlineRoot) LayoutInflater.from(mContext) + .inflate(R.layout.split_outline, null); + rootView.updateOutlineBounds(mOutlineBounds, mOutlineColor); + + final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( + 0 /* width */, 0 /* height */, TYPE_APPLICATION_OVERLAY, + FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCHABLE, PixelFormat.TRANSLUCENT); + lp.token = new Binder(); + lp.setTitle(WINDOW_NAME); + lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY; + // TODO(b/189839391): Set INPUT_FEATURE_NO_INPUT_CHANNEL after WM supports + // TRUSTED_OVERLAY for windowless window without input channel. + mViewHost.setView(rootView, lp); + + mLeash = getSurfaceControl(mViewHost.getWindowToken()); } @Override @@ -77,37 +97,21 @@ class OutlineManager extends WindowlessWindowManager { } mOutlineBounds.set(mTmpBounds); - if (mViewHost == null) { - mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this); - } - if (mViewHost.getView() == null) { - final OutlineRoot rootView = (OutlineRoot) LayoutInflater.from(mContext) - .inflate(R.layout.split_outline, null); - rootView.updateOutlineBounds(mOutlineBounds, mOutlineColor); - - final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( - rootBounds.width(), rootBounds.height(), - TYPE_APPLICATION_OVERLAY, - FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCHABLE, - PixelFormat.TRANSLUCENT); - lp.token = new Binder(); - lp.setTitle(WINDOW_NAME); - lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY; - // TODO(b/189839391): Set INPUT_FEATURE_NO_INPUT_CHANNEL after WM supports - // TRUSTED_OVERLAY for windowless window without input channel. - mViewHost.setView(rootView, lp); - } else { - ((OutlineRoot) mViewHost.getView()).updateOutlineBounds(mOutlineBounds, mOutlineColor); - final WindowManager.LayoutParams lp = - (WindowManager.LayoutParams) mViewHost.getView().getLayoutParams(); - lp.width = rootBounds.width(); - lp.height = rootBounds.height(); - mViewHost.relayout(lp); - } + ((OutlineRoot) mViewHost.getView()).updateOutlineBounds(mOutlineBounds, mOutlineColor); + final WindowManager.LayoutParams lp = + (WindowManager.LayoutParams) mViewHost.getView().getLayoutParams(); + lp.width = rootBounds.width(); + lp.height = rootBounds.height(); + mViewHost.relayout(lp); return true; } + @Nullable + SurfaceControl getLeash() { + return mLeash; + } + private static void computeOutlineBounds(Context context, Rect rootBounds, Rect outBounds) { computeDisplayStableBounds(context, outBounds); outBounds.intersect(rootBounds); 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 a0bdcc3edd5f1..5d29535ca2973 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 @@ -26,6 +26,7 @@ import android.view.SurfaceSession; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; +import com.android.internal.annotations.VisibleForTesting; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.SyncTransactionQueue; @@ -47,6 +48,14 @@ class SideStage extends StageTaskListener { mContext = context; } + @VisibleForTesting + SideStage(Context context, ShellTaskOrganizer taskOrganizer, int displayId, + StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue, + SurfaceSession surfaceSession, OutlineManager outlineManager) { + this(context, taskOrganizer, displayId, callbacks, syncQueue, surfaceSession); + mOutlineManager = outlineManager; + } + void addTask(ActivityManager.RunningTaskInfo task, Rect rootBounds, WindowContainerTransaction wct) { final WindowContainerToken rootToken = mRootTaskInfo.token; @@ -82,10 +91,16 @@ class SideStage extends StageTaskListener { @CallSuper public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { super.onTaskAppeared(taskInfo, leash); - if (mRootTaskInfo != null && mRootTaskInfo.taskId == taskInfo.taskId) { + if (mRootTaskInfo != null && mRootTaskInfo.taskId == taskInfo.taskId + && mOutlineManager == null) { mOutlineManager = new OutlineManager(mContext, mRootTaskInfo.configuration, () -> mRootLeash, Color.YELLOW); + if (mOutlineManager.getLeash() != null) { + mSyncQueue.runInSync(t -> { + t.setLayer(mOutlineManager.getLeash(), Integer.MAX_VALUE); + }); + } } } 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 0fd8eca6290e6..4f73feec750d2 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 @@ -71,8 +71,8 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { } private final StageListenerCallbacks mCallbacks; - private final SyncTransactionQueue mSyncQueue; private final SurfaceSession mSurfaceSession; + protected final SyncTransactionQueue mSyncQueue; protected ActivityManager.RunningTaskInfo mRootTaskInfo; protected SurfaceControl 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 69ead3ac9cf95..2aad38f33efbe 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 @@ -53,6 +53,7 @@ public class SideStageTests extends ShellTestCase { @Mock private SyncTransactionQueue mSyncQueue; @Mock private ActivityManager.RunningTaskInfo mRootTask; @Mock private SurfaceControl mRootLeash; + @Mock private OutlineManager mOutlineManager; @Spy private WindowContainerTransaction mWct; private SurfaceSession mSurfaceSession = new SurfaceSession(); private SideStage mSideStage; @@ -62,7 +63,7 @@ public class SideStageTests extends ShellTestCase { MockitoAnnotations.initMocks(this); mRootTask = new TestRunningTaskInfoBuilder().build(); mSideStage = new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks, - mSyncQueue, mSurfaceSession); + mSyncQueue, mSurfaceSession, mOutlineManager); 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 b6da8681d8503..44444ad255a3f 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 @@ -82,6 +82,7 @@ public class SplitTransitionTests extends ShellTestCase { @Mock private TransactionPool mTransactionPool; @Mock private Transitions mTransitions; @Mock private SurfaceSession mSurfaceSession; + @Mock private OutlineManager mOutlineManager; private SplitLayout mSplitLayout; private MainStage mMainStage; private SideStage mSideStage; @@ -103,7 +104,8 @@ public class SplitTransitionTests extends ShellTestCase { StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession); mMainStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface()); mSideStage = new SideStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock( - StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession); + StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession, + mOutlineManager); mSideStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface()); mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue, mRootTDAOrganizer, mTaskOrganizer, mMainStage, mSideStage,