From 66e601fb135ce681a88655d6306e503ebad661ff Mon Sep 17 00:00:00 2001 From: wilsonshih Date: Fri, 16 Sep 2022 16:53:53 +0800 Subject: [PATCH] [Shell Transition] Schedule another transition to update visibility. Activity could call setTranslucent during a transition playing, if the task of an activity was in transition and that activity should become invisible, it would be defer until transition finish. However, since the activity wasn't participant the running transition, there won't do commitVisibility for it after transition finish. To correct the visibility status, trigger another transition so the activities which visibility changed can be collect and commit. Bug: 246518648 Test: atest testConvertTranslucentOnTranslucentActivity Test: atest testConvertTranslucentOnNonTopTranslucentActivity Change-Id: Ic1cda79da37162cca2a1a3fbc73311cc325b3874 --- core/java/android/window/TransitionInfo.java | 9 +++- .../wm/shell/transition/Transitions.java | 50 ++++++++++--------- .../server/wm/ActivityClientController.java | 43 +++++++++++++++- .../com/android/server/wm/ActivityRecord.java | 16 ++++-- .../com/android/server/wm/Transition.java | 5 +- 5 files changed, 88 insertions(+), 35 deletions(-) diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java index 641d1a1897116..fbdd325e0814c 100644 --- a/core/java/android/window/TransitionInfo.java +++ b/core/java/android/window/TransitionInfo.java @@ -132,8 +132,11 @@ public final class TransitionInfo implements Parcelable { */ public static final int FLAG_IS_BEHIND_STARTING_WINDOW = 1 << 14; + /** This change happened underneath something else. */ + public static final int FLAG_IS_OCCLUDED = 1 << 15; + /** The first unused bit. This can be used by remotes to attach custom flags to this change. */ - public static final int FLAG_FIRST_CUSTOM = 1 << 15; + public static final int FLAG_FIRST_CUSTOM = 1 << 16; /** @hide */ @IntDef(prefix = { "FLAG_" }, value = { @@ -153,6 +156,7 @@ public final class TransitionInfo implements Parcelable { FLAG_CROSS_PROFILE_OWNER_THUMBNAIL, FLAG_CROSS_PROFILE_WORK_THUMBNAIL, FLAG_IS_BEHIND_STARTING_WINDOW, + FLAG_IS_OCCLUDED, FLAG_FIRST_CUSTOM }) public @interface ChangeFlags {} @@ -362,6 +366,9 @@ public final class TransitionInfo implements Parcelable { if ((flags & FLAG_IS_BEHIND_STARTING_WINDOW) != 0) { sb.append(sb.length() == 0 ? "" : "|").append("IS_BEHIND_STARTING_WINDOW"); } + if ((flags & FLAG_IS_OCCLUDED) != 0) { + sb.append(sb.length() == 0 ? "" : "|").append("IS_OCCLUDED"); + } if ((flags & FLAG_FIRST_CUSTOM) != 0) { sb.append(sb.length() == 0 ? "" : "|").append("FIRST_CUSTOM"); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index 29d25bc39223a..43ea695299a9d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -25,6 +25,7 @@ import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.view.WindowManager.fixScale; import static android.window.TransitionInfo.FLAG_IS_INPUT_METHOD; +import static android.window.TransitionInfo.FLAG_IS_OCCLUDED; import static android.window.TransitionInfo.FLAG_IS_WALLPAPER; import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT; @@ -441,32 +442,35 @@ public class Transitions implements RemoteCallable { return; } - // apply transfer starting window directly if there is no other task change. Since this - // is an activity->activity situation, we can detect it by selecting transitions with only - // 2 changes where neither are tasks and one is a starting-window recipient. final int changeSize = info.getChanges().size(); - if (changeSize == 2) { - boolean nonTaskChange = true; - boolean transferStartingWindow = false; - for (int i = changeSize - 1; i >= 0; --i) { - final TransitionInfo.Change change = info.getChanges().get(i); - if (change.getTaskInfo() != null) { - nonTaskChange = false; - break; - } - if ((change.getFlags() & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) { - transferStartingWindow = true; - } - } - if (nonTaskChange && transferStartingWindow) { - t.apply(); - finishT.apply(); - // Treat this as an abort since we are bypassing any merge logic and effectively - // finishing immediately. - onAbort(transitionToken); - return; + boolean taskChange = false; + boolean transferStartingWindow = false; + boolean allOccluded = changeSize > 0; + for (int i = changeSize - 1; i >= 0; --i) { + final TransitionInfo.Change change = info.getChanges().get(i); + taskChange |= change.getTaskInfo() != null; + transferStartingWindow |= change.hasFlags(FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT); + if (!change.hasFlags(FLAG_IS_OCCLUDED)) { + allOccluded = false; } } + // There does not need animation when: + // A. Transfer starting window. Apply transfer starting window directly if there is no other + // task change. Since this is an activity->activity situation, we can detect it by selecting + // transitions with only 2 changes where neither are tasks and one is a starting-window + // recipient. + if (!taskChange && transferStartingWindow && changeSize == 2 + // B. It's visibility change if the TRANSIT_TO_BACK/TO_FRONT happened when all + // changes are underneath another change. + || ((info.getType() == TRANSIT_TO_BACK || info.getType() == TRANSIT_TO_FRONT) + && allOccluded)) { + t.apply(); + finishT.apply(); + // Treat this as an abort since we are bypassing any merge logic and effectively + // finishing immediately. + onAbort(transitionToken); + return; + } final ActiveTransition active = mActiveTransitions.get(activeIdx); active.mInfo = info; diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java index d2a00af245f64..b64b9f393d280 100644 --- a/services/core/java/com/android/server/wm/ActivityClientController.java +++ b/services/core/java/com/android/server/wm/ActivityClientController.java @@ -27,6 +27,8 @@ import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.service.voice.VoiceInteractionSession.SHOW_SOURCE_APPLICATION; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; +import static android.view.WindowManager.TRANSIT_TO_BACK; +import static android.view.WindowManager.TRANSIT_TO_FRONT; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IMMERSIVE; @@ -707,7 +709,26 @@ class ActivityClientController extends IActivityClientController.Stub { try { synchronized (mGlobalLock) { final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token); - return r != null && r.setOccludesParent(true); + // Create a transition if the activity is playing in case the below activity didn't + // commit invisible. That's because if any activity below this one has changed its + // visibility while playing transition, there won't able to commit visibility until + // the running transition finish. + final Transition transition = r != null + && r.mTransitionController.inPlayingTransition(r) + ? r.mTransitionController.createTransition(TRANSIT_TO_BACK) : null; + if (transition != null) { + r.mTransitionController.requestStartTransition(transition, null /*startTask */, + null /* remoteTransition */, null /* displayChange */); + } + final boolean changed = r != null && r.setOccludesParent(true); + if (transition != null) { + if (changed) { + r.mTransitionController.setReady(r.getDisplayContent()); + } else { + transition.abort(); + } + } + return changed; } } finally { Binder.restoreCallingIdentity(origId); @@ -728,7 +749,25 @@ class ActivityClientController extends IActivityClientController.Stub { if (under != null) { under.returningOptions = safeOptions != null ? safeOptions.getOptions(r) : null; } - return r.setOccludesParent(false); + // Create a transition if the activity is playing in case the current activity + // didn't commit invisible. That's because if this activity has changed its + // visibility while playing transition, there won't able to commit visibility until + // the running transition finish. + final Transition transition = r.mTransitionController.inPlayingTransition(r) + ? r.mTransitionController.createTransition(TRANSIT_TO_FRONT) : null; + if (transition != null) { + r.mTransitionController.requestStartTransition(transition, null /*startTask */, + null /* remoteTransition */, null /* displayChange */); + } + final boolean changed = r.setOccludesParent(false); + if (transition != null) { + if (changed) { + r.mTransitionController.setReady(r.getDisplayContent()); + } else { + transition.abort(); + } + } + return changed; } } finally { Binder.restoreCallingIdentity(origId); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index b8486e7aa2b47..fab168369470e 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -120,6 +120,8 @@ import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_FLAG_OPEN_BEHIND; import static android.view.WindowManager.TRANSIT_OLD_UNSET; +import static android.window.TransitionInfo.FLAG_IS_OCCLUDED; +import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ANIM; @@ -658,7 +660,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A private final WindowState.UpdateReportedVisibilityResults mReportedVisibilityResults = new WindowState.UpdateReportedVisibilityResults(); - boolean mUseTransferredAnimation; + int mTransitionChangeFlags; /** Whether we need to setup the animation to animate only within the letterbox. */ private boolean mNeedsLetterboxedAnimation; @@ -4395,10 +4397,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // When transferring an animation, we no longer need to apply an animation to // the token we transfer the animation over. Thus, set this flag to indicate // we've transferred the animation. - mUseTransferredAnimation = true; + mTransitionChangeFlags |= FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT; } else if (mTransitionController.getTransitionPlayer() != null) { // In the new transit system, just set this every time we transfer the window - mUseTransferredAnimation = true; + mTransitionChangeFlags |= FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT; } // Post cleanup after the visibility and animation are transferred. fromActivity.postWindowRemoveStartingWindowCleanup(tStartingWindow); @@ -5247,6 +5249,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // If in a transition, defer commits for activities that are going invisible if (!visible && inTransition()) { + if (mTransitionController.inPlayingTransition(this) + && mTransitionController.isCollecting(this)) { + mTransitionChangeFlags |= FLAG_IS_OCCLUDED; + } return; } // If we are preparing an app transition, then delay changing @@ -5297,7 +5303,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A @Override boolean applyAnimation(LayoutParams lp, @TransitionOldType int transit, boolean enter, boolean isVoiceInteraction, @Nullable ArrayList sources) { - if (mUseTransferredAnimation) { + if ((mTransitionChangeFlags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) { return false; } // If it was set to true, reset the last request to force the transition. @@ -5370,7 +5376,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mWmService.mWindowPlacerLocked.performSurfacePlacement(); } displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/); - mUseTransferredAnimation = false; + mTransitionChangeFlags = 0; postApplyAnimation(visible, fromTransition); } diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index 147c9cb18315e..4a1bc6a71f914 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -55,7 +55,6 @@ import static android.window.TransitionInfo.FLAG_IS_VOICE_INTERACTION; import static android.window.TransitionInfo.FLAG_IS_WALLPAPER; import static android.window.TransitionInfo.FLAG_OCCLUDES_KEYGUARD; import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER; -import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT; import static android.window.TransitionInfo.FLAG_TRANSLUCENT; import static android.window.TransitionInfo.FLAG_WILL_IME_SHOWN; @@ -1879,12 +1878,10 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe final ActivityRecord record = wc.asActivityRecord(); if (record != null) { parentTask = record.getTask(); - if (record.mUseTransferredAnimation) { - flags |= FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT; - } if (record.mVoiceInteraction) { flags |= FLAG_IS_VOICE_INTERACTION; } + flags |= record.mTransitionChangeFlags; } final TaskFragment taskFragment = wc.asTaskFragment(); if (taskFragment != null && task == null) {