From dd637175b1e051fb3b3ea99245054e12e83a2f20 Mon Sep 17 00:00:00 2001 From: Tony Huang Date: Thu, 3 Mar 2022 16:31:46 +0800 Subject: [PATCH] Fix divider disappear when rotation on shell transition When rotation, it will re-init the split divider for new rotation on DAinfoChanged callback, but when shell transition, we should do it on startAnimation to make transition smooth and make didider could release by specfic Transcation to avoid it hide immediately but didn't sync with transition. Fix: 222158856 Test: manual Test: pass existing tests Change-Id: I797ffd0ea35d89b4ec1e3ef4828ce36e18c860be --- .../android/view/WindowlessWindowManager.java | 6 +++- .../wm/shell/common/split/SplitLayout.java | 31 ++++++++++++++----- .../common/split/SplitWindowManager.java | 23 ++++++++++++-- .../shell/splitscreen/StageCoordinator.java | 25 ++++++++++----- .../common/split/SplitWindowManagerTests.java | 2 +- 5 files changed, 69 insertions(+), 18 deletions(-) diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java index a270c9283ddc9..7fa47dc991116 100644 --- a/core/java/android/view/WindowlessWindowManager.java +++ b/core/java/android/view/WindowlessWindowManager.java @@ -217,9 +217,13 @@ public class WindowlessWindowManager implements IWindowSession { throw new IllegalArgumentException( "Invalid window token (never added or removed already)"); } + removeSurface(state.mSurfaceControl); + } + /** Separate from {@link #remove} so that subclasses can put removal on a sync transaction. */ + protected void removeSurface(SurfaceControl sc) { try (SurfaceControl.Transaction t = new SurfaceControl.Transaction()) { - t.remove(state.mSurfaceControl).apply(); + t.remove(sc).apply(); } } 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 b52c8d118711f..5246117486ed5 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 @@ -98,6 +98,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange private WindowContainerToken mWinToken2; private int mDividePosition; private boolean mInitialized = false; + private boolean mFreezeDividerWindow = false; private int mOrientation; private int mRotation; @@ -225,11 +226,6 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds, null); initDividerPosition(mTempRect); - if (mInitialized) { - release(); - init(); - } - return true; } @@ -298,20 +294,37 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange } /** Releases the surface holding the current {@link DividerView}. */ - public void release() { + public void release(SurfaceControl.Transaction t) { if (!mInitialized) return; mInitialized = false; - mSplitWindowManager.release(); + mSplitWindowManager.release(t); mDisplayImeController.removePositionProcessor(mImePositionProcessor); mImePositionProcessor.reset(); } + public void release() { + release(null /* t */); + } + + /** Releases and re-inflates {@link DividerView} on the root surface. */ + public void update(SurfaceControl.Transaction t) { + if (!mInitialized) return; + mSplitWindowManager.release(t); + mImePositionProcessor.reset(); + mSplitWindowManager.init(this, mInsetsState); + } + @Override public void insetsChanged(InsetsState insetsState) { mInsetsState.set(insetsState); if (!mInitialized) { return; } + if (mFreezeDividerWindow) { + // DO NOT change its layout before transition actually run because it might cause + // flicker. + return; + } mSplitWindowManager.onInsetsChanged(insetsState); } @@ -323,6 +336,10 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange } } + public void setFreezeDividerWindow(boolean freezeDividerWindow) { + mFreezeDividerWindow = freezeDividerWindow; + } + /** * Updates bounds with the passing position. Usually used to update recording bounds while * performing animation or dragging divider bar to resize the splits. 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 4903f9d46dc74..833d9d50701c8 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 @@ -58,6 +58,9 @@ public final class SplitWindowManager extends WindowlessWindowManager { private SurfaceControl mLeash; private DividerView mDividerView; + // Used to "pass" a transaction to WWM.remove so that view removal can be synchronized. + private SurfaceControl.Transaction mSyncTransaction = null; + public interface ParentContainerCallbacks { void attachToParentSurface(SurfaceControl.Builder b); void onLeashReady(SurfaceControl leash); @@ -130,22 +133,38 @@ public final class SplitWindowManager extends WindowlessWindowManager { * Releases the surface control of the current {@link DividerView} and tear down the view * hierarchy. */ - void release() { + void release(@Nullable SurfaceControl.Transaction t) { if (mDividerView != null) { mDividerView = null; } if (mViewHost != null){ + mSyncTransaction = t; mViewHost.release(); + mSyncTransaction = null; mViewHost = null; } if (mLeash != null) { - new SurfaceControl.Transaction().remove(mLeash).apply(); + if (t == null) { + new SurfaceControl.Transaction().remove(mLeash).apply(); + } else { + t.remove(mLeash); + } mLeash = null; } } + @Override + protected void removeSurface(SurfaceControl sc) { + // This gets called via SurfaceControlViewHost.release() + if (mSyncTransaction != null) { + mSyncTransaction.remove(sc); + } else { + super.removeSurface(sc); + } + } + void setInteractive(boolean interactive) { if (mDividerView == null) return; mDividerView.setInteractive(interactive); 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 48dd1fe3cb7e0..ec1ddf0777958 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 @@ -29,6 +29,7 @@ import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.view.WindowManager.transitTypeToString; import static android.view.WindowManagerPolicyConstants.SPLIT_DIVIDER_LAYER; +import static android.window.TransitionInfo.FLAG_IS_DISPLAY; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; @@ -1171,6 +1172,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, updateUnfoldBounds(); return; } + + mSplitLayout.update(null /* t */); onLayoutSizeChanged(mSplitLayout); } } @@ -1198,7 +1201,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (!ENABLE_SHELL_TRANSITIONS) return; final SurfaceControl.Transaction t = mTransactionPool.acquire(); - setDividerVisibility(false, t); mDisplayLayout.rotateTo(mContext.getResources(), toRotation); mSplitLayout.rotateTo(toRotation, mDisplayLayout.stableInsets()); updateWindowBounds(mSplitLayout, wct); @@ -1255,8 +1257,15 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, @Nullable TransitionRequestInfo request) { final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask(); if (triggerTask == null) { - // Still want to monitor everything while in split-screen, so return non-null. - return mMainStage.isActive() ? new WindowContainerTransaction() : null; + if (mMainStage.isActive()) { + if (request.getType() == TRANSIT_CHANGE && request.getDisplayChange() != null) { + mSplitLayout.setFreezeDividerWindow(true); + } + // Still want to monitor everything while in split-screen, so return non-null. + return new WindowContainerTransaction(); + } else { + return null; + } } else if (triggerTask.displayId != mDisplayId) { // Skip handling task on the other display. return null; @@ -1352,8 +1361,14 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, // If we're not in split-mode, just abort so something else can handle it. if (!mMainStage.isActive()) return false; + mSplitLayout.setFreezeDividerWindow(false); for (int iC = 0; iC < info.getChanges().size(); ++iC) { final TransitionInfo.Change change = info.getChanges().get(iC); + if (change.getMode() == TRANSIT_CHANGE + && (change.getFlags() & FLAG_IS_DISPLAY) != 0) { + mSplitLayout.update(startTransaction); + } + final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); if (taskInfo == null || !taskInfo.hasParentTask()) continue; final StageTaskListener stage = getStageOfTask(taskInfo); @@ -1368,10 +1383,6 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, Log.w(TAG, "Expected onTaskVanished on " + stage + " to have been called" + " with " + taskInfo.taskId + " before startAnimation()."); } - } else if (info.getType() == TRANSIT_CHANGE - && change.getStartRotation() != change.getEndRotation()) { - // Show the divider after transition finished. - setDividerVisibility(true, finishTransaction); } } if (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0) { 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 9bb54a18063f6..2e5078d86a8b2 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 @@ -61,7 +61,7 @@ public class SplitWindowManagerTests extends ShellTestCase { public void testInitRelease() { mSplitWindowManager.init(mSplitLayout, new InsetsState()); assertThat(mSplitWindowManager.getSurfaceControl()).isNotNull(); - mSplitWindowManager.release(); + mSplitWindowManager.release(null /* t */); assertThat(mSplitWindowManager.getSurfaceControl()).isNull(); } }