From 4cead1a66c96cc4e69f0a4359e1b27bc98fd1dc9 Mon Sep 17 00:00:00 2001 From: Tony Huang Date: Tue, 21 Feb 2023 10:30:51 +0000 Subject: [PATCH] Fix stuck if resize split quickly This is caused by a new resizing transition starts before the previous transition finished. Fix this by disabling interacting with the divider bar during resizing and always cancel the previous transition. Fix: 269425665 Test: manual Test: pass existing tests Change-Id: Id74ac119f8b4d574f3e82b3784b0f42c5db95ce0 --- .../wm/shell/common/split/DividerView.java | 11 ++++++-- .../wm/shell/common/split/SplitLayout.java | 26 ++++++++++++++++--- .../common/split/SplitWindowManager.java | 11 ++++++-- .../splitscreen/SplitScreenTransitions.java | 6 +++++ .../shell/splitscreen/StageCoordinator.java | 15 ++++++++--- .../splitscreen/StageCoordinatorTests.java | 1 + 6 files changed, 58 insertions(+), 12 deletions(-) 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 713dd39201a4e..69f0bad4fb450 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 @@ -371,7 +371,14 @@ public class DividerView extends FrameLayout implements View.OnTouchListener { mViewHost.relayout(lp); } - void setInteractive(boolean interactive, String from) { + /** + * Set divider should interactive to user or not. + * + * @param interactive divider interactive. + * @param hideHandle divider handle hidden or not, only work when interactive is false. + * @param from caller from where. + */ + void setInteractive(boolean interactive, boolean hideHandle, String from) { if (interactive == mInteractive) return; ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Set divider bar %s from %s", interactive ? "interactive" : "non-interactive", @@ -387,7 +394,7 @@ public class DividerView extends FrameLayout implements View.OnTouchListener { mMoving = false; } releaseTouching(); - mHandle.setVisibility(mInteractive ? View.VISIBLE : View.INVISIBLE); + mHandle.setVisibility(!mInteractive && hideHandle ? View.INVISIBLE : View.VISIBLE); } private boolean isLandscape() { 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 8a18271b029ac..7ac4d51c15023 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 @@ -485,6 +485,17 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange mWinBounds2.setEmpty(); } + /** + * Set divider should interactive to user or not. + * + * @param interactive divider interactive. + * @param hideHandle divider handle hidden or not, only work when interactive is false. + * @param from caller from where. + */ + public void setDividerInteractive(boolean interactive, boolean hideHandle, String from) { + mSplitWindowManager.setInteractive(interactive, hideHandle, from); + } + /** * Sets new divide position and updates bounds correspondingly. Notifies listener if the new * target indicates dismissing split. @@ -735,21 +746,28 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange } } - /** Apply recorded task layout to the {@link WindowContainerTransaction}. */ - public void applyTaskChanges(WindowContainerTransaction wct, + /** Apply recorded task layout to the {@link WindowContainerTransaction}. + * + * @return true if stage bounds actually update. + */ + public boolean applyTaskChanges(WindowContainerTransaction wct, ActivityManager.RunningTaskInfo task1, ActivityManager.RunningTaskInfo task2) { + boolean boundsChanged = false; if (!mBounds1.equals(mWinBounds1) || !task1.token.equals(mWinToken1)) { wct.setBounds(task1.token, mBounds1); wct.setSmallestScreenWidthDp(task1.token, getSmallestWidthDp(mBounds1)); mWinBounds1.set(mBounds1); mWinToken1 = task1.token; + boundsChanged = true; } if (!mBounds2.equals(mWinBounds2) || !task2.token.equals(mWinToken2)) { wct.setBounds(task2.token, mBounds2); wct.setSmallestScreenWidthDp(task2.token, getSmallestWidthDp(mBounds2)); mWinBounds2.set(mBounds2); mWinToken2 = task2.token; + boundsChanged = true; } + return boundsChanged; } private int getSmallestWidthDp(Rect bounds) { @@ -1091,7 +1109,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange // ImePositionProcessor#onImeVisibilityChanged directly in DividerView is not enough // because DividerView won't receive onImeVisibilityChanged callback after it being // re-inflated. - mSplitWindowManager.setInteractive(!mImeShown || !mHasImeFocus || isFloating, + setDividerInteractive(!mImeShown || !mHasImeFocus || isFloating, true, "onImeStartPositioning"); return needOffset ? IME_ANIMATION_NO_ALPHA : 0; @@ -1118,7 +1136,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange // Restore the split layout when wm-shell is not controlling IME insets anymore. if (!controlling && mImeShown) { reset(); - mSplitWindowManager.setInteractive(true, "onImeControlTargetChanged"); + setDividerInteractive(true, true, "onImeControlTargetChanged"); mSplitLayoutHandler.setLayoutOffsetTarget(0, 0, SplitLayout.this); mSplitLayoutHandler.onLayoutPositionChanging(SplitLayout.this); } 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 eb3c1df0ae73e..00361d9dd9cf9 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 @@ -168,9 +168,16 @@ public final class SplitWindowManager extends WindowlessWindowManager { } } - void setInteractive(boolean interactive, String from) { + /** + * Set divider should interactive to user or not. + * + * @param interactive divider interactive. + * @param hideHandle divider handle hidden or not, only work when interactive is false. + * @param from caller from where. + */ + void setInteractive(boolean interactive, boolean hideHandle, String from) { if (mDividerView == null) return; - mDividerView.setInteractive(interactive, from); + mDividerView.setInteractive(interactive, hideHandle, from); } View getDividerView() { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java index 520da92fad728..0d8d458a0859a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java @@ -339,6 +339,12 @@ class SplitScreenTransitions { IBinder startResizeTransition(WindowContainerTransaction wct, Transitions.TransitionHandler handler, @Nullable TransitionFinishedCallback finishCallback) { + if (mPendingResize != null) { + mPendingResize.cancel(null); + mAnimations.clear(); + onFinish(null /* wct */, null /* wctCB */); + } + IBinder transition = mTransitions.startTransition(TRANSIT_CHANGE, wct, handler); setResizeTransition(transition, finishCallback); return transition; 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 7833cfe7df9df..2ff3fc569040a 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 @@ -1924,10 +1924,14 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } final WindowContainerTransaction wct = new WindowContainerTransaction(); - updateWindowBounds(layout, wct); + boolean sizeChanged = updateWindowBounds(layout, wct); + if (!sizeChanged) return; + sendOnBoundsChanged(); if (ENABLE_SHELL_TRANSITIONS) { - mSplitTransitions.startResizeTransition(wct, this, null /* callback */); + mSplitLayout.setDividerInteractive(false, false, "onSplitResizeStart"); + mSplitTransitions.startResizeTransition(wct, this, (finishWct, t) -> + mSplitLayout.setDividerInteractive(true, false, "onSplitResizeFinish")); } else { mSyncQueue.queue(wct); mSyncQueue.runInSync(t -> { @@ -1946,13 +1950,16 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, /** * Populates `wct` with operations that match the split windows to the current layout. * To match relevant surfaces, make sure to call updateSurfaceBounds after `wct` is applied + * + * @return true if stage bounds actually . */ - private void updateWindowBounds(SplitLayout layout, WindowContainerTransaction wct) { + private boolean updateWindowBounds(SplitLayout layout, WindowContainerTransaction wct) { final StageTaskListener topLeftStage = mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage; final StageTaskListener bottomRightStage = mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage; - layout.applyTaskChanges(wct, topLeftStage.mRootTaskInfo, bottomRightStage.mRootTaskInfo); + return layout.applyTaskChanges(wct, topLeftStage.mRootTaskInfo, + bottomRightStage.mRootTaskInfo); } void updateSurfaceBounds(@Nullable SplitLayout layout, @NonNull SurfaceControl.Transaction t, 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 0bb809d354dc3..2e2e49e400306 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 @@ -134,6 +134,7 @@ public class StageCoordinatorTests extends ShellTestCase { when(mSplitLayout.getBounds2()).thenReturn(mBounds2); when(mSplitLayout.getRootBounds()).thenReturn(mRootBounds); when(mSplitLayout.isLandscape()).thenReturn(false); + when(mSplitLayout.applyTaskChanges(any(), any(), any())).thenReturn(true); mRootTask = new TestRunningTaskInfoBuilder().build(); mRootLeash = new SurfaceControl.Builder(mSurfaceSession).setName("test").build();