diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 4e55c89fa7392..d962b7c5ae32a 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -4325,6 +4325,21 @@ public final class ActivityThread { r.activity.mChangingConfigurations = true; + // If we are preserving the main window across relaunches we would also like to preserve + // the children. However the client side view system does not support preserving + // the child views so we notify the window manager to expect these windows to + // be replaced and defer requests to destroy or hide them. This way we can achieve + // visual continuity. It's important that we do this here prior to pause and destroy + // as that is when we may hide or remove the child views. + try { + if (r.mPreserveWindow) { + WindowManagerGlobal.getWindowSession().prepareToReplaceChildren(r.token); + } + } catch (RemoteException e) { + // If the system process has died, it's game over for everyone. + } + + // Need to ensure state is saved. if (!r.paused) { performPauseActivity(r.token, false, r.isPreHoneycomb()); diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index b3cd8c11f0f94..96e73eb1daba2 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -120,6 +120,14 @@ interface IWindowSession { void repositionChild(IWindow childWindow, int left, int top, int right, int bottom, long deferTransactionUntilFrame, out Rect outFrame); + /* + * Notify the window manager that an application is relaunching and + * child windows should be prepared for replacement. + * + * @param appToken The application + */ + void prepareToReplaceChildren(IBinder appToken); + /** * If a call to relayout() asked to have the surface destroy deferred, * it must call this once it is okay to destroy that surface. diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index 751f8715a1e13..e2aaf0a6a8046 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -404,6 +404,17 @@ class AppWindowToken extends WindowToken { } } + void setReplacingChildren() { + if (DEBUG_ADD_REMOVE) Slog.d(TAG_WM, "Marking app token " + appWindowToken + + " with replacing child windows."); + for (int i = allAppWindows.size() - 1; i >= 0; i--) { + final WindowState w = allAppWindows.get(i); + if (w.isChildWindow()) { + w.setReplacing(false /* animate */); + } + } + } + void resetReplacingWindows() { if (DEBUG_ADD_REMOVE) Slog.d(TAG_WM, "Resetting app token " + appWindowToken + " of replacing window marks."); diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index 1b6957d9e5541..b5cf40ca0e2c1 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -200,6 +200,11 @@ final class Session extends IWindowSession.Stub deferTransactionUntilFrame, outFrame); } + @Override + public void prepareToReplaceChildren(IBinder appToken) { + mService.setReplacingChildren(appToken); + } + public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs, int requestedWidth, int requestedHeight, int viewFlags, int flags, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets, diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 7d142eca0bb4b..ba8cb905a327c 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2690,7 +2690,14 @@ public class WindowManagerService extends IWindowManager.Stub final boolean notExitingOrAnimating = !win.mExiting && !win.isAnimatingWithSavedSurface(); result |= notExitingOrAnimating ? RELAYOUT_RES_SURFACE_CHANGED : 0; - if (notExitingOrAnimating) { + // We don't want to animate visibility of windows which are pending + // replacement. In the case of activity relaunch child windows + // could request visibility changes as they are detached from the main + // application window during the tear down process. If we satisfied + // these visibility changes though, we would cause a visual glitch + // hiding the window before it's replacement was available. + // So we just do nothing on our side. + if (notExitingOrAnimating && win.mWillReplaceWindow == false) { focusMayChange = tryStartingAnimation(win, winAnimator, isDefaultDisplay, focusMayChange); @@ -2884,9 +2891,10 @@ public class WindowManagerService extends IWindowManager.Stub try { synchronized (mWindowMap) { WindowState win = windowForClientLocked(session, client, false); - if (win == null) { + if (win == null || win.mWillReplaceWindow) { return; } + win.mWinAnimator.destroyDeferredSurfaceLocked(); } } finally { @@ -10189,6 +10197,27 @@ public class WindowManagerService extends IWindowManager.Stub } } + /** + * Hint to a token that its children will be replaced across activity relaunch. + * The children would otherwise be removed shortly following this as the + * activity is torn down. + * @param token Application token for which the activity will be relaunched. + */ + public void setReplacingChildren(IBinder token) { + AppWindowToken appWindowToken = null; + synchronized (mWindowMap) { + appWindowToken = findAppWindowToken(token); + if (appWindowToken == null || !appWindowToken.isVisible()) { + Slog.w(TAG_WM, "Attempted to set replacing window on non-existing app token " + + token); + return; + } + + appWindowToken.setReplacingChildren(); + scheduleClearReplacingWindowIfNeeded(token, true /* replacing */); + } + } + /** * If we're replacing the window, schedule a timer to clear the replaced window * after a timeout, in case the replacing window is not coming. diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java index 5c73fb6a6fbe0..d862242905927 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java @@ -230,4 +230,9 @@ public final class BridgeWindowSession implements IWindowSession { public void pokeDrawLock(IBinder window) { // pass for now. } + + @Override + public void prepareToReplaceChildren(IBinder appToken) { + // pass for now. + } }