From 4876b4a273cdefaa7dfc11d5fdaa0ee5c9ea4055 Mon Sep 17 00:00:00 2001 From: Jorim Jaggi Date: Thu, 11 Jan 2018 15:43:49 +0100 Subject: [PATCH] Defer hiding clients until animation is done This is a preparation for remote animations: We used to set app visibility state immediately after we started the animation. However, with remote animations, we'd like to allow them drawing until the transition is done. For that, we defer hiding the client until the animation is done. Instead of special-casing remote animations, we do it for all apps, as there is no harm in doing so. Test: Open YouTube, make sure it enters Auto-PIP when pressing home. Test: go/wm-smoke Test: Open trace with open/closing a couple of apps. Make sure app visibility gets dispatched at the correct time. Test: WindowStateTests Bug: 64674361 Change-Id: I8deb6a97ca1c3d8f4a70a6e045f45a6bc16604bb --- .../java/com/android/server/wm/AppWindowToken.java | 8 +++++++- .../core/java/com/android/server/wm/WindowState.java | 20 ++++++++++++++++++-- .../src/com/android/server/wm/WindowStateTests.java | 13 +++++++++++++ 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index f2098dc3ce4f..0dfef3aa63e1 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -376,7 +376,6 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree // Reset the state of mHiddenSetFromTransferredStartingWindow since visibility is actually // been set by the app now. mHiddenSetFromTransferredStartingWindow = false; - setClientHidden(!visible); // Allow for state changes and animation to be applied if: // * token is transitioning visibility state @@ -461,6 +460,12 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree mService.mActivityManagerAppTransitionNotifier.onAppTransitionFinishedLocked(token); } + // Update the client visibility if we are not running an animation. Otherwise, we'll + // update client visibility state in onAnimationFinished. + if (!visible && !delayed) { + setClientHidden(true); + } + // If we are hidden but there is no delay needed we immediately // apply the Surface transaction so that the ActivityManager // can have some guarantee on the Surface state following @@ -1721,6 +1726,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree "AppWindowToken"); clearThumbnail(); + setClientHidden(isHidden()); if (mService.mInputMethodTarget != null && mService.mInputMethodTarget.mAppToken == this) { getDisplayContent().computeImeTarget(true /* updateImeTarget */); diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 97070bd91451..46c5ceebd180 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -189,6 +189,7 @@ import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ToBooleanFunction; import com.android.server.input.InputWindowHandle; import com.android.server.policy.WindowManagerPolicy; @@ -198,7 +199,6 @@ import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Comparator; -import java.util.LinkedList; import java.util.function.Predicate; /** A window in the window manager. */ @@ -3923,6 +3923,22 @@ class WindowState extends WindowContainer implements WindowManagerP return null; } + /** + * @return True if we our one of our ancestors has {@link #mAnimatingExit} set to true, false + * otherwise. + */ + @VisibleForTesting + boolean isSelfOrAncestorWindowAnimatingExit() { + WindowState window = this; + do { + if (window.mAnimatingExit) { + return true; + } + window = window.getParentWindow(); + } while (window != null); + return false; + } + void onExitAnimationDone() { if (DEBUG_ANIM) Slog.v(TAG, "onExitAnimationDone in " + this + ": exiting=" + mAnimatingExit + " remove=" + mRemoveOnExit @@ -3958,7 +3974,7 @@ class WindowState extends WindowContainer implements WindowManagerP mService.mAccessibilityController.onSomeWindowResizedOrMovedLocked(); } - if (!mAnimatingExit) { + if (!isSelfOrAncestorWindowAnimatingExit()) { return; } diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java index 7be203a99391..6a4710bb06a4 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java @@ -223,6 +223,19 @@ public class WindowStateTests extends WindowTestsBase { assertFalse(app.canAffectSystemUiFlags()); } + @Test + public void testIsSelfOrAncestorWindowAnimating() throws Exception { + final WindowState root = createWindow(null, TYPE_APPLICATION, "root"); + final WindowState child1 = createWindow(root, FIRST_SUB_WINDOW, "child1"); + final WindowState child2 = createWindow(child1, FIRST_SUB_WINDOW, "child2"); + assertFalse(child2.isSelfOrAncestorWindowAnimatingExit()); + child2.mAnimatingExit = true; + assertTrue(child2.isSelfOrAncestorWindowAnimatingExit()); + child2.mAnimatingExit = false; + root.mAnimatingExit = true; + assertTrue(child2.isSelfOrAncestorWindowAnimatingExit()); + } + private void testPrepareWindowToDisplayDuringRelayout(boolean wasVisible) { final WindowState root = createWindow(null, TYPE_APPLICATION, "root"); root.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON; -- 2.11.0