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

Commit e0d73107 authored by Evan Rosky's avatar Evan Rosky Committed by Android (Google) Code Review
Browse files

Merge "Add support for multiple animation tracks" into udc-dev

parents b7abd253 d780bfaa
Loading
Loading
Loading
Loading
+32 −3
Original line number Diff line number Diff line
@@ -152,8 +152,14 @@ public final class TransitionInfo implements Parcelable {
    /** The task became the top-most task even if it didn't change visibility. */
    public static final int FLAG_MOVED_TO_TOP = 1 << 20;

    /**
     * This transition must be the only transition when it starts (ie. it must wait for all other
     * transition animations to finish).
     */
    public static final int FLAG_SYNC = 1 << 21;

    /** The first unused bit. This can be used by remotes to attach custom flags to this change. */
    public static final int FLAG_FIRST_CUSTOM = 1 << 21;
    public static final int FLAG_FIRST_CUSTOM = 1 << 22;

    /** The change belongs to a window that won't contain activities. */
    public static final int FLAGS_IS_NON_APP_WINDOW =
@@ -183,12 +189,14 @@ public final class TransitionInfo implements Parcelable {
            FLAG_NO_ANIMATION,
            FLAG_TASK_LAUNCHING_BEHIND,
            FLAG_MOVED_TO_TOP,
            FLAG_SYNC,
            FLAG_FIRST_CUSTOM
    })
    public @interface ChangeFlags {}

    private final @TransitionType int mType;
    private final @TransitionFlags int mFlags;
    private @TransitionFlags int mFlags;
    private int mTrack = 0;
    private final ArrayList<Change> mChanges = new ArrayList<>();
    private final ArrayList<Root> mRoots = new ArrayList<>();

@@ -210,6 +218,7 @@ public final class TransitionInfo implements Parcelable {
        in.readTypedList(mRoots, Root.CREATOR);
        mOptions = in.readTypedObject(AnimationOptions.CREATOR);
        mDebugId = in.readInt();
        mTrack = in.readInt();
    }

    @Override
@@ -221,6 +230,7 @@ public final class TransitionInfo implements Parcelable {
        dest.writeTypedList(mRoots, flags);
        dest.writeTypedObject(mOptions, flags);
        dest.writeInt(mDebugId);
        dest.writeInt(mTrack);
    }

    @NonNull
@@ -262,6 +272,10 @@ public final class TransitionInfo implements Parcelable {
        return mType;
    }

    public void setFlags(int flags) {
        mFlags = flags;
    }

    public int getFlags() {
        return mFlags;
    }
