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 d65ac80621df4..7fdd3915766e4 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 df9ae63e235c5..eca2e7441f295 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 96ad9ab5ff21c..526a366ded57d 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; @@ -1889,12 +1888,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) {