Loading libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java +8 −6 Original line number Diff line number Diff line Loading @@ -106,12 +106,14 @@ public class BubbleTransitions { @NonNull final Context mContext; @NonNull final BubbleAppInfoProvider mAppInfoProvider; @VisibleForTesting // Map of a launch cookie (used to start an activity) to the associated transition handler private final Map<IBinder, TransitionHandler> mPendingEnterTransitions = final Map<IBinder, TransitionHandler> mPendingEnterTransitions = new HashMap<>(); @VisibleForTesting // Map of a running transition token to the associated transition handler private final Map<IBinder, TransitionHandler> mEnterTransitions = final Map<IBinder, TransitionHandler> mEnterTransitions = new HashMap<>(); private BubbleController mBubbleController; Loading Loading @@ -229,13 +231,13 @@ public class BubbleTransitions { /** * Starts a new launch or convert transition to show the given bubble. */ public void startLaunchIntoOrConvertToBubble(Bubble bubble, public BubbleTransition startLaunchIntoOrConvertToBubble(Bubble bubble, BubbleExpandedViewManager expandedViewManager, BubbleTaskViewFactory factory, BubblePositioner positioner, BubbleStackView stackView, BubbleBarLayerView layerView, BubbleIconFactory iconFactory, boolean inflateSync, @Nullable BubbleBarLocation bubbleBarLocation) { new LaunchOrConvertToBubble(bubble, mContext, expandedViewManager, factory, positioner, stackView, layerView, iconFactory, inflateSync, bubbleBarLocation); return new LaunchOrConvertToBubble(bubble, mContext, expandedViewManager, factory, positioner, stackView, layerView, iconFactory, inflateSync, bubbleBarLocation); } /** Loading Loading @@ -936,7 +938,7 @@ public class BubbleTransitions { mFinishT = finishTransaction; mTaskLeash = chg.getLeash(); mSnapshot = chg.getSnapshot(); mPlayConvertTaskAnimation = !isOpeningMode(chg.getMode()); mPlayConvertTaskAnimation = !isOpeningMode(chg.getMode()) && mSnapshot != null; found = true; } else { // In core-initiated launches, the transition is of an OPEN type, and we need to Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java +175 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.wm.shell.bubbles; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_FRONT; import static com.android.window.flags.Flags.FLAG_EXCLUDE_TASK_FROM_RECENTS; Loading @@ -43,10 +44,12 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.ActivityManager; import android.content.Intent; import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; import android.os.IBinder; import android.os.UserHandle; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.view.SurfaceControl; Loading @@ -71,6 +74,7 @@ import com.android.wm.shell.bubbles.bar.BubbleBarLayerView; import com.android.wm.shell.common.HomeIntentProvider; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.shared.bubbles.BubbleBarLocation; import com.android.wm.shell.taskview.TaskView; import com.android.wm.shell.taskview.TaskViewRepository; import com.android.wm.shell.taskview.TaskViewTaskController; Loading Loading @@ -126,6 +130,8 @@ public class BubbleTransitionsTest extends ShellTestCase { private HomeIntentProvider mHomeIntentProvider; @Mock private ShellTaskOrganizer mTaskOrganizer; @Mock private BubbleController mBubbleController; private TaskViewTransitions mTaskViewTransitions; private TaskViewRepository mRepository; Loading @@ -144,6 +150,7 @@ public class BubbleTransitionsTest extends ShellTestCase { mBubbleTransitions = new BubbleTransitions(mContext, mTransitions, mTaskOrganizer, mRepository, mBubbleData, mTaskViewTransitions, new PackageManagerBubbleAppInfoProvider()); mBubbleTransitions.setBubbleController(mBubbleController); mTaskViewFactory = () -> { TaskViewTaskController taskViewTaskController = new TaskViewTaskController( mContext, mTaskOrganizer, mTaskViewTransitions, mSyncQueue); Loading @@ -169,6 +176,13 @@ public class BubbleTransitionsTest extends ShellTestCase { return taskInfo; } private ActivityManager.RunningTaskInfo setupAppBubble() { when(mBubble.isApp()).thenReturn(true); when(mBubble.getIntent()).thenReturn(new Intent()); when(mBubble.getUser()).thenReturn(new UserHandle(0)); return setupBubble(); } private TransitionInfo setupFullscreenTaskTransition(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl taskLeash, SurfaceControl snapshot) { final TransitionInfo info = new TransitionInfo(TRANSIT_CONVERT_TO_BUBBLE, 0); Loading @@ -182,6 +196,23 @@ public class BubbleTransitionsTest extends ShellTestCase { return info; } private TransitionInfo setupConvertTransition(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl taskLeash, SurfaceControl snapshot, IBinder launchCookieBinder) { final TransitionInfo info = new TransitionInfo(TRANSIT_OPEN, 0); final TransitionInfo.Change chg = new TransitionInfo.Change(taskInfo.token, taskLeash); chg.setTaskInfo(taskInfo); chg.setMode(TRANSIT_CHANGE); chg.setStartAbsBounds(new Rect(0, 0, FULLSCREEN_TASK_WIDTH, FULLSCREEN_TASK_HEIGHT)); if (snapshot != null) { chg.setSnapshot(snapshot, /* luma= */ 0f); } // Add the launch cookie to the task info taskInfo.launchCookies.add(launchCookieBinder); info.addChange(chg); info.addRoot(new TransitionInfo.Root(0, mock(SurfaceControl.class), 0, 0)); return info; } private WindowContainerToken createMockToken() { final IWindowContainerToken itoken = mock(IWindowContainerToken.class); final IBinder asBinder = mock(IBinder.class); Loading Loading @@ -672,4 +703,148 @@ public class BubbleTransitionsTest extends ShellTestCase { mBubbleTransitions.notifyUnfoldTransitionFinished(unfoldTransition); assertThat(mTaskViewTransitions.hasPending()).isFalse(); } @Test public void testLaunchOrConvert_convertTaskToBubble() { final ActivityManager.RunningTaskInfo taskInfo = setupAppBubble(); when(mLayerView.canExpandView(mBubble)).thenReturn(true); final BubbleTransitions.LaunchOrConvertToBubble bt = (BubbleTransitions.LaunchOrConvertToBubble) mBubbleTransitions .startLaunchIntoOrConvertToBubble( mBubble, mExpandedViewManager, mTaskViewFactory, mBubblePositioner, mStackView, mLayerView, mIconFactory, false /* inflateSync */, BubbleBarLocation.RIGHT); bt.onInflated(mBubble); verify(mBubble).setPreparingTransition(bt); // Check that an external transition was enqueued, and a launch cookie was set. assertThat(mTaskViewTransitions.hasPending()).isTrue(); assertThat(bt.mLaunchCookie).isNotNull(); // Prepare for startAnimation call final SurfaceControl taskLeash = new SurfaceControl.Builder().setName("taskLeash").build(); final SurfaceControl snapshot = new SurfaceControl.Builder().setName("snapshot").build(); final TransitionInfo info = setupConvertTransition(taskInfo, taskLeash, snapshot, bt.mLaunchCookie.binder); final IBinder transitionToken = mock(IBinder.class); bt.mPlayingTransition = transitionToken; final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class); final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class); final boolean[] finishCalled = new boolean[] { false }; final Transitions.TransitionFinishCallback finishCb = wct -> { assertThat(finishCalled[0]).isFalse(); finishCalled[0] = true; }; // Start playing the transition bt.startAnimation(transitionToken, info, startT, finishT, finishCb); assertThat(mTaskViewTransitions.hasPending()).isFalse(); // Verify startT modifications (position, snapshot handling) verify(startT).setPosition(taskLeash, 0, 0); verify(startT).show(snapshot); verify(startT).reparent(eq(snapshot), any(SurfaceControl.class)); verify(startT).setPosition(snapshot, 0 , 0); verify(startT).setLayer(snapshot, Integer.MAX_VALUE); // Bubble data gets updated with the correct bubble bar location verify(mBubbleData).notificationEntryUpdated(eq(mBubble), anyBoolean(), anyBoolean(), eq(BubbleBarLocation.RIGHT)); // Verify preparingTransition is not cleared yet verify(mBubble, never()).setPreparingTransition(null); // Simulate surfaceCreated and continueExpand so the animation can start bt.surfaceCreated(); bt.continueExpand(); // Verify preparingTransition is cleared now verify(mBubble).setPreparingTransition(null); // Verify animateConvert is called due to TRANSIT_CHANGE and snapshot exists ArgumentCaptor<Runnable> animCb = ArgumentCaptor.forClass(Runnable.class); verify(mLayerView).animateConvert( any(), // Check that task bounds are passed in as the initial bounds eq(new Rect(0, 0, FULLSCREEN_TASK_WIDTH, FULLSCREEN_TASK_HEIGHT)), eq(1f), eq(snapshot), eq(taskLeash), animCb.capture() ); // Trigger animation callback to finish assertThat(finishCalled[0]).isFalse(); animCb.getValue().run(); assertThat(finishCalled[0]).isTrue(); // Verify that the playing transition and pending cookie are removed assertThat(mBubbleTransitions.mEnterTransitions).doesNotContainKey(transitionToken); assertThat(mBubbleTransitions.mPendingEnterTransitions).doesNotContainKey( bt.mLaunchCookie.binder); } @Test public void testLaunchOrConvert_convertTaskToBubble_noSnapshot() { final ActivityManager.RunningTaskInfo taskInfo = setupAppBubble(); when(mLayerView.canExpandView(mBubble)).thenReturn(true); final BubbleTransitions.LaunchOrConvertToBubble bt = (BubbleTransitions.LaunchOrConvertToBubble) mBubbleTransitions .startLaunchIntoOrConvertToBubble( mBubble, mExpandedViewManager, mTaskViewFactory, mBubblePositioner, mStackView, mLayerView, mIconFactory, false /* inflateSync */, BubbleBarLocation.RIGHT); bt.onInflated(mBubble); // Prepare for startAnimation call final SurfaceControl taskLeash = new SurfaceControl.Builder().setName("taskLeash").build(); // Snapshot is not available final SurfaceControl snapshot = null; final TransitionInfo info = setupConvertTransition(taskInfo, taskLeash, snapshot, bt.mLaunchCookie.binder); final IBinder transitionToken = mock(IBinder.class); bt.mPlayingTransition = transitionToken; final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class); final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class); final boolean[] finishCalled = new boolean[] { false }; final Transitions.TransitionFinishCallback finishCb = wct -> { assertThat(finishCalled[0]).isFalse(); finishCalled[0] = true; }; // Start playing the transition bt.startAnimation(transitionToken, info, startT, finishT, finishCb); // Verify startT modifications (position only) verify(startT).setPosition(taskLeash, 0, 0); // Simulate surfaceCreated and continueExpand so the animation can start bt.surfaceCreated(); bt.continueExpand(); // Verify animateExpand is called due to TRANSIT_CHANGE and but no snapshot ArgumentCaptor<Runnable> animCb = ArgumentCaptor.forClass(Runnable.class); verify(mLayerView).animateExpand(isNull(), animCb.capture()); // Trigger animation callback to finish assertThat(finishCalled[0]).isFalse(); animCb.getValue().run(); assertThat(finishCalled[0]).isTrue(); // Verify that the playing transition and pending cookie are removed assertThat(mBubbleTransitions.mEnterTransitions).doesNotContainKey(transitionToken); assertThat(mBubbleTransitions.mPendingEnterTransitions).doesNotContainKey( bt.mLaunchCookie.binder); } } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java +8 −6 Original line number Diff line number Diff line Loading @@ -106,12 +106,14 @@ public class BubbleTransitions { @NonNull final Context mContext; @NonNull final BubbleAppInfoProvider mAppInfoProvider; @VisibleForTesting // Map of a launch cookie (used to start an activity) to the associated transition handler private final Map<IBinder, TransitionHandler> mPendingEnterTransitions = final Map<IBinder, TransitionHandler> mPendingEnterTransitions = new HashMap<>(); @VisibleForTesting // Map of a running transition token to the associated transition handler private final Map<IBinder, TransitionHandler> mEnterTransitions = final Map<IBinder, TransitionHandler> mEnterTransitions = new HashMap<>(); private BubbleController mBubbleController; Loading Loading @@ -229,13 +231,13 @@ public class BubbleTransitions { /** * Starts a new launch or convert transition to show the given bubble. */ public void startLaunchIntoOrConvertToBubble(Bubble bubble, public BubbleTransition startLaunchIntoOrConvertToBubble(Bubble bubble, BubbleExpandedViewManager expandedViewManager, BubbleTaskViewFactory factory, BubblePositioner positioner, BubbleStackView stackView, BubbleBarLayerView layerView, BubbleIconFactory iconFactory, boolean inflateSync, @Nullable BubbleBarLocation bubbleBarLocation) { new LaunchOrConvertToBubble(bubble, mContext, expandedViewManager, factory, positioner, stackView, layerView, iconFactory, inflateSync, bubbleBarLocation); return new LaunchOrConvertToBubble(bubble, mContext, expandedViewManager, factory, positioner, stackView, layerView, iconFactory, inflateSync, bubbleBarLocation); } /** Loading Loading @@ -936,7 +938,7 @@ public class BubbleTransitions { mFinishT = finishTransaction; mTaskLeash = chg.getLeash(); mSnapshot = chg.getSnapshot(); mPlayConvertTaskAnimation = !isOpeningMode(chg.getMode()); mPlayConvertTaskAnimation = !isOpeningMode(chg.getMode()) && mSnapshot != null; found = true; } else { // In core-initiated launches, the transition is of an OPEN type, and we need to Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java +175 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.wm.shell.bubbles; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_FRONT; import static com.android.window.flags.Flags.FLAG_EXCLUDE_TASK_FROM_RECENTS; Loading @@ -43,10 +44,12 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.ActivityManager; import android.content.Intent; import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; import android.os.IBinder; import android.os.UserHandle; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.view.SurfaceControl; Loading @@ -71,6 +74,7 @@ import com.android.wm.shell.bubbles.bar.BubbleBarLayerView; import com.android.wm.shell.common.HomeIntentProvider; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.shared.bubbles.BubbleBarLocation; import com.android.wm.shell.taskview.TaskView; import com.android.wm.shell.taskview.TaskViewRepository; import com.android.wm.shell.taskview.TaskViewTaskController; Loading Loading @@ -126,6 +130,8 @@ public class BubbleTransitionsTest extends ShellTestCase { private HomeIntentProvider mHomeIntentProvider; @Mock private ShellTaskOrganizer mTaskOrganizer; @Mock private BubbleController mBubbleController; private TaskViewTransitions mTaskViewTransitions; private TaskViewRepository mRepository; Loading @@ -144,6 +150,7 @@ public class BubbleTransitionsTest extends ShellTestCase { mBubbleTransitions = new BubbleTransitions(mContext, mTransitions, mTaskOrganizer, mRepository, mBubbleData, mTaskViewTransitions, new PackageManagerBubbleAppInfoProvider()); mBubbleTransitions.setBubbleController(mBubbleController); mTaskViewFactory = () -> { TaskViewTaskController taskViewTaskController = new TaskViewTaskController( mContext, mTaskOrganizer, mTaskViewTransitions, mSyncQueue); Loading @@ -169,6 +176,13 @@ public class BubbleTransitionsTest extends ShellTestCase { return taskInfo; } private ActivityManager.RunningTaskInfo setupAppBubble() { when(mBubble.isApp()).thenReturn(true); when(mBubble.getIntent()).thenReturn(new Intent()); when(mBubble.getUser()).thenReturn(new UserHandle(0)); return setupBubble(); } private TransitionInfo setupFullscreenTaskTransition(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl taskLeash, SurfaceControl snapshot) { final TransitionInfo info = new TransitionInfo(TRANSIT_CONVERT_TO_BUBBLE, 0); Loading @@ -182,6 +196,23 @@ public class BubbleTransitionsTest extends ShellTestCase { return info; } private TransitionInfo setupConvertTransition(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl taskLeash, SurfaceControl snapshot, IBinder launchCookieBinder) { final TransitionInfo info = new TransitionInfo(TRANSIT_OPEN, 0); final TransitionInfo.Change chg = new TransitionInfo.Change(taskInfo.token, taskLeash); chg.setTaskInfo(taskInfo); chg.setMode(TRANSIT_CHANGE); chg.setStartAbsBounds(new Rect(0, 0, FULLSCREEN_TASK_WIDTH, FULLSCREEN_TASK_HEIGHT)); if (snapshot != null) { chg.setSnapshot(snapshot, /* luma= */ 0f); } // Add the launch cookie to the task info taskInfo.launchCookies.add(launchCookieBinder); info.addChange(chg); info.addRoot(new TransitionInfo.Root(0, mock(SurfaceControl.class), 0, 0)); return info; } private WindowContainerToken createMockToken() { final IWindowContainerToken itoken = mock(IWindowContainerToken.class); final IBinder asBinder = mock(IBinder.class); Loading Loading @@ -672,4 +703,148 @@ public class BubbleTransitionsTest extends ShellTestCase { mBubbleTransitions.notifyUnfoldTransitionFinished(unfoldTransition); assertThat(mTaskViewTransitions.hasPending()).isFalse(); } @Test public void testLaunchOrConvert_convertTaskToBubble() { final ActivityManager.RunningTaskInfo taskInfo = setupAppBubble(); when(mLayerView.canExpandView(mBubble)).thenReturn(true); final BubbleTransitions.LaunchOrConvertToBubble bt = (BubbleTransitions.LaunchOrConvertToBubble) mBubbleTransitions .startLaunchIntoOrConvertToBubble( mBubble, mExpandedViewManager, mTaskViewFactory, mBubblePositioner, mStackView, mLayerView, mIconFactory, false /* inflateSync */, BubbleBarLocation.RIGHT); bt.onInflated(mBubble); verify(mBubble).setPreparingTransition(bt); // Check that an external transition was enqueued, and a launch cookie was set. assertThat(mTaskViewTransitions.hasPending()).isTrue(); assertThat(bt.mLaunchCookie).isNotNull(); // Prepare for startAnimation call final SurfaceControl taskLeash = new SurfaceControl.Builder().setName("taskLeash").build(); final SurfaceControl snapshot = new SurfaceControl.Builder().setName("snapshot").build(); final TransitionInfo info = setupConvertTransition(taskInfo, taskLeash, snapshot, bt.mLaunchCookie.binder); final IBinder transitionToken = mock(IBinder.class); bt.mPlayingTransition = transitionToken; final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class); final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class); final boolean[] finishCalled = new boolean[] { false }; final Transitions.TransitionFinishCallback finishCb = wct -> { assertThat(finishCalled[0]).isFalse(); finishCalled[0] = true; }; // Start playing the transition bt.startAnimation(transitionToken, info, startT, finishT, finishCb); assertThat(mTaskViewTransitions.hasPending()).isFalse(); // Verify startT modifications (position, snapshot handling) verify(startT).setPosition(taskLeash, 0, 0); verify(startT).show(snapshot); verify(startT).reparent(eq(snapshot), any(SurfaceControl.class)); verify(startT).setPosition(snapshot, 0 , 0); verify(startT).setLayer(snapshot, Integer.MAX_VALUE); // Bubble data gets updated with the correct bubble bar location verify(mBubbleData).notificationEntryUpdated(eq(mBubble), anyBoolean(), anyBoolean(), eq(BubbleBarLocation.RIGHT)); // Verify preparingTransition is not cleared yet verify(mBubble, never()).setPreparingTransition(null); // Simulate surfaceCreated and continueExpand so the animation can start bt.surfaceCreated(); bt.continueExpand(); // Verify preparingTransition is cleared now verify(mBubble).setPreparingTransition(null); // Verify animateConvert is called due to TRANSIT_CHANGE and snapshot exists ArgumentCaptor<Runnable> animCb = ArgumentCaptor.forClass(Runnable.class); verify(mLayerView).animateConvert( any(), // Check that task bounds are passed in as the initial bounds eq(new Rect(0, 0, FULLSCREEN_TASK_WIDTH, FULLSCREEN_TASK_HEIGHT)), eq(1f), eq(snapshot), eq(taskLeash), animCb.capture() ); // Trigger animation callback to finish assertThat(finishCalled[0]).isFalse(); animCb.getValue().run(); assertThat(finishCalled[0]).isTrue(); // Verify that the playing transition and pending cookie are removed assertThat(mBubbleTransitions.mEnterTransitions).doesNotContainKey(transitionToken); assertThat(mBubbleTransitions.mPendingEnterTransitions).doesNotContainKey( bt.mLaunchCookie.binder); } @Test public void testLaunchOrConvert_convertTaskToBubble_noSnapshot() { final ActivityManager.RunningTaskInfo taskInfo = setupAppBubble(); when(mLayerView.canExpandView(mBubble)).thenReturn(true); final BubbleTransitions.LaunchOrConvertToBubble bt = (BubbleTransitions.LaunchOrConvertToBubble) mBubbleTransitions .startLaunchIntoOrConvertToBubble( mBubble, mExpandedViewManager, mTaskViewFactory, mBubblePositioner, mStackView, mLayerView, mIconFactory, false /* inflateSync */, BubbleBarLocation.RIGHT); bt.onInflated(mBubble); // Prepare for startAnimation call final SurfaceControl taskLeash = new SurfaceControl.Builder().setName("taskLeash").build(); // Snapshot is not available final SurfaceControl snapshot = null; final TransitionInfo info = setupConvertTransition(taskInfo, taskLeash, snapshot, bt.mLaunchCookie.binder); final IBinder transitionToken = mock(IBinder.class); bt.mPlayingTransition = transitionToken; final SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class); final SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class); final boolean[] finishCalled = new boolean[] { false }; final Transitions.TransitionFinishCallback finishCb = wct -> { assertThat(finishCalled[0]).isFalse(); finishCalled[0] = true; }; // Start playing the transition bt.startAnimation(transitionToken, info, startT, finishT, finishCb); // Verify startT modifications (position only) verify(startT).setPosition(taskLeash, 0, 0); // Simulate surfaceCreated and continueExpand so the animation can start bt.surfaceCreated(); bt.continueExpand(); // Verify animateExpand is called due to TRANSIT_CHANGE and but no snapshot ArgumentCaptor<Runnable> animCb = ArgumentCaptor.forClass(Runnable.class); verify(mLayerView).animateExpand(isNull(), animCb.capture()); // Trigger animation callback to finish assertThat(finishCalled[0]).isFalse(); animCb.getValue().run(); assertThat(finishCalled[0]).isTrue(); // Verify that the playing transition and pending cookie are removed assertThat(mBubbleTransitions.mEnterTransitions).doesNotContainKey(transitionToken); assertThat(mBubbleTransitions.mPendingEnterTransitions).doesNotContainKey( bt.mLaunchCookie.binder); } }