Fixed transition animation from pipable activities
- Defer telling the client it is hidden if it can enter Pip and isn't
current stopped or stopping. This gives it a chance to enter Pip in
onPause().
- Once pause is complete set the visiblity to false to stop deferring
hiding client.
- Don't allow FLAG_RESUME_WHILE_PAUSING activity to resume until the
currently resumed activity is puased if the currently resumed activity
can enter Pip.
- Detach child surfaces added by the client process in
WindowState.sendAppVisibilityToClients() right before we notify the
client.
Test: manual
Change-Id: I3848f2b93f4f1d3ceec5a1ccd2e127c614f70fe4
Fixes: 37370508
Fixes: 37622341
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
index 292734d..c625cbe 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java
@@ -323,7 +323,7 @@
}
}
- public void setVisibility(boolean visible) {
+ public void setVisibility(boolean visible, boolean deferHidingClient) {
synchronized(mWindowMap) {
if (mContainer == null) {
Slog.w(TAG_WM, "Attempted to set visibility of non-existing app token: "
@@ -342,6 +342,7 @@
mService.mClosingApps.remove(wtoken);
wtoken.waitingToShow = false;
wtoken.hiddenRequested = !visible;
+ wtoken.mDeferHidingClient = deferHidingClient;
if (!visible) {
// If the app is dead while it was visible, we kept its dead window on screen.
@@ -368,15 +369,12 @@
wtoken.waitingToShow = true;
}
- if (wtoken.clientHidden) {
- // In the case where we are making an app visible
- // but holding off for a transition, we still need
- // to tell the client to make its windows visible so
- // they get drawn. Otherwise, we will wait on
- // performing the transition until all windows have
- // been drawn, they never will be, and we are sad.
- wtoken.clientHidden = false;
- wtoken.sendAppVisibilityToClients();
+ if (wtoken.isClientHidden()) {
+ // In the case where we are making an app visible but holding off for a
+ // transition, we still need to tell the client to make its windows visible
+ // so they get drawn. Otherwise, we will wait on performing the transition
+ // until all windows have been drawn, they never will be, and we are sad.
+ wtoken.setClientHidden(false);
}
}
wtoken.requestUpdateWallpaperIfNeeded();
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 7634644..31fdacd 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -126,7 +126,11 @@
boolean hiddenRequested;
// Have we told the window clients to hide themselves?
- boolean clientHidden;
+ private boolean mClientHidden;
+
+ // If true we will defer setting mClientHidden to true and reporting to the client that it is
+ // hidden.
+ boolean mDeferHidingClient;
// Last visibility state we reported to the app token.
boolean reportedVisible;
@@ -307,16 +311,25 @@
}
}
+ boolean isClientHidden() {
+ return mClientHidden;
+ }
+
+ void setClientHidden(boolean hideClient) {
+ if (mClientHidden == hideClient || (hideClient && mDeferHidingClient)) {
+ return;
+ }
+ mClientHidden = hideClient;
+ sendAppVisibilityToClients();
+ }
+
boolean setVisibility(WindowManager.LayoutParams lp,
boolean visible, int transit, boolean performLayout, boolean isVoiceInteraction) {
boolean delayed = false;
inPendingTransaction = false;
- if (clientHidden == visible) {
- clientHidden = !visible;
- sendAppVisibilityToClients();
- }
+ setClientHidden(!visible);
// Allow for state changes and animation to be applied if:
// * token is transitioning visibility state
@@ -1143,10 +1156,7 @@
hidden = false;
hiddenRequested = false;
}
- if (clientHidden != fromToken.clientHidden) {
- clientHidden = fromToken.clientHidden;
- sendAppVisibilityToClients();
- }
+ setClientHidden(fromToken.mClientHidden);
fromToken.mAppAnimator.transferCurrentAnimation(
mAppAnimator, tStartingWindow.mWinAnimator);
@@ -1511,10 +1521,9 @@
pw.print(prefix); pw.print("task="); pw.println(getTask());
pw.print(prefix); pw.print(" mFillsParent="); pw.print(mFillsParent);
pw.print(" mOrientation="); pw.println(mOrientation);
- pw.print(prefix); pw.print("hiddenRequested="); pw.print(hiddenRequested);
- pw.print(" clientHidden="); pw.print(clientHidden);
- pw.print(" reportedDrawn="); pw.print(reportedDrawn);
- pw.print(" reportedVisible="); pw.println(reportedVisible);
+ pw.println(prefix + "hiddenRequested=" + hiddenRequested + " mClientHidden=" + mClientHidden
+ + ((mDeferHidingClient) ? " mDeferHidingClient=" + mDeferHidingClient : "")
+ + " reportedDrawn=" + reportedDrawn + " reportedVisible=" + reportedVisible);
if (paused) {
pw.print(prefix); pw.print("paused="); pw.println(paused);
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 1823610..e2f313a 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -2245,7 +2245,7 @@
wsa.destroySurface();
mService.mForceRemoves.add(w);
mTmpWindow = w;
- } else if (w.mAppToken != null && w.mAppToken.clientHidden) {
+ } else if (w.mAppToken != null && w.mAppToken.isClientHidden()) {
Slog.w(TAG_WM, "LEAKED SURFACE (app token hidden): "
+ w + " surface=" + wsa.mSurfaceController
+ " token=" + w.mAppToken
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 4262d12..bc749e1 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -439,7 +439,7 @@
for (int i = mChildren.size() - 1; i >= 0; i--) {
final AppWindowToken token = mChildren.get(i);
// skip hidden (or about to hide) apps
- if (token.mIsExiting || token.clientHidden || token.hiddenRequested) {
+ if (token.mIsExiting || token.isClientHidden() || token.hiddenRequested) {
continue;
}
final WindowState win = token.findMainWindow();
@@ -607,7 +607,7 @@
for (int i = mChildren.size() - 1; i >= 0; i--) {
final AppWindowToken token = mChildren.get(i);
// skip hidden (or about to hide) apps
- if (!token.mIsExiting && !token.clientHidden && !token.hiddenRequested) {
+ if (!token.mIsExiting && !token.isClientHidden() && !token.hiddenRequested) {
return token;
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 252b4d4..95878801 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1470,7 +1470,7 @@
if (mInTouchMode) {
res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE;
}
- if (win.mAppToken == null || !win.mAppToken.clientHidden) {
+ if (win.mAppToken == null || !win.mAppToken.isClientHidden()) {
res |= WindowManagerGlobal.ADD_FLAG_APP_VISIBLE;
}
@@ -1950,7 +1950,7 @@
}
if (viewVisibility == View.VISIBLE &&
(win.mAppToken == null || win.mAttrs.type == TYPE_APPLICATION_STARTING
- || !win.mAppToken.clientHidden)) {
+ || !win.mAppToken.isClientHidden())) {
result = win.relayoutVisibleWindow(mergedConfiguration, result, attrChanges,
oldVisibility);
try {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index b9776a3..9bdcc36 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1399,7 +1399,7 @@
* @return true if the window should be considered while evaluating allDrawn flags.
*/
boolean mightAffectAllDrawn(boolean visibleOnly) {
- final boolean isViewVisible = (mAppToken == null || !mAppToken.clientHidden)
+ final boolean isViewVisible = (mAppToken == null || !mAppToken.isClientHidden())
&& (mViewVisibility == View.VISIBLE) && !mWindowRemovalAllowed;
return (isOnScreen() && (!visibleOnly || isViewVisible)
|| mWinAnimator.mAttrType == TYPE_BASE_APPLICATION
@@ -2312,7 +2312,7 @@
* interacts with it.
*/
boolean shouldKeepVisibleDeadAppWindow() {
- if (!isWinVisibleLw() || mAppToken == null || mAppToken.clientHidden) {
+ if (!isWinVisibleLw() || mAppToken == null || mAppToken.isClientHidden()) {
// Not a visible app window or the app isn't dead.
return false;
}
@@ -2570,12 +2570,24 @@
void sendAppVisibilityToClients() {
super.sendAppVisibilityToClients();
- final boolean clientHidden = mAppToken.clientHidden;
+ final boolean clientHidden = mAppToken.isClientHidden();
if (mAttrs.type == TYPE_APPLICATION_STARTING && clientHidden) {
// Don't hide the starting window.
return;
}
+ if (clientHidden) {
+ // Once we are notifying the client that it's visibility has changed, we need to prevent
+ // it from destroying child surfaces until the animation has finished. We do this by
+ // detaching any surface control the client added from the client.
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ final WindowState c = mChildren.get(i);
+ c.mWinAnimator.detachChildren();
+ }
+
+ mWinAnimator.detachChildren();
+ }
+
try {
if (DEBUG_VISIBILITY) Slog.v(TAG,
"Setting visibility of " + this + ": " + (!clientHidden));