diff options
| author | 2024-10-31 21:25:16 +0000 | |
|---|---|---|
| committer | 2024-10-31 21:32:00 +0000 | |
| commit | 3567d3f64a226f86af66bf8370f807ac8919e26e (patch) | |
| tree | 6467aa970f385fcfa18c51f66a8390218d5d4936 | |
| parent | 7e1bc07dcb8d85533e2d3837febef272f39cd90b (diff) | |
Avoid showing/hiding task surfaces in window decor relayout
WindowDecorations relayout in both transition callbacks and
onTaskInfoChanged callbacks, which can sometimes cause the task surface
to be shown or hidden too early during a task animation because
onTaskInfoChanged might get invoked before the animation plays or
finishes.
This change removes show()/hide() calls on the task surface in window
decoration #relayout to leave that responsibility entirely to the
transition handlers, with the exception of #relayouts originating from
onTaskInfoChanged during fluid drag-resizing gestures, which we know
aren't using shell transitions. Similar logic already existed for
setting the task position/crop, so visibility updates are just added to
the existing mechanism they use.
Fix: 365703208
Test: Enter PIP with a multi-activity app (e.g. Netflix), verify there's
no flicker where the surface disappears for 1 frame towards the end of
the animation.
Flag: EXEMPT refactor
Change-Id: Ib215d5fa5005a7270e2f154a1a18111749554e36
4 files changed, 48 insertions, 37 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java index 3946b6173b0b..c9546731a193 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java @@ -180,12 +180,13 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL // all other cases, it is expected that the transition handler positions and crops the task // in order to allow the handler time to animate before the task before the final // position and crop are set. - final boolean shouldSetTaskPositionAndCrop = mTaskDragResizer.isResizingOrAnimating(); + final boolean shouldSetTaskVisibilityPositionAndCrop = + mTaskDragResizer.isResizingOrAnimating(); // Use |applyStartTransactionOnDraw| so that the transaction (that applies task crop) is // synced with the buffer transaction (that draws the View). Both will be shown on screen // at the same, whereas applying them independently causes flickering. See b/270202228. relayout(taskInfo, t, t, true /* applyStartTransactionOnDraw */, - shouldSetTaskPositionAndCrop, hasGlobalFocus); + shouldSetTaskVisibilityPositionAndCrop, hasGlobalFocus); } @VisibleForTesting @@ -193,7 +194,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL RelayoutParams relayoutParams, ActivityManager.RunningTaskInfo taskInfo, boolean applyStartTransactionOnDraw, - boolean setTaskCropAndPosition, + boolean shouldSetTaskVisibilityPositionAndCrop, boolean isStatusBarVisible, boolean isKeyguardVisibleAndOccluded, InsetsState displayInsetsState, @@ -206,7 +207,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL ? R.dimen.freeform_decor_shadow_focused_thickness : R.dimen.freeform_decor_shadow_unfocused_thickness; relayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw; - relayoutParams.mSetTaskPositionAndCrop = setTaskCropAndPosition; + relayoutParams.mSetTaskVisibilityPositionAndCrop = shouldSetTaskVisibilityPositionAndCrop; relayoutParams.mIsCaptionVisible = taskInfo.isFreeform() || (isStatusBarVisible && !isKeyguardVisibleAndOccluded); @@ -234,7 +235,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL @SuppressLint("MissingPermission") void relayout(RunningTaskInfo taskInfo, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT, - boolean applyStartTransactionOnDraw, boolean setTaskCropAndPosition, + boolean applyStartTransactionOnDraw, boolean shouldSetTaskVisibilityPositionAndCrop, boolean hasGlobalFocus) { final boolean isFreeform = taskInfo.getWindowingMode() == WindowConfiguration.WINDOWING_MODE_FREEFORM; @@ -246,7 +247,8 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL final WindowContainerTransaction wct = new WindowContainerTransaction(); updateRelayoutParams(mRelayoutParams, taskInfo, applyStartTransactionOnDraw, - setTaskCropAndPosition, mIsStatusBarVisible, mIsKeyguardVisibleAndOccluded, + shouldSetTaskVisibilityPositionAndCrop, mIsStatusBarVisible, + mIsKeyguardVisibleAndOccluded, mDisplayController.getInsetsState(taskInfo.displayId), hasGlobalFocus); relayout(mRelayoutParams, startT, finishT, wct, oldRootView, mResult); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java index 9f37358b6044..62c2e191a6a2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java @@ -392,18 +392,25 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin @Override void relayout(ActivityManager.RunningTaskInfo taskInfo, boolean hasGlobalFocus) { final SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get(); - // The crop and position of the task should only be set when a task is fluid resizing. In - // all other cases, it is expected that the transition handler positions and crops the task - // in order to allow the handler time to animate before the task before the final - // position and crop are set. - final boolean shouldSetTaskPositionAndCrop = !DesktopModeStatus.isVeiledResizeEnabled() - && mTaskDragResizer.isResizingOrAnimating(); + // The visibility, crop and position of the task should only be set when a task is + // fluid resizing. In all other cases, it is expected that the transition handler sets + // those task properties to allow the handler time to animate with full control of the task + // leash. In general, allowing the window decoration to set any of these is likely to cause + // incorrect frames and flickering because relayouts from TaskListener#onTaskInfoChanged + // aren't synchronized with shell transition callbacks, so if they come too early it + // might show/hide or crop the task at a bad time. + // Fluid resizing is exempt from this because it intentionally doesn't use shell + // transitions to resize the task, so onTaskInfoChanged relayouts is the only way to make + // sure the crop is set correctly. + final boolean shouldSetTaskVisibilityPositionAndCrop = + !DesktopModeStatus.isVeiledResizeEnabled() + && mTaskDragResizer.isResizingOrAnimating(); // For headers only (i.e. in freeform): use |applyStartTransactionOnDraw| so that the // transaction (that applies task crop) is synced with the buffer transaction (that draws // the View). Both will be shown on screen at the same, whereas applying them independently // causes flickering. See b/270202228. final boolean applyTransactionOnDraw = taskInfo.isFreeform(); - relayout(taskInfo, t, t, applyTransactionOnDraw, shouldSetTaskPositionAndCrop, + relayout(taskInfo, t, t, applyTransactionOnDraw, shouldSetTaskVisibilityPositionAndCrop, hasGlobalFocus); if (!applyTransactionOnDraw) { t.apply(); @@ -430,19 +437,19 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin void relayout(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT, - boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop, + boolean applyStartTransactionOnDraw, boolean shouldSetTaskVisibilityPositionAndCrop, boolean hasGlobalFocus) { Trace.beginSection("DesktopModeWindowDecoration#relayout"); if (taskInfo.isFreeform()) { // The Task is in Freeform mode -> show its header in sync since it's an integral part // of the window itself - a delayed header might cause bad UX. relayoutInSync(taskInfo, startT, finishT, applyStartTransactionOnDraw, - shouldSetTaskPositionAndCrop, hasGlobalFocus); + shouldSetTaskVisibilityPositionAndCrop, hasGlobalFocus); } else { // The Task is outside Freeform mode -> allow the handle view to be delayed since the // handle is just a small addition to the window. relayoutWithDelayedViewHost(taskInfo, startT, finishT, applyStartTransactionOnDraw, - shouldSetTaskPositionAndCrop, hasGlobalFocus); + shouldSetTaskVisibilityPositionAndCrop, hasGlobalFocus); } Trace.endSection(); } @@ -450,12 +457,12 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin /** Run the whole relayout phase immediately without delay. */ private void relayoutInSync(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT, - boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop, + boolean applyStartTransactionOnDraw, boolean shouldSetTaskVisibilityPositionAndCrop, boolean hasGlobalFocus) { // Clear the current ViewHost runnable as we will update the ViewHost here clearCurrentViewHostRunnable(); updateRelayoutParamsAndSurfaces(taskInfo, startT, finishT, applyStartTransactionOnDraw, - shouldSetTaskPositionAndCrop, hasGlobalFocus); + shouldSetTaskVisibilityPositionAndCrop, hasGlobalFocus); if (mResult.mRootView != null) { updateViewHost(mRelayoutParams, startT, mResult); } @@ -477,7 +484,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin */ private void relayoutWithDelayedViewHost(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT, - boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop, + boolean applyStartTransactionOnDraw, boolean shouldSetTaskVisibilityPositionAndCrop, boolean hasGlobalFocus) { if (applyStartTransactionOnDraw) { throw new IllegalArgumentException( @@ -486,7 +493,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin // Clear the current ViewHost runnable as we will update the ViewHost here clearCurrentViewHostRunnable(); updateRelayoutParamsAndSurfaces(taskInfo, startT, finishT, - false /* applyStartTransactionOnDraw */, shouldSetTaskPositionAndCrop, + false /* applyStartTransactionOnDraw */, shouldSetTaskVisibilityPositionAndCrop, hasGlobalFocus); if (mResult.mRootView == null) { // This means something blocks the window decor from showing, e.g. the task is hidden. @@ -501,7 +508,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin @SuppressLint("MissingPermission") private void updateRelayoutParamsAndSurfaces(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT, - boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop, + boolean applyStartTransactionOnDraw, boolean shouldSetTaskVisibilityPositionAndCrop, boolean hasGlobalFocus) { Trace.beginSection("DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces"); @@ -526,9 +533,9 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin final boolean inFullImmersive = mDesktopRepository .isTaskInFullImmersiveState(taskInfo.taskId); updateRelayoutParams(mRelayoutParams, mContext, taskInfo, applyStartTransactionOnDraw, - shouldSetTaskPositionAndCrop, mIsStatusBarVisible, mIsKeyguardVisibleAndOccluded, - inFullImmersive, mDisplayController.getInsetsState(taskInfo.displayId), - hasGlobalFocus); + shouldSetTaskVisibilityPositionAndCrop, mIsStatusBarVisible, + mIsKeyguardVisibleAndOccluded, inFullImmersive, + mDisplayController.getInsetsState(taskInfo.displayId), hasGlobalFocus); final WindowDecorLinearLayout oldRootView = mResult.mRootView; final SurfaceControl oldDecorationSurface = mDecorationContainerSurface; @@ -857,7 +864,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin Context context, ActivityManager.RunningTaskInfo taskInfo, boolean applyStartTransactionOnDraw, - boolean shouldSetTaskPositionAndCrop, + boolean shouldSetTaskVisibilityPositionAndCrop, boolean isStatusBarVisible, boolean isKeyguardVisibleAndOccluded, boolean inFullImmersiveMode, @@ -953,7 +960,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin : R.dimen.freeform_decor_shadow_unfocused_thickness; } relayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw; - relayoutParams.mSetTaskPositionAndCrop = shouldSetTaskPositionAndCrop; + relayoutParams.mSetTaskVisibilityPositionAndCrop = shouldSetTaskVisibilityPositionAndCrop; // The configuration used to layout the window decoration. A copy is made instead of using // the original reference so that the configuration isn't mutated on config changes and diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java index f97dfb89bc0d..b016c755e323 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java @@ -249,7 +249,9 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> if (!mTaskInfo.isVisible) { releaseViews(wct); - finishT.hide(mTaskSurface); + if (params.mSetTaskVisibilityPositionAndCrop) { + finishT.hide(mTaskSurface); + } return; } @@ -422,7 +424,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> private void updateTaskSurface(RelayoutParams params, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT, RelayoutResult<T> outResult) { - if (params.mSetTaskPositionAndCrop) { + if (params.mSetTaskVisibilityPositionAndCrop) { final Point taskPosition = mTaskInfo.positionInParent; startT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight); finishT.setWindowCrop(mTaskSurface, outResult.mWidth, outResult.mHeight) @@ -437,9 +439,13 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> shadowRadius = loadDimension(mDecorWindowContext.getResources(), params.mShadowRadiusId); } - startT.setShadowRadius(mTaskSurface, shadowRadius).show(mTaskSurface); + startT.setShadowRadius(mTaskSurface, shadowRadius); finishT.setShadowRadius(mTaskSurface, shadowRadius); + if (params.mSetTaskVisibilityPositionAndCrop) { + startT.show(mTaskSurface); + } + if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) { if (!DesktopModeStatus.isVeiledResizeEnabled()) { // When fluid resize is enabled, add a background to freeform tasks @@ -758,7 +764,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> Configuration mWindowDecorConfig; boolean mApplyStartTransactionOnDraw; - boolean mSetTaskPositionAndCrop; + boolean mSetTaskVisibilityPositionAndCrop; boolean mHasGlobalFocus; void reset() { @@ -777,7 +783,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> mIsCaptionVisible = false; mApplyStartTransactionOnDraw = false; - mSetTaskPositionAndCrop = false; + mSetTaskVisibilityPositionAndCrop = false; mWindowDecorConfig = null; mHasGlobalFocus = false; } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java index cb7fadee9822..8e0434cb28f7 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java @@ -218,8 +218,6 @@ public class WindowDecorationTests extends ShellTestCase { verify(captionContainerSurfaceBuilder, never()).build(); verify(mMockSurfaceControlViewHostFactory, never()).create(any(), any(), any()); - verify(mMockSurfaceControlFinishT).hide(mMockTaskSurface); - assertNull(mRelayoutResult.mRootView); } @@ -281,8 +279,6 @@ public class WindowDecorationTests extends ShellTestCase { verify(mMockSurfaceControlStartT).setCornerRadius(mMockTaskSurface, CORNER_RADIUS); verify(mMockSurfaceControlFinishT).setCornerRadius(mMockTaskSurface, CORNER_RADIUS); - verify(mMockSurfaceControlStartT) - .show(mMockTaskSurface); verify(mMockSurfaceControlStartT).setShadowRadius(mMockTaskSurface, 10); assertEquals(300, mRelayoutResult.mWidth); @@ -863,7 +859,7 @@ public class WindowDecorationTests extends ShellTestCase { final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo); - mRelayoutParams.mSetTaskPositionAndCrop = false; + mRelayoutParams.mSetTaskVisibilityPositionAndCrop = false; windowDecor.relayout(taskInfo, true /* hasGlobalFocus */); verify(mMockSurfaceControlStartT, never()).setWindowCrop( @@ -891,7 +887,7 @@ public class WindowDecorationTests extends ShellTestCase { taskInfo.configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT * 2; final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo); - mRelayoutParams.mSetTaskPositionAndCrop = true; + mRelayoutParams.mSetTaskVisibilityPositionAndCrop = true; windowDecor.relayout(taskInfo, true /* hasGlobalFocus */); verify(mMockSurfaceControlStartT).setWindowCrop( |