Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9621dce5 authored by Evan Rosky's avatar Evan Rosky
Browse files

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
parent 19ae59af
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -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.
+14 −7
Original line number Diff line number Diff line
@@ -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();
+55 −23
Original line number Diff line number Diff line
@@ -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<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;
@@ -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<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());
    }

    /**
@@ -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;
        }
    }
}
+76 −82
Original line number Diff line number Diff line
@@ -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();
@@ -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;
@@ -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;
@@ -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);
@@ -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;
        }