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
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 1fb34eb..7634644 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 @@
// 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 e634552..7f3c89c 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 @@
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 @@
}
}
+ 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,10 +205,26 @@
// 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();
final float remains = 1 - value;
@@ -371,4 +396,15 @@
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 d141f7c..1feb743 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.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 @@
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 @@
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 9555c8d..b9776a3 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 @@
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 @@
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 a2889b1..b945cf1 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 @@
}
}
+ 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 @@
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 7f150a2..cd7a7c7 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 @@
mAwaitingAnimationStart = false;
mAnimationStarted = true;
mSchedulePipModeChangedOnStart = schedulePipModeChangedCallback;
+
+ mController.onAllWindowsDrawn();
}
@Override