From bf5d97ba93c7d7965ab8d21204e3dd89a765b08c Mon Sep 17 00:00:00 2001 From: "jorgegil@google.com" Date: Fri, 9 Apr 2021 12:36:18 -0700 Subject: [PATCH] Fix race when rotating the display during an entry into PIP Rotating the display while the PIP task had just appeared caused a race between the alpha entry animation starting and the rotation controller handling that changes the PIP bounds, resulting in a bounds mismatch between the surface that was animated and the PipBoundsState.mBounds This CL tracks whether we're still waiting for the alpha entry animation to start when a rotation callback comes in, and restarts the fade in after adjusting to the rotation. Bug: 184664409 Test: In gesure nav, open YT go to fullscreen landscape mode and launch another an activity from a notification - verify PIP fades in and its bounds match the ones in PipBoundsState Change-Id: I339d5088fb5f23f08fe8d548b9d02a34c16779d6 --- .../wm/shell/pip/PipAnimationController.java | 2 ++ .../wm/shell/pip/PipTaskOrganizer.java | 28 +++++++++++++++++-- .../wm/shell/pip/phone/PipController.java | 4 ++- 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java index a52db24aa1848..b396ce528beb7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java @@ -37,6 +37,7 @@ import com.android.wm.shell.animation.Interpolators; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Objects; /** * Controller class of PiP animations (both from and to PiP mode). @@ -112,6 +113,7 @@ public class PipAnimationController { PipTransitionAnimator.ofAlpha(taskInfo, leash, destinationBounds, alphaStart, alphaEnd)); } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA + && Objects.equals(destinationBounds, mCurrentAnimator.getDestinationBounds()) && mCurrentAnimator.isRunning()) { mCurrentAnimator.updateEndValue(alphaEnd); } else { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index ffa821daf7f81..447e4f59ed5d7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -98,9 +98,10 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, private enum State { UNDEFINED(0), TASK_APPEARED(1), - ENTERING_PIP(2), - ENTERED_PIP(3), - EXITING_PIP(4); + ENTRY_SCHEDULED(2), + ENTERING_PIP(3), + ENTERED_PIP(4), + EXITING_PIP(5); private final int mStateValue; @@ -263,6 +264,13 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, return mState.isInPip() && mWaitForFixedRotation; } + /** + * Returns whether the entry animation is waiting to be started. + */ + public boolean isEntryScheduled() { + return mState == State.ENTRY_SCHEDULED; + } + /** * Registers a callback when a display change has been detected when we enter PiP. */ @@ -481,6 +489,19 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } } + /** + * Called when the display rotation handling is skipped (e.g. when rotation happens while in + * the middle of an entry transition). + */ + public void onDisplayRotationSkipped() { + if (isEntryScheduled()) { + // The PIP animation is scheduled to start with the previous orientation's bounds, + // re-calculate the entry bounds and restart the alpha animation. + final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds(); + enterPipWithAlphaAnimation(destinationBounds, mEnterAnimationDuration); + } + } + @VisibleForTesting void enterPipWithAlphaAnimation(Rect destinationBounds, long durationMs) { // If we are fading the PIP in, then we should move the pip to the final location as @@ -490,6 +511,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mSurfaceControlTransactionFactory.getTransaction(); tx.setAlpha(mLeash, 0f); tx.apply(); + mState = State.ENTRY_SCHEDULED; applyEnterPipSyncTransaction(destinationBounds, () -> { mPipAnimationController .getAnimator(mTaskInfo, mLeash, destinationBounds, 0f, 1f) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index d75c1d65614dc..4b62b5f6c09bf 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -115,7 +115,8 @@ public class PipController implements PipTransitionController.PipTransitionCallb int displayId, int fromRotation, int toRotation, WindowContainerTransaction t) -> { if (!mPipTaskOrganizer.isInPip() || mPipBoundsState.getDisplayLayout().rotation() == toRotation - || mPipTaskOrganizer.isDeferringEnterPipAnimation()) { + || mPipTaskOrganizer.isDeferringEnterPipAnimation() + || mPipTaskOrganizer.isEntryScheduled()) { // Skip if the same rotation has been set or we aren't in PIP or haven't actually // entered PIP yet. We still need to update the display layout in the bounds handler // in this case. @@ -123,6 +124,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb // do not forget to update the movement bounds as well. updateMovementBounds(mPipBoundsState.getNormalBounds(), true /* fromRotation */, false /* fromImeAdjustment */, false /* fromShelfAdjustment */, t); + mPipTaskOrganizer.onDisplayRotationSkipped(); return; } // If there is an animation running (ie. from a shelf offset), then ensure that we calculate