blob: 42c221a91422e003aad2c44ba941b7c3046380af [file] [log] [blame]
Mady Mellorebdbbb92018-11-15 14:36:48 -08001/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.systemui.bubbles;
18
Mady Melloredd4ee12019-01-18 10:45:11 -080019import static org.junit.Assert.assertEquals;
Mady Mellorebdbbb92018-11-15 14:36:48 -080020import static org.junit.Assert.assertFalse;
21import static org.junit.Assert.assertTrue;
Ned Burns01e38212019-01-03 16:32:52 -050022import static org.mockito.Mockito.atLeastOnce;
Mark Renouf08bc42a2019-03-07 13:01:59 -050023import static org.mockito.Mockito.never;
24import static org.mockito.Mockito.times;
Ned Burns01e38212019-01-03 16:32:52 -050025import static org.mockito.Mockito.verify;
26import static org.mockito.Mockito.when;
Mady Mellorebdbbb92018-11-15 14:36:48 -080027
28import android.app.IActivityManager;
Mark Renouf08bc42a2019-03-07 13:01:59 -050029import android.app.PendingIntent;
Mady Mellorebdbbb92018-11-15 14:36:48 -080030import android.content.Context;
Mady Mellorebdbbb92018-11-15 14:36:48 -080031import android.testing.AndroidTestingRunner;
32import android.testing.TestableLooper;
33import android.view.WindowManager;
34import android.widget.FrameLayout;
35
Brett Chabot84151d92019-02-27 15:37:59 -080036import androidx.test.filters.SmallTest;
37
Mady Mellorebdbbb92018-11-15 14:36:48 -080038import com.android.systemui.SysuiTestCase;
39import com.android.systemui.statusbar.NotificationTestHelper;
Ned Burns01e38212019-01-03 16:32:52 -050040import com.android.systemui.statusbar.notification.NotificationEntryListener;
41import com.android.systemui.statusbar.notification.NotificationEntryManager;
Ned Burnsf81c4c42019-01-07 14:10:43 -050042import com.android.systemui.statusbar.notification.collection.NotificationData;
Mady Mellor9bad2242019-01-28 11:21:51 -080043import com.android.systemui.statusbar.notification.collection.NotificationEntry;
Mady Mellorebdbbb92018-11-15 14:36:48 -080044import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
45import com.android.systemui.statusbar.phone.DozeParameters;
46import com.android.systemui.statusbar.phone.StatusBarWindowController;
47
48import org.junit.Before;
49import org.junit.Test;
50import org.junit.runner.RunWith;
Ned Burns01e38212019-01-03 16:32:52 -050051import org.mockito.ArgumentCaptor;
52import org.mockito.Captor;
Mady Mellorebdbbb92018-11-15 14:36:48 -080053import org.mockito.Mock;
54import org.mockito.MockitoAnnotations;
55
56@SmallTest
57@RunWith(AndroidTestingRunner.class)
58@TestableLooper.RunWithLooper(setAsMainLooper = true)
59public class BubbleControllerTest extends SysuiTestCase {
60
61 @Mock
Ned Burns01e38212019-01-03 16:32:52 -050062 private NotificationEntryManager mNotificationEntryManager;
63 @Mock
Mady Mellorebdbbb92018-11-15 14:36:48 -080064 private WindowManager mWindowManager;
65 @Mock
66 private IActivityManager mActivityManager;
67 @Mock
68 private DozeParameters mDozeParameters;
Mady Mellorebdbbb92018-11-15 14:36:48 -080069 private FrameLayout mStatusBarView;
Ned Burns01e38212019-01-03 16:32:52 -050070 @Captor
71 private ArgumentCaptor<NotificationEntryListener> mEntryListenerCaptor;
Mady Mellorebdbbb92018-11-15 14:36:48 -080072
73 private TestableBubbleController mBubbleController;
74 private StatusBarWindowController mStatusBarWindowController;
Ned Burns01e38212019-01-03 16:32:52 -050075 private NotificationEntryListener mEntryListener;
Mady Mellorebdbbb92018-11-15 14:36:48 -080076
77 private NotificationTestHelper mNotificationTestHelper;
78 private ExpandableNotificationRow mRow;
79 private ExpandableNotificationRow mRow2;
Mady Mellorc18ba962019-01-29 11:11:56 -080080 private ExpandableNotificationRow mNoChannelRow;
Mady Mellorebdbbb92018-11-15 14:36:48 -080081
Mady Mellorb4991e62019-01-10 15:14:51 -080082 @Mock
83 private NotificationData mNotificationData;
Mady Melloracb12152019-01-29 15:24:48 -080084 @Mock
85 private BubbleController.BubbleStateChangeListener mBubbleStateChangeListener;
86 @Mock
87 private BubbleController.BubbleExpandListener mBubbleExpandListener;
Ned Burns01e38212019-01-03 16:32:52 -050088
Mark Renouf08bc42a2019-03-07 13:01:59 -050089 @Mock
90 private PendingIntent mDeleteIntent;
91
Mady Mellorcfd06c12019-02-13 14:32:12 -080092 private BubbleData mBubbleData;
93
Mady Mellorebdbbb92018-11-15 14:36:48 -080094 @Before
95 public void setUp() throws Exception {
96 MockitoAnnotations.initMocks(this);
Joshua Tsujib1a796b2019-01-16 15:43:12 -080097 mStatusBarView = new FrameLayout(mContext);
Ned Burns01e38212019-01-03 16:32:52 -050098 mDependency.injectTestDependency(NotificationEntryManager.class, mNotificationEntryManager);
Mady Mellorebdbbb92018-11-15 14:36:48 -080099
100 // Bubbles get added to status bar window view
101 mStatusBarWindowController = new StatusBarWindowController(mContext, mWindowManager,
102 mActivityManager, mDozeParameters);
103 mStatusBarWindowController.add(mStatusBarView, 120 /* height */);
104
105 // Need notifications for bubbles
106 mNotificationTestHelper = new NotificationTestHelper(mContext);
Mark Renouf08bc42a2019-03-07 13:01:59 -0500107 mRow = mNotificationTestHelper.createBubble(mDeleteIntent);
108 mRow2 = mNotificationTestHelper.createBubble(mDeleteIntent);
109 mNoChannelRow = mNotificationTestHelper.createBubble(mDeleteIntent);
Mady Mellorebdbbb92018-11-15 14:36:48 -0800110
Ned Burns01e38212019-01-03 16:32:52 -0500111 // Return non-null notification data from the NEM
112 when(mNotificationEntryManager.getNotificationData()).thenReturn(mNotificationData);
Mady Mellorb4991e62019-01-10 15:14:51 -0800113 when(mNotificationData.getChannel(mRow.getEntry().key)).thenReturn(mRow.getEntry().channel);
Mady Mellorc18ba962019-01-29 11:11:56 -0800114 when(mNotificationData.getChannel(mNoChannelRow.getEntry().key)).thenReturn(null);
Ned Burns01e38212019-01-03 16:32:52 -0500115
Mady Mellorcfd06c12019-02-13 14:32:12 -0800116 mBubbleData = new BubbleData();
117 mBubbleController = new TestableBubbleController(mContext, mStatusBarWindowController,
118 mBubbleData);
Mady Melloracb12152019-01-29 15:24:48 -0800119 mBubbleController.setBubbleStateChangeListener(mBubbleStateChangeListener);
120 mBubbleController.setExpandListener(mBubbleExpandListener);
Ned Burns01e38212019-01-03 16:32:52 -0500121
122 // Get a reference to the BubbleController's entry listener
123 verify(mNotificationEntryManager, atLeastOnce())
124 .addNotificationEntryListener(mEntryListenerCaptor.capture());
125 mEntryListener = mEntryListenerCaptor.getValue();
Mady Mellorebdbbb92018-11-15 14:36:48 -0800126 }
127
128 @Test
Mady Mellorebdbbb92018-11-15 14:36:48 -0800129 public void testAddBubble() {
Mady Mellor3f2efdb2018-11-21 11:30:45 -0800130 mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */);
Mady Mellorebdbbb92018-11-15 14:36:48 -0800131 assertTrue(mBubbleController.hasBubbles());
Mady Melloracb12152019-01-29 15:24:48 -0800132
133 verify(mBubbleStateChangeListener).onHasBubblesChanged(true);
Mady Mellorebdbbb92018-11-15 14:36:48 -0800134 }
135
136 @Test
137 public void testHasBubbles() {
138 assertFalse(mBubbleController.hasBubbles());
Mady Mellor3f2efdb2018-11-21 11:30:45 -0800139 mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */);
Mady Mellorebdbbb92018-11-15 14:36:48 -0800140 assertTrue(mBubbleController.hasBubbles());
141 }
142
143 @Test
144 public void testRemoveBubble() {
Mady Mellor3f2efdb2018-11-21 11:30:45 -0800145 mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */);
Mady Mellorebdbbb92018-11-15 14:36:48 -0800146 assertTrue(mBubbleController.hasBubbles());
147
Mady Melloracb12152019-01-29 15:24:48 -0800148 verify(mBubbleStateChangeListener).onHasBubblesChanged(true);
149
Mark Renouf08bc42a2019-03-07 13:01:59 -0500150 mBubbleController.removeBubble(mRow.getEntry().key, BubbleController.DISMISS_USER_GESTURE);
Mady Mellorebdbbb92018-11-15 14:36:48 -0800151 assertFalse(mStatusBarWindowController.getBubblesShowing());
Ned Burns01e38212019-01-03 16:32:52 -0500152 assertTrue(mRow.getEntry().isBubbleDismissed());
153 verify(mNotificationEntryManager).updateNotifications();
Mady Melloracb12152019-01-29 15:24:48 -0800154 verify(mBubbleStateChangeListener).onHasBubblesChanged(false);
Mady Mellorebdbbb92018-11-15 14:36:48 -0800155 }
156
157 @Test
158 public void testDismissStack() {
Mady Mellor3f2efdb2018-11-21 11:30:45 -0800159 mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */);
160 mBubbleController.updateBubble(mRow2.getEntry(), true /* updatePosition */);
Mady Mellorebdbbb92018-11-15 14:36:48 -0800161 assertTrue(mBubbleController.hasBubbles());
162
Mark Renouf08bc42a2019-03-07 13:01:59 -0500163 mBubbleController.dismissStack(BubbleController.DISMISS_USER_GESTURE);
Mady Mellorebdbbb92018-11-15 14:36:48 -0800164 assertFalse(mStatusBarWindowController.getBubblesShowing());
Mady Mellor3f2efdb2018-11-21 11:30:45 -0800165 verify(mNotificationEntryManager).updateNotifications();
Mady Melloracb12152019-01-29 15:24:48 -0800166 assertTrue(mRow.getEntry().isBubbleDismissed());
167 assertTrue(mRow2.getEntry().isBubbleDismissed());
Mady Mellorebdbbb92018-11-15 14:36:48 -0800168 }
169
170 @Test
Mady Melloracb12152019-01-29 15:24:48 -0800171 public void testExpandCollapseStack() {
Mady Mellorebdbbb92018-11-15 14:36:48 -0800172 assertFalse(mBubbleController.isStackExpanded());
Mady Melloracb12152019-01-29 15:24:48 -0800173
174 // Mark it as a bubble and add it explicitly
175 mEntryListener.onPendingEntryAdded(mRow.getEntry());
Mady Mellor3f2efdb2018-11-21 11:30:45 -0800176 mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */);
Mady Mellorebdbbb92018-11-15 14:36:48 -0800177
Mady Melloracb12152019-01-29 15:24:48 -0800178 // We should have bubbles & their notifs should show in the shade
179 assertTrue(mBubbleController.hasBubbles());
180 assertTrue(mRow.getEntry().showInShadeWhenBubble());
Issei Suzukiac9fcb72019-02-04 17:45:57 +0100181 assertFalse(mStatusBarWindowController.getBubbleExpanded());
Mady Melloracb12152019-01-29 15:24:48 -0800182
183 // Expand the stack
Mady Mellorebdbbb92018-11-15 14:36:48 -0800184 BubbleStackView stackView = mBubbleController.getStackView();
Mady Mellor3f2efdb2018-11-21 11:30:45 -0800185 stackView.expandStack();
Mady Mellorebdbbb92018-11-15 14:36:48 -0800186 assertTrue(mBubbleController.isStackExpanded());
Mady Melloracb12152019-01-29 15:24:48 -0800187 verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().key);
Issei Suzukiac9fcb72019-02-04 17:45:57 +0100188 assertTrue(mStatusBarWindowController.getBubbleExpanded());
Mady Mellorebdbbb92018-11-15 14:36:48 -0800189
Mady Melloracb12152019-01-29 15:24:48 -0800190 // Make sure it's no longer in the shade
191 assertFalse(mRow.getEntry().showInShadeWhenBubble());
192
193 // Collapse
Mady Mellor3f2efdb2018-11-21 11:30:45 -0800194 stackView.collapseStack();
Mady Melloracb12152019-01-29 15:24:48 -0800195 verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().key);
Mady Mellorebdbbb92018-11-15 14:36:48 -0800196 assertFalse(mBubbleController.isStackExpanded());
Issei Suzukiac9fcb72019-02-04 17:45:57 +0100197 assertFalse(mStatusBarWindowController.getBubbleExpanded());
Mady Mellorebdbbb92018-11-15 14:36:48 -0800198 }
199
200 @Test
Mady Melloracb12152019-01-29 15:24:48 -0800201 public void testCollapseAfterChangingExpandedBubble() {
202 // Mark it as a bubble and add it explicitly
203 mEntryListener.onPendingEntryAdded(mRow.getEntry());
204 mEntryListener.onPendingEntryAdded(mRow2.getEntry());
Mady Mellor3f2efdb2018-11-21 11:30:45 -0800205 mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */);
206 mBubbleController.updateBubble(mRow2.getEntry(), true /* updatePosition */);
Mady Mellorebdbbb92018-11-15 14:36:48 -0800207
Mady Melloracb12152019-01-29 15:24:48 -0800208 // We should have bubbles & their notifs should show in the shade
209 assertTrue(mBubbleController.hasBubbles());
210 assertTrue(mRow.getEntry().showInShadeWhenBubble());
211 assertTrue(mRow2.getEntry().showInShadeWhenBubble());
212
213 // Expand
Mady Mellorebdbbb92018-11-15 14:36:48 -0800214 BubbleStackView stackView = mBubbleController.getStackView();
Mady Mellor3f2efdb2018-11-21 11:30:45 -0800215 stackView.expandStack();
Mady Mellorebdbbb92018-11-15 14:36:48 -0800216 assertTrue(mBubbleController.isStackExpanded());
Mady Melloracb12152019-01-29 15:24:48 -0800217 verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().key);
Mady Mellorebdbbb92018-11-15 14:36:48 -0800218
Mady Melloracb12152019-01-29 15:24:48 -0800219 // Last added is the one that is expanded
Mady Mellor3dff9e62019-02-05 18:12:53 -0800220 assertEquals(mRow2.getEntry(), stackView.getExpandedBubbleView().getEntry());
Mady Melloracb12152019-01-29 15:24:48 -0800221 assertFalse(mRow2.getEntry().showInShadeWhenBubble());
222
223 // Switch which bubble is expanded
Mady Melloredd4ee12019-01-18 10:45:11 -0800224 stackView.setExpandedBubble(mRow.getEntry());
Mady Mellor3dff9e62019-02-05 18:12:53 -0800225 assertEquals(mRow.getEntry(), stackView.getExpandedBubbleView().getEntry());
Mady Melloracb12152019-01-29 15:24:48 -0800226 assertFalse(mRow.getEntry().showInShadeWhenBubble());
Mady Melloredd4ee12019-01-18 10:45:11 -0800227
Mady Melloracb12152019-01-29 15:24:48 -0800228 // collapse for previous bubble
229 verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().key);
230 // expand for selected bubble
231 verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().key);
Mady Melloredd4ee12019-01-18 10:45:11 -0800232
Mady Melloracb12152019-01-29 15:24:48 -0800233 // Collapse
Mady Mellorebdbbb92018-11-15 14:36:48 -0800234 mBubbleController.collapseStack();
235 assertFalse(mBubbleController.isStackExpanded());
236 }
237
Ned Burns01e38212019-01-03 16:32:52 -0500238 @Test
Mady Melloracb12152019-01-29 15:24:48 -0800239 public void testExpansionRemovesShowInShade() {
240 // Mark it as a bubble and add it explicitly
241 mEntryListener.onPendingEntryAdded(mRow.getEntry());
242 mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */);
243
244 // We should have bubbles & their notifs should show in the shade
245 assertTrue(mBubbleController.hasBubbles());
246 assertTrue(mRow.getEntry().showInShadeWhenBubble());
247
248 // Expand
249 BubbleStackView stackView = mBubbleController.getStackView();
250 stackView.expandStack();
251 assertTrue(mBubbleController.isStackExpanded());
252 verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().key);
253
254 // No longer show shade in notif after expansion
255 assertFalse(mRow.getEntry().showInShadeWhenBubble());
256 }
257
258 @Test
259 public void testRemoveLastExpandedCollapses() {
260 // Mark it as a bubble and add it explicitly
261 mEntryListener.onPendingEntryAdded(mRow.getEntry());
262 mEntryListener.onPendingEntryAdded(mRow2.getEntry());
263 mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */);
264 mBubbleController.updateBubble(mRow2.getEntry(), true /* updatePosition */);
265 verify(mBubbleStateChangeListener).onHasBubblesChanged(true);
266
267 // Expand
268 BubbleStackView stackView = mBubbleController.getStackView();
269 stackView.expandStack();
270
271 assertTrue(mBubbleController.isStackExpanded());
272 verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow2.getEntry().key);
273
274 // Last added is the one that is expanded
Mady Mellor3dff9e62019-02-05 18:12:53 -0800275 assertEquals(mRow2.getEntry(), stackView.getExpandedBubbleView().getEntry());
Mady Melloracb12152019-01-29 15:24:48 -0800276 assertFalse(mRow2.getEntry().showInShadeWhenBubble());
277
278 // Dismiss currently expanded
Mark Renouf08bc42a2019-03-07 13:01:59 -0500279 mBubbleController.removeBubble(stackView.getExpandedBubbleView().getKey(),
280 BubbleController.DISMISS_USER_GESTURE);
Mady Melloracb12152019-01-29 15:24:48 -0800281 verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow2.getEntry().key);
282
283 // Make sure next bubble is selected
Mady Mellor3dff9e62019-02-05 18:12:53 -0800284 assertEquals(mRow.getEntry(), stackView.getExpandedBubbleView().getEntry());
Mady Melloracb12152019-01-29 15:24:48 -0800285 verify(mBubbleExpandListener).onBubbleExpandChanged(true, mRow.getEntry().key);
286
287 // Dismiss that one
Mark Renouf08bc42a2019-03-07 13:01:59 -0500288 mBubbleController.removeBubble(stackView.getExpandedBubbleView().getKey(),
289 BubbleController.DISMISS_USER_GESTURE);
Mady Melloracb12152019-01-29 15:24:48 -0800290
291 // Make sure state changes and collapse happens
292 verify(mBubbleExpandListener).onBubbleExpandChanged(false, mRow.getEntry().key);
293 verify(mBubbleStateChangeListener).onHasBubblesChanged(false);
294 assertFalse(mBubbleController.hasBubbles());
295 }
296
297 @Test
Ned Burns01e38212019-01-03 16:32:52 -0500298 public void testMarkNewNotificationAsBubble() {
299 mEntryListener.onPendingEntryAdded(mRow.getEntry());
300 assertTrue(mRow.getEntry().isBubble());
301 }
302
Mady Mellor3f2efdb2018-11-21 11:30:45 -0800303 @Test
304 public void testMarkNewNotificationAsShowInShade() {
305 mEntryListener.onPendingEntryAdded(mRow.getEntry());
306 assertTrue(mRow.getEntry().showInShadeWhenBubble());
307 }
308
Mark Renouf08bc42a2019-03-07 13:01:59 -0500309 @Test
310 public void testDeleteIntent_removeBubble_aged() throws PendingIntent.CanceledException {
311 mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */);
312 mBubbleController.removeBubble(mRow.getEntry().key, BubbleController.DISMISS_AGED);
313 verify(mDeleteIntent, never()).send();
314 }
315
316 @Test
317 public void testDeleteIntent_removeBubble_user() throws PendingIntent.CanceledException {
318 mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */);
319 mBubbleController.removeBubble(mRow.getEntry().key, BubbleController.DISMISS_USER_GESTURE);
320 verify(mDeleteIntent, times(1)).send();
321 }
322
323 @Test
324 public void testDeleteIntent_dismissStack() throws PendingIntent.CanceledException {
325 mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */);
326 mBubbleController.updateBubble(mRow2.getEntry(), true /* updatePosition */);
327 mBubbleController.dismissStack(BubbleController.DISMISS_USER_GESTURE);
328 verify(mDeleteIntent, times(2)).send();
329 }
330
Mady Mellorebdbbb92018-11-15 14:36:48 -0800331 static class TestableBubbleController extends BubbleController {
332
333 TestableBubbleController(Context context,
Mady Mellorcfd06c12019-02-13 14:32:12 -0800334 StatusBarWindowController statusBarWindowController, BubbleData data) {
335 super(context, statusBarWindowController, data);
Mady Mellorebdbbb92018-11-15 14:36:48 -0800336 }
Mady Mellor9bad2242019-01-28 11:21:51 -0800337
338 @Override
Mady Mellorf6e3ac02019-01-29 10:37:52 -0800339 public boolean shouldAutoBubbleForFlags(Context c, NotificationEntry entry) {
Mady Mellor9bad2242019-01-28 11:21:51 -0800340 return entry.notification.getNotification().getBubbleMetadata() != null;
341 }
Mady Mellorebdbbb92018-11-15 14:36:48 -0800342 }
343}