From d1a010f279447bbf2b186e4c24ff6bdb8ecedbf0 Mon Sep 17 00:00:00 2001 From: Robert Carr Date: Thu, 7 Apr 2016 22:36:22 -0700 Subject: [PATCH 1/7] Replace secondary app windows across activity relaunch. Windows of TYPE_APPLICATION (as opposed to TYPE_BASE_APPLICATION) are not child windows in the sense of SurfaceView, etc, as they are independent windows like Modal Dialogs rather than embedded parts of other windows. Still though, we expect them to reappear following activity relaunch, and they won't be covered by window preservation, so we need to mark them for replacement. Bug: 26668339 Change-Id: I652b4137085f6ef4d6c9d54de609727f966ef4d6 --- .../java/com/android/server/wm/AppWindowToken.java | 2 +- .../java/com/android/server/wm/WindowState.java | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index 4ec297e310152..e513713895e0f 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -499,7 +499,7 @@ class AppWindowToken extends WindowToken { + " with replacing child windows."); for (int i = allAppWindows.size() - 1; i >= 0; i--) { final WindowState w = allAppWindows.get(i); - if (w.isChildWindow()) { + if (w.shouldBeReplacedWithChildren()) { w.setReplacing(false /* animate */); } } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index c991130b55e76..1dc486c3182be 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -82,6 +82,7 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; @@ -2704,4 +2705,16 @@ final class WindowState implements WindowManagerPolicy.WindowState { } return winY; } + + // During activity relaunch due to resize, we sometimes use window replacement + // for only child windows (as the main window is handled by window preservation) + // and the big surface. + // + // Though windows of TYPE_APPLICATION (as opposed to TYPE_BASE_APPLICATION) + // are not children in the sense of an attached window, we also want to replace + // them at such phases, as they won't be covered by window preservation, + // and in general we expect them to return following relaunch. + boolean shouldBeReplacedWithChildren() { + return isChildWindow() || mAttrs.type == TYPE_APPLICATION; + } } From b439a63fdf9398e46ca44811fbfde35fd02911c4 Mon Sep 17 00:00:00 2001 From: Robert Carr Date: Thu, 7 Apr 2016 22:52:10 -0700 Subject: [PATCH 2/7] Correctly prevent entrance animation for replacing windows. Prevention of entrance animation for seamlessly replacing windows, was not working for non child windows. To correct it, we simply bail from applying the app entrance transition. Bug: 26668339 Change-Id: I4349e6aef55c3957d81a0a168cf6ac1d7c8866f1 --- .../core/java/com/android/server/wm/AppWindowToken.java | 4 ++++ services/core/java/com/android/server/wm/WindowState.java | 6 ++++++ .../java/com/android/server/wm/WindowStateAnimator.java | 6 ++++++ 3 files changed, 16 insertions(+) diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index e513713895e0f..e69cb292fb404 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -541,6 +541,7 @@ class AppWindowToken extends WindowToken { if (candidate.mWillReplaceWindow && candidate.mReplacingWindow == null && candidate.getWindowTag().equals(w.getWindowTag().toString())) { candidate.mReplacingWindow = w; + w.mSkipEnterAnimationForSeamlessReplacement = !candidate.mAnimateReplacingWindow; // if we got a replacement window, reset the timeout to give drawing more time service.mH.removeMessages(H.WINDOW_REPLACEMENT_TIMEOUT); @@ -575,6 +576,9 @@ class AppWindowToken extends WindowToken { continue; } candidate.mWillReplaceWindow = false; + if (candidate.mReplacingWindow != null) { + candidate.mReplacingWindow.mSkipEnterAnimationForSeamlessReplacement = false; + } // Since the window already timed out, remove it immediately now. // Use removeWindowInnerLocked() instead of removeWindowLocked(), as the latter // delays removal on certain conditions, which will leave the stale window in the diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 1dc486c3182be..43cc8c75734b2 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -445,6 +445,11 @@ final class WindowState implements WindowManagerPolicy.WindowState { // If not null, the window that will be used to replace the old one. This is being set when // the window is added and unset when this window reports its first draw. WindowState mReplacingWindow = null; + // For the new window in the replacement transition, if we have + // requested to replace without animation, then we should + // make sure we also don't apply an enter animation for + // the new window. + boolean mSkipEnterAnimationForSeamlessReplacement = false; // Whether this window is being moved via the resize API boolean mMovedByResize; @@ -1574,6 +1579,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { win.mAnimateReplacingWindow = false; win.mReplacingRemoveRequested = false; win.mReplacingWindow = null; + mSkipEnterAnimationForSeamlessReplacement = false; if (win.mAnimatingExit) { mService.removeWindowInnerLocked(win); } diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 9c25f63dfa2ab..3267f5c831486 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -1661,6 +1661,12 @@ class WindowStateAnimator { } void applyEnterAnimationLocked() { + // If we are the new part of a window replacement transition and we have requested + // not to animate, we instead want to make it seamless, so we don't want to apply + // an enter transition. + if (mWin.mSkipEnterAnimationForSeamlessReplacement) { + return; + } final int transit; if (mEnterAnimationPending) { mEnterAnimationPending = false; From dcdca58cd5eb60a32de583eda5a334eb17f38034 Mon Sep 17 00:00:00 2001 From: Robert Carr Date: Thu, 7 Apr 2016 22:59:24 -0700 Subject: [PATCH 3/7] Prevent premature window replacement. maybeRemoveReplacedWindows is called when any window has reported drawing, we have to verify that we have actually ourselves drawn, before commencing the replacement. Bug: 26668339 Change-Id: Iabfc2e813989381f9f20f3bb111100911405686b --- services/core/java/com/android/server/wm/WindowState.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 43cc8c75734b2..331536f016879 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -1573,7 +1573,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { } for (int i = mAppToken.allAppWindows.size() - 1; i >= 0; i--) { final WindowState win = mAppToken.allAppWindows.get(i); - if (win.mWillReplaceWindow && win.mReplacingWindow == this) { + if (win.mWillReplaceWindow && win.mReplacingWindow == this && hasDrawnLw()) { if (DEBUG_ADD_REMOVE) Slog.d(TAG, "Removing replaced window: " + win); win.mWillReplaceWindow = false; win.mAnimateReplacingWindow = false; From 9fe459da7ee3fd462a646b638f647c917c229eb4 Mon Sep 17 00:00:00 2001 From: Robert Carr Date: Thu, 7 Apr 2016 23:32:28 -0700 Subject: [PATCH 4/7] Replace DimLayers with windows. When replacing windows seamlessly we should also replace their DimLayers. Bug: 26668339 Change-Id: I44d8dbacf1b2213cfb882a40a1c878666a1ebef0 --- .../com/android/server/wm/DimLayerController.java | 6 +++++- .../java/com/android/server/wm/WindowState.java | 14 +++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/services/core/java/com/android/server/wm/DimLayerController.java b/services/core/java/com/android/server/wm/DimLayerController.java index 3ec02b9f66fc7..c240d0733a0a6 100644 --- a/services/core/java/com/android/server/wm/DimLayerController.java +++ b/services/core/java/com/android/server/wm/DimLayerController.java @@ -169,6 +169,10 @@ class DimLayerController { + " dimLayerUser=" + dimLayerUser.toShortString() + " state.continueDimming=" + state.continueDimming + " state.dimLayer.isDimming=" + state.dimLayer.isDimming()); + if (state.animator != null && state.animator.mWin.mWillReplaceWindow) { + return; + } + if (!state.continueDimming && state.dimLayer.isDimming()) { state.animator = null; dimLayerUser.getDimBounds(mTmpBounds); @@ -303,7 +307,7 @@ class DimLayerController { applyDim(dimLayerUser, animator, true /* aboveApp */); } - private void applyDim( + void applyDim( DimLayer.DimLayerUser dimLayerUser, WindowStateAnimator animator, boolean aboveApp) { if (dimLayerUser == null) { Slog.e(TAG, "Trying to apply dim layer for: " + this diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 331536f016879..3a76f2f605620 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -1554,7 +1554,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { // If app died visible, apply a dim over the window to indicate that it's inactive mDisplayContent.mDimLayerController.applyDimAbove(getDimLayerUser(), mWinAnimator); } else if ((mAttrs.flags & FLAG_DIM_BEHIND) != 0 - && mDisplayContent != null && !mAnimatingExit && isDisplayedLw()) { + && mDisplayContent != null && !mAnimatingExit && isVisibleUnchecked()) { mDisplayContent.mDimLayerController.applyDimBehind(getDimLayerUser(), mWinAnimator); } } @@ -1575,6 +1575,9 @@ final class WindowState implements WindowManagerPolicy.WindowState { final WindowState win = mAppToken.allAppWindows.get(i); if (win.mWillReplaceWindow && win.mReplacingWindow == this && hasDrawnLw()) { if (DEBUG_ADD_REMOVE) Slog.d(TAG, "Removing replaced window: " + win); + if (win.isDimming()) { + win.transferDimToReplacement(); + } win.mWillReplaceWindow = false; win.mAnimateReplacingWindow = false; win.mReplacingRemoveRequested = false; @@ -2712,6 +2715,15 @@ final class WindowState implements WindowManagerPolicy.WindowState { return winY; } + void transferDimToReplacement() { + final DimLayer.DimLayerUser dimLayerUser = getDimLayerUser(); + if (dimLayerUser != null && mDisplayContent != null) { + mDisplayContent.mDimLayerController.applyDim(dimLayerUser, + mReplacingWindow.mWinAnimator, + (mAttrs.flags & FLAG_DIM_BEHIND) != 0 ? true : false); + } + } + // During activity relaunch due to resize, we sometimes use window replacement // for only child windows (as the main window is handled by window preservation) // and the big surface. From 8bc8907ef142b4d60c1728f20cf18836833f1aad Mon Sep 17 00:00:00 2001 From: Robert Carr Date: Fri, 8 Apr 2016 13:29:59 -0700 Subject: [PATCH 5/7] Correct window replacement string comparison. We were comparing the objects, not the strings, which happened to work in many cases as they were both the same package name String. Bug: 26668339 Change-Id: I025d05586cc6c11e788add967c7e6ad916cba276 --- services/core/java/com/android/server/wm/AppWindowToken.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index e69cb292fb404..545b9db75ecd7 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -539,7 +539,7 @@ class AppWindowToken extends WindowToken { for (int i = allAppWindows.size() - 1; i >= 0; i--) { WindowState candidate = allAppWindows.get(i); if (candidate.mWillReplaceWindow && candidate.mReplacingWindow == null && - candidate.getWindowTag().equals(w.getWindowTag().toString())) { + candidate.getWindowTag().toString().equals(w.getWindowTag().toString())) { candidate.mReplacingWindow = w; w.mSkipEnterAnimationForSeamlessReplacement = !candidate.mAnimateReplacingWindow; From a86a6bf73e8f6e58590c1b53972cd2d1cc7c137f Mon Sep 17 00:00:00 2001 From: Robert Carr Date: Fri, 8 Apr 2016 17:34:16 -0700 Subject: [PATCH 6/7] Fix Task dim with docked resize. When are are docked resizing, just fake the task bounds as the stack bounds for the purposes of DimLayers, even if we don't want to relayout the application interactively we want the DimLayer to keep up with the divider. Bug: 28154322 Change-Id: I86e41324cf384f2dceea15cd5e8ddd753dc5bfbd --- .../core/java/com/android/server/wm/Task.java | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 7b16dbec68a5f..ce82dbe09a979 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -456,6 +456,11 @@ class Task implements DimLayer.DimLayerUser { /** Bounds of the task to be used for dimming, as well as touch related tests. */ @Override public void getDimBounds(Rect out) { + final DisplayContent displayContent = mStack.getDisplayContent(); + // It doesn't matter if we in particular are part of the resize, since we couldn't have + // a DimLayer anyway if we weren't visible. + final boolean dockedResizing = displayContent != null ? + displayContent.mDividerControllerLocked.isResizing() : false; if (useCurrentBounds()) { if (inFreeformWorkspace() && getMaxVisibleBounds(out)) { return; @@ -464,8 +469,16 @@ class Task implements DimLayer.DimLayerUser { if (!mFullscreen) { // When minimizing the docked stack when going home, we don't adjust the task bounds // so we need to intersect the task bounds with the stack bounds here. - mStack.getBounds(mTmpRect); - mTmpRect.intersect(mBounds); + // + // If we are Docked Resizing with snap points, the task bounds could be smaller than the stack + // bounds and so we don't even want to use them. Even if the app should not be resized the Dim + // should keep up with the divider. + if (dockedResizing) { + mStack.getBounds(out); + } else { + mStack.getBounds(mTmpRect); + mTmpRect.intersect(mBounds); + } out.set(mTmpRect); } else { out.set(mBounds); @@ -476,7 +489,7 @@ class Task implements DimLayer.DimLayerUser { // The bounds has been adjusted to accommodate for a docked stack, but the docked stack // is not currently visible. Go ahead a represent it as fullscreen to the rest of the // system. - mStack.getDisplayContent().getLogicalDisplayRect(out); + displayContent.getLogicalDisplayRect(out); } void setDragResizing(boolean dragResizing, int dragResizeMode) { From 2c17cd254ba334462265c7b55938a7531545b2d3 Mon Sep 17 00:00:00 2001 From: Robert Carr Date: Fri, 8 Apr 2016 17:52:28 -0700 Subject: [PATCH 7/7] Only set mResizedWhileNotDragResizing for base windows. Dialogs, etc, may appear to not be drag resizing as they are not base windows. Still though, when they resize we don't want them to enter in to this mResizedWhileNotDragResizing mode or we will freeze surface boundary updates and lose the ability to crop them to the stack. Bug: 26668339 Change-Id: Id603816cf5f33b281f46c7812779ba29a024f34f --- services/core/java/com/android/server/wm/Task.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index ce82dbe09a979..bff431997e1cf 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -27,6 +27,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.WindowManagerService.H.RESIZE_TASK; +import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import android.app.ActivityManager.StackId; import android.content.pm.ActivityInfo; @@ -574,7 +575,16 @@ class Task implements DimLayer.DimLayerUser { // If we are not drag resizing, force recreating of a new surface so updating // the content and positioning that surface will be in sync. - if (!win.computeDragResizing()) { + // + // As we use this flag as a hint to freeze surface boundary updates, + // we'd like to only apply this to TYPE_BASE_APPLICATION, + // windows of TYPE_APPLICATION like dialogs, could appear + // to not be drag resizing while they resize, but we'd + // still like to manipulate their frame to update crop, etc... + // + // Anyway we don't need to synchronize position and content updates for these + // windows since they aren't at the base layer and could be moved around anyway. + if (!win.computeDragResizing() && win.mAttrs.type == TYPE_BASE_APPLICATION) { win.mResizedWhileNotDragResizing = true; } }