From ecc06b32305ac234db24e3f76bcae0199b80c395 Mon Sep 17 00:00:00 2001 From: Robert Carr Date: Tue, 18 Apr 2017 14:25:10 -0700 Subject: [PATCH] Delay PiP transition to fullscreen until activities draw. To avoid awful stretching. Bug: 37473110 Test: Transition app fullscreen verify no awful video stretching. Change-Id: I810a72207e45b8f83a63c9f0b3cc9a433569852c --- .../com/android/server/wm/AppWindowToken.java | 5 +++ .../server/wm/BoundsAnimationController.java | 36 +++++++++++++++++++ .../java/com/android/server/wm/TaskStack.java | 16 +++++++++ .../com/android/server/wm/WindowState.java | 6 ++-- .../server/wm/WindowStateAnimator.java | 27 ++++++++------ .../wm/BoundsAnimationControllerTests.java | 2 ++ 6 files changed, 80 insertions(+), 12 deletions(-) diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index 1fb34eb66d20e..763464422e563 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -1280,6 +1280,11 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree // WindowStateAnimator#commitFinishDrawingLocked() will call performShowLocked(). dc.setLayoutNeeded(); mService.mH.obtainMessage(NOTIFY_ACTIVITY_DRAWN, token).sendToTarget(); + + final TaskStack s = getStack(); + if (s != null) { + s.onAllWindowsDrawn(); + } } } diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java index e6345523bd149..7f3c89c58a288 100644 --- a/services/core/java/com/android/server/wm/BoundsAnimationController.java +++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java @@ -112,6 +112,8 @@ public class BoundsAnimationController { private final Interpolator mFastOutSlowInInterpolator; private boolean mFinishAnimationAfterTransition = false; + private static final int WAIT_FOR_DRAW_TIMEOUT_MS = 3000; + BoundsAnimationController(Context context, AppTransition transition, Handler handler) { mHandler = handler; mAppTransition = transition; @@ -175,6 +177,13 @@ public class BoundsAnimationController { } } + final Runnable mResumeRunnable = new Runnable() { + @Override + public void run() { + resume(); + } + }; + @Override public void onAnimationStart(Animator animation) { if (DEBUG) Slog.d(TAG, "onAnimationStart: mTarget=" + mTarget @@ -196,9 +205,25 @@ public class BoundsAnimationController { // the starting position so we don't jump at the beginning of the animation. if (animatingToLargerSize()) { mTarget.setPinnedStackSize(mFrom, mTmpRect); + + // We pause the animation until the app has drawn at the new size. + // The target will notify us via BoundsAnimationController#resume. + // We do this here and pause the animation, rather than just defer starting it + // so we can enter the animating state and have WindowStateAnimator apply the + // correct logic to make this resize seamless. + if (mMoveToFullscreen) { + pause(); + mHandler.postDelayed(mResumeRunnable, WAIT_FOR_DRAW_TIMEOUT_MS); + } } } + @Override + public void resume() { + mHandler.removeCallbacks(mResumeRunnable); + super.resume(); + } + @Override public void onAnimationUpdate(ValueAnimator animation) { final float value = (Float) animation.getAnimatedValue(); @@ -371,4 +396,15 @@ public class BoundsAnimationController { animator.start(); return animator; } + + private void resume() { + for (int i = 0; i < mRunningAnimations.size(); i++) { + final BoundsAnimator b = mRunningAnimations.valueAt(i); + b.resume(); + } + } + + public void onAllWindowsDrawn() { + mHandler.post(this::resume); + } } diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index d141f7cb2eb3d..1feb743b66dcf 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -52,6 +52,7 @@ import com.android.internal.policy.DividerSnapAlgorithm; import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget; import com.android.internal.policy.DockedDividerUtils; import com.android.server.EventLogTags; +import com.android.server.UiThread; import java.io.PrintWriter; @@ -1475,6 +1476,14 @@ public class TaskStack extends WindowContainer implements DimLayer.DimLaye return true; } + void onAllWindowsDrawn() { + if (!mBoundsAnimating) { + return; + } + + mService.mBoundsAnimationController.onAllWindowsDrawn(); + } + @Override // AnimatesBounds public void onAnimationStart(boolean schedulePipModeChangedCallback) { // Hold the lock since this is called from the BoundsAnimator running on the UiThread @@ -1482,6 +1491,13 @@ public class TaskStack extends WindowContainer implements DimLayer.DimLaye mBoundsAnimatingRequested = false; mBoundsAnimating = true; mCancelCurrentBoundsAnimation = false; + + // If we are changing UI mode, as in the PiP to fullscreen + // transition, then we need to wait for the window to draw. + if (schedulePipModeChangedCallback) { + forAllWindows((w) -> { w.mWinAnimator.resetDrawState(); }, + false /* traverseTopToBottom */); + } } if (mStackId == PINNED_STACK_ID) { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 9555c8dff03c7..b9776a362b8b4 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -4329,7 +4329,9 @@ class WindowState extends WindowContainer implements WindowManagerP int relayoutVisibleWindow(MergedConfiguration mergedConfiguration, int result, int attrChanges, int oldVisibility) { - result |= !isVisibleLw() ? RELAYOUT_RES_FIRST_TIME : 0; + final boolean wasVisible = isVisibleLw(); + + result |= (!wasVisible || !isDrawnLw()) ? RELAYOUT_RES_FIRST_TIME : 0; if (mAnimatingExit) { Slog.d(TAG, "relayoutVisibleWindow: " + this + " mAnimatingExit=true, mRemoveOnExit=" + mRemoveOnExit + ", mDestroying=" + mDestroying); @@ -4348,7 +4350,7 @@ class WindowState extends WindowContainer implements WindowManagerP mLastVisibleLayoutRotation = getDisplayContent().getRotation(); mWinAnimator.mEnteringAnimation = true; - if ((result & RELAYOUT_RES_FIRST_TIME) != 0) { + if (!wasVisible) { prepareWindowToDisplayDuringRelayout(mergedConfiguration); } if ((attrChanges & FORMAT_CHANGED) != 0) { diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index a2889b145f168..b945cf15fe1c2 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -602,6 +602,22 @@ class WindowStateAnimator { } } + void resetDrawState() { + mDrawState = DRAW_PENDING; + + if (mWin.mAppToken == null) { + return; + } + + if (mWin.mAppToken.mAppAnimator.animation == null) { + mWin.mAppToken.clearAllDrawn(); + } else { + // Currently animating, persist current state of allDrawn until animation + // is complete. + mWin.mAppToken.deferClearAllDrawn = true; + } + } + WindowSurfaceController createSurfaceLocked(int windowType, int ownerUid) { final WindowState w = mWin; if (w.restoreSavedSurface()) { @@ -619,16 +635,7 @@ class WindowStateAnimator { if (DEBUG_ANIM || DEBUG_ORIENTATION) Slog.i(TAG, "createSurface " + this + ": mDrawState=DRAW_PENDING"); - mDrawState = DRAW_PENDING; - if (w.mAppToken != null) { - if (w.mAppToken.mAppAnimator.animation == null) { - w.mAppToken.clearAllDrawn(); - } else { - // Currently animating, persist current state of allDrawn until animation - // is complete. - w.mAppToken.deferClearAllDrawn = true; - } - } + resetDrawState(); mService.makeWindowFreezingScreenIfNeededLocked(w); diff --git a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java index 7f150a21a20e8..cd7a7c7090fc1 100644 --- a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java @@ -152,6 +152,8 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { mAwaitingAnimationStart = false; mAnimationStarted = true; mSchedulePipModeChangedOnStart = schedulePipModeChangedCallback; + + mController.onAllWindowsDrawn(); } @Override