blob: 8224c88e6c75b16723b42d04c72982e4d93a2d50 [file] [log] [blame]
Mark Renouf9ba6cea2019-04-17 11:53:50 -04001/*
2 * Copyright (C) 2019 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
Lyn Han72f50902019-10-25 15:55:49 -070019import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
20
Mark Renouf9ba6cea2019-04-17 11:53:50 -040021import static com.google.common.truth.Truth.assertThat;
22
Mark Renouf9ba6cea2019-04-17 11:53:50 -040023import static org.mockito.Mockito.mock;
Mark Renoufba5ab512019-05-02 15:21:01 -040024import static org.mockito.Mockito.reset;
Mark Renouf9ba6cea2019-04-17 11:53:50 -040025import static org.mockito.Mockito.verify;
Mark Renouf82a40e62019-05-23 16:16:24 -040026import static org.mockito.Mockito.verifyZeroInteractions;
Mark Renouf9ba6cea2019-04-17 11:53:50 -040027import static org.mockito.Mockito.when;
28
29import android.app.Notification;
30import android.app.PendingIntent;
31import android.graphics.drawable.Icon;
32import android.os.UserHandle;
33import android.service.notification.StatusBarNotification;
34import android.testing.AndroidTestingRunner;
35import android.testing.TestableLooper;
Mark Renouf82a40e62019-05-23 16:16:24 -040036import android.util.Pair;
Mark Renouf9ba6cea2019-04-17 11:53:50 -040037
38import androidx.test.filters.SmallTest;
39
40import com.android.systemui.SysuiTestCase;
41import com.android.systemui.bubbles.BubbleData.TimeSource;
Mark Renouf9ba6cea2019-04-17 11:53:50 -040042import com.android.systemui.statusbar.notification.collection.NotificationEntry;
Beverly79c89ec2019-12-13 10:33:01 -050043import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
Mady Mellorb25f9062019-12-09 12:12:57 -080044import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
Kevin Han933dc7c2020-01-29 11:17:46 -080045import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
Mark Renouf9ba6cea2019-04-17 11:53:50 -040046
47import com.google.common.collect.ImmutableList;
48
49import org.junit.Before;
50import org.junit.Test;
51import org.junit.runner.RunWith;
Mark Renouf82a40e62019-05-23 16:16:24 -040052import org.mockito.ArgumentCaptor;
53import org.mockito.Captor;
Mark Renouf9ba6cea2019-04-17 11:53:50 -040054import org.mockito.Mock;
55import org.mockito.MockitoAnnotations;
56
Mark Renouf82a40e62019-05-23 16:16:24 -040057/**
58 * Tests operations and the resulting state managed by BubbleData.
59 * <p>
60 * After each operation to verify, {@link #verifyUpdateReceived()} ensures the listener was called
61 * and captures the Update object received there.
62 * <p>
63 * Other methods beginning with 'assert' access the captured update object and assert on specific
64 * aspects of it.
65 */
Mark Renouf9ba6cea2019-04-17 11:53:50 -040066@SmallTest
67@RunWith(AndroidTestingRunner.class)
68@TestableLooper.RunWithLooper(setAsMainLooper = true)
69public class BubbleDataTest extends SysuiTestCase {
70
71 private NotificationEntry mEntryA1;
72 private NotificationEntry mEntryA2;
73 private NotificationEntry mEntryA3;
74 private NotificationEntry mEntryB1;
75 private NotificationEntry mEntryB2;
76 private NotificationEntry mEntryB3;
77 private NotificationEntry mEntryC1;
Lyn Han72f50902019-10-25 15:55:49 -070078 private NotificationEntry mEntryInterruptive;
79 private NotificationEntry mEntryDismissed;
Mark Renouf9ba6cea2019-04-17 11:53:50 -040080
81 private Bubble mBubbleA1;
82 private Bubble mBubbleA2;
83 private Bubble mBubbleA3;
84 private Bubble mBubbleB1;
85 private Bubble mBubbleB2;
86 private Bubble mBubbleB3;
87 private Bubble mBubbleC1;
Mady Mellor3df7ab02019-12-09 15:07:10 -080088 private Bubble mBubbleInterruptive;
89 private Bubble mBubbleDismissed;
Mark Renouf9ba6cea2019-04-17 11:53:50 -040090
91 private BubbleData mBubbleData;
92
93 @Mock
94 private TimeSource mTimeSource;
95 @Mock
96 private BubbleData.Listener mListener;
97 @Mock
98 private PendingIntent mExpandIntent;
99 @Mock
100 private PendingIntent mDeleteIntent;
101
102 private NotificationTestHelper mNotificationTestHelper;
103
Mark Renouf82a40e62019-05-23 16:16:24 -0400104 @Captor
105 private ArgumentCaptor<BubbleData.Update> mUpdateCaptor;
106
Mady Mellorf44b6832020-01-14 13:26:14 -0800107 @Mock
108 private BubbleController.NotificationSuppressionChangedListener mSuppressionListener;
109
Mark Renouf9ba6cea2019-04-17 11:53:50 -0400110 @Before
111 public void setUp() throws Exception {
Kevin Hana7c21be2020-04-01 17:58:35 -0700112 mNotificationTestHelper = new NotificationTestHelper(
113 mContext,
114 mDependency,
115 TestableLooper.get(this));
Mark Renouf9ba6cea2019-04-17 11:53:50 -0400116 MockitoAnnotations.initMocks(this);
117
118 mEntryA1 = createBubbleEntry(1, "a1", "package.a");
119 mEntryA2 = createBubbleEntry(1, "a2", "package.a");
120 mEntryA3 = createBubbleEntry(1, "a3", "package.a");
121 mEntryB1 = createBubbleEntry(1, "b1", "package.b");
122 mEntryB2 = createBubbleEntry(1, "b2", "package.b");
123 mEntryB3 = createBubbleEntry(1, "b3", "package.b");
124 mEntryC1 = createBubbleEntry(1, "c1", "package.c");
125
Lyn Han72f50902019-10-25 15:55:49 -0700126 mEntryInterruptive = createBubbleEntry(1, "interruptive", "package.d");
127 modifyRanking(mEntryInterruptive)
128 .setVisuallyInterruptive(true)
129 .build();
Mady Mellorf44b6832020-01-14 13:26:14 -0800130 mBubbleInterruptive = new Bubble(mEntryInterruptive, mSuppressionListener);
Lyn Han72f50902019-10-25 15:55:49 -0700131
Mady Mellorb25f9062019-12-09 12:12:57 -0800132 ExpandableNotificationRow row = mNotificationTestHelper.createBubble();
Lyn Han72f50902019-10-25 15:55:49 -0700133 mEntryDismissed = createBubbleEntry(1, "dismissed", "package.d");
Mady Mellorb25f9062019-12-09 12:12:57 -0800134 mEntryDismissed.setRow(row);
Mady Mellorf44b6832020-01-14 13:26:14 -0800135 mBubbleDismissed = new Bubble(mEntryDismissed, mSuppressionListener);
Lyn Han72f50902019-10-25 15:55:49 -0700136
Mady Mellorf44b6832020-01-14 13:26:14 -0800137 mBubbleA1 = new Bubble(mEntryA1, mSuppressionListener);
138 mBubbleA2 = new Bubble(mEntryA2, mSuppressionListener);
139 mBubbleA3 = new Bubble(mEntryA3, mSuppressionListener);
140 mBubbleB1 = new Bubble(mEntryB1, mSuppressionListener);
141 mBubbleB2 = new Bubble(mEntryB2, mSuppressionListener);
142 mBubbleB3 = new Bubble(mEntryB3, mSuppressionListener);
143 mBubbleC1 = new Bubble(mEntryC1, mSuppressionListener);
Mark Renouf9ba6cea2019-04-17 11:53:50 -0400144
145 mBubbleData = new BubbleData(getContext());
146
147 // Used by BubbleData to set lastAccessedTime
148 when(mTimeSource.currentTimeMillis()).thenReturn(1000L);
149 mBubbleData.setTimeSource(mTimeSource);
Mark Renoufba5ab512019-05-02 15:21:01 -0400150
151 // Assert baseline starting state
152 assertThat(mBubbleData.hasBubbles()).isFalse();
153 assertThat(mBubbleData.isExpanded()).isFalse();
154 assertThat(mBubbleData.getSelectedBubble()).isNull();
155 }
156
157 @Test
158 public void testAddBubble() {
159 // Setup
160 mBubbleData.setListener(mListener);
161
162 // Test
163 sendUpdatedEntryAtTime(mEntryA1, 1000);
164
165 // Verify
Mark Renouf82a40e62019-05-23 16:16:24 -0400166 verifyUpdateReceived();
167 assertBubbleAdded(mBubbleA1);
168 assertSelectionChangedTo(mBubbleA1);
Mark Renoufba5ab512019-05-02 15:21:01 -0400169 }
170
171 @Test
172 public void testRemoveBubble() {
173 // Setup
174 sendUpdatedEntryAtTime(mEntryA1, 1000);
175 sendUpdatedEntryAtTime(mEntryA2, 2000);
176 sendUpdatedEntryAtTime(mEntryA3, 3000);
177 mBubbleData.setListener(mListener);
178
179 // Test
Pinyao Ting3c930612020-05-19 00:26:03 +0000180 mBubbleData.notificationEntryRemoved(
181 mEntryA1.getKey(), BubbleController.DISMISS_USER_GESTURE);
Mark Renoufba5ab512019-05-02 15:21:01 -0400182
183 // Verify
Mark Renouf82a40e62019-05-23 16:16:24 -0400184 verifyUpdateReceived();
185 assertBubbleRemoved(mBubbleA1, BubbleController.DISMISS_USER_GESTURE);
Mark Renoufba5ab512019-05-02 15:21:01 -0400186 }
187
Lyn Han72f50902019-10-25 15:55:49 -0700188 @Test
189 public void ifSuppress_hideFlyout() {
190 // Setup
191 mBubbleData.setListener(mListener);
192
193 // Test
Mady Mellor3df7ab02019-12-09 15:07:10 -0800194 mBubbleData.notificationEntryUpdated(mBubbleC1, /* suppressFlyout */ true, /* showInShade */
Lyn Han72f50902019-10-25 15:55:49 -0700195 true);
196
197 // Verify
198 verifyUpdateReceived();
199 BubbleData.Update update = mUpdateCaptor.getValue();
Mady Mellorb8aaf972019-11-26 10:28:00 -0800200 assertThat(update.addedBubble.showFlyout()).isFalse();
Lyn Han72f50902019-10-25 15:55:49 -0700201 }
202
203 @Test
204 public void ifInterruptiveAndNotSuppressed_thenShowFlyout() {
205 // Setup
206 mBubbleData.setListener(mListener);
207
208 // Test
Mady Mellor3df7ab02019-12-09 15:07:10 -0800209 mBubbleData.notificationEntryUpdated(mBubbleInterruptive,
Mady Mellorb25f9062019-12-09 12:12:57 -0800210 false /* suppressFlyout */, true /* showInShade */);
Lyn Han72f50902019-10-25 15:55:49 -0700211
212 // Verify
213 verifyUpdateReceived();
214 BubbleData.Update update = mUpdateCaptor.getValue();
Mady Mellorb8aaf972019-11-26 10:28:00 -0800215 assertThat(update.addedBubble.showFlyout()).isTrue();
Lyn Han72f50902019-10-25 15:55:49 -0700216 }
217
218 @Test
219 public void sameUpdate_InShade_thenHideFlyout() {
220 // Setup
221 mBubbleData.setListener(mListener);
222
223 // Test
Mady Mellor3df7ab02019-12-09 15:07:10 -0800224 mBubbleData.notificationEntryUpdated(mBubbleC1, false /* suppressFlyout */,
Mady Mellorb25f9062019-12-09 12:12:57 -0800225 true /* showInShade */);
Lyn Han72f50902019-10-25 15:55:49 -0700226 verifyUpdateReceived();
227
Mady Mellor3df7ab02019-12-09 15:07:10 -0800228 mBubbleData.notificationEntryUpdated(mBubbleC1, false /* suppressFlyout */,
Mady Mellorb25f9062019-12-09 12:12:57 -0800229 true /* showInShade */);
Lyn Han72f50902019-10-25 15:55:49 -0700230 verifyUpdateReceived();
231
232 // Verify
233 BubbleData.Update update = mUpdateCaptor.getValue();
Mady Mellorb8aaf972019-11-26 10:28:00 -0800234 assertThat(update.updatedBubble.showFlyout()).isFalse();
Lyn Han72f50902019-10-25 15:55:49 -0700235 }
236
237 @Test
Mady Mellor8e97a962020-01-21 16:20:15 -0800238 public void sameUpdate_NotInShade_NotVisuallyInterruptive_dontShowFlyout() {
Lyn Han72f50902019-10-25 15:55:49 -0700239 // Setup
240 mBubbleData.setListener(mListener);
Lyn Han72f50902019-10-25 15:55:49 -0700241
242 // Test
Mady Mellor3df7ab02019-12-09 15:07:10 -0800243 mBubbleData.notificationEntryUpdated(mBubbleDismissed, false /* suppressFlyout */,
244 true /* showInShade */);
Lyn Han72f50902019-10-25 15:55:49 -0700245 verifyUpdateReceived();
246
Mady Mellorf44b6832020-01-14 13:26:14 -0800247 // Suppress the notif / make it look dismissed
248 mBubbleDismissed.setSuppressNotification(true);
Mady Mellorb25f9062019-12-09 12:12:57 -0800249
Mady Mellor3df7ab02019-12-09 15:07:10 -0800250 mBubbleData.notificationEntryUpdated(mBubbleDismissed, false /* suppressFlyout */,
251 true /* showInShade */);
Lyn Han72f50902019-10-25 15:55:49 -0700252 verifyUpdateReceived();
253
254 // Verify
255 BubbleData.Update update = mUpdateCaptor.getValue();
Mady Mellor8e97a962020-01-21 16:20:15 -0800256 assertThat(update.updatedBubble.showFlyout()).isFalse();
Lyn Han72f50902019-10-25 15:55:49 -0700257 }
258
Mady Mellora55133d2020-05-01 15:11:10 -0700259 //
260 // Overflow
261 //
Mark Renoufba5ab512019-05-02 15:21:01 -0400262
263 /**
Mady Mellora55133d2020-05-01 15:11:10 -0700264 * Verifies that when the bubble stack reaches its maximum, the oldest bubble is overflowed.
Mark Renoufba5ab512019-05-02 15:21:01 -0400265 */
266 @Test
Mady Mellora55133d2020-05-01 15:11:10 -0700267 public void testOverflow_add_stackAtMaxBubbles_overflowsOldest() {
Mark Renoufba5ab512019-05-02 15:21:01 -0400268 // Setup
269 sendUpdatedEntryAtTime(mEntryA1, 1000);
270 sendUpdatedEntryAtTime(mEntryA2, 2000);
271 sendUpdatedEntryAtTime(mEntryA3, 3000);
272 sendUpdatedEntryAtTime(mEntryB1, 4000);
273 sendUpdatedEntryAtTime(mEntryB2, 5000);
274 mBubbleData.setListener(mListener);
275
Mark Renoufba5ab512019-05-02 15:21:01 -0400276 sendUpdatedEntryAtTime(mEntryC1, 6000);
Mark Renouf82a40e62019-05-23 16:16:24 -0400277 verifyUpdateReceived();
278 assertBubbleRemoved(mBubbleA1, BubbleController.DISMISS_AGED);
Lyn Hanbf1b3d62020-03-12 10:31:19 -0700279 assertOverflowChangedTo(ImmutableList.of(mBubbleA1));
280
281 Bubble bubbleA1 = mBubbleData.getOrCreateBubble(mEntryA1);
282 bubbleA1.markUpdatedAt(7000L);
283 mBubbleData.notificationEntryUpdated(bubbleA1, false /* suppressFlyout*/,
284 true /* showInShade */);
285 verifyUpdateReceived();
286 assertBubbleRemoved(mBubbleA2, BubbleController.DISMISS_AGED);
287 assertOverflowChangedTo(ImmutableList.of(mBubbleA2));
Mark Renoufba5ab512019-05-02 15:21:01 -0400288 }
289
Mady Mellora55133d2020-05-01 15:11:10 -0700290 /**
291 * Verifies that once the number of overflowed bubbles reaches its maximum, the oldest
292 * overflow bubble is removed.
293 */
Lyn Han2f6e89d2020-04-15 10:01:01 -0700294 @Test
Mady Mellora55133d2020-05-01 15:11:10 -0700295 public void testOverflow_maxReached_bubbleRemoved() {
Lyn Han2f6e89d2020-04-15 10:01:01 -0700296 // Setup
297 sendUpdatedEntryAtTime(mEntryA1, 1000);
298 sendUpdatedEntryAtTime(mEntryA2, 2000);
299 sendUpdatedEntryAtTime(mEntryA3, 3000);
300 mBubbleData.setListener(mListener);
301
302 mBubbleData.setMaxOverflowBubbles(1);
Pinyao Ting3c930612020-05-19 00:26:03 +0000303 mBubbleData.notificationEntryRemoved(
304 mEntryA1.getKey(), BubbleController.DISMISS_USER_GESTURE);
Lyn Han2f6e89d2020-04-15 10:01:01 -0700305 verifyUpdateReceived();
306 assertOverflowChangedTo(ImmutableList.of(mBubbleA1));
307
308 // Overflow max of 1 is reached; A1 is oldest, so it gets removed
Pinyao Ting3c930612020-05-19 00:26:03 +0000309 mBubbleData.notificationEntryRemoved(
310 mEntryA2.getKey(), BubbleController.DISMISS_USER_GESTURE);
Lyn Han2f6e89d2020-04-15 10:01:01 -0700311 verifyUpdateReceived();
312 assertOverflowChangedTo(ImmutableList.of(mBubbleA2));
313 }
314
Mark Renoufba5ab512019-05-02 15:21:01 -0400315 /**
Mady Mellora55133d2020-05-01 15:11:10 -0700316 * Verifies that overflow bubbles are canceled on notif entry removal.
Mark Renoufba5ab512019-05-02 15:21:01 -0400317 */
318 @Test
Mady Mellora55133d2020-05-01 15:11:10 -0700319 public void testOverflow_notifCanceled_removesOverflowBubble() {
320 // Setup
321 sendUpdatedEntryAtTime(mEntryA1, 1000);
322 sendUpdatedEntryAtTime(mEntryA2, 2000);
323 sendUpdatedEntryAtTime(mEntryA3, 3000);
324 sendUpdatedEntryAtTime(mEntryB1, 4000);
325 sendUpdatedEntryAtTime(mEntryB2, 5000);
326 sendUpdatedEntryAtTime(mEntryB3, 6000); // [A2, A3, B1, B2, B3], overflow: [A1]
327 sendUpdatedEntryAtTime(mEntryC1, 7000); // [A3, B1, B2, B3, C1], overflow: [A2, A1]
328 mBubbleData.setListener(mListener);
329
330 // Test
Pinyao Ting3c930612020-05-19 00:26:03 +0000331 mBubbleData.notificationEntryRemoved(mEntryA1.getKey(),
332 BubbleController.DISMISS_NOTIF_CANCEL);
Mady Mellora55133d2020-05-01 15:11:10 -0700333 verifyUpdateReceived();
334 assertOverflowChangedTo(ImmutableList.of(mBubbleA2));
335
336 // Test
Pinyao Ting3c930612020-05-19 00:26:03 +0000337 mBubbleData.notificationEntryRemoved(mEntryA2.getKey(),
338 BubbleController.DISMISS_GROUP_CANCELLED);
Mady Mellora55133d2020-05-01 15:11:10 -0700339 verifyUpdateReceived();
340 assertOverflowChangedTo(ImmutableList.of());
341 }
342
343 // COLLAPSED / ADD
344
345 /**
346 * Verifies that new bubbles insert to the left when collapsed.
347 * <p>
348 * Placement within the list is based on {@link Bubble#getLastActivity()}, descending
349 * order (with most recent first).
350 */
351 @Test
352 public void test_collapsed_addBubble() {
Mark Renoufba5ab512019-05-02 15:21:01 -0400353 // Setup
354 mBubbleData.setListener(mListener);
355
356 // Test
357 sendUpdatedEntryAtTime(mEntryA1, 1000);
Mark Renouf82a40e62019-05-23 16:16:24 -0400358 verifyUpdateReceived();
359 assertOrderNotChanged();
Mark Renoufba5ab512019-05-02 15:21:01 -0400360
Mark Renoufba5ab512019-05-02 15:21:01 -0400361 sendUpdatedEntryAtTime(mEntryB1, 2000);
Mark Renouf82a40e62019-05-23 16:16:24 -0400362 verifyUpdateReceived();
363 assertOrderChangedTo(mBubbleB1, mBubbleA1);
Mark Renoufba5ab512019-05-02 15:21:01 -0400364
Mark Renoufba5ab512019-05-02 15:21:01 -0400365 sendUpdatedEntryAtTime(mEntryB2, 3000);
Mark Renouf82a40e62019-05-23 16:16:24 -0400366 verifyUpdateReceived();
367 assertOrderChangedTo(mBubbleB2, mBubbleB1, mBubbleA1);
Mark Renoufba5ab512019-05-02 15:21:01 -0400368
Mark Renoufba5ab512019-05-02 15:21:01 -0400369 sendUpdatedEntryAtTime(mEntryA2, 4000);
Mark Renouf82a40e62019-05-23 16:16:24 -0400370 verifyUpdateReceived();
Mady Mellora55133d2020-05-01 15:11:10 -0700371 assertOrderChangedTo(mBubbleA2, mBubbleB2, mBubbleB1, mBubbleA1);
Mark Renoufba5ab512019-05-02 15:21:01 -0400372 }
373
374 /**
375 * Verifies that new bubbles become the selected bubble when they appear when the stack is in
376 * the collapsed state.
377 *
378 * @see #test_collapsed_updateBubble_selectionChanges()
Mark Renoufba5ab512019-05-02 15:21:01 -0400379 */
380 @Test
381 public void test_collapsed_addBubble_selectionChanges() {
382 // Setup
383 mBubbleData.setListener(mListener);
384
385 // Test
386 sendUpdatedEntryAtTime(mEntryA1, 1000);
Mark Renouf82a40e62019-05-23 16:16:24 -0400387 verifyUpdateReceived();
388 assertSelectionChangedTo(mBubbleA1);
Mark Renoufba5ab512019-05-02 15:21:01 -0400389
Mark Renoufba5ab512019-05-02 15:21:01 -0400390 sendUpdatedEntryAtTime(mEntryB1, 2000);
Mark Renouf82a40e62019-05-23 16:16:24 -0400391 verifyUpdateReceived();
392 assertSelectionChangedTo(mBubbleB1);
Mark Renoufba5ab512019-05-02 15:21:01 -0400393
Mark Renoufba5ab512019-05-02 15:21:01 -0400394 sendUpdatedEntryAtTime(mEntryB2, 3000);
Mark Renouf82a40e62019-05-23 16:16:24 -0400395 verifyUpdateReceived();
396 assertSelectionChangedTo(mBubbleB2);
Mark Renoufba5ab512019-05-02 15:21:01 -0400397
Mark Renoufba5ab512019-05-02 15:21:01 -0400398 sendUpdatedEntryAtTime(mEntryA2, 4000);
Mark Renouf82a40e62019-05-23 16:16:24 -0400399 verifyUpdateReceived();
400 assertSelectionChangedTo(mBubbleA2);
Mark Renoufba5ab512019-05-02 15:21:01 -0400401 }
Mark Renouf82a40e62019-05-23 16:16:24 -0400402
Mark Renoufba5ab512019-05-02 15:21:01 -0400403 // COLLAPSED / REMOVE
404
405 /**
Mady Mellora55133d2020-05-01 15:11:10 -0700406 * Verifies order of bubbles after a removal.
Mark Renoufba5ab512019-05-02 15:21:01 -0400407 */
408 @Test
Mady Mellora55133d2020-05-01 15:11:10 -0700409 public void test_collapsed_removeBubble_sort() {
Mark Renoufba5ab512019-05-02 15:21:01 -0400410 // Setup
411 sendUpdatedEntryAtTime(mEntryA1, 1000);
412 sendUpdatedEntryAtTime(mEntryB1, 2000);
413 sendUpdatedEntryAtTime(mEntryB2, 3000);
Mady Mellora55133d2020-05-01 15:11:10 -0700414 sendUpdatedEntryAtTime(mEntryA2, 4000); // [A2, B2, B1, A1]
Mark Renoufba5ab512019-05-02 15:21:01 -0400415 mBubbleData.setListener(mListener);
416
417 // Test
Pinyao Ting3c930612020-05-19 00:26:03 +0000418 mBubbleData.notificationEntryRemoved(
419 mEntryA2.getKey(), BubbleController.DISMISS_USER_GESTURE);
Mark Renouf82a40e62019-05-23 16:16:24 -0400420 verifyUpdateReceived();
Mady Mellora55133d2020-05-01 15:11:10 -0700421 // TODO: this should fail if things work as I expect them to?
Mark Renouf82a40e62019-05-23 16:16:24 -0400422 assertOrderChangedTo(mBubbleB2, mBubbleB1, mBubbleA1);
Mark Renoufba5ab512019-05-02 15:21:01 -0400423 }
424
Mark Renoufba5ab512019-05-02 15:21:01 -0400425 /**
426 * Verifies that onOrderChanged is not called when a bubble is removed if the removal does not
427 * cause other bubbles to change position.
428 */
429 @Test
430 public void test_collapsed_removeOldestBubble_doesNotCallOnOrderChanged() {
431 // Setup
432 sendUpdatedEntryAtTime(mEntryA1, 1000);
433 sendUpdatedEntryAtTime(mEntryB1, 2000);
434 sendUpdatedEntryAtTime(mEntryB2, 3000);
Mady Mellora55133d2020-05-01 15:11:10 -0700435 sendUpdatedEntryAtTime(mEntryA2, 4000); // [A2, B2, B1, A1]
Mark Renoufba5ab512019-05-02 15:21:01 -0400436 mBubbleData.setListener(mListener);
437
438 // Test
Pinyao Ting3c930612020-05-19 00:26:03 +0000439 mBubbleData.notificationEntryRemoved(
440 mEntryA1.getKey(), BubbleController.DISMISS_USER_GESTURE);
Mark Renouf82a40e62019-05-23 16:16:24 -0400441 verifyUpdateReceived();
442 assertOrderNotChanged();
Mark Renoufba5ab512019-05-02 15:21:01 -0400443 }
444
445 /**
Mark Renoufba5ab512019-05-02 15:21:01 -0400446 * Verifies that when the selected bubble is removed with the stack in the collapsed state,
447 * the selection moves to the next most-recently updated bubble.
448 */
449 @Test
450 public void test_collapsed_removeBubble_selectionChanges() {
451 // Setup
452 sendUpdatedEntryAtTime(mEntryA1, 1000);
453 sendUpdatedEntryAtTime(mEntryB1, 2000);
454 sendUpdatedEntryAtTime(mEntryB2, 3000);
Mady Mellora55133d2020-05-01 15:11:10 -0700455 sendUpdatedEntryAtTime(mEntryA2, 4000); // [A2, B2, B1, A1]
Mark Renoufba5ab512019-05-02 15:21:01 -0400456 mBubbleData.setListener(mListener);
457
458 // Test
Pinyao Ting3c930612020-05-19 00:26:03 +0000459 mBubbleData.notificationEntryRemoved(
460 mEntryA2.getKey(), BubbleController.DISMISS_NOTIF_CANCEL);
Mark Renouf82a40e62019-05-23 16:16:24 -0400461 verifyUpdateReceived();
462 assertSelectionChangedTo(mBubbleB2);
Mark Renoufba5ab512019-05-02 15:21:01 -0400463 }
464
465 // COLLAPSED / UPDATE
466
467 /**
Mady Mellora55133d2020-05-01 15:11:10 -0700468 * Verifies that bubble ordering changes with updates while the stack is in the
Mark Renoufba5ab512019-05-02 15:21:01 -0400469 * collapsed state.
470 */
471 @Test
Mady Mellora55133d2020-05-01 15:11:10 -0700472 public void test_collapsed_updateBubble() {
Mark Renoufba5ab512019-05-02 15:21:01 -0400473 // Setup
474 sendUpdatedEntryAtTime(mEntryA1, 1000);
475 sendUpdatedEntryAtTime(mEntryB1, 2000);
476 sendUpdatedEntryAtTime(mEntryB2, 3000);
Mady Mellora55133d2020-05-01 15:11:10 -0700477 sendUpdatedEntryAtTime(mEntryA2, 4000); // [A2, B2, B1, A1]
Mark Renoufba5ab512019-05-02 15:21:01 -0400478 mBubbleData.setListener(mListener);
479
480 // Test
481 sendUpdatedEntryAtTime(mEntryB1, 5000);
Mark Renouf82a40e62019-05-23 16:16:24 -0400482 verifyUpdateReceived();
Mady Mellora55133d2020-05-01 15:11:10 -0700483 assertOrderChangedTo(mBubbleB1, mBubbleA2, mBubbleB2, mBubbleA1);
Mark Renoufba5ab512019-05-02 15:21:01 -0400484
Mark Renoufba5ab512019-05-02 15:21:01 -0400485 sendUpdatedEntryAtTime(mEntryA1, 6000);
Mark Renouf82a40e62019-05-23 16:16:24 -0400486 verifyUpdateReceived();
Mady Mellora55133d2020-05-01 15:11:10 -0700487 assertOrderChangedTo(mBubbleA1, mBubbleB1, mBubbleA2, mBubbleB2);
Mark Renoufba5ab512019-05-02 15:21:01 -0400488 }
489
490 /**
491 * Verifies that selection tracks the most recently updated bubble while in the collapsed state.
492 */
493 @Test
494 public void test_collapsed_updateBubble_selectionChanges() {
495 // Setup
496 sendUpdatedEntryAtTime(mEntryA1, 1000);
497 sendUpdatedEntryAtTime(mEntryB1, 2000);
498 sendUpdatedEntryAtTime(mEntryB2, 3000);
Mady Mellora55133d2020-05-01 15:11:10 -0700499 sendUpdatedEntryAtTime(mEntryA2, 4000); // [A2, B2, B1, A1]
Mark Renoufba5ab512019-05-02 15:21:01 -0400500 mBubbleData.setListener(mListener);
501
502 // Test
503 sendUpdatedEntryAtTime(mEntryB1, 5000);
Mark Renouf82a40e62019-05-23 16:16:24 -0400504 verifyUpdateReceived();
505 assertSelectionChangedTo(mBubbleB1);
Mark Renoufba5ab512019-05-02 15:21:01 -0400506
Mark Renoufba5ab512019-05-02 15:21:01 -0400507 sendUpdatedEntryAtTime(mEntryA1, 6000);
Mark Renouf82a40e62019-05-23 16:16:24 -0400508 verifyUpdateReceived();
509 assertSelectionChangedTo(mBubbleA1);
Mark Renoufba5ab512019-05-02 15:21:01 -0400510 }
511
512 /**
Mark Renoufba5ab512019-05-02 15:21:01 -0400513 * Verifies that a request to expand the stack has no effect if there are no bubbles.
514 */
515 @Test
516 public void test_collapsed_expansion_whenEmpty_doesNothing() {
517 assertThat(mBubbleData.hasBubbles()).isFalse();
Mark Renouf82a40e62019-05-23 16:16:24 -0400518 mBubbleData.setListener(mListener);
Mark Renoufba5ab512019-05-02 15:21:01 -0400519
Mark Renouf82a40e62019-05-23 16:16:24 -0400520 changeExpandedStateAtTime(true, 2000L);
521 verifyZeroInteractions(mListener);
Mark Renoufba5ab512019-05-02 15:21:01 -0400522 }
523
Mady Mellora55133d2020-05-01 15:11:10 -0700524 /**
525 * Verifies that removing the last bubble clears the selected bubble and collapses the stack.
526 */
Mark Renoufba5ab512019-05-02 15:21:01 -0400527 @Test
528 public void test_collapsed_removeLastBubble_clearsSelectedBubble() {
529 // Setup
530 sendUpdatedEntryAtTime(mEntryA1, 1000);
531 mBubbleData.setListener(mListener);
532
533 // Test
Pinyao Ting3c930612020-05-19 00:26:03 +0000534 mBubbleData.notificationEntryRemoved(
535 mEntryA1.getKey(), BubbleController.DISMISS_USER_GESTURE);
Mark Renoufba5ab512019-05-02 15:21:01 -0400536
537 // Verify the selection was cleared.
Mark Renouf82a40e62019-05-23 16:16:24 -0400538 verifyUpdateReceived();
Mady Mellora55133d2020-05-01 15:11:10 -0700539 assertThat(mBubbleData.isExpanded()).isFalse();
Mark Renouf82a40e62019-05-23 16:16:24 -0400540 assertSelectionCleared();
Mark Renoufba5ab512019-05-02 15:21:01 -0400541 }
542
Mady Mellora55133d2020-05-01 15:11:10 -0700543 // EXPANDED / ADD / UPDATE
Mark Renoufba5ab512019-05-02 15:21:01 -0400544
545 /**
Mady Mellora55133d2020-05-01 15:11:10 -0700546 * Verifies that bubbles are added at the front of the stack.
Mark Renoufba5ab512019-05-02 15:21:01 -0400547 * <p>
Mady Mellora55133d2020-05-01 15:11:10 -0700548 * Placement within the list is based on {@link Bubble#getLastActivity()}, descending
Mark Renoufba5ab512019-05-02 15:21:01 -0400549 * order (with most recent first).
550 *
Mady Mellora55133d2020-05-01 15:11:10 -0700551 * @see #test_collapsed_addBubble()
Mark Renoufba5ab512019-05-02 15:21:01 -0400552 */
553 @Test
Mady Mellora55133d2020-05-01 15:11:10 -0700554 public void test_expanded_addBubble() {
Mark Renoufba5ab512019-05-02 15:21:01 -0400555 // Setup
556 sendUpdatedEntryAtTime(mEntryA1, 1000);
557 sendUpdatedEntryAtTime(mEntryA2, 2000);
558 sendUpdatedEntryAtTime(mEntryB1, 3000); // [B1, A2, A1]
Lyn Hanb9be52d2020-03-31 22:13:34 -0700559 changeExpandedStateAtTime(true, 4000L); // B1 marked updated at 4000L
Mark Renoufba5ab512019-05-02 15:21:01 -0400560 mBubbleData.setListener(mListener);
561
562 // Test
563 sendUpdatedEntryAtTime(mEntryC1, 4000);
Mark Renouf82a40e62019-05-23 16:16:24 -0400564 verifyUpdateReceived();
Mady Mellora55133d2020-05-01 15:11:10 -0700565 assertOrderChangedTo(mBubbleC1, mBubbleB1, mBubbleA2, mBubbleA1);
Mark Renoufba5ab512019-05-02 15:21:01 -0400566 }
567
568 /**
Mark Renoufba5ab512019-05-02 15:21:01 -0400569 * Verifies that updates to bubbles while expanded do not result in any change to sorting
Mady Mellora55133d2020-05-01 15:11:10 -0700570 * of bubbles.
Mark Renoufba5ab512019-05-02 15:21:01 -0400571 */
572 @Test
Mady Mellora55133d2020-05-01 15:11:10 -0700573 public void test_expanded_updateBubble_noChanges() {
Mark Renoufba5ab512019-05-02 15:21:01 -0400574 // Setup
575 sendUpdatedEntryAtTime(mEntryA1, 1000);
576 sendUpdatedEntryAtTime(mEntryA2, 2000);
577 sendUpdatedEntryAtTime(mEntryB1, 3000);
578 sendUpdatedEntryAtTime(mEntryB2, 4000); // [B2, B1, A2, A1]
579 changeExpandedStateAtTime(true, 5000L);
580 mBubbleData.setListener(mListener);
581
582 // Test
583 sendUpdatedEntryAtTime(mEntryA1, 4000);
Mark Renouf82a40e62019-05-23 16:16:24 -0400584 verifyUpdateReceived();
585 assertOrderNotChanged();
Mark Renoufba5ab512019-05-02 15:21:01 -0400586 }
587
588 /**
589 * Verifies that updates to bubbles while expanded do not result in any change to selection.
590 *
591 * @see #test_collapsed_addBubble_selectionChanges()
Mark Renoufba5ab512019-05-02 15:21:01 -0400592 */
593 @Test
594 public void test_expanded_updateBubble_noSelectionChanges() {
595 // Setup
596 sendUpdatedEntryAtTime(mEntryA1, 1000);
597 sendUpdatedEntryAtTime(mEntryA2, 2000);
598 sendUpdatedEntryAtTime(mEntryB1, 3000);
599 sendUpdatedEntryAtTime(mEntryB2, 4000); // [B2, B1, A2, A1]
600 changeExpandedStateAtTime(true, 5000L);
601 mBubbleData.setListener(mListener);
602
603 // Test
604 sendUpdatedEntryAtTime(mEntryA1, 6000);
Mark Renouf82a40e62019-05-23 16:16:24 -0400605 verifyUpdateReceived();
606 assertOrderNotChanged();
607
Mark Renoufba5ab512019-05-02 15:21:01 -0400608 sendUpdatedEntryAtTime(mEntryA2, 7000);
Mark Renouf82a40e62019-05-23 16:16:24 -0400609 verifyUpdateReceived();
610 assertOrderNotChanged();
611
Mark Renoufba5ab512019-05-02 15:21:01 -0400612 sendUpdatedEntryAtTime(mEntryB1, 8000);
Mark Renouf82a40e62019-05-23 16:16:24 -0400613 verifyUpdateReceived();
614 assertOrderNotChanged();
Mark Renoufba5ab512019-05-02 15:21:01 -0400615 }
616
617 // EXPANDED / REMOVE
618
619 /**
Mady Mellora55133d2020-05-01 15:11:10 -0700620 * Verifies that removing a bubble while expanded does not result in reordering of bubbles.
Mark Renoufba5ab512019-05-02 15:21:01 -0400621 *
Mady Mellora55133d2020-05-01 15:11:10 -0700622 * @see #test_collapsed_addBubble()
Mark Renoufba5ab512019-05-02 15:21:01 -0400623 */
624 @Test
Mady Mellora55133d2020-05-01 15:11:10 -0700625 public void test_expanded_removeBubble() {
Mark Renoufba5ab512019-05-02 15:21:01 -0400626 // Setup
627 sendUpdatedEntryAtTime(mEntryA1, 1000);
628 sendUpdatedEntryAtTime(mEntryB1, 2000);
629 sendUpdatedEntryAtTime(mEntryA2, 3000);
Mady Mellora55133d2020-05-01 15:11:10 -0700630 sendUpdatedEntryAtTime(mEntryB2, 4000); // [B2, A2, B1, A1]
Mark Renoufba5ab512019-05-02 15:21:01 -0400631 changeExpandedStateAtTime(true, 5000L);
632 mBubbleData.setListener(mListener);
633
634 // Test
Pinyao Ting3c930612020-05-19 00:26:03 +0000635 mBubbleData.notificationEntryRemoved(
636 mEntryB2.getKey(), BubbleController.DISMISS_USER_GESTURE);
Mark Renouf82a40e62019-05-23 16:16:24 -0400637 verifyUpdateReceived();
Mady Mellora55133d2020-05-01 15:11:10 -0700638 assertOrderChangedTo(mBubbleA2, mBubbleB1, mBubbleA1);
Mark Renoufba5ab512019-05-02 15:21:01 -0400639 }
640
641 /**
642 * Verifies that removing the selected bubble while expanded causes another bubble to become
643 * selected. The replacement selection is the bubble which appears at the same index as the
644 * previous one, or the previous index if this was the last position.
645 *
Mady Mellora55133d2020-05-01 15:11:10 -0700646 * @see #test_collapsed_addBubble()
Mark Renoufba5ab512019-05-02 15:21:01 -0400647 */
648 @Test
649 public void test_expanded_removeBubble_selectionChanges_whenSelectedRemoved() {
650 // Setup
651 sendUpdatedEntryAtTime(mEntryA1, 1000);
652 sendUpdatedEntryAtTime(mEntryB1, 2000);
653 sendUpdatedEntryAtTime(mEntryA2, 3000);
654 sendUpdatedEntryAtTime(mEntryB2, 4000);
655 changeExpandedStateAtTime(true, 5000L);
Mady Mellora55133d2020-05-01 15:11:10 -0700656 mBubbleData.setSelectedBubble(mBubbleA2); // [B2, A2^, B1, A1]
Mark Renoufba5ab512019-05-02 15:21:01 -0400657 mBubbleData.setListener(mListener);
658
659 // Test
Pinyao Ting3c930612020-05-19 00:26:03 +0000660 mBubbleData.notificationEntryRemoved(
661 mEntryA2.getKey(), BubbleController.DISMISS_USER_GESTURE);
Mark Renouf82a40e62019-05-23 16:16:24 -0400662 verifyUpdateReceived();
Mark Renouf82a40e62019-05-23 16:16:24 -0400663 assertSelectionChangedTo(mBubbleB1);
Mady Mellora55133d2020-05-01 15:11:10 -0700664
Pinyao Ting3c930612020-05-19 00:26:03 +0000665 mBubbleData.notificationEntryRemoved(
666 mEntryB1.getKey(), BubbleController.DISMISS_USER_GESTURE);
Mady Mellora55133d2020-05-01 15:11:10 -0700667 verifyUpdateReceived();
668 assertSelectionChangedTo(mBubbleA1);
Mark Renoufba5ab512019-05-02 15:21:01 -0400669 }
670
671 @Test
672 public void test_expandAndCollapse_callsOnExpandedChanged() {
673 // Setup
674 sendUpdatedEntryAtTime(mEntryA1, 1000);
675 mBubbleData.setListener(mListener);
676
677 // Test
678 changeExpandedStateAtTime(true, 3000L);
Mark Renouf82a40e62019-05-23 16:16:24 -0400679 verifyUpdateReceived();
680 assertExpandedChangedTo(true);
Mark Renoufba5ab512019-05-02 15:21:01 -0400681
Mark Renoufba5ab512019-05-02 15:21:01 -0400682 changeExpandedStateAtTime(false, 4000L);
Mark Renouf82a40e62019-05-23 16:16:24 -0400683 verifyUpdateReceived();
684 assertExpandedChangedTo(false);
Mark Renoufba5ab512019-05-02 15:21:01 -0400685 }
686
687 /**
688 * Verifies that transitions between the collapsed and expanded state maintain sorting and
689 * grouping rules.
690 * <p>
691 * While collapsing, sorting is applied since no sorting happens while expanded. The resulting
692 * state is the new expanded ordering. This state is saved and restored if possible when next
693 * expanded.
694 * <p>
695 * When the stack transitions to the collapsed state, the selected bubble is brought to the top.
Mark Renoufba5ab512019-05-02 15:21:01 -0400696 * <p>
Lyn Hanb9be52d2020-03-31 22:13:34 -0700697 * When the stack transitions back to the expanded state, this new order is kept as is.
Mark Renoufba5ab512019-05-02 15:21:01 -0400698 */
699 @Test
700 public void test_expansionChanges() {
701 // Setup
702 sendUpdatedEntryAtTime(mEntryA1, 1000);
703 sendUpdatedEntryAtTime(mEntryB1, 2000);
704 sendUpdatedEntryAtTime(mEntryA2, 3000);
705 sendUpdatedEntryAtTime(mEntryB2, 4000);
Mady Mellora55133d2020-05-01 15:11:10 -0700706 changeExpandedStateAtTime(true, 5000L); // [B2=4000, A2=3000, B1=2000, A1=1000]
707 sendUpdatedEntryAtTime(mEntryB1, 6000); // [B2=4000, A2=3000, B1=6000, A1=1000]
Mark Renoufba5ab512019-05-02 15:21:01 -0400708 setCurrentTime(7000);
709 mBubbleData.setSelectedBubble(mBubbleA2);
710 mBubbleData.setListener(mListener);
711 assertThat(mBubbleData.getBubbles()).isEqualTo(
Mady Mellora55133d2020-05-01 15:11:10 -0700712 ImmutableList.of(mBubbleB2, mBubbleA2, mBubbleB1, mBubbleA1));
Mark Renoufba5ab512019-05-02 15:21:01 -0400713
714 // Test
715
716 // At this point, B1 has been updated but sorting has not been changed because the
717 // stack is expanded. When next collapsed, sorting will be applied and saved, just prior
718 // to moving the selected bubble to the top (first).
719 //
Mady Mellora55133d2020-05-01 15:11:10 -0700720 // In this case, the expected re-expand state will be: [A2^, B1, B2, A1]
Mark Renoufba5ab512019-05-02 15:21:01 -0400721 //
722 // collapse -> selected bubble (A2) moves first.
723 changeExpandedStateAtTime(false, 8000L);
Mark Renouf82a40e62019-05-23 16:16:24 -0400724 verifyUpdateReceived();
Mady Mellora55133d2020-05-01 15:11:10 -0700725 assertOrderChangedTo(mBubbleA2, mBubbleB1, mBubbleB2, mBubbleA1);
Mark Renoufba5ab512019-05-02 15:21:01 -0400726 }
727
728 /**
729 * When a change occurs while collapsed (any update, add, remove), the previous expanded
Mady Mellora55133d2020-05-01 15:11:10 -0700730 * order becomes invalidated, the stack is resorted and will reflect that when next expanded.
Mark Renoufba5ab512019-05-02 15:21:01 -0400731 */
732 @Test
733 public void test_expansionChanges_withUpdatesWhileCollapsed() {
734 // Setup
735 sendUpdatedEntryAtTime(mEntryA1, 1000);
736 sendUpdatedEntryAtTime(mEntryB1, 2000);
737 sendUpdatedEntryAtTime(mEntryA2, 3000);
738 sendUpdatedEntryAtTime(mEntryB2, 4000);
Mady Mellora55133d2020-05-01 15:11:10 -0700739 changeExpandedStateAtTime(true, 5000L); // [B2=4000, A2=3000, B1=2000, A1=1000]
740 sendUpdatedEntryAtTime(mEntryB1, 6000); // [B2=4000, A2=3000, B1=6000, A1=1000]
Mark Renoufba5ab512019-05-02 15:21:01 -0400741 setCurrentTime(7000);
Mady Mellora55133d2020-05-01 15:11:10 -0700742 mBubbleData.setSelectedBubble(mBubbleA2); // [B2, A2^, B1, A1]
Mark Renoufba5ab512019-05-02 15:21:01 -0400743 mBubbleData.setListener(mListener);
744
745 // Test
746
747 // At this point, B1 has been updated but sorting has not been changed because the
748 // stack is expanded. When next collapsed, sorting will be applied and saved, just prior
749 // to moving the selected bubble to the top (first).
750 //
Mady Mellora55133d2020-05-01 15:11:10 -0700751 // In this case, the expected re-expand state will be: [A2^, B1, B2, A1]
Mark Renoufba5ab512019-05-02 15:21:01 -0400752 //
753 // That state is restored as long as no changes occur (add/remove/update) while in
754 // the collapsed state.
755 //
756 // collapse -> selected bubble (A2) moves first.
757 changeExpandedStateAtTime(false, 8000L);
Mark Renouf82a40e62019-05-23 16:16:24 -0400758 verifyUpdateReceived();
Mady Mellora55133d2020-05-01 15:11:10 -0700759 assertOrderChangedTo(mBubbleA2, mBubbleB1, mBubbleB2, mBubbleA1);
Mark Renoufba5ab512019-05-02 15:21:01 -0400760
761 // An update occurs, which causes sorting, and this invalidates the previously saved order.
Mady Mellora55133d2020-05-01 15:11:10 -0700762 sendUpdatedEntryAtTime(mEntryA1, 9000);
Mark Renouf82a40e62019-05-23 16:16:24 -0400763 verifyUpdateReceived();
Mady Mellora55133d2020-05-01 15:11:10 -0700764 assertOrderChangedTo(mBubbleA1, mBubbleA2, mBubbleB1, mBubbleB2);
Mark Renoufba5ab512019-05-02 15:21:01 -0400765
766 // No order changes when expanding because the new sorted order remains.
Mark Renoufba5ab512019-05-02 15:21:01 -0400767 changeExpandedStateAtTime(true, 10000L);
Mark Renouf82a40e62019-05-23 16:16:24 -0400768 verifyUpdateReceived();
769 assertOrderNotChanged();
Mark Renoufba5ab512019-05-02 15:21:01 -0400770 }
771
772 @Test
773 public void test_expanded_removeLastBubble_collapsesStack() {
774 // Setup
775 sendUpdatedEntryAtTime(mEntryA1, 1000);
776 changeExpandedStateAtTime(true, 2000);
777 mBubbleData.setListener(mListener);
778
779 // Test
Pinyao Ting3c930612020-05-19 00:26:03 +0000780 mBubbleData.notificationEntryRemoved(
781 mEntryA1.getKey(), BubbleController.DISMISS_USER_GESTURE);
Mark Renouf82a40e62019-05-23 16:16:24 -0400782 verifyUpdateReceived();
783 assertExpandedChangedTo(false);
Mark Renouf9ba6cea2019-04-17 11:53:50 -0400784 }
785
Mark Renouf82a40e62019-05-23 16:16:24 -0400786 private void verifyUpdateReceived() {
787 verify(mListener).applyUpdate(mUpdateCaptor.capture());
788 reset(mListener);
789 }
790
791 private void assertBubbleAdded(Bubble expected) {
792 BubbleData.Update update = mUpdateCaptor.getValue();
793 assertThat(update.addedBubble).named("addedBubble").isEqualTo(expected);
794 }
795
796 private void assertBubbleRemoved(Bubble expected, @BubbleController.DismissReason int reason) {
797 BubbleData.Update update = mUpdateCaptor.getValue();
798 assertThat(update.removedBubbles).named("removedBubbles")
799 .isEqualTo(ImmutableList.of(Pair.create(expected, reason)));
800 }
801
802 private void assertOrderNotChanged() {
803 BubbleData.Update update = mUpdateCaptor.getValue();
804 assertThat(update.orderChanged).named("orderChanged").isFalse();
805 }
806
807 private void assertOrderChangedTo(Bubble... order) {
808 BubbleData.Update update = mUpdateCaptor.getValue();
809 assertThat(update.orderChanged).named("orderChanged").isTrue();
810 assertThat(update.bubbles).named("bubble order").isEqualTo(ImmutableList.copyOf(order));
811 }
812
813 private void assertSelectionNotChanged() {
814 BubbleData.Update update = mUpdateCaptor.getValue();
815 assertThat(update.selectionChanged).named("selectionChanged").isFalse();
816 }
817
818 private void assertSelectionChangedTo(Bubble bubble) {
819 BubbleData.Update update = mUpdateCaptor.getValue();
820 assertThat(update.selectionChanged).named("selectionChanged").isTrue();
821 assertThat(update.selectedBubble).named("selectedBubble").isEqualTo(bubble);
822 }
823
824 private void assertSelectionCleared() {
825 BubbleData.Update update = mUpdateCaptor.getValue();
826 assertThat(update.selectionChanged).named("selectionChanged").isTrue();
827 assertThat(update.selectedBubble).named("selectedBubble").isNull();
828 }
829
830 private void assertExpandedChangedTo(boolean expected) {
831 BubbleData.Update update = mUpdateCaptor.getValue();
832 assertThat(update.expandedChanged).named("expandedChanged").isTrue();
833 assertThat(update.expanded).named("expanded").isEqualTo(expected);
834 }
835
Lyn Hanbf1b3d62020-03-12 10:31:19 -0700836 private void assertOverflowChangedTo(ImmutableList<Bubble> bubbles) {
837 BubbleData.Update update = mUpdateCaptor.getValue();
838 assertThat(update.overflowBubbles).isEqualTo(bubbles);
839 }
840
Mark Renouf82a40e62019-05-23 16:16:24 -0400841
Mark Renouf9ba6cea2019-04-17 11:53:50 -0400842 private NotificationEntry createBubbleEntry(int userId, String notifKey, String packageName) {
843 return createBubbleEntry(userId, notifKey, packageName, 1000);
844 }
845
846 private void setPostTime(NotificationEntry entry, long postTime) {
Ned Burns00b4b2d2019-10-17 22:09:27 -0400847 when(entry.getSbn().getPostTime()).thenReturn(postTime);
Mark Renouf9ba6cea2019-04-17 11:53:50 -0400848 }
849
850 private void setOngoing(NotificationEntry entry, boolean ongoing) {
851 if (ongoing) {
Ned Burns00b4b2d2019-10-17 22:09:27 -0400852 entry.getSbn().getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE;
Mark Renouf9ba6cea2019-04-17 11:53:50 -0400853 } else {
Ned Burns00b4b2d2019-10-17 22:09:27 -0400854 entry.getSbn().getNotification().flags &= ~Notification.FLAG_FOREGROUND_SERVICE;
Mark Renouf9ba6cea2019-04-17 11:53:50 -0400855 }
856 }
857
858 /**
859 * No ExpandableNotificationRow is required to test BubbleData. This setup is all that is
860 * required for BubbleData functionality and verification. NotificationTestHelper is used only
861 * as a convenience to create a Notification w/BubbleMetadata.
862 */
863 private NotificationEntry createBubbleEntry(int userId, String notifKey, String packageName,
864 long postTime) {
865 // BubbleMetadata
Mady Melloraa9ce172020-03-17 10:34:20 -0700866 Notification.BubbleMetadata bubbleMetadata = new Notification.BubbleMetadata.Builder(
867 mExpandIntent, Icon.createWithResource("", 0))
Mark Renouf9ba6cea2019-04-17 11:53:50 -0400868 .setDeleteIntent(mDeleteIntent)
Mark Renouf9ba6cea2019-04-17 11:53:50 -0400869 .build();
870 // Notification -> BubbleMetadata
871 Notification notification = mNotificationTestHelper.createNotification(false,
872 null /* groupKey */, bubbleMetadata);
873
874 // StatusBarNotification
875 StatusBarNotification sbn = mock(StatusBarNotification.class);
876 when(sbn.getKey()).thenReturn(notifKey);
877 when(sbn.getUser()).thenReturn(new UserHandle(userId));
878 when(sbn.getPackageName()).thenReturn(packageName);
879 when(sbn.getPostTime()).thenReturn(postTime);
880 when(sbn.getNotification()).thenReturn(notification);
881
882 // NotificationEntry -> StatusBarNotification -> Notification -> BubbleMetadata
Ned Burns636c3792019-09-11 16:59:07 -0400883 return new NotificationEntryBuilder().setSbn(sbn).build();
Mark Renouf9ba6cea2019-04-17 11:53:50 -0400884 }
885
Mark Renoufba5ab512019-05-02 15:21:01 -0400886 private void setCurrentTime(long time) {
887 when(mTimeSource.currentTimeMillis()).thenReturn(time);
888 }
889
Mark Renouf9ba6cea2019-04-17 11:53:50 -0400890 private void sendUpdatedEntryAtTime(NotificationEntry entry, long postTime) {
891 setPostTime(entry, postTime);
Mady Mellor3df7ab02019-12-09 15:07:10 -0800892 // BubbleController calls this:
893 Bubble b = mBubbleData.getOrCreateBubble(entry);
894 // And then this
895 mBubbleData.notificationEntryUpdated(b, false /* suppressFlyout*/,
Mady Mellor7f234902019-10-20 12:06:29 -0700896 true /* showInShade */);
Mark Renouf9ba6cea2019-04-17 11:53:50 -0400897 }
898
899 private void changeExpandedStateAtTime(boolean shouldBeExpanded, long time) {
Mark Renoufba5ab512019-05-02 15:21:01 -0400900 setCurrentTime(time);
Mark Renouf9ba6cea2019-04-17 11:53:50 -0400901 mBubbleData.setExpanded(shouldBeExpanded);
902 }
Lyn Han6c40fe72019-05-08 14:06:33 -0700903}