diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index fb3007daa0a2f..50587e5717613 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -3043,6 +3043,12 @@ public final class ActivityStackSupervisor implements DisplayListener { ensureActivitiesVisibleLocked(r, 0); if (!kept) { resumeTopActivitiesLocked(stack, null, null); + // We are about to relaunch the activity because its configuration changed due + // to size change. The activity will first remove the old window and then add a + // new one. This call will tell window manager about this, so it can preserve + // the old window until the new one is drawn. This prevents having a gap between + // the removal and addition, in which no window is visible. + mWindowManager.setReplacingWindow(r.appToken); } } } diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index a210223dc0794..6ee2c3952f2d3 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -112,6 +112,11 @@ class AppWindowToken extends WindowToken { boolean mLaunchTaskBehind; boolean mEnteringAnimation; + // This application will have its window replaced due to relaunch. This allows window manager + // to differentiate between simple removal of a window and replacement. In the latter case it + // will preserve the old window until the new one is drawn. + boolean mReplacingWindow; + AppWindowToken(WindowManagerService _service, IApplicationToken _token, boolean _voiceInteraction) { super(_service, _token.asBinder(), diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index cf093f5ffd433..738e8622cab0a 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2068,6 +2068,15 @@ public class WindowManagerService extends IWindowManager.Stub // If the display is frozen, just remove immediately, since the // animation wouldn't be seen. if (win.mHasSurface && okToDisplay()) { + final AppWindowToken appToken = win.mAppToken; + if (appToken != null && appToken.mReplacingWindow) { + // This window is going to be replaced. We need to kepp it around until the new one + // gets added, then we will get rid of this one. + if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Preserving " + win + " until the new one is " + + "added"); + win.mExiting = true; + return; + } // If we are not currently running the exit animation, we // need to see about starting one. wasVisible = win.isWinVisibleLw(); @@ -2087,7 +2096,6 @@ public class WindowManagerService extends IWindowManager.Stub mAccessibilityController.onWindowTransitionLocked(win, transit); } } - final AppWindowToken appToken = win.mAppToken; final boolean isAnimating = win.mWinAnimator.isAnimating(); // The starting window is the last window in this app token and it isn't animating. // Allow it to be removed now as there is no additional window or animation that will @@ -9718,6 +9726,17 @@ public class WindowManagerService extends IWindowManager.Stub return mWindowMap; } + public void setReplacingWindow(IBinder token) { + synchronized (mWindowMap) { + AppWindowToken appWindowToken = findAppWindowToken(token); + if (appWindowToken == null) { + Slog.w(TAG, "Attempted to set replacing window on non-existing app token " + token); + return; + } + appWindowToken.mReplacingWindow = true; + } + } + private final class LocalService extends WindowManagerInternal { @Override public void requestTraversalFromDisplayManager() { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 7e15dd57ff59f..49d260e46f192 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -1270,6 +1270,19 @@ final class WindowState implements WindowManagerPolicy.WindowState { } } + void maybeRemoveReplacedWindow() { + AppWindowToken token = mAppToken; + if (token != null && token.mReplacingWindow) { + token.mReplacingWindow = false; + for (int i = token.allAppWindows.size() - 1; i >= 0; i--) { + WindowState win = token.allAppWindows.get(i); + if (win.mExiting) { + mService.removeWindowInnerLocked(win); + } + } + } + } + private class DeathRecipient implements IBinder.DeathRecipient { @Override public void binderDied() { diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index a79d2aa7ad870..efad8bf6bef9d 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -1765,6 +1765,8 @@ class WindowStateAnimator { mWin.mAppToken.updateReportedVisibilityLocked(); } + mWin.maybeRemoveReplacedWindow(); + return true; }