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; }