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

Commit 3fef7ff6 authored by Evan Rosky's avatar Evan Rosky
Browse files

Initial implementation of merge-able transitions

This adds a couple things to support "merging" of transition
animations in shell.

1. Moves the finishing surface operations into a transaction that
   gets sent to shell. This does a couple things: it complies with
   the contract we have where during a transition, only the player
   should touch relevant surfaces; and, it makes it so that the
   player can merge the transaction with other ones.

2. Keeps a queue of "pending" transition animations in shell and
   either merges them or runs them serially.

3. Any transition that becomes ready while another is playing will
   first be sent to the playing handler to give it a chance to
   "merge" the incoming transition.

There are 3 expected responses to overlapping transition animations:
1. Cancel the currently playing transition and immediately start
   the incoming one. This can be achieved by having the currently
   playing transition cancel itself (ie. immediately finishing) when
   a merge request comes in. Then the rest of the shell transition
   logic will immediately start the next transition.

2. Queue up the incoming transition to play once the current one
   finishes animating. This is basically the default as long as
   the current transition simply rejects/ignores merge requests

3. Merge the incoming transition. This is achieved when the
   currently playing transition actually does some special logic
   to handle the incoming transition. It then calls the finish
   callback for the incoming transition (before it finishes its
   own animation) to indicate that it has been merged (or "consumed").
   Basically, any time the finish callback for a transition is
   called before a pre-ceding transition, that transition is assumed
   to have been merged.

Bug: 183994113
Test: atest ShellTransitionTests
Change-Id: I3cb54e221d57642306ddf15827c21d8881b014d0
parent 79613628
Loading
Loading
Loading
Loading
+17 −1
Original line number Diff line number Diff line
@@ -40,7 +40,23 @@ oneway interface IRemoteTransition {
    /**
     * Starts a transition animation. Once complete, the implementation should call
     * `finishCallback`.
     *
     * @param token An identifier for the transition that should be animated.
     */
    void startAnimation(in IBinder token, in TransitionInfo info, in SurfaceControl.Transaction t,
            in IRemoteTransitionFinishedCallback finishCallback);

    /**
     * Attempts to merge a transition animation into the animation that is currently
     * being played by this remote. If merge is not possible/supported, this should be a no-op.
     * If it *is* merged, the implementation should call `finishCallback` immediately.
     *
     * @param transition An identifier for the transition that wants to be merged.
     * @param mergeTarget The transition that is currently being animated by this remote.
     *                    If it can be merged, call `finishCallback`; otherwise, do
     *                    nothing.
     */
    void startAnimation(in TransitionInfo info, in SurfaceControl.Transaction t,
    void mergeAnimation(in IBinder transition, in TransitionInfo info,
            in SurfaceControl.Transaction t, in IBinder mergeTarget,
            in IRemoteTransitionFinishedCallback finishCallback);
}
+4 −1
Original line number Diff line number Diff line
@@ -47,9 +47,12 @@ oneway interface ITransitionPlayer {
     * @param transitionToken An identifying token for the transition that is now ready to animate.
     * @param info A collection of all the changes encapsulated by this transition.
     * @param t A surface transaction containing the surface state prior to animating.
     * @param finishT A surface transaction that will reset parenting/layering and generally put
     *                surfaces into their final (post-transition) state. Apply this after playing
     *                the animation but before calling finish.
     */
    void onTransitionReady(in IBinder transitionToken, in TransitionInfo info,
            in SurfaceControl.Transaction t);
            in SurfaceControl.Transaction t, in SurfaceControl.Transaction finishT);

    /**
     * Called when something in WMCore requires a transition to play -- for example when an Activity
+0 −2
Original line number Diff line number Diff line
@@ -129,6 +129,4 @@ android_library {
    ],
    kotlincflags: ["-Xjvm-default=enable"],
    manifest: "AndroidManifest.xml",

    min_sdk_version: "26",
}
+23 −2
Original line number Diff line number Diff line
@@ -82,17 +82,38 @@ public class OneShotRemoteHandler implements Transitions.TransitionHandler {
            if (mRemote.asBinder() != null) {
                mRemote.asBinder().linkToDeath(remoteDied, 0 /* flags */);
            }
            mRemote.startAnimation(info, t, cb);
            mRemote.startAnimation(transition, info, t, cb);
        } catch (RemoteException e) {
            Log.e(Transitions.TAG, "Error running remote transition.", e);
            if (mRemote.asBinder() != null) {
                mRemote.asBinder().unlinkToDeath(remoteDied, 0 /* flags */);
            }
            Log.e(Transitions.TAG, "Error running remote transition.", e);
            finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
        }
        return true;
    }

    @Override
    public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
            @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
            @NonNull Transitions.TransitionFinishCallback finishCallback) {
        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Using registered One-shot remote"
                + " transition %s for %s.", mRemote, transition);

        IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() {
            @Override
            public void onTransitionFinished(WindowContainerTransaction wct) {
                mMainExecutor.execute(
                        () -> finishCallback.onTransitionFinished(wct, null /* wctCB */));
            }
        };
        try {
            mRemote.mergeAnimation(transition, info, t, mergeTarget, cb);
        } catch (RemoteException e) {
            Log.e(Transitions.TAG, "Error merging remote transition.", e);
        }
    }

    @Override
    @Nullable
    public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