@@ -356,6 +370,16 @@ public final class TransitionInfo implements Parcelable {
        return (mFlags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0;
    }

    /** Gets which animation track this transition should run on. */
    public int getTrack() {
        return mTrack;
    }

    /** Sets which animation track this transition should run on. */
    public void setTrack(int track) {
        mTrack = track;
    }

    /**
     * Set an arbitrary "debug" id for this info. This id will not be used for any "real work",
     * it is just for debugging and logging.
@@ -373,7 +397,8 @@ public final class TransitionInfo implements Parcelable {
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("{id=").append(mDebugId).append(" t=").append(transitTypeToString(mType))
                .append(" f=0x").append(Integer.toHexString(mFlags)).append(" r=[");
                .append(" f=0x").append(Integer.toHexString(mFlags)).append(" trk=").append(mTrack)
                .append(" r=[");
        for (int i = 0; i < mRoots.size(); ++i) {
            if (i > 0) {
                sb.append(',');
@@ -461,6 +486,9 @@ public final class TransitionInfo implements Parcelable {
        if ((flags & FLAG_TASK_LAUNCHING_BEHIND) != 0) {
            sb.append((sb.length() == 0 ? "" : "|") + "TASK_LAUNCHING_BEHIND");
        }
        if ((flags & FLAG_SYNC) != 0) {
            sb.append((sb.length() == 0 ? "" : "|") + "SYNC");
        }
        if ((flags & FLAG_FIRST_CUSTOM) != 0) {
            sb.append(sb.length() == 0 ? "" : "|").append("FIRST_CUSTOM");
        }
@@ -532,6 +560,7 @@ public final class TransitionInfo implements Parcelable {
     */
    public TransitionInfo localRemoteCopy() {
        final TransitionInfo out = new TransitionInfo(mType, mFlags);
        out.mTrack = mTrack;
        out.mDebugId = mDebugId;
        for (int i = 0; i < mChanges.size(); ++i) {
            out.mChanges.add(mChanges.get(i).localRemoteCopy());
+1 −0
Original line number Diff line number Diff line
@@ -235,6 +235,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler,
    private TransitionInfo subCopy(@NonNull TransitionInfo info,
            @WindowManager.TransitionType int newType, boolean withChanges) {
        final TransitionInfo out = new TransitionInfo(newType, withChanges ? info.getFlags() : 0);
        out.setTrack(info.getTrack());
        out.setDebugId(info.getDebugId());
        if (withChanges) {
            for (int i = 0; i < info.getChanges().size(); ++i) {
+215 −105

File changed.

Preview size limit exceeded, changes collapsed.

+2 −3
Original line number Diff line number Diff line
@@ -48,9 +48,8 @@ public class TestShellExecutor implements ShellExecutor {
    }

    public void flushAll() {
        for (Runnable r : mRunnables) {
            r.run();
        while (!mRunnables.isEmpty()) {
            mRunnables.remove(0).run();
        }
        mRunnables.clear();
    }
}
+238 −7
Original line number Diff line number Diff line
@@ -30,10 +30,12 @@ import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FIRST_CUSTOM;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_SLEEP;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.window.TransitionInfo.FLAG_DISPLAY_HAS_ALERT_WINDOWS;
import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
import static android.window.TransitionInfo.FLAG_SYNC;
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;

import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -63,6 +65,7 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.util.ArraySet;
import android.util.Pair;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.WindowManager;
@@ -587,7 +590,8 @@ public class ShellTransitionTests extends ShellTestCase {
        requestStartTransition(transitions, transitTokenNotReady);

        mDefaultHandler.setSimulateMerge(true);
        mDefaultHandler.mFinishes.get(0).onTransitionFinished(null /* wct */, null /* wctCB */);
        mDefaultHandler.mFinishes.get(0).second.onTransitionFinished(
                null /* wct */, null /* wctCB */);

        // Make sure that the non-ready transition is not merged.
        assertEquals(0, mDefaultHandler.mergeCount());
@@ -1059,6 +1063,223 @@ public class ShellTransitionTests extends ShellTestCase {
        assertEquals(1, mDefaultHandler.activeCount());
    }

    @Test
    public void testMultipleTracks() {
        Transitions transitions = createTestTransitions();
        transitions.replaceDefaultHandlerForTest(mDefaultHandler);
        TestTransitionHandler alwaysMergeHandler = new TestTransitionHandler();
        alwaysMergeHandler.setSimulateMerge(true);

        final boolean[] becameIdle = new boolean[]{false};

        final WindowContainerTransaction emptyWCT = new WindowContainerTransaction();
        final SurfaceControl.Transaction mockSCT = mock(SurfaceControl.Transaction.class);

        // Make this always merge so we can ensure that it does NOT get a merge-attempt for a
        // different track.
        IBinder transitA = transitions.startTransition(TRANSIT_OPEN, emptyWCT, alwaysMergeHandler);
        // start tracking idle
        transitions.runOnIdle(() -> becameIdle[0] = true);

        IBinder transitB = transitions.startTransition(TRANSIT_OPEN, emptyWCT, mDefaultHandler);
        IBinder transitC = transitions.startTransition(TRANSIT_CLOSE, emptyWCT, mDefaultHandler);

        TransitionInfo infoA = new TransitionInfoBuilder(TRANSIT_OPEN)
                .addChange(TRANSIT_OPEN).build();
        infoA.setTrack(0);
        TransitionInfo infoB = new TransitionInfoBuilder(TRANSIT_OPEN)
                .addChange(TRANSIT_OPEN).build();
        infoB.setTrack(1);
        TransitionInfo infoC = new TransitionInfoBuilder(TRANSIT_CLOSE)
                .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
        infoC.setTrack(1);

        transitions.onTransitionReady(transitA, infoA, mockSCT, mockSCT);
        assertEquals(1, alwaysMergeHandler.activeCount());
        transitions.onTransitionReady(transitB, infoB, mockSCT, mockSCT);
        // should now be running in parallel
        assertEquals(1, mDefaultHandler.activeCount());
        assertEquals(1, alwaysMergeHandler.activeCount());
        // make sure we didn't try to merge into a different track.
        assertEquals(0, alwaysMergeHandler.mergeCount());

        // This should be queued-up since it is on track 1 (same as B)
        transitions.onTransitionReady(transitC, infoC, mockSCT, mockSCT);
        assertEquals(1, mDefaultHandler.activeCount());
        assertEquals(1, alwaysMergeHandler.activeCount());

        // Now finish B and make sure C starts
        mDefaultHandler.finishOne();
        mMainExecutor.flushAll();

        // Now C and A running in parallel
        assertEquals(1, mDefaultHandler.activeCount());
        assertEquals(1, alwaysMergeHandler.activeCount());
        assertEquals(0, alwaysMergeHandler.mergeCount());

        // Finish A
        alwaysMergeHandler.finishOne();
        mMainExecutor.flushAll();

        // C still running
        assertEquals(0, alwaysMergeHandler.activeCount());
        assertEquals(1, mDefaultHandler.activeCount());
        assertFalse(becameIdle[0]);

        mDefaultHandler.finishOne();
        mMainExecutor.flushAll();

        assertEquals(0, mDefaultHandler.activeCount());
        assertTrue(becameIdle[0]);
    }

    @Test
    public void testSyncMultipleTracks() {
        Transitions transitions = createTestTransitions();
        transitions.replaceDefaultHandlerForTest(mDefaultHandler);
        TestTransitionHandler secondHandler = new TestTransitionHandler();

        // Disable the forced early-sync-finish so that we can test the ordering mechanics.
        transitions.setDisableForceSyncForTest(true);
        mDefaultHandler.mFinishOnSync = false;
        secondHandler.mFinishOnSync = false;

        final WindowContainerTransaction emptyWCT = new WindowContainerTransaction();
        final SurfaceControl.Transaction mockSCT = mock(SurfaceControl.Transaction.class);

        // Make this always merge so we can ensure that it does NOT get a merge-attempt for a
        // different track.
        IBinder transitA = transitions.startTransition(TRANSIT_OPEN, emptyWCT, mDefaultHandler);
        IBinder transitB = transitions.startTransition(TRANSIT_OPEN, emptyWCT, secondHandler);
        IBinder transitC = transitions.startTransition(TRANSIT_CLOSE, emptyWCT, secondHandler);
        IBinder transitSync = transitions.startTransition(TRANSIT_CLOSE, emptyWCT, mDefaultHandler);
        IBinder transitD = transitions.startTransition(TRANSIT_OPEN, emptyWCT, secondHandler);
        IBinder transitE = transitions.startTransition(TRANSIT_OPEN, emptyWCT, mDefaultHandler);

        TransitionInfo infoA = new TransitionInfoBuilder(TRANSIT_OPEN)
                .addChange(TRANSIT_OPEN).build();
        infoA.setTrack(0);
        TransitionInfo infoB = new TransitionInfoBuilder(TRANSIT_OPEN)
                .addChange(TRANSIT_OPEN).build();
        infoB.setTrack(1);
        TransitionInfo infoC = new TransitionInfoBuilder(TRANSIT_CLOSE)
                .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
        infoC.setTrack(1);
        TransitionInfo infoSync = new TransitionInfoBuilder(TRANSIT_CLOSE)
                .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
        infoSync.setTrack(0);
        infoSync.setFlags(FLAG_SYNC);
        TransitionInfo infoD = new TransitionInfoBuilder(TRANSIT_OPEN)
                .addChange(TRANSIT_OPEN).build();
        infoD.setTrack(1);
        TransitionInfo infoE = new TransitionInfoBuilder(TRANSIT_OPEN)
                .addChange(TRANSIT_OPEN).build();
        infoE.setTrack(0);

        // Start A B and C where A is track 0, B and C are track 1 (C should be queued)
        transitions.onTransitionReady(transitA, infoA, mockSCT, mockSCT);
        transitions.onTransitionReady(transitB, infoB, mockSCT, mockSCT);
        transitions.onTransitionReady(transitC, infoC, mockSCT, mockSCT);
        // should now be running in parallel (with one queued)
        assertEquals(1, mDefaultHandler.activeCount());
        assertEquals(1, secondHandler.activeCount());

        // Make the sync ready and the following (D, E) ready.
        transitions.onTransitionReady(transitSync, infoSync, mockSCT, mockSCT);
        transitions.onTransitionReady(transitD, infoD, mockSCT, mockSCT);
        transitions.onTransitionReady(transitE, infoE, mockSCT, mockSCT);

        // nothing should have happened yet since the sync is queued and blocking everything.
        assertEquals(1, mDefaultHandler.activeCount());
        assertEquals(1, secondHandler.activeCount());

        // Finish A (which is track 0 like the sync).
        mDefaultHandler.finishOne();
        mMainExecutor.flushAll();

        // Even though the sync is on track 0 and track 0 became idle, it should NOT be started yet
        // because it must wait for everything. Additionally, D/E shouldn't start yet either.
        assertEquals(0, mDefaultHandler.activeCount());
        assertEquals(1, secondHandler.activeCount());

        // Now finish B and C -- this should then allow the sync to start and D to run (in parallel)
        secondHandler.finishOne();
        secondHandler.finishOne();
        mMainExecutor.flushAll();

        // Now the sync and D (on track 1) should be running
        assertEquals(1, mDefaultHandler.activeCount());
        assertEquals(1, secondHandler.activeCount());

        // finish the sync. track 0 still has E
        mDefaultHandler.finishOne();
        mMainExecutor.flushAll();
        assertEquals(1, mDefaultHandler.activeCount());

        mDefaultHandler.finishOne();
        secondHandler.finishOne();
        mMainExecutor.flushAll();

        assertEquals(0, mDefaultHandler.activeCount());
        assertEquals(0, secondHandler.activeCount());
    }

    @Test
    public void testForceSyncTracks() {
        Transitions transitions = createTestTransitions();
        transitions.replaceDefaultHandlerForTest(mDefaultHandler);
        TestTransitionHandler secondHandler = new TestTransitionHandler();

        final WindowContainerTransaction emptyWCT = new WindowContainerTransaction();
        final SurfaceControl.Transaction mockSCT = mock(SurfaceControl.Transaction.class);

        // Make this always merge so we can ensure that it does NOT get a merge-attempt for a
        // different track.
        IBinder transitA = transitions.startTransition(TRANSIT_OPEN, emptyWCT, mDefaultHandler);
        IBinder transitB = transitions.startTransition(TRANSIT_OPEN, emptyWCT, mDefaultHandler);
        IBinder transitC = transitions.startTransition(TRANSIT_CLOSE, emptyWCT, secondHandler);
        IBinder transitD = transitions.startTransition(TRANSIT_OPEN, emptyWCT, secondHandler);
        IBinder transitSync = transitions.startTransition(TRANSIT_CLOSE, emptyWCT, mDefaultHandler);

        TransitionInfo infoA = new TransitionInfoBuilder(TRANSIT_OPEN)
                .addChange(TRANSIT_OPEN).build();
        infoA.setTrack(0);
        TransitionInfo infoB = new TransitionInfoBuilder(TRANSIT_OPEN)
                .addChange(TRANSIT_OPEN).build();
        infoB.setTrack(0);
        TransitionInfo infoC = new TransitionInfoBuilder(TRANSIT_CLOSE)
                .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
        infoC.setTrack(1);
        TransitionInfo infoD = new TransitionInfoBuilder(TRANSIT_OPEN)
                .addChange(TRANSIT_OPEN).build();
        infoD.setTrack(1);
        TransitionInfo infoSync = new TransitionInfoBuilder(TRANSIT_CLOSE)
                .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
        infoSync.setTrack(0);
        infoSync.setFlags(FLAG_SYNC);

        transitions.onTransitionReady(transitA, infoA, mockSCT, mockSCT);
        transitions.onTransitionReady(transitB, infoB, mockSCT, mockSCT);
        transitions.onTransitionReady(transitC, infoC, mockSCT, mockSCT);
        transitions.onTransitionReady(transitD, infoD, mockSCT, mockSCT);
        // should now be running in parallel (with one queued in each)
        assertEquals(1, mDefaultHandler.activeCount());
        assertEquals(1, secondHandler.activeCount());

        // Make the sync ready.
        transitions.onTransitionReady(transitSync, infoSync, mockSCT, mockSCT);
        mMainExecutor.flushAll();

        // Everything should be forced-finish now except the sync
        assertEquals(1, mDefaultHandler.activeCount());
        assertEquals(0, secondHandler.activeCount());

        mDefaultHandler.finishOne();
        mMainExecutor.flushAll();

        assertEquals(0, mDefaultHandler.activeCount());
    }

    class ChangeBuilder {
        final TransitionInfo.Change mChange;

@@ -1097,9 +1318,11 @@ public class ShellTransitionTests extends ShellTestCase {
    }

    class TestTransitionHandler implements Transitions.TransitionHandler {
        ArrayList<Transitions.TransitionFinishCallback> mFinishes = new ArrayList<>();
        ArrayList<Pair<IBinder, Transitions.TransitionFinishCallback>> mFinishes =
                new ArrayList<>();
        final ArrayList<IBinder> mMerged = new ArrayList<>();
        boolean mSimulateMerge = false;
        boolean mFinishOnSync = true;
        final ArraySet<IBinder> mShouldMerge = new ArraySet<>();

        @Override
@@ -1107,7 +1330,7 @@ public class ShellTransitionTests extends ShellTestCase {
                @NonNull SurfaceControl.Transaction startTransaction,
                @NonNull SurfaceControl.Transaction finishTransaction,
                @NonNull Transitions.TransitionFinishCallback finishCallback) {
            mFinishes.add(finishCallback);
            mFinishes.add(new Pair<>(transition, finishCallback));
            return true;
        }

@@ -1115,6 +1338,13 @@ public class ShellTransitionTests extends ShellTestCase {
        public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
                @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
                @NonNull Transitions.TransitionFinishCallback finishCallback) {
            if (mFinishOnSync && info.getType() == TRANSIT_SLEEP) {
                for (int i = 0; i < mFinishes.size(); ++i) {
                    if (mFinishes.get(i).first != mergeTarget) continue;
                    mFinishes.remove(i).second.onTransitionFinished(null, null);
                    return;
                }
            }
            if (!(mSimulateMerge || mShouldMerge.contains(transition))) return;
            mMerged.add(transition);
            finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
@@ -1136,18 +1366,19 @@ public class ShellTransitionTests extends ShellTestCase {
        }

        void finishAll() {
            final ArrayList<Transitions.TransitionFinishCallback> finishes = mFinishes;
            final ArrayList<Pair<IBinder, Transitions.TransitionFinishCallback>> finishes =
                    mFinishes;
            mFinishes = new ArrayList<>();
            for (int i = finishes.size() - 1; i >= 0; --i) {
                finishes.get(i).onTransitionFinished(null /* wct */, null /* wctCB */);
                finishes.get(i).second.onTransitionFinished(null /* wct */, null /* wctCB */);
            }
            mShouldMerge.clear();
        }

        void finishOne() {
            Transitions.TransitionFinishCallback fin = mFinishes.remove(0);
            Pair<IBinder, Transitions.TransitionFinishCallback> fin = mFinishes.remove(0);
            mMerged.clear();
            fin.onTransitionFinished(null /* wct */, null /* wctCB */);
            fin.second.onTransitionFinished(null /* wct */, null /* wctCB */);
        }

        int activeCount() {