Loading core/java/android/window/IWindowOrganizerController.aidl +1 −1 Original line number Diff line number Diff line Loading @@ -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. Loading libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +14 −7 Original line number Diff line number Diff line Loading @@ -709,15 +709,22 @@ public class Transitions implements RemoteCallable<Transitions> { 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(); Loading packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java +55 −23 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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. Loading Loading @@ -145,6 +144,18 @@ public class RemoteAnimationTargetCompat { public static RemoteAnimationTarget newTarget(TransitionInfo.Change change, int order, TransitionInfo info, SurfaceControl.Transaction t, @Nullable ArrayMap<SurfaceControl, SurfaceControl> 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; Loading @@ -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. Loading @@ -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; } Loading @@ -204,18 +212,7 @@ public class RemoteAnimationTargetCompat { */ public static RemoteAnimationTarget[] wrapApps(TransitionInfo info, SurfaceControl.Transaction t, ArrayMap<SurfaceControl, SurfaceControl> 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()); } /** Loading @@ -228,21 +225,56 @@ public class RemoteAnimationTargetCompat { */ public static RemoteAnimationTarget[] wrapNonApps(TransitionInfo info, boolean wallpapers, SurfaceControl.Transaction t, ArrayMap<SurfaceControl, SurfaceControl> 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<SurfaceControl, SurfaceControl> leashMap, BiPredicate<Change, TaskInfo> filter) { Predicate<Change> filter) { final ArrayList<RemoteAnimationTarget> 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<Change> { 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; } } } packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java +76 −82 Original line number Diff line number Diff line Loading @@ -72,71 +72,23 @@ public class RemoteTransitionCompat { public void startAnimation(IBinder transition, TransitionInfo info, SurfaceControl.Transaction t, IRemoteTransitionFinishedCallback finishedCallback) { final ArrayMap<SurfaceControl, SurfaceControl> 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<WindowContainerToken> 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(); Loading @@ -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<WindowContainerToken> mPausingTasks = null; Loading @@ -160,7 +113,7 @@ public class RemoteTransitionCompat { private int mRecentsTaskId = 0; private TransitionInfo mInfo = null; private ArrayList<SurfaceControl> mOpeningLeashes = null; private boolean mOpeningHome = false; private boolean mOpeningSeparateHome = false; private ArrayMap<SurfaceControl, SurfaceControl> mLeashMap = null; private PictureInPictureSurfaceTransaction mPipTransaction = null; private IBinder mTransition = null; Loading @@ -168,34 +121,74 @@ public class RemoteTransitionCompat { private RemoteAnimationTarget[] mAppearedTargets; private boolean mWillFinishToHome = false; void setup(RecentsAnimationControllerCompat wrapped, TransitionInfo info, IRemoteTransitionFinishedCallback finishCB, ArrayList<WindowContainerToken> pausingTasks, WindowContainerToken pipTask, WindowContainerToken recentsTask, int recentsTaskId, ArrayMap<SurfaceControl, SurfaceControl> 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<TransitionInfo.Change> 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); Loading @@ -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<>(); Loading @@ -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 */); Loading @@ -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; Loading @@ -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) { Loading @@ -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; } } Loading Loading @@ -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 Loading Loading @@ -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; } Loading Loading
core/java/android/window/IWindowOrganizerController.aidl +1 −1 Original line number Diff line number Diff line Loading @@ -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. Loading
libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +14 −7 Original line number Diff line number Diff line Loading @@ -709,15 +709,22 @@ public class Transitions implements RemoteCallable<Transitions> { 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(); Loading
packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java +55 −23 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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. Loading Loading @@ -145,6 +144,18 @@ public class RemoteAnimationTargetCompat { public static RemoteAnimationTarget newTarget(TransitionInfo.Change change, int order, TransitionInfo info, SurfaceControl.Transaction t, @Nullable ArrayMap<SurfaceControl, SurfaceControl> 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; Loading @@ -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. Loading @@ -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; } Loading @@ -204,18 +212,7 @@ public class RemoteAnimationTargetCompat { */ public static RemoteAnimationTarget[] wrapApps(TransitionInfo info, SurfaceControl.Transaction t, ArrayMap<SurfaceControl, SurfaceControl> 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()); } /** Loading @@ -228,21 +225,56 @@ public class RemoteAnimationTargetCompat { */ public static RemoteAnimationTarget[] wrapNonApps(TransitionInfo info, boolean wallpapers, SurfaceControl.Transaction t, ArrayMap<SurfaceControl, SurfaceControl> 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<SurfaceControl, SurfaceControl> leashMap, BiPredicate<Change, TaskInfo> filter) { Predicate<Change> filter) { final ArrayList<RemoteAnimationTarget> 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<Change> { 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; } } }
packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java +76 −82 Original line number Diff line number Diff line Loading @@ -72,71 +72,23 @@ public class RemoteTransitionCompat { public void startAnimation(IBinder transition, TransitionInfo info, SurfaceControl.Transaction t, IRemoteTransitionFinishedCallback finishedCallback) { final ArrayMap<SurfaceControl, SurfaceControl> 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<WindowContainerToken> 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(); Loading @@ -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<WindowContainerToken> mPausingTasks = null; Loading @@ -160,7 +113,7 @@ public class RemoteTransitionCompat { private int mRecentsTaskId = 0; private TransitionInfo mInfo = null; private ArrayList<SurfaceControl> mOpeningLeashes = null; private boolean mOpeningHome = false; private boolean mOpeningSeparateHome = false; private ArrayMap<SurfaceControl, SurfaceControl> mLeashMap = null; private PictureInPictureSurfaceTransaction mPipTransaction = null; private IBinder mTransition = null; Loading @@ -168,34 +121,74 @@ public class RemoteTransitionCompat { private RemoteAnimationTarget[] mAppearedTargets; private boolean mWillFinishToHome = false; void setup(RecentsAnimationControllerCompat wrapped, TransitionInfo info, IRemoteTransitionFinishedCallback finishCB, ArrayList<WindowContainerToken> pausingTasks, WindowContainerToken pipTask, WindowContainerToken recentsTask, int recentsTaskId, ArrayMap<SurfaceControl, SurfaceControl> 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<TransitionInfo.Change> 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); Loading @@ -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<>(); Loading @@ -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 */); Loading @@ -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; Loading @@ -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) { Loading @@ -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; } } Loading Loading @@ -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 Loading Loading @@ -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; } Loading