+52 −14
Original line number Diff line number Diff line
@@ -16,8 +16,6 @@

package com.android.wm.shell.transition;

import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.IBinder;
@@ -52,7 +50,7 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
    private final ShellExecutor mMainExecutor;

    /** Includes remotes explicitly requested by, eg, ActivityOptions */
    private final ArrayMap<IBinder, IRemoteTransition> mPendingRemotes = new ArrayMap<>();
    private final ArrayMap<IBinder, IRemoteTransition> mRequestedRemotes = new ArrayMap<>();

    /** Ordered by specificity. Last filters will be checked first */
    private final ArrayList<Pair<TransitionFilter, IRemoteTransition>> mFilters =
@@ -63,9 +61,7 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
                @Override
                @BinderThread
                public void binderDied() {
                    mMainExecutor.execute(() -> {
                        mFilters.clear();
                    });
                    mMainExecutor.execute(() -> mFilters.clear());
                }
            };

@@ -96,11 +92,16 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
        }
    }

    @Override
    public void onTransitionMerged(@NonNull IBinder transition) {
        mRequestedRemotes.remove(transition);
    }

    @Override
    public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
            @NonNull SurfaceControl.Transaction t,
            @NonNull Transitions.TransitionFinishCallback finishCallback) {
        IRemoteTransition pendingRemote = mPendingRemotes.remove(transition);
        IRemoteTransition pendingRemote = mRequestedRemotes.get(transition);
        if (pendingRemote == null) {
            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition %s doesn't have "
                    + "explicit remote, search filters for match for %s", transition, info);
@@ -110,6 +111,8 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
                        mFilters.get(i));
                if (mFilters.get(i).first.matches(info)) {
                    pendingRemote = mFilters.get(i).second;
                    // Add to requested list so that it can be found for merge requests.
                    mRequestedRemotes.put(transition, pendingRemote);
                    break;
                }
            }
@@ -122,8 +125,10 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
        final IRemoteTransition remote = pendingRemote;
        final IBinder.DeathRecipient remoteDied = () -> {
            Log.e(Transitions.TAG, "Remote transition died, finishing");
            mMainExecutor.execute(
                    () -> finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */));
            mMainExecutor.execute(() -> {
                mRequestedRemotes.remove(transition);
                finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
            });
        };
        IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() {
            @Override
@@ -131,33 +136,66 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler {
                if (remote.asBinder() != null) {
                    remote.asBinder().unlinkToDeath(remoteDied, 0 /* flags */);
                }
                mMainExecutor.execute(
                        () -> finishCallback.onTransitionFinished(wct, null /* wctCB */));
                mMainExecutor.execute(() -> {
                    mRequestedRemotes.remove(transition);
                    finishCallback.onTransitionFinished(wct, null /* wctCB */);
                });
            }
        };
        try {
            if (remote.asBinder() != null) {
                remote.asBinder().linkToDeath(remoteDied, 0 /* flags */);
            }
            remote.startAnimation(info, t, cb);
            remote.startAnimation(transition, info, t, cb);
        } catch (RemoteException e) {
            Log.e(Transitions.TAG, "Error running remote transition.", e);
            if (remote.asBinder() != null) {
                remote.asBinder().unlinkToDeath(remoteDied, 0 /* flags */);
            }
            Log.e(Transitions.TAG, "Error running remote transition.", e);
            mRequestedRemotes.remove(transition);
            mMainExecutor.execute(
                    () -> finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */));
        }
        return true;
    }

    @Override
    public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
            @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
            @NonNull Transitions.TransitionFinishCallback finishCallback) {
        final IRemoteTransition remote = mRequestedRemotes.get(mergeTarget);
        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Attempt merge %s into %s",
                transition, remote);
        if (remote == null) return;

        IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() {
            @Override
            public void onTransitionFinished(WindowContainerTransaction wct) {
                mMainExecutor.execute(() -> {
                    if (!mRequestedRemotes.containsKey(mergeTarget)) {
                        Log.e(TAG, "Merged transition finished after it's mergeTarget (the "
                                + "transition it was supposed to merge into). This usually means "
                                + "that the mergeTarget's RemoteTransition impl erroneously "
                                + "accepted/ran the merge request after finishing the mergeTarget");
                    }
                    finishCallback.onTransitionFinished(wct, null /* wctCB */);
                });
            }
        };
        try {
            remote.mergeAnimation(transition, info, t, mergeTarget, cb);
        } catch (RemoteException e) {
            Log.e(Transitions.TAG, "Error attempting to merge remote transition.", e);
        }
    }

    @Override
    @Nullable
    public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
            @Nullable TransitionRequestInfo request) {
        IRemoteTransition remote = request.getRemoteTransition();
        if (remote == null) return null;
        mPendingRemotes.put(transition, remote);
        mRequestedRemotes.put(transition, remote);
        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "RemoteTransition directly requested"
                + " for %s: %s", transition, remote);
        return new WindowContainerTransaction();
Loading