From 9621dce59aa3efb61cc497c5637960fa1b7396db Mon Sep 17 00:00:00 2001 From: Evan Rosky Date: Sat, 18 Feb 2023 20:24:03 -0800 Subject: [PATCH] re-organize recents transition code for future changes Some code has to move around both for clarity and so that future CLs can be more focused on logic changes. The movements here are: Create static functions for non-app and wallpaper tests used to filter TransitionInfo changes to wrap with RemoteAnimationTargets. This lets other code use the same checks. Make a dedicate "LeafTaskFilter" that can also be used by other code to select only leaf-tasks from a TransitionInfo. It also attaches a semantic name to it rather than being an anonymous lambda. Moved recents setup logic into the wrapper, we were passing so many parameters in and basically 100% of the logic is only used by the wrapper anyways (and isn't relevant for general remotes). Also made startNewTransition two-way because one-ways go into a different queue so we can't guarantee ordering between startNew and normal start. Also renamed some variables to better communicate what they mean. Bug: 269691848 Bug: 269691226 Test: no logic changes... existing tests pass Change-Id: I67c153ed452a818f326c98c9a507251bd0c9eafa --- .../window/IWindowOrganizerController.aidl | 2 +- .../wm/shell/transition/Transitions.java | 21 ++- .../system/RemoteAnimationTargetCompat.java | 78 ++++++--- .../shared/system/RemoteTransitionCompat.java | 158 +++++++++--------- 4 files changed, 146 insertions(+), 113 deletions(-) diff --git a/core/java/android/window/IWindowOrganizerController.aidl b/core/java/android/window/IWindowOrganizerController.aidl index 57e0ce8ce8ed5..534c9de8102c0 100644 --- a/core/java/android/window/IWindowOrganizerController.aidl +++ b/core/java/android/window/IWindowOrganizerController.aidl @@ -63,7 +63,7 @@ interface IWindowOrganizerController { * @param transitionToken A token associated with the transition to start. * @param t Operations that are part of the transition. */ - oneway void startTransition(IBinder transitionToken, in @nullable WindowContainerTransaction t); + void startTransition(IBinder transitionToken, in @nullable WindowContainerTransaction t); /** * Starts a legacy transition. 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 0826fe236d58c..0a67477212e9d 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 @@ -709,15 +709,22 @@ public class Transitions implements RemoteCallable { for (int iA = activeIdx + 1; iA < mActiveTransitions.size(); ++iA) { final ActiveTransition toMerge = mActiveTransitions.get(iA); if (!toMerge.mMerged) break; - // aborted transitions have no start/finish transactions - if (mActiveTransitions.get(iA).mStartT == null) break; - if (fullFinish == null) { - fullFinish = new SurfaceControl.Transaction(); - } // Include start. It will be a no-op if it was already applied. Otherwise, we need it // to maintain consistent state. - fullFinish.merge(mActiveTransitions.get(iA).mStartT); - fullFinish.merge(mActiveTransitions.get(iA).mFinishT); + if (toMerge.mStartT != null) { + if (fullFinish == null) { + fullFinish = toMerge.mStartT; + } else { + fullFinish.merge(toMerge.mStartT); + } + } + if (toMerge.mFinishT != null) { + if (fullFinish == null) { + fullFinish = toMerge.mFinishT; + } else { + fullFinish.merge(toMerge.mFinishT); + } + } } if (fullFinish != null) { fullFinish.apply(); diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java index e1e806319ba05..90f44a75b0050 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java @@ -36,7 +36,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.app.ActivityManager; -import android.app.TaskInfo; import android.app.WindowConfiguration; import android.graphics.Rect; import android.util.ArrayMap; @@ -48,7 +47,7 @@ import android.window.TransitionInfo; import android.window.TransitionInfo.Change; import java.util.ArrayList; -import java.util.function.BiPredicate; +import java.util.function.Predicate; /** * Some utility methods for creating {@link RemoteAnimationTarget} instances. @@ -145,6 +144,18 @@ public class RemoteAnimationTargetCompat { public static RemoteAnimationTarget newTarget(TransitionInfo.Change change, int order, TransitionInfo info, SurfaceControl.Transaction t, @Nullable ArrayMap leashMap) { + final SurfaceControl leash = createLeash(info, change, order, t); + if (leashMap != null) { + leashMap.put(change.getLeash(), leash); + } + return newTarget(change, order, leash); + } + + /** + * Creates a new RemoteAnimationTarget from the provided change and leash + */ + public static RemoteAnimationTarget newTarget(TransitionInfo.Change change, int order, + SurfaceControl leash) { int taskId; boolean isNotInRecents; ActivityManager.RunningTaskInfo taskInfo; @@ -169,7 +180,7 @@ public class RemoteAnimationTargetCompat { newModeToLegacyMode(change.getMode()), // TODO: once we can properly sync transactions across process, // then get rid of this leash. - createLeash(info, change, order, t), + leash, (change.getFlags() & TransitionInfo.FLAG_TRANSLUCENT) != 0, null, // TODO(shell-transitions): we need to send content insets? evaluate how its used. @@ -190,9 +201,6 @@ public class RemoteAnimationTargetCompat { target.setWillShowImeOnTarget( (change.getFlags() & TransitionInfo.FLAG_WILL_IME_SHOWN) != 0); target.setRotationChange(change.getEndRotation() - change.getStartRotation()); - if (leashMap != null) { - leashMap.put(change.getLeash(), target.leash); - } return target; } @@ -204,18 +212,7 @@ public class RemoteAnimationTargetCompat { */ public static RemoteAnimationTarget[] wrapApps(TransitionInfo info, SurfaceControl.Transaction t, ArrayMap leashMap) { - SparseBooleanArray childTaskTargets = new SparseBooleanArray(); - return wrap(info, t, leashMap, (change, taskInfo) -> { - // Children always come before parent since changes are in top-to-bottom z-order. - if ((taskInfo == null) || childTaskTargets.get(taskInfo.taskId)) { - // has children, so not a leaf. Skip. - return false; - } - if (taskInfo.hasParentTask()) { - childTaskTargets.put(taskInfo.parentTaskId, true); - } - return true; - }); + return wrap(info, t, leashMap, new LeafTaskFilter()); } /** @@ -228,21 +225,56 @@ public class RemoteAnimationTargetCompat { */ public static RemoteAnimationTarget[] wrapNonApps(TransitionInfo info, boolean wallpapers, SurfaceControl.Transaction t, ArrayMap leashMap) { - return wrap(info, t, leashMap, (change, taskInfo) -> (taskInfo == null) - && wallpapers == change.hasFlags(FLAG_IS_WALLPAPER) - && !change.hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY)); + return wrap(info, t, leashMap, (change) -> + (wallpapers ? isWallpaper(change) : isNonApp(change))); } private static RemoteAnimationTarget[] wrap(TransitionInfo info, SurfaceControl.Transaction t, ArrayMap leashMap, - BiPredicate filter) { + Predicate filter) { final ArrayList out = new ArrayList<>(); for (int i = 0; i < info.getChanges().size(); i++) { TransitionInfo.Change change = info.getChanges().get(i); - if (filter.test(change, change.getTaskInfo())) { + if (filter.test(change)) { out.add(newTarget(change, info.getChanges().size() - i, info, t, leashMap)); } } return out.toArray(new RemoteAnimationTarget[out.size()]); } + + /** Returns `true` if `change` is a wallpaper. */ + public static boolean isWallpaper(Change change) { + return (change.getTaskInfo() == null) + && change.hasFlags(FLAG_IS_WALLPAPER) + && !change.hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY); + } + + /** Returns `true` if `change` is not an app window or wallpaper. */ + public static boolean isNonApp(Change change) { + return (change.getTaskInfo() == null) + && !change.hasFlags(FLAG_IS_WALLPAPER) + && !change.hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY); + } + + /** + * Filter that selects leaf-tasks only. THIS IS ORDER-DEPENDENT! For it to work properly, you + * MUST call `test` in the same order that the changes appear in the TransitionInfo. + */ + public static class LeafTaskFilter implements Predicate { + private final SparseBooleanArray mChildTaskTargets = new SparseBooleanArray(); + + @Override + public boolean test(Change change) { + final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); + // Children always come before parent since changes are in top-to-bottom z-order. + if ((taskInfo == null) || mChildTaskTargets.get(taskInfo.taskId)) { + // has children, so not a leaf. Skip. + return false; + } + if (taskInfo.hasParentTask()) { + mChildTaskTargets.put(taskInfo.parentTaskId, true); + } + return true; + } + } } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java index 44c0e169b6d91..098aa60d728a4 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java @@ -72,71 +72,23 @@ public class RemoteTransitionCompat { public void startAnimation(IBinder transition, TransitionInfo info, SurfaceControl.Transaction t, IRemoteTransitionFinishedCallback finishedCallback) { - final ArrayMap leashMap = new ArrayMap<>(); - final RemoteAnimationTarget[] apps = - RemoteAnimationTargetCompat.wrapApps(info, t, leashMap); - final RemoteAnimationTarget[] wallpapers = - RemoteAnimationTargetCompat.wrapNonApps( - info, true /* wallpapers */, t, leashMap); // TODO(b/177438007): Move this set-up logic into launcher's animation impl. mToken = transition; - // This transition is for opening recents, so recents is on-top. We want to draw - // the current going-away tasks on top of recents, though, so move them to front. - // Note that we divide up the "layer space" into 3 regions each the size of - // the change count. This way we can easily move changes into above/below/between - // while maintaining their relative ordering. - final ArrayList pausingTasks = new ArrayList<>(); - WindowContainerToken pipTask = null; - WindowContainerToken recentsTask = null; - int recentsTaskId = -1; - for (int i = apps.length - 1; i >= 0; --i) { - final ActivityManager.RunningTaskInfo taskInfo = apps[i].taskInfo; - if (apps[i].mode == MODE_CLOSING) { - t.setLayer(apps[i].leash, info.getChanges().size() * 3 - i); - if (taskInfo == null) { - continue; - } - // Add to front since we are iterating backwards. - pausingTasks.add(0, taskInfo.token); - if (taskInfo.pictureInPictureParams != null - && taskInfo.pictureInPictureParams.isAutoEnterEnabled()) { - pipTask = taskInfo.token; - } - } else if (taskInfo != null - && taskInfo.topActivityType == ACTIVITY_TYPE_RECENTS) { - // This task is for recents, keep it on top. - t.setLayer(apps[i].leash, info.getChanges().size() * 3 - i); - recentsTask = taskInfo.token; - recentsTaskId = taskInfo.taskId; - } else if (taskInfo != null && taskInfo.topActivityType == ACTIVITY_TYPE_HOME) { - recentsTask = taskInfo.token; - recentsTaskId = taskInfo.taskId; - } - } - // Also make all the wallpapers opaque since we want the visible from the start - for (int i = wallpapers.length - 1; i >= 0; --i) { - t.setAlpha(wallpapers[i].leash, 1); - } - t.apply(); - mRecentsSession.setup(controller, info, finishedCallback, pausingTasks, pipTask, - recentsTask, recentsTaskId, leashMap, mToken, - (info.getFlags() & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0); - recents.onAnimationStart(mRecentsSession, apps, wallpapers, new Rect(0, 0, 0, 0), - new Rect()); + mRecentsSession.start(controller, recents, mToken, info, t, finishedCallback); } @Override public void mergeAnimation(IBinder transition, TransitionInfo info, SurfaceControl.Transaction t, IBinder mergeTarget, IRemoteTransitionFinishedCallback finishedCallback) { - if (mergeTarget.equals(mToken) && mRecentsSession.merge(info, t, recents)) { + if (mergeTarget.equals(mToken) && mRecentsSession.merge(info, t)) { try { finishedCallback.onTransitionFinished(null /* wct */, null /* sct */); } catch (RemoteException e) { Log.e(TAG, "Error merging transition.", e); } // commit taskAppeared after merge transition finished. - mRecentsSession.commitTasksAppearedIfNeeded(recents); + mRecentsSession.commitTasksAppearedIfNeeded(); } else { t.close(); info.releaseAllSurfaces(); @@ -152,6 +104,7 @@ public class RemoteTransitionCompat { */ @VisibleForTesting static class RecentsControllerWrap extends RecentsAnimationControllerCompat { + private RecentsAnimationListener mListener = null; private RecentsAnimationControllerCompat mWrapped = null; private IRemoteTransitionFinishedCallback mFinishCB = null; private ArrayList mPausingTasks = null; @@ -160,7 +113,7 @@ public class RemoteTransitionCompat { private int mRecentsTaskId = 0; private TransitionInfo mInfo = null; private ArrayList mOpeningLeashes = null; - private boolean mOpeningHome = false; + private boolean mOpeningSeparateHome = false; private ArrayMap mLeashMap = null; private PictureInPictureSurfaceTransaction mPipTransaction = null; private IBinder mTransition = null; @@ -168,34 +121,74 @@ public class RemoteTransitionCompat { private RemoteAnimationTarget[] mAppearedTargets; private boolean mWillFinishToHome = false; - void setup(RecentsAnimationControllerCompat wrapped, TransitionInfo info, - IRemoteTransitionFinishedCallback finishCB, - ArrayList pausingTasks, WindowContainerToken pipTask, - WindowContainerToken recentsTask, int recentsTaskId, ArrayMap leashMap, IBinder transition, boolean keyguardLocked) { + void start(RecentsAnimationControllerCompat wrapped, RecentsAnimationListener listener, + IBinder transition, TransitionInfo info, SurfaceControl.Transaction t, + IRemoteTransitionFinishedCallback finishedCallback) { if (mInfo != null) { throw new IllegalStateException("Trying to run a new recents animation while" + " recents is already active."); } + mListener = listener; mWrapped = wrapped; mInfo = info; - mFinishCB = finishCB; - mPausingTasks = pausingTasks; - mPipTask = pipTask; - mRecentsTask = recentsTask; - mRecentsTaskId = recentsTaskId; - mLeashMap = leashMap; + mFinishCB = finishedCallback; + mPausingTasks = new ArrayList<>(); + mPipTask = null; + mRecentsTask = null; + mRecentsTaskId = -1; + mLeashMap = new ArrayMap<>(); mTransition = transition; - mKeyguardLocked = keyguardLocked; + mKeyguardLocked = (info.getFlags() & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0; + + final RemoteAnimationTarget[] apps = + RemoteAnimationTargetCompat.wrapApps(info, t, mLeashMap); + final RemoteAnimationTarget[] wallpapers = + RemoteAnimationTargetCompat.wrapNonApps( + info, true /* wallpapers */, t, mLeashMap); + + // This transition is for opening recents, so recents is on-top. We want to draw + // the current going-away tasks on top of recents, though, so move them to front. + // Note that we divide up the "layer space" into 3 regions each the size of + // the change count. This way we can easily move changes into above/below/between + // while maintaining their relative ordering. + for (int i = apps.length - 1; i >= 0; --i) { + final ActivityManager.RunningTaskInfo taskInfo = apps[i].taskInfo; + if (apps[i].mode == MODE_CLOSING) { + t.setLayer(apps[i].leash, info.getChanges().size() * 3 - i); + if (taskInfo == null) { + continue; + } + // Add to front since we are iterating backwards. + mPausingTasks.add(0, taskInfo.token); + if (taskInfo.pictureInPictureParams != null + && taskInfo.pictureInPictureParams.isAutoEnterEnabled()) { + mPipTask = taskInfo.token; + } + } else if (taskInfo != null + && taskInfo.topActivityType == ACTIVITY_TYPE_RECENTS) { + // This task is for recents, keep it on top. + t.setLayer(apps[i].leash, info.getChanges().size() * 3 - i); + mRecentsTask = taskInfo.token; + mRecentsTaskId = taskInfo.taskId; + } else if (taskInfo != null && taskInfo.topActivityType == ACTIVITY_TYPE_HOME) { + mRecentsTask = taskInfo.token; + mRecentsTaskId = taskInfo.taskId; + } + } + // Also make all the wallpapers opaque since we want the visible from the start + for (int i = wallpapers.length - 1; i >= 0; --i) { + t.setAlpha(wallpapers[i].leash, 1); + } + t.apply(); + mListener.onAnimationStart(this, apps, wallpapers, new Rect(0, 0, 0, 0), new Rect()); } @SuppressLint("NewApi") - boolean merge(TransitionInfo info, SurfaceControl.Transaction t, - RecentsAnimationListener recents) { + boolean merge(TransitionInfo info, SurfaceControl.Transaction t) { SparseArray openingTasks = null; mAppearedTargets = null; - boolean cancelRecents = false; - boolean homeGoingAway = false; + boolean foundHomeOpening = false; + boolean foundRecentsClosing = false; boolean hasChangingApp = false; for (int i = info.getChanges().size() - 1; i >= 0; --i) { final TransitionInfo.Change change = info.getChanges().get(i); @@ -203,8 +196,8 @@ public class RemoteTransitionCompat { final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); if (taskInfo != null) { if (taskInfo.topActivityType == ACTIVITY_TYPE_HOME) { - // canceling recents animation - cancelRecents = true; + // This is usually a 3p launcher + foundHomeOpening = true; } if (openingTasks == null) { openingTasks = new SparseArray<>(); @@ -219,19 +212,18 @@ public class RemoteTransitionCompat { } else if (change.getMode() == TRANSIT_CLOSE || change.getMode() == TRANSIT_TO_BACK) { if (mRecentsTask.equals(change.getContainer())) { - homeGoingAway = true; + foundRecentsClosing = true; } } else if (change.getMode() == TRANSIT_CHANGE) { hasChangingApp = true; } } - if (hasChangingApp && homeGoingAway) { + if (hasChangingApp && foundRecentsClosing) { // This happens when a visible app is expanding (usually PiP). In this case, - // The transition probably has a special-purpose animation, so finish recents + // that transition probably has a special-purpose animation, so finish recents // now and let it do its animation (since recents is going to be occluded). - if (!recents.onSwitchToScreenshot(() -> { - finish(true /* toHome */, false /* userLeaveHint */); - })) { + if (!mListener.onSwitchToScreenshot( + () -> finish(true /* toHome */, false /* userLeaveHint */))) { Log.w(TAG, "Recents callback doesn't support support switching to screenshot" + ", there might be a flicker."); finish(true /* toHome */, false /* userLeaveHint */); @@ -240,7 +232,7 @@ public class RemoteTransitionCompat { } if (openingTasks == null) return false; int pauseMatches = 0; - if (!cancelRecents) { + if (!foundHomeOpening) { for (int i = 0; i < openingTasks.size(); ++i) { if (mPausingTasks.contains(openingTasks.valueAt(i).getContainer())) { ++pauseMatches; @@ -262,7 +254,7 @@ public class RemoteTransitionCompat { } final int layer = mInfo.getChanges().size() * 3; mOpeningLeashes = new ArrayList<>(); - mOpeningHome = cancelRecents; + mOpeningSeparateHome = foundHomeOpening; final RemoteAnimationTarget[] targets = new RemoteAnimationTarget[openingTasks.size()]; for (int i = 0; i < openingTasks.size(); ++i) { @@ -280,9 +272,9 @@ public class RemoteTransitionCompat { return true; } - private void commitTasksAppearedIfNeeded(RecentsAnimationListener recents) { + private void commitTasksAppearedIfNeeded() { if (mAppearedTargets != null) { - recents.onTasksAppeared(mAppearedTargets); + mListener.onTasksAppeared(mAppearedTargets); mAppearedTargets = null; } } @@ -352,7 +344,7 @@ public class RemoteTransitionCompat { if (!mKeyguardLocked && mRecentsTask != null) { wct.restoreTransientOrder(mRecentsTask); } - } else if (toHome && mOpeningHome && mPausingTasks != null) { + } else if (toHome && mOpeningSeparateHome && mPausingTasks != null) { // Special situaition where 3p launcher was changed during recents (this happens // during tapltests...). Here we get both "return to home" AND "home opening". // This is basically going home, but we have to restore recents order and also @@ -404,11 +396,13 @@ public class RemoteTransitionCompat { mInfo.releaseAllSurfaces(); // Reset all members. mWrapped = null; + mListener = null; mFinishCB = null; mPausingTasks = null; + mAppearedTargets = null; mInfo = null; mOpeningLeashes = null; - mOpeningHome = false; + mOpeningSeparateHome = false; mLeashMap = null; mTransition = null; }