Loading libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +15 −17 Original line number Diff line number Diff line Loading @@ -16,7 +16,6 @@ package com.android.wm.shell.bubbles; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_DELETED; import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED; import static android.service.notification.NotificationListenerService.REASON_CANCEL; Loading Loading @@ -115,6 +114,7 @@ import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.taskview.TaskView; import com.android.wm.shell.taskview.TaskViewTransitions; import com.android.wm.shell.transition.Transitions; import java.io.PrintWriter; import java.util.ArrayList; Loading Loading @@ -182,6 +182,7 @@ public class BubbleController implements ConfigurationChangeListener, private final ShellTaskOrganizer mTaskOrganizer; private final DisplayController mDisplayController; private final TaskViewTransitions mTaskViewTransitions; private final Transitions mTransitions; private final SyncTransactionQueue mSyncQueue; private final ShellController mShellController; private final ShellCommandHandler mShellCommandHandler; Loading Loading @@ -282,6 +283,7 @@ public class BubbleController implements ConfigurationChangeListener, @ShellMainThread Handler mainHandler, @ShellBackgroundThread ShellExecutor bgExecutor, TaskViewTransitions taskViewTransitions, Transitions transitions, SyncTransactionQueue syncQueue, IWindowManager wmService, BubbleProperties bubbleProperties) { Loading Loading @@ -317,6 +319,7 @@ public class BubbleController implements ConfigurationChangeListener, com.android.internal.R.dimen.importance_ring_stroke_width)); mDisplayController = displayController; mTaskViewTransitions = taskViewTransitions; mTransitions = transitions; mOneHandedOptional = oneHandedOptional; mDragAndDropController = dragAndDropController; mSyncQueue = syncQueue; Loading Loading @@ -416,23 +419,9 @@ public class BubbleController implements ConfigurationChangeListener, } }, mMainHandler); mTaskStackListener.addListener(new TaskStackListenerCallback() { @Override public void onTaskMovedToFront(int taskId) { mMainExecutor.execute(() -> { int expandedId = INVALID_TASK_ID; if (mStackView != null && mStackView.getExpandedBubble() != null && isStackExpanded() && !mStackView.isExpansionAnimating() && !mStackView.isSwitchAnimating()) { expandedId = mStackView.getExpandedBubble().getTaskId(); } if (expandedId != INVALID_TASK_ID && expandedId != taskId) { mBubbleData.setExpanded(false); } }); } mTransitions.registerObserver(new BubblesTransitionObserver(this, mBubbleData)); mTaskStackListener.addListener(new TaskStackListenerCallback() { @Override public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task, boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) { Loading Loading @@ -1961,6 +1950,15 @@ public class BubbleController implements ConfigurationChangeListener, } } /** * Returns whether the stack is animating or not. */ public boolean isStackAnimating() { return mStackView != null && (mStackView.isExpansionAnimating() || mStackView.isSwitchAnimating()); } @VisibleForTesting @Nullable public BubbleStackView getStackView() { Loading libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesTransitionObserver.java 0 → 100644 +83 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.wm.shell.bubbles; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import android.app.ActivityManager; import android.os.IBinder; import android.view.SurfaceControl; import android.window.TransitionInfo; import androidx.annotation.NonNull; import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.util.TransitionUtil; /** * Observer used to identify tasks that are opening or moving to front. If a bubble activity is * currently opened when this happens, we'll collapse the bubbles. */ public class BubblesTransitionObserver implements Transitions.TransitionObserver { private BubbleController mBubbleController; private BubbleData mBubbleData; public BubblesTransitionObserver(BubbleController controller, BubbleData bubbleData) { mBubbleController = controller; mBubbleData = bubbleData; } @Override public void onTransitionReady(@NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction) { for (TransitionInfo.Change change : info.getChanges()) { final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); // We only care about opens / move to fronts when bubbles are expanded & not animating. if (taskInfo == null || taskInfo.taskId == INVALID_TASK_ID || !TransitionUtil.isOpeningType(change.getMode()) || mBubbleController.isStackAnimating() || !mBubbleData.isExpanded() || mBubbleData.getSelectedBubble() == null) { continue; } int expandedId = mBubbleData.getSelectedBubble().getTaskId(); // If the task id that's opening is the same as the expanded bubble, skip collapsing // because it is our bubble that is opening. if (expandedId != INVALID_TASK_ID && expandedId != taskInfo.taskId) { mBubbleData.setExpanded(false); } } } @Override public void onTransitionStarting(@NonNull IBinder transition) { } @Override public void onTransitionMerged(@NonNull IBinder merged, @NonNull IBinder playing) { } @Override public void onTransitionFinished(@NonNull IBinder transition, boolean aborted) { } } libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +3 −1 Original line number Diff line number Diff line Loading @@ -167,6 +167,7 @@ public abstract class WMShellModule { @ShellMainThread Handler mainHandler, @ShellBackgroundThread ShellExecutor bgExecutor, TaskViewTransitions taskViewTransitions, Transitions transitions, SyncTransactionQueue syncQueue, IWindowManager wmService) { return new BubbleController(context, shellInit, shellCommandHandler, shellController, data, Loading @@ -176,7 +177,8 @@ public abstract class WMShellModule { statusBarService, windowManager, windowManagerShellWrapper, userManager, launcherApps, logger, taskStackListener, organizer, positioner, displayController, oneHandedOptional, dragAndDropController, mainExecutor, mainHandler, bgExecutor, taskViewTransitions, syncQueue, wmService, ProdBubbleProperties.INSTANCE); taskViewTransitions, transitions, syncQueue, wmService, ProdBubbleProperties.INSTANCE); } // Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesTransitionObserverTest.java 0 → 100644 +234 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.wm.shell.bubbles; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.ActivityManager; import android.os.IBinder; import android.view.SurfaceControl; import android.window.IWindowContainerToken; import android.window.TransitionInfo; import android.window.WindowContainerToken; import androidx.test.filters.SmallTest; import com.android.wm.shell.TransitionInfoBuilder; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; /** * Tests of {@link BubblesTransitionObserver}. */ @SmallTest public class BubblesTransitionObserverTest { @Mock private BubbleController mBubbleController; @Mock private BubbleData mBubbleData; @Mock private IBinder mTransition; @Mock private SurfaceControl.Transaction mStartT; @Mock private SurfaceControl.Transaction mFinishT; @Mock private Bubble mBubble; private BubblesTransitionObserver mTransitionObserver; @Before public void setUp() { MockitoAnnotations.initMocks(this); mTransitionObserver = new BubblesTransitionObserver(mBubbleController, mBubbleData); } @Test public void testOnTransitionReady_open_collapsesStack() { when(mBubbleData.isExpanded()).thenReturn(true); when(mBubbleData.getSelectedBubble()).thenReturn(mBubble); when(mBubble.getTaskId()).thenReturn(1); when(mBubbleController.isStackAnimating()).thenReturn(false); TransitionInfo info = createTransitionInfo(TRANSIT_OPEN, createTaskInfo(2)); mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT); verify(mBubbleData).setExpanded(eq(false)); } @Test public void testOnTransitionReady_toFront_collapsesStack() { when(mBubbleData.isExpanded()).thenReturn(true); when(mBubbleData.getSelectedBubble()).thenReturn(mBubble); when(mBubble.getTaskId()).thenReturn(1); when(mBubbleController.isStackAnimating()).thenReturn(false); TransitionInfo info = createTransitionInfo(TRANSIT_TO_FRONT, createTaskInfo(2)); mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT); verify(mBubbleData).setExpanded(eq(false)); } @Test public void testOnTransitionReady_noTaskInfo_skip() { when(mBubbleData.isExpanded()).thenReturn(true); when(mBubbleData.getSelectedBubble()).thenReturn(mBubble); when(mBubble.getTaskId()).thenReturn(1); when(mBubbleController.isStackAnimating()).thenReturn(false); // Null task info TransitionInfo info = createTransitionInfo(TRANSIT_TO_FRONT, null /* taskInfo */); mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT); verify(mBubbleData, never()).setExpanded(eq(false)); } @Test public void testOnTransitionReady_noTaskId_skip() { when(mBubbleData.isExpanded()).thenReturn(true); when(mBubbleData.getSelectedBubble()).thenReturn(mBubble); when(mBubble.getTaskId()).thenReturn(1); when(mBubbleController.isStackAnimating()).thenReturn(false); // Invalid task id TransitionInfo info = createTransitionInfo(TRANSIT_TO_FRONT, createTaskInfo(INVALID_TASK_ID)); mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT); verify(mBubbleData, never()).setExpanded(eq(false)); } @Test public void testOnTransitionReady_notOpening_skip() { when(mBubbleData.isExpanded()).thenReturn(true); when(mBubbleData.getSelectedBubble()).thenReturn(mBubble); when(mBubble.getTaskId()).thenReturn(1); when(mBubbleController.isStackAnimating()).thenReturn(false); // Transits that aren't opening TransitionInfo info = createTransitionInfo(TRANSIT_CHANGE, createTaskInfo(2)); mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT); info = createTransitionInfo(TRANSIT_CLOSE, createTaskInfo(3)); mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT); info = createTransitionInfo(TRANSIT_TO_BACK, createTaskInfo(4)); mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT); verify(mBubbleData, never()).setExpanded(eq(false)); } @Test public void testOnTransitionReady_stackAnimating_skip() { when(mBubbleData.isExpanded()).thenReturn(true); when(mBubbleData.getSelectedBubble()).thenReturn(mBubble); when(mBubble.getTaskId()).thenReturn(1); when(mBubbleController.isStackAnimating()).thenReturn(true); // Stack is animating TransitionInfo info = createTransitionInfo(TRANSIT_OPEN, createTaskInfo(2)); mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT); verify(mBubbleData, never()).setExpanded(eq(false)); } @Test public void testOnTransitionReady_stackNotExpanded_skip() { when(mBubbleData.isExpanded()).thenReturn(false); // Stack is not expanded when(mBubbleData.getSelectedBubble()).thenReturn(mBubble); when(mBubble.getTaskId()).thenReturn(1); when(mBubbleController.isStackAnimating()).thenReturn(false); TransitionInfo info = createTransitionInfo(TRANSIT_TO_FRONT, createTaskInfo(2)); mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT); verify(mBubbleData, never()).setExpanded(eq(false)); } @Test public void testOnTransitionReady_noSelectedBubble_skip() { when(mBubbleData.isExpanded()).thenReturn(true); when(mBubbleData.getSelectedBubble()).thenReturn(null); // No selected bubble when(mBubble.getTaskId()).thenReturn(1); when(mBubbleController.isStackAnimating()).thenReturn(false); TransitionInfo info = createTransitionInfo(TRANSIT_OPEN, createTaskInfo(2)); mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT); verify(mBubbleData, never()).setExpanded(eq(false)); } @Test public void testOnTransitionReady_openingMatchesExpanded_skip() { when(mBubbleData.isExpanded()).thenReturn(true); when(mBubbleData.getSelectedBubble()).thenReturn(mBubble); when(mBubble.getTaskId()).thenReturn(1); when(mBubbleController.isStackAnimating()).thenReturn(false); // What's moving to front is same as the opened bubble TransitionInfo info = createTransitionInfo(TRANSIT_TO_FRONT, createTaskInfo(1)); mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT); verify(mBubbleData, never()).setExpanded(eq(false)); } private ActivityManager.RunningTaskInfo createTaskInfo(int taskId) { final ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo(); taskInfo.taskId = taskId; taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); return taskInfo; } private TransitionInfo createTransitionInfo(int changeType, ActivityManager.RunningTaskInfo info) { final TransitionInfo.Change change = new TransitionInfo.Change( new WindowContainerToken(mock(IWindowContainerToken.class)), mock(SurfaceControl.class)); change.setMode(changeType); change.setTaskInfo(info); return new TransitionInfoBuilder(TRANSIT_OPEN, 0) .addChange(change).build(); } } packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +6 −0 Original line number Diff line number Diff line Loading @@ -419,6 +419,7 @@ public class BubblesTest extends SysuiTestCase { syncExecutor, mock(Handler.class), mTaskViewTransitions, mTransitions, mock(SyncTransactionQueue.class), mock(IWindowManager.class), mBubbleProperties); Loading Loading @@ -509,6 +510,11 @@ public class BubblesTest extends SysuiTestCase { verify(mShellController, times(1)).addConfigurationChangeListener(any()); } @Test public void instantiateController_registerTransitionObserver() { verify(mTransitions).registerObserver(any()); } @Test public void testAddBubble() { mBubbleController.updateBubble(mBubbleEntry); Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +15 −17 Original line number Diff line number Diff line Loading @@ -16,7 +16,6 @@ package com.android.wm.shell.bubbles; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_DELETED; import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED; import static android.service.notification.NotificationListenerService.REASON_CANCEL; Loading Loading @@ -115,6 +114,7 @@ import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.taskview.TaskView; import com.android.wm.shell.taskview.TaskViewTransitions; import com.android.wm.shell.transition.Transitions; import java.io.PrintWriter; import java.util.ArrayList; Loading Loading @@ -182,6 +182,7 @@ public class BubbleController implements ConfigurationChangeListener, private final ShellTaskOrganizer mTaskOrganizer; private final DisplayController mDisplayController; private final TaskViewTransitions mTaskViewTransitions; private final Transitions mTransitions; private final SyncTransactionQueue mSyncQueue; private final ShellController mShellController; private final ShellCommandHandler mShellCommandHandler; Loading Loading @@ -282,6 +283,7 @@ public class BubbleController implements ConfigurationChangeListener, @ShellMainThread Handler mainHandler, @ShellBackgroundThread ShellExecutor bgExecutor, TaskViewTransitions taskViewTransitions, Transitions transitions, SyncTransactionQueue syncQueue, IWindowManager wmService, BubbleProperties bubbleProperties) { Loading Loading @@ -317,6 +319,7 @@ public class BubbleController implements ConfigurationChangeListener, com.android.internal.R.dimen.importance_ring_stroke_width)); mDisplayController = displayController; mTaskViewTransitions = taskViewTransitions; mTransitions = transitions; mOneHandedOptional = oneHandedOptional; mDragAndDropController = dragAndDropController; mSyncQueue = syncQueue; Loading Loading @@ -416,23 +419,9 @@ public class BubbleController implements ConfigurationChangeListener, } }, mMainHandler); mTaskStackListener.addListener(new TaskStackListenerCallback() { @Override public void onTaskMovedToFront(int taskId) { mMainExecutor.execute(() -> { int expandedId = INVALID_TASK_ID; if (mStackView != null && mStackView.getExpandedBubble() != null && isStackExpanded() && !mStackView.isExpansionAnimating() && !mStackView.isSwitchAnimating()) { expandedId = mStackView.getExpandedBubble().getTaskId(); } if (expandedId != INVALID_TASK_ID && expandedId != taskId) { mBubbleData.setExpanded(false); } }); } mTransitions.registerObserver(new BubblesTransitionObserver(this, mBubbleData)); mTaskStackListener.addListener(new TaskStackListenerCallback() { @Override public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task, boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) { Loading Loading @@ -1961,6 +1950,15 @@ public class BubbleController implements ConfigurationChangeListener, } } /** * Returns whether the stack is animating or not. */ public boolean isStackAnimating() { return mStackView != null && (mStackView.isExpansionAnimating() || mStackView.isSwitchAnimating()); } @VisibleForTesting @Nullable public BubbleStackView getStackView() { Loading
libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesTransitionObserver.java 0 → 100644 +83 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.wm.shell.bubbles; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import android.app.ActivityManager; import android.os.IBinder; import android.view.SurfaceControl; import android.window.TransitionInfo; import androidx.annotation.NonNull; import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.util.TransitionUtil; /** * Observer used to identify tasks that are opening or moving to front. If a bubble activity is * currently opened when this happens, we'll collapse the bubbles. */ public class BubblesTransitionObserver implements Transitions.TransitionObserver { private BubbleController mBubbleController; private BubbleData mBubbleData; public BubblesTransitionObserver(BubbleController controller, BubbleData bubbleData) { mBubbleController = controller; mBubbleData = bubbleData; } @Override public void onTransitionReady(@NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction) { for (TransitionInfo.Change change : info.getChanges()) { final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); // We only care about opens / move to fronts when bubbles are expanded & not animating. if (taskInfo == null || taskInfo.taskId == INVALID_TASK_ID || !TransitionUtil.isOpeningType(change.getMode()) || mBubbleController.isStackAnimating() || !mBubbleData.isExpanded() || mBubbleData.getSelectedBubble() == null) { continue; } int expandedId = mBubbleData.getSelectedBubble().getTaskId(); // If the task id that's opening is the same as the expanded bubble, skip collapsing // because it is our bubble that is opening. if (expandedId != INVALID_TASK_ID && expandedId != taskInfo.taskId) { mBubbleData.setExpanded(false); } } } @Override public void onTransitionStarting(@NonNull IBinder transition) { } @Override public void onTransitionMerged(@NonNull IBinder merged, @NonNull IBinder playing) { } @Override public void onTransitionFinished(@NonNull IBinder transition, boolean aborted) { } }
libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +3 −1 Original line number Diff line number Diff line Loading @@ -167,6 +167,7 @@ public abstract class WMShellModule { @ShellMainThread Handler mainHandler, @ShellBackgroundThread ShellExecutor bgExecutor, TaskViewTransitions taskViewTransitions, Transitions transitions, SyncTransactionQueue syncQueue, IWindowManager wmService) { return new BubbleController(context, shellInit, shellCommandHandler, shellController, data, Loading @@ -176,7 +177,8 @@ public abstract class WMShellModule { statusBarService, windowManager, windowManagerShellWrapper, userManager, launcherApps, logger, taskStackListener, organizer, positioner, displayController, oneHandedOptional, dragAndDropController, mainExecutor, mainHandler, bgExecutor, taskViewTransitions, syncQueue, wmService, ProdBubbleProperties.INSTANCE); taskViewTransitions, transitions, syncQueue, wmService, ProdBubbleProperties.INSTANCE); } // Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesTransitionObserverTest.java 0 → 100644 +234 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.wm.shell.bubbles; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.ActivityManager; import android.os.IBinder; import android.view.SurfaceControl; import android.window.IWindowContainerToken; import android.window.TransitionInfo; import android.window.WindowContainerToken; import androidx.test.filters.SmallTest; import com.android.wm.shell.TransitionInfoBuilder; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; /** * Tests of {@link BubblesTransitionObserver}. */ @SmallTest public class BubblesTransitionObserverTest { @Mock private BubbleController mBubbleController; @Mock private BubbleData mBubbleData; @Mock private IBinder mTransition; @Mock private SurfaceControl.Transaction mStartT; @Mock private SurfaceControl.Transaction mFinishT; @Mock private Bubble mBubble; private BubblesTransitionObserver mTransitionObserver; @Before public void setUp() { MockitoAnnotations.initMocks(this); mTransitionObserver = new BubblesTransitionObserver(mBubbleController, mBubbleData); } @Test public void testOnTransitionReady_open_collapsesStack() { when(mBubbleData.isExpanded()).thenReturn(true); when(mBubbleData.getSelectedBubble()).thenReturn(mBubble); when(mBubble.getTaskId()).thenReturn(1); when(mBubbleController.isStackAnimating()).thenReturn(false); TransitionInfo info = createTransitionInfo(TRANSIT_OPEN, createTaskInfo(2)); mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT); verify(mBubbleData).setExpanded(eq(false)); } @Test public void testOnTransitionReady_toFront_collapsesStack() { when(mBubbleData.isExpanded()).thenReturn(true); when(mBubbleData.getSelectedBubble()).thenReturn(mBubble); when(mBubble.getTaskId()).thenReturn(1); when(mBubbleController.isStackAnimating()).thenReturn(false); TransitionInfo info = createTransitionInfo(TRANSIT_TO_FRONT, createTaskInfo(2)); mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT); verify(mBubbleData).setExpanded(eq(false)); } @Test public void testOnTransitionReady_noTaskInfo_skip() { when(mBubbleData.isExpanded()).thenReturn(true); when(mBubbleData.getSelectedBubble()).thenReturn(mBubble); when(mBubble.getTaskId()).thenReturn(1); when(mBubbleController.isStackAnimating()).thenReturn(false); // Null task info TransitionInfo info = createTransitionInfo(TRANSIT_TO_FRONT, null /* taskInfo */); mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT); verify(mBubbleData, never()).setExpanded(eq(false)); } @Test public void testOnTransitionReady_noTaskId_skip() { when(mBubbleData.isExpanded()).thenReturn(true); when(mBubbleData.getSelectedBubble()).thenReturn(mBubble); when(mBubble.getTaskId()).thenReturn(1); when(mBubbleController.isStackAnimating()).thenReturn(false); // Invalid task id TransitionInfo info = createTransitionInfo(TRANSIT_TO_FRONT, createTaskInfo(INVALID_TASK_ID)); mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT); verify(mBubbleData, never()).setExpanded(eq(false)); } @Test public void testOnTransitionReady_notOpening_skip() { when(mBubbleData.isExpanded()).thenReturn(true); when(mBubbleData.getSelectedBubble()).thenReturn(mBubble); when(mBubble.getTaskId()).thenReturn(1); when(mBubbleController.isStackAnimating()).thenReturn(false); // Transits that aren't opening TransitionInfo info = createTransitionInfo(TRANSIT_CHANGE, createTaskInfo(2)); mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT); info = createTransitionInfo(TRANSIT_CLOSE, createTaskInfo(3)); mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT); info = createTransitionInfo(TRANSIT_TO_BACK, createTaskInfo(4)); mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT); verify(mBubbleData, never()).setExpanded(eq(false)); } @Test public void testOnTransitionReady_stackAnimating_skip() { when(mBubbleData.isExpanded()).thenReturn(true); when(mBubbleData.getSelectedBubble()).thenReturn(mBubble); when(mBubble.getTaskId()).thenReturn(1); when(mBubbleController.isStackAnimating()).thenReturn(true); // Stack is animating TransitionInfo info = createTransitionInfo(TRANSIT_OPEN, createTaskInfo(2)); mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT); verify(mBubbleData, never()).setExpanded(eq(false)); } @Test public void testOnTransitionReady_stackNotExpanded_skip() { when(mBubbleData.isExpanded()).thenReturn(false); // Stack is not expanded when(mBubbleData.getSelectedBubble()).thenReturn(mBubble); when(mBubble.getTaskId()).thenReturn(1); when(mBubbleController.isStackAnimating()).thenReturn(false); TransitionInfo info = createTransitionInfo(TRANSIT_TO_FRONT, createTaskInfo(2)); mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT); verify(mBubbleData, never()).setExpanded(eq(false)); } @Test public void testOnTransitionReady_noSelectedBubble_skip() { when(mBubbleData.isExpanded()).thenReturn(true); when(mBubbleData.getSelectedBubble()).thenReturn(null); // No selected bubble when(mBubble.getTaskId()).thenReturn(1); when(mBubbleController.isStackAnimating()).thenReturn(false); TransitionInfo info = createTransitionInfo(TRANSIT_OPEN, createTaskInfo(2)); mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT); verify(mBubbleData, never()).setExpanded(eq(false)); } @Test public void testOnTransitionReady_openingMatchesExpanded_skip() { when(mBubbleData.isExpanded()).thenReturn(true); when(mBubbleData.getSelectedBubble()).thenReturn(mBubble); when(mBubble.getTaskId()).thenReturn(1); when(mBubbleController.isStackAnimating()).thenReturn(false); // What's moving to front is same as the opened bubble TransitionInfo info = createTransitionInfo(TRANSIT_TO_FRONT, createTaskInfo(1)); mTransitionObserver.onTransitionReady(mTransition, info, mStartT, mFinishT); verify(mBubbleData, never()).setExpanded(eq(false)); } private ActivityManager.RunningTaskInfo createTaskInfo(int taskId) { final ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo(); taskInfo.taskId = taskId; taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); return taskInfo; } private TransitionInfo createTransitionInfo(int changeType, ActivityManager.RunningTaskInfo info) { final TransitionInfo.Change change = new TransitionInfo.Change( new WindowContainerToken(mock(IWindowContainerToken.class)), mock(SurfaceControl.class)); change.setMode(changeType); change.setTaskInfo(info); return new TransitionInfoBuilder(TRANSIT_OPEN, 0) .addChange(change).build(); } }
packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +6 −0 Original line number Diff line number Diff line Loading @@ -419,6 +419,7 @@ public class BubblesTest extends SysuiTestCase { syncExecutor, mock(Handler.class), mTaskViewTransitions, mTransitions, mock(SyncTransactionQueue.class), mock(IWindowManager.class), mBubbleProperties); Loading Loading @@ -509,6 +510,11 @@ public class BubblesTest extends SysuiTestCase { verify(mShellController, times(1)).addConfigurationChangeListener(any()); } @Test public void instantiateController_registerTransitionObserver() { verify(mTransitions).registerObserver(any()); } @Test public void testAddBubble() { mBubbleController.updateBubble(mBubbleEntry); Loading