blob: c8005ddacdb1fa91849b82fc5f76735866df35fe [file] [log] [blame]
Eliot Courtneya6d8cf22017-10-20 13:26:58 +09001/*
2 * Copyright (C) 2017 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
Rohan Shah20790b82018-07-02 17:21:04 -070014 * limitations under the License
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090015 */
16
Rohan Shah20790b82018-07-02 17:21:04 -070017package com.android.systemui.statusbar.notification;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090018
19import static junit.framework.Assert.assertNotNull;
20import static junit.framework.Assert.assertNull;
21import static junit.framework.Assert.assertTrue;
22
23import static org.junit.Assert.assertEquals;
24import static org.mockito.ArgumentMatchers.any;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090025import static org.mockito.ArgumentMatchers.eq;
26import static org.mockito.Mockito.doAnswer;
Mady Mellor0ad5b9d2019-01-08 14:59:55 -080027import static org.mockito.Mockito.inOrder;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090028import static org.mockito.Mockito.mock;
29import static org.mockito.Mockito.never;
30import static org.mockito.Mockito.verify;
31import static org.mockito.Mockito.when;
32
33import android.app.ActivityManager;
34import android.app.Notification;
Dan Sandler1d958f82018-01-09 21:10:26 -050035import android.app.NotificationManager;
Tony Mak628cb932018-06-19 18:30:41 +010036import android.app.PendingIntent;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090037import android.content.Context;
Tony Mak628cb932018-06-19 18:30:41 +010038import android.content.Intent;
39import android.graphics.drawable.Icon;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090040import android.os.Handler;
41import android.os.Looper;
42import android.os.UserHandle;
43import android.service.notification.NotificationListenerService;
44import android.service.notification.StatusBarNotification;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090045import android.testing.AndroidTestingRunner;
46import android.testing.TestableLooper;
Ned Burnsa5dad552019-01-29 17:59:10 -050047import android.util.ArraySet;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +090048import android.widget.FrameLayout;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090049
Ned Burnsa5dad552019-01-29 17:59:10 -050050import androidx.annotation.NonNull;
Brett Chabot84151d92019-02-27 15:37:59 -080051import androidx.test.filters.SmallTest;
Ned Burnsa5dad552019-01-29 17:59:10 -050052
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090053import com.android.internal.logging.MetricsLogger;
Jason Monk297c04e2018-08-23 17:16:59 -040054import com.android.systemui.Dependency;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090055import com.android.systemui.ForegroundServiceController;
Jason Monk297c04e2018-08-23 17:16:59 -040056import com.android.systemui.InitController;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090057import com.android.systemui.R;
58import com.android.systemui.SysuiTestCase;
Kevina5ff1fa2018-08-21 16:35:48 -070059import com.android.systemui.statusbar.NotificationLifetimeExtender;
Rohan Shah20790b82018-07-02 17:21:04 -070060import com.android.systemui.statusbar.NotificationListener;
61import com.android.systemui.statusbar.NotificationLockscreenUserManager;
Rohan Shah20790b82018-07-02 17:21:04 -070062import com.android.systemui.statusbar.NotificationPresenter;
63import com.android.systemui.statusbar.NotificationRemoteInputManager;
64import com.android.systemui.statusbar.RemoteInputController;
65import com.android.systemui.statusbar.SmartReplyController;
66import com.android.systemui.statusbar.StatusBarIconView;
Mady Mellor0ad5b9d2019-01-08 14:59:55 -080067import com.android.systemui.statusbar.notification.collection.NotificationData;
Ned Burnsf81c4c42019-01-07 14:10:43 -050068import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment;
69import com.android.systemui.statusbar.notification.collection.NotificationEntry;
Ned Burnsc5864672019-02-20 12:57:29 -050070import com.android.systemui.statusbar.notification.collection.NotificationRowBinder;
71import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
Rohan Shah20790b82018-07-02 17:21:04 -070072import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
Ned Burns1a5e22f2019-02-14 15:11:52 -050073import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag;
Rohan Shah20790b82018-07-02 17:21:04 -070074import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
75import com.android.systemui.statusbar.notification.row.RowInflaterTask;
76import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090077import com.android.systemui.statusbar.phone.NotificationGroupManager;
Jason Monk297c04e2018-08-23 17:16:59 -040078import com.android.systemui.statusbar.phone.ShadeController;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090079import com.android.systemui.statusbar.policy.DeviceProvisionedController;
80import com.android.systemui.statusbar.policy.HeadsUpManager;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090081
82import org.junit.Before;
83import org.junit.Test;
84import org.junit.runner.RunWith;
85import org.mockito.ArgumentCaptor;
Mady Mellor0ad5b9d2019-01-08 14:59:55 -080086import org.mockito.InOrder;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090087import org.mockito.Mock;
88import org.mockito.MockitoAnnotations;
89
Tony Mak628cb932018-06-19 18:30:41 +010090import java.util.ArrayList;
91import java.util.Arrays;
Ned Burnsa5dad552019-01-29 17:59:10 -050092import java.util.Set;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +090093import java.util.concurrent.CountDownLatch;
94import java.util.concurrent.TimeUnit;
95
96@SmallTest
97@RunWith(AndroidTestingRunner.class)
98@TestableLooper.RunWithLooper
99public class NotificationEntryManagerTest extends SysuiTestCase {
100 private static final String TEST_PACKAGE_NAME = "test";
101 private static final int TEST_UID = 0;
102
103 @Mock private NotificationPresenter mPresenter;
Jason Monk297c04e2018-08-23 17:16:59 -0400104 @Mock private KeyguardEnvironment mEnvironment;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900105 @Mock private ExpandableNotificationRow mRow;
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900106 @Mock private NotificationListContainer mListContainer;
Ned Burnsc5864672019-02-20 12:57:29 -0500107 @Mock private NotificationEntryListener mEntryListener;
108 @Mock private NotificationRowBinderImpl.BindRowCallback mBindCallback;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900109 @Mock private HeadsUpManager mHeadsUpManager;
110 @Mock private NotificationListenerService.RankingMap mRankingMap;
111 @Mock private RemoteInputController mRemoteInputController;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900112
Eliot Courtney8f56b0e2017-12-14 18:54:28 +0900113 // Dependency mocks:
114 @Mock private ForegroundServiceController mForegroundServiceController;
115 @Mock private NotificationLockscreenUserManager mLockscreenUserManager;
116 @Mock private NotificationGroupManager mGroupManager;
117 @Mock private NotificationGutsManager mGutsManager;
118 @Mock private NotificationRemoteInputManager mRemoteInputManager;
Eliot Courtney8f56b0e2017-12-14 18:54:28 +0900119 @Mock private NotificationListener mNotificationListener;
120 @Mock private DeviceProvisionedController mDeviceProvisionedController;
121 @Mock private VisualStabilityManager mVisualStabilityManager;
122 @Mock private MetricsLogger mMetricsLogger;
Kenny Guy8cc15d22018-05-09 09:50:55 +0100123 @Mock private SmartReplyController mSmartReplyController;
Tony Mak628cb932018-06-19 18:30:41 +0100124 @Mock private RowInflaterTask mAsyncInflationTask;
Ned Burnsef2ef6c2019-01-02 16:48:08 -0500125 @Mock private NotificationRowBinder mMockedRowBinder;
Eliot Courtney8f56b0e2017-12-14 18:54:28 +0900126
Ned Burnsf81c4c42019-01-07 14:10:43 -0500127 private NotificationEntry mEntry;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900128 private StatusBarNotification mSbn;
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900129 private TestableNotificationEntryManager mEntryManager;
130 private CountDownLatch mCountDownLatch;
131
132 private class TestableNotificationEntryManager extends NotificationEntryManager {
133 private final CountDownLatch mCountDownLatch;
134
Gus Prevasca1b6f72018-12-28 10:53:11 -0500135 TestableNotificationEntryManager(Context context) {
Eliot Courtney6c313d32017-12-14 19:57:51 +0900136 super(context);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900137 mCountDownLatch = new CountDownLatch(1);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900138 }
139
Mady Mellor0ad5b9d2019-01-08 14:59:55 -0800140 public void setNotificationData(NotificationData data) {
141 mNotificationData = data;
142 }
143
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900144 @Override
Ned Burnsf81c4c42019-01-07 14:10:43 -0500145 public void onAsyncInflationFinished(NotificationEntry entry,
Ned Burns1a5e22f2019-02-14 15:11:52 -0500146 @InflationFlag int inflatedFlags) {
Kevind4660b22018-09-27 10:57:35 -0700147 super.onAsyncInflationFinished(entry, inflatedFlags);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900148
149 mCountDownLatch.countDown();
150 }
151
152 public CountDownLatch getCountDownLatch() {
153 return mCountDownLatch;
154 }
Kevina5ff1fa2018-08-21 16:35:48 -0700155
156 public ArrayList<NotificationLifetimeExtender> getLifetimeExtenders() {
157 return mNotificationLifetimeExtenders;
158 }
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900159 }
160
Dan Sandler1d958f82018-01-09 21:10:26 -0500161 private void setUserSentiment(String key, int sentiment) {
162 doAnswer(invocationOnMock -> {
163 NotificationListenerService.Ranking ranking = (NotificationListenerService.Ranking)
164 invocationOnMock.getArguments()[1];
165 ranking.populate(
166 key,
167 0,
168 false,
169 0,
170 0,
171 NotificationManager.IMPORTANCE_DEFAULT,
172 null, null,
Julia Reynolds4509ce72019-01-31 13:12:43 -0500173 null, null, null, true, sentiment, false, -1, false, null, null, false);
Tony Mak628cb932018-06-19 18:30:41 +0100174 return true;
175 }).when(mRankingMap).getRanking(eq(key), any(NotificationListenerService.Ranking.class));
176 }
177
178 private void setSmartActions(String key, ArrayList<Notification.Action> smartActions) {
179 doAnswer(invocationOnMock -> {
180 NotificationListenerService.Ranking ranking = (NotificationListenerService.Ranking)
181 invocationOnMock.getArguments()[1];
182 ranking.populate(
183 key,
184 0,
185 false,
186 0,
187 0,
188 NotificationManager.IMPORTANCE_DEFAULT,
189 null, null,
190 null, null, null, true,
Gus Prevas7306b902018-12-11 10:57:06 -0500191 NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL, false, -1,
Julia Reynolds4509ce72019-01-31 13:12:43 -0500192 false, smartActions, null, false);
Dan Sandler1d958f82018-01-09 21:10:26 -0500193 return true;
194 }).when(mRankingMap).getRanking(eq(key), any(NotificationListenerService.Ranking.class));
195 }
196
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900197 @Before
198 public void setUp() {
199 MockitoAnnotations.initMocks(this);
Jason Monk297c04e2018-08-23 17:16:59 -0400200 mDependency.injectMockDependency(ShadeController.class);
Eliot Courtney8f56b0e2017-12-14 18:54:28 +0900201 mDependency.injectTestDependency(ForegroundServiceController.class,
202 mForegroundServiceController);
203 mDependency.injectTestDependency(NotificationLockscreenUserManager.class,
204 mLockscreenUserManager);
205 mDependency.injectTestDependency(NotificationGroupManager.class, mGroupManager);
206 mDependency.injectTestDependency(NotificationGutsManager.class, mGutsManager);
207 mDependency.injectTestDependency(NotificationRemoteInputManager.class, mRemoteInputManager);
Eliot Courtney8f56b0e2017-12-14 18:54:28 +0900208 mDependency.injectTestDependency(NotificationListener.class, mNotificationListener);
209 mDependency.injectTestDependency(DeviceProvisionedController.class,
210 mDeviceProvisionedController);
211 mDependency.injectTestDependency(VisualStabilityManager.class, mVisualStabilityManager);
212 mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger);
Kenny Guy8cc15d22018-05-09 09:50:55 +0100213 mDependency.injectTestDependency(SmartReplyController.class, mSmartReplyController);
Jason Monk297c04e2018-08-23 17:16:59 -0400214 mDependency.injectTestDependency(KeyguardEnvironment.class, mEnvironment);
Eliot Courtney8f56b0e2017-12-14 18:54:28 +0900215
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900216 mCountDownLatch = new CountDownLatch(1);
217
Jason Monk297c04e2018-08-23 17:16:59 -0400218 mDependency.injectTestDependency(Dependency.MAIN_HANDLER,
219 Handler.createAsync(Looper.myLooper()));
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900220 when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
Eliot Courtney2b4c3a02017-11-27 13:27:46 +0900221 when(mListContainer.getViewParentForNotification(any())).thenReturn(
222 new FrameLayout(mContext));
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900223
224 Notification.Builder n = new Notification.Builder(mContext, "")
225 .setSmallIcon(R.drawable.ic_person)
226 .setContentTitle("Title")
227 .setContentText("Text");
228 mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID,
229 0, n.build(), new UserHandle(ActivityManager.getCurrentUser()), null, 0);
Ned Burnsf81c4c42019-01-07 14:10:43 -0500230 mEntry = new NotificationEntry(mSbn);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900231 mEntry.expandedIcon = mock(StatusBarIconView.class);
232
Gus Prevasca1b6f72018-12-28 10:53:11 -0500233 mEntryManager = new TestableNotificationEntryManager(mContext);
Jason Monk297c04e2018-08-23 17:16:59 -0400234 Dependency.get(InitController.class).executePostInitTasks();
Gus Prevas8621bd22018-12-20 15:04:25 -0500235 mEntryManager.setUpWithPresenter(mPresenter, mListContainer, mHeadsUpManager);
236 mEntryManager.addNotificationEntryListener(mEntryListener);
Gus Prevas8ba88a82018-12-18 11:13:44 -0500237
Ned Burnsc5864672019-02-20 12:57:29 -0500238 NotificationRowBinderImpl notificationRowBinder =
239 new NotificationRowBinderImpl(mContext, true /* allowLongPress */);
Gus Prevas8ba88a82018-12-18 11:13:44 -0500240 notificationRowBinder.setUpWithPresenter(
241 mPresenter, mListContainer, mHeadsUpManager, mEntryManager, mBindCallback);
242 notificationRowBinder.setNotificationClicker(mock(NotificationClicker.class));
Ned Burnsc5864672019-02-20 12:57:29 -0500243 mEntryManager.setRowBinder(notificationRowBinder);
Dan Sandler1d958f82018-01-09 21:10:26 -0500244
245 setUserSentiment(mEntry.key, NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900246 }
247
248 @Test
249 public void testAddNotification() throws Exception {
250 com.android.systemui.util.Assert.isNotMainThread();
Jason Monk6dceace2018-05-15 20:24:07 -0400251 TestableLooper.get(this).processAllMessages();
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900252
253 doAnswer(invocation -> {
254 mCountDownLatch.countDown();
255 return null;
Gus Prevas8ba88a82018-12-18 11:13:44 -0500256 }).when(mBindCallback).onBindRow(any(), any(), any(), any());
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900257
258 // Post on main thread, otherwise we will be stuck waiting here for the inflation finished
259 // callback forever, since it won't execute until the tests ends.
Jason Monk6dceace2018-05-15 20:24:07 -0400260 mEntryManager.addNotification(mSbn, mRankingMap);
261 TestableLooper.get(this).processMessages(1);
262 assertTrue(mCountDownLatch.await(10, TimeUnit.SECONDS));
263 assertTrue(mEntryManager.getCountDownLatch().await(10, TimeUnit.SECONDS));
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900264
265 // Check that no inflation error occurred.
Gus Prevasca1b6f72018-12-28 10:53:11 -0500266 verify(mEntryListener, never()).onInflationError(any(), any());
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900267
268 // Row inflation:
Ned Burnsf81c4c42019-01-07 14:10:43 -0500269 ArgumentCaptor<NotificationEntry> entryCaptor = ArgumentCaptor.forClass(
270 NotificationEntry.class);
Gus Prevas8ba88a82018-12-18 11:13:44 -0500271 verify(mBindCallback).onBindRow(entryCaptor.capture(), any(), eq(mSbn), any());
Ned Burnsf81c4c42019-01-07 14:10:43 -0500272 NotificationEntry entry = entryCaptor.getValue();
Evan Laird94492852018-10-25 13:43:01 -0400273 verify(mRemoteInputManager).bindRow(entry.getRow());
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900274
275 // Row content inflation:
Gus Prevas8621bd22018-12-20 15:04:25 -0500276 verify(mEntryListener).onNotificationAdded(entry);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900277 verify(mPresenter).updateNotificationViews();
278
279 assertEquals(mEntryManager.getNotificationData().get(mSbn.getKey()), entry);
Evan Laird94492852018-10-25 13:43:01 -0400280 assertNotNull(entry.getRow());
Dan Sandler1d958f82018-01-09 21:10:26 -0500281 assertEquals(mEntry.userSentiment,
282 NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900283 }
284
285 @Test
286 public void testUpdateNotification() throws Exception {
287 com.android.systemui.util.Assert.isNotMainThread();
Jason Monk6dceace2018-05-15 20:24:07 -0400288 TestableLooper.get(this).processAllMessages();
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900289
290 mEntryManager.getNotificationData().add(mEntry);
291
Dan Sandler1d958f82018-01-09 21:10:26 -0500292 setUserSentiment(mEntry.key, NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
293
Jason Monk6dceace2018-05-15 20:24:07 -0400294 mEntryManager.updateNotification(mSbn, mRankingMap);
295 TestableLooper.get(this).processMessages(1);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900296 // Wait for content update.
Jason Monk6dceace2018-05-15 20:24:07 -0400297 assertTrue(mEntryManager.getCountDownLatch().await(10, TimeUnit.SECONDS));
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900298
Gus Prevasca1b6f72018-12-28 10:53:11 -0500299 verify(mEntryListener, never()).onInflationError(any(), any());
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900300
Mady Mellor0ad5b9d2019-01-08 14:59:55 -0800301 verify(mEntryListener).onPreEntryUpdated(mEntry);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900302 verify(mPresenter).updateNotificationViews();
Mady Mellor0ad5b9d2019-01-08 14:59:55 -0800303 verify(mEntryListener).onPostEntryUpdated(mEntry);
304
Evan Laird94492852018-10-25 13:43:01 -0400305 assertNotNull(mEntry.getRow());
Dan Sandler1d958f82018-01-09 21:10:26 -0500306 assertEquals(mEntry.userSentiment,
307 NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900308 }
309
310 @Test
Mady Mellor0ad5b9d2019-01-08 14:59:55 -0800311 public void testUpdateNotification_prePostEntryOrder() throws Exception {
312 com.android.systemui.util.Assert.isNotMainThread();
313 TestableLooper.get(this).processAllMessages();
314
315 NotificationData notifData = mock(NotificationData.class);
316 when(notifData.get(mEntry.key)).thenReturn(mEntry);
317
318 mEntryManager.setNotificationData(notifData);
319
320 mEntryManager.updateNotification(mSbn, mRankingMap);
321 TestableLooper.get(this).processMessages(1);
322 // Wait for content update.
323 assertTrue(mEntryManager.getCountDownLatch().await(10, TimeUnit.SECONDS));
324
325 verify(mEntryListener, never()).onInflationError(any(), any());
326
327 // Ensure that update callbacks happen in correct order
328 InOrder order = inOrder(mEntryListener, notifData, mPresenter, mEntryListener);
329 order.verify(mEntryListener).onPreEntryUpdated(mEntry);
330 order.verify(notifData).filterAndSort();
331 order.verify(mPresenter).updateNotificationViews();
332 order.verify(mEntryListener).onPostEntryUpdated(mEntry);
333
334 assertNotNull(mEntry.getRow());
335 }
336
337 @Test
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900338 public void testRemoveNotification() throws Exception {
339 com.android.systemui.util.Assert.isNotMainThread();
340
Evan Laird94492852018-10-25 13:43:01 -0400341 mEntry.setRow(mRow);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900342 mEntryManager.getNotificationData().add(mEntry);
343
Jason Monk6dceace2018-05-15 20:24:07 -0400344 mEntryManager.removeNotification(mSbn.getKey(), mRankingMap);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900345
Gus Prevasca1b6f72018-12-28 10:53:11 -0500346 verify(mEntryListener, never()).onInflationError(any(), any());
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900347
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900348 verify(mPresenter).updateNotificationViews();
Ned Burnsef2ef6c2019-01-02 16:48:08 -0500349 verify(mEntryListener).onEntryRemoved(
Selim Cinek7bc9be72019-03-06 18:42:05 -0800350 eq(mEntry), any(), eq(false) /* removedByUser */);
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900351 verify(mRow).setRemoved();
352
353 assertNull(mEntryManager.getNotificationData().get(mSbn.getKey()));
354 }
Julia Reynoldsfc640012018-02-21 12:25:27 -0500355
356 @Test
Ned Burnsef2ef6c2019-01-02 16:48:08 -0500357 public void testRemoveNotification_onEntryRemoveNotFiredIfEntryDoesntExist() {
358 com.android.systemui.util.Assert.isNotMainThread();
359
360 mEntryManager.removeNotification("not_a_real_key", mRankingMap);
361
362 verify(mEntryListener, never()).onEntryRemoved(
Selim Cinek7bc9be72019-03-06 18:42:05 -0800363 eq(mEntry), any(), eq(false) /* removedByUser */);
Ned Burnsef2ef6c2019-01-02 16:48:08 -0500364 }
365
366 @Test
367 public void testRemoveNotification_whilePending() throws InterruptedException {
368 com.android.systemui.util.Assert.isNotMainThread();
369
370 mEntryManager.setRowBinder(mMockedRowBinder);
371
372 mEntryManager.addNotification(mSbn, mRankingMap);
373 mEntryManager.removeNotification(mSbn.getKey(), mRankingMap);
374
375 verify(mEntryListener, never()).onEntryRemoved(
Selim Cinek7bc9be72019-03-06 18:42:05 -0800376 eq(mEntry), any(), eq(false /* removedByUser */));
Kenny Guy8cc15d22018-05-09 09:50:55 +0100377 }
378
379 @Test
Tony Mak628cb932018-06-19 18:30:41 +0100380 public void testUpdateNotificationRanking() {
Jason Monk297c04e2018-08-23 17:16:59 -0400381 when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
382 when(mEnvironment.isDeviceProvisioned()).thenReturn(true);
383 when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
Tony Mak628cb932018-06-19 18:30:41 +0100384
Evan Laird94492852018-10-25 13:43:01 -0400385 mEntry.setRow(mRow);
Tony Mak628cb932018-06-19 18:30:41 +0100386 mEntry.setInflationTask(mAsyncInflationTask);
387 mEntryManager.getNotificationData().add(mEntry);
388 setSmartActions(mEntry.key, new ArrayList<>(Arrays.asList(createAction())));
389
390 mEntryManager.updateNotificationRanking(mRankingMap);
Kevind4660b22018-09-27 10:57:35 -0700391 verify(mRow).setEntry(eq(mEntry));
Gustav Sennton1463d832018-11-06 16:12:48 +0000392 assertEquals(1, mEntry.systemGeneratedSmartActions.size());
393 assertEquals("action", mEntry.systemGeneratedSmartActions.get(0).title);
Tony Mak628cb932018-06-19 18:30:41 +0100394 }
395
396 @Test
397 public void testUpdateNotificationRanking_noChange() {
Jason Monk297c04e2018-08-23 17:16:59 -0400398 when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
399 when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
Tony Mak628cb932018-06-19 18:30:41 +0100400
Evan Laird94492852018-10-25 13:43:01 -0400401 mEntry.setRow(mRow);
Tony Mak628cb932018-06-19 18:30:41 +0100402 mEntryManager.getNotificationData().add(mEntry);
403 setSmartActions(mEntry.key, null);
404
405 mEntryManager.updateNotificationRanking(mRankingMap);
Kevind4660b22018-09-27 10:57:35 -0700406 verify(mRow, never()).setEntry(eq(mEntry));
Gustav Sennton1463d832018-11-06 16:12:48 +0000407 assertEquals(0, mEntry.systemGeneratedSmartActions.size());
Tony Mak628cb932018-06-19 18:30:41 +0100408 }
409
410 @Test
411 public void testUpdateNotificationRanking_rowNotInflatedYet() {
Jason Monk297c04e2018-08-23 17:16:59 -0400412 when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
413 when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
Tony Mak628cb932018-06-19 18:30:41 +0100414
Evan Laird94492852018-10-25 13:43:01 -0400415 mEntry.setRow(null);
Tony Mak628cb932018-06-19 18:30:41 +0100416 mEntryManager.getNotificationData().add(mEntry);
417 setSmartActions(mEntry.key, new ArrayList<>(Arrays.asList(createAction())));
418
419 mEntryManager.updateNotificationRanking(mRankingMap);
Kevind4660b22018-09-27 10:57:35 -0700420 verify(mRow, never()).setEntry(eq(mEntry));
Gustav Sennton1463d832018-11-06 16:12:48 +0000421 assertEquals(1, mEntry.systemGeneratedSmartActions.size());
422 assertEquals("action", mEntry.systemGeneratedSmartActions.get(0).title);
Tony Mak628cb932018-06-19 18:30:41 +0100423 }
424
425 @Test
426 public void testUpdateNotificationRanking_pendingNotification() {
Jason Monk297c04e2018-08-23 17:16:59 -0400427 when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
428 when(mEnvironment.isNotificationForCurrentProfiles(any())).thenReturn(true);
Tony Mak628cb932018-06-19 18:30:41 +0100429
Evan Laird94492852018-10-25 13:43:01 -0400430 mEntry.setRow(null);
Tony Mak628cb932018-06-19 18:30:41 +0100431 mEntryManager.mPendingNotifications.put(mEntry.key, mEntry);
432 setSmartActions(mEntry.key, new ArrayList<>(Arrays.asList(createAction())));
433
434 mEntryManager.updateNotificationRanking(mRankingMap);
Kevind4660b22018-09-27 10:57:35 -0700435 verify(mRow, never()).setEntry(eq(mEntry));
Gustav Sennton1463d832018-11-06 16:12:48 +0000436 assertEquals(1, mEntry.systemGeneratedSmartActions.size());
437 assertEquals("action", mEntry.systemGeneratedSmartActions.get(0).title);
Tony Mak628cb932018-06-19 18:30:41 +0100438 }
439
Ned Burnsa5dad552019-01-29 17:59:10 -0500440 @Test
441 public void testLifetimeExtenders_ifNotificationIsRetainedItIsntRemoved() {
442 // GIVEN an entry manager with a notification
443 mEntryManager.setRowBinder(mMockedRowBinder);
444 mEntryManager.getNotificationData().add(mEntry);
445
446 // GIVEN a lifetime extender that always tries to extend lifetime
447 NotificationLifetimeExtender extender = mock(NotificationLifetimeExtender.class);
448 when(extender.shouldExtendLifetime(mEntry)).thenReturn(true);
449 mEntryManager.addNotificationLifetimeExtender(extender);
450
451 // WHEN the notification is removed
452 mEntryManager.removeNotification(mEntry.key, mRankingMap);
453
454 // THEN the extender is asked to manage the lifetime
455 verify(extender).setShouldManageLifetime(mEntry, true);
456 // THEN the notification is retained
457 assertNotNull(mEntryManager.getNotificationData().get(mSbn.getKey()));
Selim Cinek7bc9be72019-03-06 18:42:05 -0800458 verify(mEntryListener, never()).onEntryRemoved(eq(mEntry), any(), eq(false));
Ned Burnsa5dad552019-01-29 17:59:10 -0500459 }
460
461 @Test
462 public void testLifetimeExtenders_whenRetentionEndsNotificationIsRemoved() {
463 // GIVEN an entry manager with a notification whose life has been extended
464 mEntryManager.setRowBinder(mMockedRowBinder);
465 mEntryManager.getNotificationData().add(mEntry);
466 final FakeNotificationLifetimeExtender extender = new FakeNotificationLifetimeExtender();
467 mEntryManager.addNotificationLifetimeExtender(extender);
468 mEntryManager.removeNotification(mEntry.key, mRankingMap);
469 assertTrue(extender.isManaging(mEntry.key));
470
471 // WHEN the extender finishes its extension
472 extender.setExtendLifetimes(false);
473 extender.getCallback().onSafeToRemove(mEntry.key);
474
475 // THEN the notification is removed
476 assertNull(mEntryManager.getNotificationData().get(mSbn.getKey()));
Selim Cinek7bc9be72019-03-06 18:42:05 -0800477 verify(mEntryListener).onEntryRemoved(eq(mEntry), any(), eq(false));
Ned Burnsa5dad552019-01-29 17:59:10 -0500478 }
479
480 @Test
481 public void testLifetimeExtenders_whenNotificationUpdatedRetainersAreCanceled() {
482 // GIVEN an entry manager with a notification whose life has been extended
483 mEntryManager.setRowBinder(mMockedRowBinder);
484 mEntryManager.getNotificationData().add(mEntry);
485 NotificationLifetimeExtender extender = mock(NotificationLifetimeExtender.class);
486 when(extender.shouldExtendLifetime(mEntry)).thenReturn(true);
487 mEntryManager.addNotificationLifetimeExtender(extender);
488 mEntryManager.removeNotification(mEntry.key, mRankingMap);
489
490 // WHEN the notification is updated
491 mEntryManager.updateNotification(mEntry.notification, mRankingMap);
492
493 // THEN the lifetime extension is canceled
494 verify(extender).setShouldManageLifetime(mEntry, false);
495 }
496
497 @Test
498 public void testLifetimeExtenders_whenNewExtenderTakesPrecedenceOldExtenderIsCanceled() {
499 // GIVEN an entry manager with a notification
500 mEntryManager.setRowBinder(mMockedRowBinder);
501 mEntryManager.getNotificationData().add(mEntry);
502
503 // GIVEN two lifetime extenders, the first which never extends and the second which
504 // always extends
505 NotificationLifetimeExtender extender1 = mock(NotificationLifetimeExtender.class);
506 when(extender1.shouldExtendLifetime(mEntry)).thenReturn(false);
507 NotificationLifetimeExtender extender2 = mock(NotificationLifetimeExtender.class);
508 when(extender2.shouldExtendLifetime(mEntry)).thenReturn(true);
509 mEntryManager.addNotificationLifetimeExtender(extender1);
510 mEntryManager.addNotificationLifetimeExtender(extender2);
511
512 // GIVEN a notification was lifetime-extended and extender2 is managing it
513 mEntryManager.removeNotification(mEntry.key, mRankingMap);
514 verify(extender1, never()).setShouldManageLifetime(mEntry, true);
515 verify(extender2).setShouldManageLifetime(mEntry, true);
516
517 // WHEN the extender1 changes its mind and wants to extend the lifetime of the notif
518 when(extender1.shouldExtendLifetime(mEntry)).thenReturn(true);
519 mEntryManager.removeNotification(mEntry.key, mRankingMap);
520
521 // THEN extender2 stops managing the notif and extender1 starts managing it
522 verify(extender1).setShouldManageLifetime(mEntry, true);
523 verify(extender2).setShouldManageLifetime(mEntry, false);
524 }
525
Gustav Sennton744e7e12019-01-31 14:45:03 +0000526 /**
527 * Ensure that calling NotificationEntryManager.performRemoveNotification() doesn't crash when
528 * given a notification that has already been removed from NotificationData.
529 */
530 @Test
531 public void testPerformRemoveNotification_removedEntry() {
532 mEntryManager.getNotificationData().remove(mSbn.getKey(), null /* ranking */);
533 mEntryManager.performRemoveNotification(mSbn);
534 }
535
Tony Mak628cb932018-06-19 18:30:41 +0100536 private Notification.Action createAction() {
537 return new Notification.Action.Builder(
538 Icon.createWithResource(getContext(), android.R.drawable.sym_def_app_icon),
539 "action",
540 PendingIntent.getBroadcast(getContext(), 0, new Intent("Action"), 0)).build();
541 }
Ned Burnsa5dad552019-01-29 17:59:10 -0500542
543 private static class FakeNotificationLifetimeExtender implements NotificationLifetimeExtender {
544 private NotificationSafeToRemoveCallback mCallback;
545 private boolean mExtendLifetimes = true;
546 private Set<String> mManagedNotifs = new ArraySet<>();
547
548 @Override
549 public void setCallback(@NonNull NotificationSafeToRemoveCallback callback) {
550 mCallback = callback;
551 }
552
553 @Override
554 public boolean shouldExtendLifetime(@NonNull NotificationEntry entry) {
555 return mExtendLifetimes;
556 }
557
558 @Override
559 public void setShouldManageLifetime(
560 @NonNull NotificationEntry entry,
561 boolean shouldManage) {
562 final boolean hasEntry = mManagedNotifs.contains(entry.key);
563 if (shouldManage) {
564 if (hasEntry) {
565 throw new RuntimeException("Already managing this entry: " + entry.key);
566 }
567 mManagedNotifs.add(entry.key);
568 } else {
569 if (!hasEntry) {
570 throw new RuntimeException("Not managing this entry: " + entry.key);
571 }
572 mManagedNotifs.remove(entry.key);
573 }
574 }
575
576 public void setExtendLifetimes(boolean extendLifetimes) {
577 mExtendLifetimes = extendLifetimes;
578 }
579
580 public NotificationSafeToRemoveCallback getCallback() {
581 return mCallback;
582 }
583
584 public boolean isManaging(String notificationKey) {
585 return mManagedNotifs.contains(notificationKey);
586 }
587 }
Eliot Courtneya6d8cf22017-10-20 13:26:58 +0900588}