1 /* 2 * Copyright (C) 2022 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 17 package com.android.car.notification; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import static org.mockito.ArgumentMatchers.any; 22 import static org.mockito.ArgumentMatchers.anyInt; 23 import static org.mockito.ArgumentMatchers.anyLong; 24 import static org.mockito.ArgumentMatchers.anyString; 25 import static org.mockito.ArgumentMatchers.eq; 26 import static org.mockito.ArgumentMatchers.nullable; 27 import static org.mockito.Mockito.mock; 28 import static org.mockito.Mockito.never; 29 import static org.mockito.Mockito.times; 30 import static org.mockito.Mockito.verify; 31 import static org.mockito.Mockito.when; 32 33 import android.app.ActivityManager; 34 import android.app.ActivityTaskManager; 35 import android.app.Notification; 36 import android.app.NotificationManager; 37 import android.app.PendingIntent; 38 import android.app.TaskStackListener; 39 import android.content.ComponentName; 40 import android.os.RemoteException; 41 import android.os.UserHandle; 42 import android.service.notification.NotificationListenerService; 43 import android.service.notification.StatusBarNotification; 44 import android.testing.TestableContext; 45 46 import androidx.test.ext.junit.runners.AndroidJUnit4; 47 import androidx.test.platform.app.InstrumentationRegistry; 48 49 import com.android.dx.mockito.inline.extended.ExtendedMockito; 50 51 import org.junit.After; 52 import org.junit.Before; 53 import org.junit.Rule; 54 import org.junit.Test; 55 import org.junit.runner.RunWith; 56 import org.mockito.ArgumentCaptor; 57 import org.mockito.Captor; 58 import org.mockito.Mock; 59 import org.mockito.MockitoSession; 60 import org.mockito.quality.Strictness; 61 62 import java.time.Clock; 63 import java.time.Instant; 64 import java.time.ZoneId; 65 import java.util.ArrayList; 66 import java.util.Collections; 67 import java.util.PriorityQueue; 68 import java.util.concurrent.ScheduledExecutorService; 69 import java.util.concurrent.ScheduledFuture; 70 import java.util.concurrent.TimeUnit; 71 72 @RunWith(AndroidJUnit4.class) 73 public class CarHeadsUpNotificationQueueTest { 74 private static final int USER_ID = ActivityManager.getCurrentUser(); 75 76 private MockitoSession mSession; 77 private CarHeadsUpNotificationQueue mCarHeadsUpNotificationQueue; 78 79 @Mock 80 private CarHeadsUpNotificationQueue.CarHeadsUpNotificationQueueCallback 81 mCarHeadsUpNotificationQueueCallback; 82 @Mock 83 private NotificationListenerService.RankingMap mRankingMap; 84 @Mock 85 private ActivityTaskManager mActivityTaskManager; 86 @Mock 87 private ScheduledExecutorService mScheduledExecutorService; 88 @Mock 89 private NotificationManager mNotificationManager; 90 91 @Captor 92 private ArgumentCaptor<TaskStackListener> mTaskStackListenerArg; 93 @Captor 94 private ArgumentCaptor<AlertEntry> mAlertEntryArg; 95 @Captor 96 private ArgumentCaptor<Notification> mNotificationArg; 97 98 @Rule 99 public final TestableContext mContext = new TestableContext( 100 InstrumentationRegistry.getInstrumentation().getTargetContext()); 101 102 private static final String PKG_1 = "PKG_1"; 103 private static final String PKG_2 = "PKG_2"; 104 private static final String PKG_3 = "PKG_3"; 105 private static final String CHANNEL_ID = "CHANNEL_ID"; 106 107 @Before setup()108 public void setup() { 109 mSession = ExtendedMockito.mockitoSession() 110 .initMocks(this) 111 .spyStatic(NotificationUtils.class) 112 .strictness(Strictness.LENIENT) 113 .startMocking(); 114 // To add elements to the queue rather than displaying immediately 115 when(mCarHeadsUpNotificationQueueCallback.getActiveHeadsUpNotifications()).thenReturn( 116 new ArrayList<>(Collections.singletonList(mock(AlertEntry.class)))); 117 ExtendedMockito.doReturn(USER_ID).when(() -> NotificationUtils.getCurrentUser(any())); 118 } 119 120 @After tearDown()121 public void tearDown() { 122 if (mSession != null) { 123 mSession.finishMocking(); 124 mSession = null; 125 } 126 } 127 createCarHeadsUpNotificationQueue()128 private CarHeadsUpNotificationQueue createCarHeadsUpNotificationQueue() { 129 return new CarHeadsUpNotificationQueue(mContext, mActivityTaskManager, mNotificationManager, 130 mScheduledExecutorService, mCarHeadsUpNotificationQueueCallback); 131 } 132 133 @Test addToQueue_prioritises_postTimeOfHeadsUp()134 public void addToQueue_prioritises_postTimeOfHeadsUp() { 135 mCarHeadsUpNotificationQueue = createCarHeadsUpNotificationQueue(); 136 AlertEntry alertEntry1 = new AlertEntry(generateMockStatusBarNotification( 137 "key1", "msg"), 4000); 138 AlertEntry alertEntry2 = new AlertEntry(generateMockStatusBarNotification( 139 "key2", "msg"), 2000); 140 AlertEntry alertEntry3 = new AlertEntry(generateMockStatusBarNotification( 141 "key3", "msg"), 3000); 142 AlertEntry alertEntry4 = new AlertEntry(generateMockStatusBarNotification( 143 "key4", "msg"), 5000); 144 AlertEntry alertEntry5 = new AlertEntry(generateMockStatusBarNotification( 145 "key5", "msg"), 1000); 146 147 mCarHeadsUpNotificationQueue.addToQueue(alertEntry1, mRankingMap); 148 mCarHeadsUpNotificationQueue.addToQueue(alertEntry2, mRankingMap); 149 mCarHeadsUpNotificationQueue.addToQueue(alertEntry3, mRankingMap); 150 mCarHeadsUpNotificationQueue.addToQueue(alertEntry4, mRankingMap); 151 mCarHeadsUpNotificationQueue.addToQueue(alertEntry5, mRankingMap); 152 153 PriorityQueue<String> result = mCarHeadsUpNotificationQueue.getPriorityQueue(); 154 assertThat(result.size()).isEqualTo(5); 155 assertThat(result.poll()).isEqualTo("key5"); 156 assertThat(result.poll()).isEqualTo("key2"); 157 assertThat(result.poll()).isEqualTo("key3"); 158 assertThat(result.poll()).isEqualTo("key1"); 159 assertThat(result.poll()).isEqualTo("key4"); 160 } 161 162 @Test addToQueue_prioritises_categoriesOfHeadsUp()163 public void addToQueue_prioritises_categoriesOfHeadsUp() { 164 mContext.getOrCreateTestableResources().addOverride( 165 R.array.headsup_category_immediate_show, /* value= */ new String[0]); 166 mContext.getOrCreateTestableResources().addOverride( 167 R.array.headsup_category_priority, /* value= */ new String[]{ 168 "car_emergency", "navigation", "call", "msg"}); 169 mCarHeadsUpNotificationQueue = createCarHeadsUpNotificationQueue(); 170 AlertEntry alertEntry1 = new AlertEntry(generateMockStatusBarNotification( 171 "key1", "navigation"), 1000); 172 AlertEntry alertEntry2 = new AlertEntry(generateMockStatusBarNotification( 173 "key2", "car_emergency"), 1000); 174 AlertEntry alertEntry3 = new AlertEntry(generateMockStatusBarNotification( 175 "key3", "msg"), 1000); 176 AlertEntry alertEntry4 = new AlertEntry(generateMockStatusBarNotification( 177 "key4", "call"), 1000); 178 AlertEntry alertEntry5 = new AlertEntry(generateMockStatusBarNotification( 179 "key5", "msg"), 1000); 180 181 mCarHeadsUpNotificationQueue.addToQueue(alertEntry1, mRankingMap); 182 mCarHeadsUpNotificationQueue.addToQueue(alertEntry2, mRankingMap); 183 mCarHeadsUpNotificationQueue.addToQueue(alertEntry3, mRankingMap); 184 mCarHeadsUpNotificationQueue.addToQueue(alertEntry4, mRankingMap); 185 mCarHeadsUpNotificationQueue.addToQueue(alertEntry5, mRankingMap); 186 187 PriorityQueue<String> result = mCarHeadsUpNotificationQueue.getPriorityQueue(); 188 assertThat(result.size()).isEqualTo(5); 189 assertThat(result.poll()).isEqualTo("key2"); 190 assertThat(result.poll()).isEqualTo("key1"); 191 assertThat(result.poll()).isEqualTo("key4"); 192 assertThat(result.poll()).isEqualTo("key3"); 193 assertThat(result.poll()).isEqualTo("key5"); 194 } 195 196 @Test addToQueue_prioritises_internalCategoryAsLeastPriority()197 public void addToQueue_prioritises_internalCategoryAsLeastPriority() { 198 mContext.getOrCreateTestableResources().addOverride( 199 R.array.headsup_category_immediate_show, /* value= */ new String[0]); 200 mContext.getOrCreateTestableResources().addOverride( 201 R.array.headsup_category_priority, /* value= */ new String[]{"msg"}); 202 mCarHeadsUpNotificationQueue = createCarHeadsUpNotificationQueue(); 203 AlertEntry alertEntry1 = new AlertEntry(generateMockStatusBarNotification( 204 "key1", "msg"), 1000); 205 AlertEntry alertEntry2 = new AlertEntry(generateMockStatusBarNotification( 206 "key2", "HUN_QUEUE_INTERNAL"), 1000); 207 AlertEntry alertEntry3 = new AlertEntry(generateMockStatusBarNotification( 208 "key3", "msg"), 1000); 209 AlertEntry alertEntry4 = new AlertEntry(generateMockStatusBarNotification( 210 "key4", "msg"), 1000); 211 212 mCarHeadsUpNotificationQueue.addToQueue(alertEntry1, mRankingMap); 213 mCarHeadsUpNotificationQueue.addToQueue(alertEntry2, mRankingMap); 214 mCarHeadsUpNotificationQueue.addToQueue(alertEntry3, mRankingMap); 215 mCarHeadsUpNotificationQueue.addToQueue(alertEntry4, mRankingMap); 216 217 PriorityQueue<String> result = mCarHeadsUpNotificationQueue.getPriorityQueue(); 218 assertThat(result.size()).isEqualTo(4); 219 assertThat(result.poll()).isNotEqualTo("key2"); 220 assertThat(result.poll()).isNotEqualTo("key2"); 221 assertThat(result.poll()).isNotEqualTo("key2"); 222 assertThat(result.poll()).isEqualTo("key2"); 223 } 224 225 @Test addToQueue_merges_newHeadsUpWithSameKey()226 public void addToQueue_merges_newHeadsUpWithSameKey() { 227 mCarHeadsUpNotificationQueue = createCarHeadsUpNotificationQueue(); 228 AlertEntry alertEntry1 = new AlertEntry(generateMockStatusBarNotification( 229 "key1", "msg"), 1000); 230 AlertEntry alertEntry2 = new AlertEntry(generateMockStatusBarNotification( 231 "key2", "msg"), 2000); 232 AlertEntry alertEntry3 = new AlertEntry(generateMockStatusBarNotification( 233 "key1", "msg"), 3000); 234 235 mCarHeadsUpNotificationQueue.addToQueue(alertEntry1, mRankingMap); 236 mCarHeadsUpNotificationQueue.addToQueue(alertEntry2, mRankingMap); 237 mCarHeadsUpNotificationQueue.addToQueue(alertEntry3, mRankingMap); 238 239 PriorityQueue<String> result = mCarHeadsUpNotificationQueue.getPriorityQueue(); 240 assertThat(result.size()).isEqualTo(2); 241 assertThat(result.poll()).isEqualTo("key1"); 242 assertThat(result.poll()).isEqualTo("key2"); 243 } 244 245 @Test addToQueue_shows_immediateShowHeadsUp()246 public void addToQueue_shows_immediateShowHeadsUp() { 247 mContext.getOrCreateTestableResources().addOverride( 248 R.array.headsup_category_immediate_show, /* value= */ 249 new String[]{"car_emergency"}); 250 mCarHeadsUpNotificationQueue = createCarHeadsUpNotificationQueue(); 251 AlertEntry alertEntry1 = new AlertEntry(generateMockStatusBarNotification( 252 "key1", "msg"), 1000); 253 AlertEntry alertEntry2 = new AlertEntry(generateMockStatusBarNotification( 254 "key2", "car_emergency"), 2000); 255 AlertEntry alertEntry3 = new AlertEntry(generateMockStatusBarNotification( 256 "key3", "msg"), 3000); 257 when(mCarHeadsUpNotificationQueueCallback.getActiveHeadsUpNotifications()).thenReturn( 258 new ArrayList<>(Collections.singletonList(alertEntry3))); 259 260 mCarHeadsUpNotificationQueue.addToQueue(alertEntry1, mRankingMap); 261 mCarHeadsUpNotificationQueue.addToQueue(alertEntry2, mRankingMap); 262 263 verify(mCarHeadsUpNotificationQueueCallback).dismissHeadsUp(alertEntry3); 264 verify(mCarHeadsUpNotificationQueueCallback) 265 .showAsHeadsUp(mAlertEntryArg.capture(), 266 any(NotificationListenerService.RankingMap.class)); 267 assertThat(mAlertEntryArg.getValue().getKey()).isEqualTo("key2"); 268 } 269 270 @Test addToQueue_handles_notificationWithNoCategory()271 public void addToQueue_handles_notificationWithNoCategory() { 272 mCarHeadsUpNotificationQueue = createCarHeadsUpNotificationQueue(); 273 AlertEntry alertEntry1 = new AlertEntry(generateMockStatusBarNotification( 274 "key1", /* category= */ null), 4000); 275 276 mCarHeadsUpNotificationQueue.addToQueue(alertEntry1, mRankingMap); 277 278 PriorityQueue<String> result = mCarHeadsUpNotificationQueue.getPriorityQueue(); 279 assertThat(result.size()).isEqualTo(1); 280 assertThat(result.poll()).isEqualTo("key1"); 281 } 282 283 @Test triggerCallback_expireNotifications_whenParked()284 public void triggerCallback_expireNotifications_whenParked() { 285 mContext.getOrCreateTestableResources().addOverride( 286 R.bool.config_expireHeadsUpWhenParked, /* value= */ true); 287 mContext.getOrCreateTestableResources().addOverride( 288 R.integer.headsup_queue_expire_parked_duration_ms, /* value= */ 1000); 289 mCarHeadsUpNotificationQueue = createCarHeadsUpNotificationQueue(); 290 mCarHeadsUpNotificationQueue.setActiveUxRestriction(false); // car is parked 291 Instant now = Instant.now(); 292 mCarHeadsUpNotificationQueue.setClock(Clock.fixed(now, ZoneId.systemDefault())); 293 AlertEntry alertEntry1 = new AlertEntry(generateMockStatusBarNotification( 294 "key1", "msg"), now.minusMillis(3000).toEpochMilli()); 295 AlertEntry alertEntry2 = new AlertEntry(generateMockStatusBarNotification( 296 "key2", "msg"), now.minusMillis(500).toEpochMilli()); 297 AlertEntry alertEntry3 = new AlertEntry(generateMockStatusBarNotification( 298 "key3", "msg"), now.toEpochMilli()); 299 mCarHeadsUpNotificationQueue.addToPriorityQueue(alertEntry1); 300 mCarHeadsUpNotificationQueue.addToPriorityQueue(alertEntry2); 301 mCarHeadsUpNotificationQueue.addToPriorityQueue(alertEntry3); 302 when(mCarHeadsUpNotificationQueueCallback.getActiveHeadsUpNotifications()).thenReturn( 303 new ArrayList<>()); 304 305 mCarHeadsUpNotificationQueue.triggerCallback(); 306 307 PriorityQueue<String> result = mCarHeadsUpNotificationQueue.getPriorityQueue(); 308 verify(mCarHeadsUpNotificationQueueCallback) 309 .removedFromHeadsUpQueue(mAlertEntryArg.capture()); 310 assertThat(mAlertEntryArg.getValue().getKey()).isEqualTo("key1"); 311 verify(mCarHeadsUpNotificationQueueCallback) 312 .showAsHeadsUp(mAlertEntryArg.capture(), 313 nullable(NotificationListenerService.RankingMap.class)); 314 assertThat(mAlertEntryArg.getValue().getKey()).isEqualTo("key2"); 315 assertThat(result.contains("key3")).isTrue(); 316 } 317 318 @Test triggerCallback_doesNot_expireNotifications_whenParked()319 public void triggerCallback_doesNot_expireNotifications_whenParked() { 320 mContext.getOrCreateTestableResources().addOverride( 321 R.bool.config_expireHeadsUpWhenParked, /* value= */ false); 322 mContext.getOrCreateTestableResources().addOverride( 323 R.integer.headsup_queue_expire_driving_duration_ms, /* value= */ 1000); 324 mCarHeadsUpNotificationQueue = createCarHeadsUpNotificationQueue(); 325 mCarHeadsUpNotificationQueue.setActiveUxRestriction(false); // car is parked 326 Instant now = Instant.now(); 327 mCarHeadsUpNotificationQueue.setClock(Clock.fixed(now, ZoneId.systemDefault())); 328 AlertEntry alertEntry1 = new AlertEntry(generateMockStatusBarNotification( 329 "key1", "msg"), now.minusMillis(3000).toEpochMilli()); 330 AlertEntry alertEntry2 = new AlertEntry(generateMockStatusBarNotification( 331 "key2", "msg"), now.minusMillis(500).toEpochMilli()); 332 AlertEntry alertEntry3 = new AlertEntry(generateMockStatusBarNotification( 333 "key3", "msg"), now.toEpochMilli()); 334 mCarHeadsUpNotificationQueue.addToPriorityQueue(alertEntry1); 335 mCarHeadsUpNotificationQueue.addToPriorityQueue(alertEntry2); 336 mCarHeadsUpNotificationQueue.addToPriorityQueue(alertEntry3); 337 when(mCarHeadsUpNotificationQueueCallback.getActiveHeadsUpNotifications()).thenReturn( 338 new ArrayList<>()); 339 340 mCarHeadsUpNotificationQueue.triggerCallback(); 341 342 PriorityQueue<String> result = mCarHeadsUpNotificationQueue.getPriorityQueue(); 343 verify(mCarHeadsUpNotificationQueueCallback) 344 .showAsHeadsUp(mAlertEntryArg.capture(), 345 nullable(NotificationListenerService.RankingMap.class)); 346 assertThat(mAlertEntryArg.getValue().getKey()).isEqualTo("key1"); 347 assertThat(result.contains("key2")).isTrue(); 348 assertThat(result.contains("key3")).isTrue(); 349 } 350 351 @Test triggerCallback_expireNotifications_whenDriving()352 public void triggerCallback_expireNotifications_whenDriving() { 353 mContext.getOrCreateTestableResources().addOverride( 354 R.bool.config_expireHeadsUpWhenDriving, /* value= */ true); 355 mContext.getOrCreateTestableResources().addOverride( 356 R.integer.headsup_queue_expire_driving_duration_ms, /* value= */ 1000); 357 mCarHeadsUpNotificationQueue = createCarHeadsUpNotificationQueue(); 358 mCarHeadsUpNotificationQueue.setActiveUxRestriction(true); // car is driving 359 Instant now = Instant.now(); 360 mCarHeadsUpNotificationQueue.setClock(Clock.fixed(now, ZoneId.systemDefault())); 361 AlertEntry alertEntry1 = new AlertEntry(generateMockStatusBarNotification( 362 "key1", "msg"), now.minusMillis(3000).toEpochMilli()); 363 AlertEntry alertEntry2 = new AlertEntry(generateMockStatusBarNotification( 364 "key2", "msg"), now.minusMillis(500).toEpochMilli()); 365 AlertEntry alertEntry3 = new AlertEntry(generateMockStatusBarNotification( 366 "key3", "msg"), now.toEpochMilli()); 367 mCarHeadsUpNotificationQueue.addToPriorityQueue(alertEntry1); 368 mCarHeadsUpNotificationQueue.addToPriorityQueue(alertEntry2); 369 mCarHeadsUpNotificationQueue.addToPriorityQueue(alertEntry3); 370 when(mCarHeadsUpNotificationQueueCallback.getActiveHeadsUpNotifications()).thenReturn( 371 new ArrayList<>()); 372 373 mCarHeadsUpNotificationQueue.triggerCallback(); 374 375 PriorityQueue<String> result = mCarHeadsUpNotificationQueue.getPriorityQueue(); 376 verify(mCarHeadsUpNotificationQueueCallback) 377 .removedFromHeadsUpQueue(mAlertEntryArg.capture()); 378 assertThat(mAlertEntryArg.getValue().getKey()).isEqualTo("key1"); 379 verify(mCarHeadsUpNotificationQueueCallback) 380 .showAsHeadsUp(mAlertEntryArg.capture(), 381 nullable(NotificationListenerService.RankingMap.class)); 382 assertThat(mAlertEntryArg.getValue().getKey()).isEqualTo("key2"); 383 assertThat(result.contains("key3")).isTrue(); 384 } 385 386 @Test triggerCallback_doesNot_expireNotifications_forInternalCategory()387 public void triggerCallback_doesNot_expireNotifications_forInternalCategory() { 388 mContext.getOrCreateTestableResources().addOverride( 389 R.bool.config_expireHeadsUpWhenParked, /* value= */ true); 390 mContext.getOrCreateTestableResources().addOverride( 391 R.integer.headsup_queue_expire_parked_duration_ms, /* value= */ 1000); 392 mCarHeadsUpNotificationQueue = createCarHeadsUpNotificationQueue(); 393 mCarHeadsUpNotificationQueue.setActiveUxRestriction(false); // car is parked 394 Instant now = Instant.now(); 395 mCarHeadsUpNotificationQueue.setClock(Clock.fixed(now, ZoneId.systemDefault())); 396 AlertEntry alertEntry1 = new AlertEntry(generateMockStatusBarNotification( 397 "key1", "HUN_QUEUE_INTERNAL"), now.minusMillis(3000).toEpochMilli()); 398 mCarHeadsUpNotificationQueue.addToPriorityQueue(alertEntry1); 399 when(mCarHeadsUpNotificationQueueCallback.getActiveHeadsUpNotifications()).thenReturn( 400 new ArrayList<>()); 401 402 mCarHeadsUpNotificationQueue.triggerCallback(); 403 404 verify(mCarHeadsUpNotificationQueueCallback, times(0)) 405 .removedFromHeadsUpQueue(mAlertEntryArg.capture()); 406 } 407 408 @Test triggerCallback_setHunExpiredFlagToTrue_onHunExpired()409 public void triggerCallback_setHunExpiredFlagToTrue_onHunExpired() { 410 mContext.getOrCreateTestableResources().addOverride( 411 R.bool.config_expireHeadsUpWhenParked, /* value= */ true); 412 mContext.getOrCreateTestableResources().addOverride( 413 R.integer.headsup_queue_expire_parked_duration_ms, /* value= */ 1000); 414 mCarHeadsUpNotificationQueue = createCarHeadsUpNotificationQueue(); 415 mCarHeadsUpNotificationQueue.setActiveUxRestriction(false); // car is parked 416 Instant now = Instant.now(); 417 mCarHeadsUpNotificationQueue.setClock(Clock.fixed(now, ZoneId.systemDefault())); 418 AlertEntry alertEntry_expired = new AlertEntry(generateMockStatusBarNotification( 419 "key1", "msg"), now.minusMillis(3000).toEpochMilli()); 420 AlertEntry alertEntry_notExpired = new AlertEntry(generateMockStatusBarNotification( 421 "key2", "msg"), now.minusMillis(500).toEpochMilli()); 422 mCarHeadsUpNotificationQueue.addToPriorityQueue(alertEntry_expired); 423 mCarHeadsUpNotificationQueue.addToPriorityQueue(alertEntry_notExpired); 424 when(mCarHeadsUpNotificationQueueCallback.getActiveHeadsUpNotifications()).thenReturn( 425 new ArrayList<>()); 426 427 mCarHeadsUpNotificationQueue.triggerCallback(); 428 429 verify(mCarHeadsUpNotificationQueueCallback).removedFromHeadsUpQueue(any(AlertEntry.class)); 430 assertThat(mCarHeadsUpNotificationQueue.mAreNotificationsExpired).isTrue(); 431 } 432 433 @Test triggerCallback_setHunExpiredFlagToFalse_onHunExpiredAndEmptyQueue()434 public void triggerCallback_setHunExpiredFlagToFalse_onHunExpiredAndEmptyQueue() { 435 mContext.getOrCreateTestableResources().addOverride( 436 R.bool.config_expireHeadsUpWhenDriving, /* value= */ true); 437 mContext.getOrCreateTestableResources().addOverride( 438 R.integer.headsup_queue_expire_driving_duration_ms, /* value= */ 1000); 439 mCarHeadsUpNotificationQueue = createCarHeadsUpNotificationQueue(); 440 mCarHeadsUpNotificationQueue.setActiveUxRestriction(true); // car is driving 441 Instant now = Instant.now(); 442 mCarHeadsUpNotificationQueue.setClock(Clock.fixed(now, ZoneId.systemDefault())); 443 AlertEntry alertEntry1 = new AlertEntry(generateMockStatusBarNotification( 444 "key1", "msg"), now.minusMillis(2000).toEpochMilli()); 445 mCarHeadsUpNotificationQueue.addToPriorityQueue(alertEntry1); 446 when(mCarHeadsUpNotificationQueueCallback.getActiveHeadsUpNotifications()).thenReturn( 447 new ArrayList<>()); 448 449 mCarHeadsUpNotificationQueue.triggerCallback(); 450 451 assertThat(mCarHeadsUpNotificationQueue.mAreNotificationsExpired).isFalse(); 452 } 453 454 @Test triggerCallback_setHunRemovalFlagToTrue_onHunExpiredAndEmptyQueue()455 public void triggerCallback_setHunRemovalFlagToTrue_onHunExpiredAndEmptyQueue() { 456 mContext.getOrCreateTestableResources().addOverride( 457 R.bool.config_expireHeadsUpWhenDriving, /* value= */ true); 458 mContext.getOrCreateTestableResources().addOverride( 459 R.integer.headsup_queue_expire_driving_duration_ms, /* value= */ 1000); 460 mCarHeadsUpNotificationQueue = createCarHeadsUpNotificationQueue(); 461 mCarHeadsUpNotificationQueue.setActiveUxRestriction(true); // car is driving 462 Instant now = Instant.now(); 463 mCarHeadsUpNotificationQueue.setClock(Clock.fixed(now, ZoneId.systemDefault())); 464 AlertEntry alertEntry1 = new AlertEntry(generateMockStatusBarNotification( 465 "key1", "msg"), now.minusMillis(2000).toEpochMilli()); 466 mCarHeadsUpNotificationQueue.addToPriorityQueue(alertEntry1); 467 when(mCarHeadsUpNotificationQueueCallback.getActiveHeadsUpNotifications()).thenReturn( 468 new ArrayList<>()); 469 470 mCarHeadsUpNotificationQueue.triggerCallback(); 471 472 assertThat(mCarHeadsUpNotificationQueue.mCancelInternalNotificationOnStateChange).isTrue(); 473 } 474 475 @Test triggerCallback_sendsNotificationToCurrentUser_onHunExpiredAndEmptyQueue()476 public void triggerCallback_sendsNotificationToCurrentUser_onHunExpiredAndEmptyQueue() { 477 mCarHeadsUpNotificationQueue = createCarHeadsUpNotificationQueue(); 478 when(mCarHeadsUpNotificationQueueCallback.getActiveHeadsUpNotifications()).thenReturn( 479 new ArrayList<>()); 480 AlertEntry alertEntry1 = new AlertEntry(generateMockStatusBarNotification( 481 "key1", "msg"), /* postTime= */ 1000); 482 mCarHeadsUpNotificationQueue.addToPriorityQueue(alertEntry1); 483 mCarHeadsUpNotificationQueue.mAreNotificationsExpired = true; 484 485 mCarHeadsUpNotificationQueue.triggerCallback(); 486 487 verify(mNotificationManager).notifyAsUser(anyString(), anyInt(), mNotificationArg.capture(), 488 eq(UserHandle.of(USER_ID))); 489 assertThat(mNotificationArg.getValue().category).isEqualTo("HUN_QUEUE_INTERNAL"); 490 } 491 492 @Test triggerCallback_doesNot_expireNotifications_whenDriving()493 public void triggerCallback_doesNot_expireNotifications_whenDriving() { 494 mContext.getOrCreateTestableResources().addOverride( 495 R.bool.config_expireHeadsUpWhenDriving, /* value= */ false); 496 mContext.getOrCreateTestableResources().addOverride( 497 R.integer.headsup_queue_expire_driving_duration_ms, /* value= */ 1000); 498 mCarHeadsUpNotificationQueue = createCarHeadsUpNotificationQueue(); 499 mCarHeadsUpNotificationQueue.setActiveUxRestriction(true); // car is driving 500 Instant now = Instant.now(); 501 mCarHeadsUpNotificationQueue.setClock(Clock.fixed(now, ZoneId.systemDefault())); 502 AlertEntry alertEntry1 = new AlertEntry(generateMockStatusBarNotification( 503 "key1", "msg"), now.minusMillis(3000).toEpochMilli()); 504 AlertEntry alertEntry2 = new AlertEntry(generateMockStatusBarNotification( 505 "key2", "msg"), now.minusMillis(500).toEpochMilli()); 506 AlertEntry alertEntry3 = new AlertEntry(generateMockStatusBarNotification( 507 "key3", "msg"), now.toEpochMilli()); 508 mCarHeadsUpNotificationQueue.addToPriorityQueue(alertEntry1); 509 mCarHeadsUpNotificationQueue.addToPriorityQueue(alertEntry2); 510 mCarHeadsUpNotificationQueue.addToPriorityQueue(alertEntry3); 511 when(mCarHeadsUpNotificationQueueCallback.getActiveHeadsUpNotifications()).thenReturn( 512 new ArrayList<>()); 513 514 mCarHeadsUpNotificationQueue.triggerCallback(); 515 516 PriorityQueue<String> result = mCarHeadsUpNotificationQueue.getPriorityQueue(); 517 verify(mCarHeadsUpNotificationQueueCallback) 518 .showAsHeadsUp(mAlertEntryArg.capture(), 519 nullable(NotificationListenerService.RankingMap.class)); 520 assertThat(mAlertEntryArg.getValue().getKey()).isEqualTo("key1"); 521 assertThat(result.contains("key2")).isTrue(); 522 assertThat(result.contains("key3")).isTrue(); 523 } 524 525 @Test triggerCallback_doesNot_showNotifications_whenAllowlistAppsAreInForeground()526 public void triggerCallback_doesNot_showNotifications_whenAllowlistAppsAreInForeground() 527 throws RemoteException { 528 mContext.getOrCreateTestableResources().addOverride( 529 R.bool.config_expireHeadsUpWhenParked, /* value= */ false); 530 mContext.getOrCreateTestableResources().addOverride( 531 R.array.headsup_throttled_foreground_packages, /* value= */ new String[]{PKG_1}); 532 mCarHeadsUpNotificationQueue = createCarHeadsUpNotificationQueue(); 533 verify(mActivityTaskManager).registerTaskStackListener(mTaskStackListenerArg.capture()); 534 AlertEntry alertEntry1 = new AlertEntry(generateMockStatusBarNotification( 535 "key1", "msg"), /* postTime= */ 1000); 536 mCarHeadsUpNotificationQueue.addToPriorityQueue(alertEntry1); 537 when(mCarHeadsUpNotificationQueueCallback.getActiveHeadsUpNotifications()).thenReturn( 538 new ArrayList<>()); 539 ActivityManager.RunningTaskInfo mockRunningTaskInfo = 540 generateRunningTaskInfo(PKG_1, /* displayAreaFeatureId= */ 111); 541 542 mTaskStackListenerArg.getValue().onTaskMovedToFront(mockRunningTaskInfo); 543 mCarHeadsUpNotificationQueue.triggerCallback(); 544 545 verify(mCarHeadsUpNotificationQueueCallback, never()).showAsHeadsUp( 546 any(AlertEntry.class), nullable(NotificationListenerService.RankingMap.class)); 547 } 548 549 @Test triggerCallback_does_showNotifications_whenAllowlistAppsAreNotInForeground()550 public void triggerCallback_does_showNotifications_whenAllowlistAppsAreNotInForeground() 551 throws RemoteException { 552 mContext.getOrCreateTestableResources().addOverride( 553 R.bool.config_expireHeadsUpWhenParked, /* value= */ false); 554 mContext.getOrCreateTestableResources().addOverride( 555 R.array.headsup_throttled_foreground_packages, /* value= */ new String[]{PKG_1}); 556 mCarHeadsUpNotificationQueue = createCarHeadsUpNotificationQueue(); 557 verify(mActivityTaskManager).registerTaskStackListener(mTaskStackListenerArg.capture()); 558 AlertEntry alertEntry1 = new AlertEntry(generateMockStatusBarNotification( 559 "key1", "msg"), /* postTime= */ 1000); 560 mCarHeadsUpNotificationQueue.addToPriorityQueue(alertEntry1); 561 when(mCarHeadsUpNotificationQueueCallback.getActiveHeadsUpNotifications()).thenReturn( 562 new ArrayList<>()); 563 ActivityManager.RunningTaskInfo mockRunningTaskInfo = 564 generateRunningTaskInfo(PKG_2, /* displayAreaFeatureId= */ 111); 565 566 mTaskStackListenerArg.getValue().onTaskMovedToFront(mockRunningTaskInfo); 567 mCarHeadsUpNotificationQueue.triggerCallback(); 568 569 verify(mCarHeadsUpNotificationQueueCallback).showAsHeadsUp( 570 mAlertEntryArg.capture(), nullable(NotificationListenerService.RankingMap.class)); 571 assertThat(mAlertEntryArg.getValue().getKey()).isEqualTo("key1"); 572 } 573 574 @Test nonAllowlistAppInForeground_afterAllowlistApp_callbackScheduled()575 public void nonAllowlistAppInForeground_afterAllowlistApp_callbackScheduled() 576 throws RemoteException { 577 mContext.getOrCreateTestableResources().addOverride( 578 R.bool.config_expireHeadsUpWhenParked, /* value= */ false); 579 mContext.getOrCreateTestableResources().addOverride( 580 R.array.headsup_throttled_foreground_packages, /* value= */ new String[]{PKG_1}); 581 mCarHeadsUpNotificationQueue = createCarHeadsUpNotificationQueue(); 582 verify(mActivityTaskManager).registerTaskStackListener(mTaskStackListenerArg.capture()); 583 AlertEntry alertEntry1 = new AlertEntry(generateMockStatusBarNotification( 584 "key1", "msg"), /* postTime= */ 1000); 585 mCarHeadsUpNotificationQueue.addToPriorityQueue(alertEntry1); 586 when(mCarHeadsUpNotificationQueueCallback.getActiveHeadsUpNotifications()).thenReturn( 587 new ArrayList<>()); 588 ActivityManager.RunningTaskInfo mockRunningTaskInfo_AllowlistPkg = 589 generateRunningTaskInfo(PKG_1, /* displayAreaFeatureId= */ 111); 590 ActivityManager.RunningTaskInfo mockRunningTaskInfo_nonAllowlistPkg = 591 generateRunningTaskInfo(PKG_2, /* displayAreaFeatureId= */ 111); 592 593 mTaskStackListenerArg.getValue().onTaskMovedToFront(mockRunningTaskInfo_AllowlistPkg); 594 mTaskStackListenerArg.getValue().onTaskMovedToFront(mockRunningTaskInfo_nonAllowlistPkg); 595 596 verify(mScheduledExecutorService).schedule(any(Runnable.class), anyLong(), 597 any(TimeUnit.class)); 598 } 599 600 @Test nonAllowlistAppInForeground_afterNonAllowlistApp_callbackNotScheduled()601 public void nonAllowlistAppInForeground_afterNonAllowlistApp_callbackNotScheduled() 602 throws RemoteException { 603 mContext.getOrCreateTestableResources().addOverride( 604 R.bool.config_expireHeadsUpWhenParked, /* value= */ false); 605 mContext.getOrCreateTestableResources().addOverride( 606 R.array.headsup_throttled_foreground_packages, /* value= */ new String[]{PKG_1}); 607 mCarHeadsUpNotificationQueue = createCarHeadsUpNotificationQueue(); 608 verify(mActivityTaskManager).registerTaskStackListener(mTaskStackListenerArg.capture()); 609 AlertEntry alertEntry1 = new AlertEntry(generateMockStatusBarNotification( 610 "key1", "msg"), /* postTime= */ 1000); 611 mCarHeadsUpNotificationQueue.addToPriorityQueue(alertEntry1); 612 when(mCarHeadsUpNotificationQueueCallback.getActiveHeadsUpNotifications()).thenReturn( 613 new ArrayList<>()); 614 ActivityManager.RunningTaskInfo mockRunningTaskInfo_nonAllowlistPkg_1 = 615 generateRunningTaskInfo(PKG_2, /* displayAreaFeatureId= */ 111); 616 ActivityManager.RunningTaskInfo mockRunningTaskInfo_nonAllowlistPkg_2 = 617 generateRunningTaskInfo(PKG_3, /* displayAreaFeatureId= */ 111); 618 619 mTaskStackListenerArg.getValue().onTaskMovedToFront(mockRunningTaskInfo_nonAllowlistPkg_1); 620 mTaskStackListenerArg.getValue().onTaskMovedToFront(mockRunningTaskInfo_nonAllowlistPkg_2); 621 622 verify(mScheduledExecutorService, never()).schedule(any(Runnable.class), anyLong(), 623 any(TimeUnit.class)); 624 } 625 626 @Test removeFromQueue_returnsFalse_whenNotificationNotInQueue()627 public void removeFromQueue_returnsFalse_whenNotificationNotInQueue() { 628 mCarHeadsUpNotificationQueue = createCarHeadsUpNotificationQueue(); 629 AlertEntry alertEntry1 = new AlertEntry(generateMockStatusBarNotification( 630 "key1", "msg"), 1000); 631 AlertEntry alertEntry2 = new AlertEntry(generateMockStatusBarNotification( 632 "key2", "msg"), 2000); 633 AlertEntry alertEntryNotAddedToQueue = new AlertEntry(generateMockStatusBarNotification( 634 "key3", "msg"), 3000); 635 mCarHeadsUpNotificationQueue.addToPriorityQueue(alertEntry1); 636 mCarHeadsUpNotificationQueue.addToPriorityQueue(alertEntry2); 637 638 boolean result = mCarHeadsUpNotificationQueue.removeFromQueue(alertEntryNotAddedToQueue); 639 640 assertThat(result).isFalse(); 641 } 642 643 @Test removeFromQueue_returnsTrue_whenNotificationInQueue()644 public void removeFromQueue_returnsTrue_whenNotificationInQueue() { 645 mCarHeadsUpNotificationQueue = createCarHeadsUpNotificationQueue(); 646 AlertEntry alertEntry1 = new AlertEntry(generateMockStatusBarNotification( 647 "key1", "msg"), 1000); 648 AlertEntry alertEntry2 = new AlertEntry(generateMockStatusBarNotification( 649 "key2", "msg"), 2000); 650 mCarHeadsUpNotificationQueue.addToPriorityQueue(alertEntry1); 651 mCarHeadsUpNotificationQueue.addToPriorityQueue(alertEntry2); 652 653 boolean result = mCarHeadsUpNotificationQueue.removeFromQueue(alertEntry2); 654 655 assertThat(result).isTrue(); 656 } 657 658 @Test releaseQueue_removes_notificationsFromQueue()659 public void releaseQueue_removes_notificationsFromQueue() { 660 mContext.getOrCreateTestableResources().addOverride( 661 R.bool.config_dismissHeadsUpWhenNotificationCenterOpens, /* value= */ false); 662 mCarHeadsUpNotificationQueue = createCarHeadsUpNotificationQueue(); 663 AlertEntry alertEntry1 = new AlertEntry(generateMockStatusBarNotification( 664 "key1", "msg"), /* postTime= */ 1000); 665 AlertEntry alertEntry2 = new AlertEntry(generateMockStatusBarNotification( 666 "key2", "msg"), /* postTime= */ 2000); 667 mCarHeadsUpNotificationQueue.addToPriorityQueue(alertEntry1); 668 mCarHeadsUpNotificationQueue.addToPriorityQueue(alertEntry2); 669 670 mCarHeadsUpNotificationQueue.releaseQueue(); 671 672 verify(mCarHeadsUpNotificationQueueCallback, times(2)).removedFromHeadsUpQueue( 673 mAlertEntryArg.capture()); 674 assertThat(mAlertEntryArg.getAllValues().size()).isEqualTo(2); 675 } 676 677 @Test releaseQueue_dismiss_activeHUNs()678 public void releaseQueue_dismiss_activeHUNs() { 679 mContext.getOrCreateTestableResources().addOverride( 680 R.bool.config_dismissHeadsUpWhenNotificationCenterOpens, /* value= */ true); 681 mCarHeadsUpNotificationQueue = createCarHeadsUpNotificationQueue(); 682 AlertEntry alertEntry = new AlertEntry(generateMockStatusBarNotification( 683 "key1", "msg"), /* postTime= */ 1000); 684 when(mCarHeadsUpNotificationQueueCallback.getActiveHeadsUpNotifications()).thenReturn( 685 new ArrayList<>(Collections.singletonList(alertEntry))); 686 687 mCarHeadsUpNotificationQueue.releaseQueue(); 688 689 verify(mCarHeadsUpNotificationQueueCallback).dismissHeadsUp(mAlertEntryArg.capture()); 690 assertThat(mAlertEntryArg.getValue().getKey()).isEqualTo("key1"); 691 } 692 693 @Test releaseQueue_doesNot_dismiss_nonDismissibleHUNs()694 public void releaseQueue_doesNot_dismiss_nonDismissibleHUNs() { 695 mContext.getOrCreateTestableResources().addOverride( 696 R.bool.config_dismissHeadsUpWhenNotificationCenterOpens, /* value= */ true); 697 mCarHeadsUpNotificationQueue = createCarHeadsUpNotificationQueue(); 698 AlertEntry alertEntry = new AlertEntry( 699 generateMockStatusBarNotification("key1", Notification.CATEGORY_CALL, 700 /* isOngoing= */ true, /* hasFullScreenIntent= */ true), 701 /* postTime= */ 1000); 702 when(mCarHeadsUpNotificationQueueCallback.getActiveHeadsUpNotifications()).thenReturn( 703 new ArrayList<>(Collections.singletonList(alertEntry))); 704 705 mCarHeadsUpNotificationQueue.releaseQueue(); 706 707 verify(mCarHeadsUpNotificationQueueCallback, times(0)).removedFromHeadsUpQueue(any()); 708 } 709 710 @Test onStateChange_internalCategory_hunRemovalFlagTrue_setHunRemovalFlagToFalse()711 public void onStateChange_internalCategory_hunRemovalFlagTrue_setHunRemovalFlagToFalse() { 712 mCarHeadsUpNotificationQueue = createCarHeadsUpNotificationQueue(); 713 AlertEntry alertEntry = new AlertEntry(generateMockStatusBarNotification( 714 "key1", "HUN_QUEUE_INTERNAL"), 1000); 715 mCarHeadsUpNotificationQueue.mCancelInternalNotificationOnStateChange = true; 716 717 mCarHeadsUpNotificationQueue.onStateChange(alertEntry, 718 CarHeadsUpNotificationManager.HeadsUpState.DISMISSED); 719 720 assertThat(mCarHeadsUpNotificationQueue.mCancelInternalNotificationOnStateChange).isFalse(); 721 } 722 723 @Test onStateChange_internalCategory_hunRemovalFlagTrue_setHunExpiredToFalse()724 public void onStateChange_internalCategory_hunRemovalFlagTrue_setHunExpiredToFalse() { 725 mCarHeadsUpNotificationQueue = createCarHeadsUpNotificationQueue(); 726 AlertEntry alertEntry = new AlertEntry(generateMockStatusBarNotification( 727 "key1", "HUN_QUEUE_INTERNAL"), 1000); 728 mCarHeadsUpNotificationQueue.mCancelInternalNotificationOnStateChange = true; 729 730 mCarHeadsUpNotificationQueue.onStateChange(alertEntry, 731 CarHeadsUpNotificationManager.HeadsUpState.DISMISSED); 732 733 assertThat(mCarHeadsUpNotificationQueue.mAreNotificationsExpired).isFalse(); 734 } 735 736 @Test onStateChange_internalCategory_hunRemovalFlagTrue_cancelHunForCurrentUser()737 public void onStateChange_internalCategory_hunRemovalFlagTrue_cancelHunForCurrentUser() { 738 mCarHeadsUpNotificationQueue = createCarHeadsUpNotificationQueue(); 739 AlertEntry alertEntry = new AlertEntry(generateMockStatusBarNotification( 740 "key1", "HUN_QUEUE_INTERNAL"), 1000); 741 mCarHeadsUpNotificationQueue.mCancelInternalNotificationOnStateChange = true; 742 743 mCarHeadsUpNotificationQueue.onStateChange(alertEntry, 744 CarHeadsUpNotificationManager.HeadsUpState.DISMISSED); 745 746 verify(mNotificationManager).cancelAsUser(anyString(), eq(/* id= */ 2000), 747 eq(UserHandle.of(USER_ID))); 748 } 749 750 @Test onStateChange_notInternalCategory_hunRemovalFlagTrue_notCancelHunForCurrentUser()751 public void onStateChange_notInternalCategory_hunRemovalFlagTrue_notCancelHunForCurrentUser() { 752 mCarHeadsUpNotificationQueue = createCarHeadsUpNotificationQueue(); 753 AlertEntry alertEntry = new AlertEntry(generateMockStatusBarNotification( 754 "key1", "msg"), 1000); 755 mCarHeadsUpNotificationQueue.mCancelInternalNotificationOnStateChange = true; 756 757 mCarHeadsUpNotificationQueue.onStateChange(alertEntry, 758 CarHeadsUpNotificationManager.HeadsUpState.DISMISSED); 759 760 verify(mNotificationManager, times(0)).cancelAsUser(anyString(), anyInt(), 761 any(UserHandle.class)); 762 } 763 764 @Test onStateChange_notInternalCategory_hunRemovalFlagTrue_doesNotsetHunRemovalFlag()765 public void onStateChange_notInternalCategory_hunRemovalFlagTrue_doesNotsetHunRemovalFlag() { 766 mCarHeadsUpNotificationQueue = createCarHeadsUpNotificationQueue(); 767 AlertEntry alertEntry = new AlertEntry(generateMockStatusBarNotification( 768 "key1", "msg"), 1000); 769 mCarHeadsUpNotificationQueue.mCancelInternalNotificationOnStateChange = true; 770 771 mCarHeadsUpNotificationQueue.onStateChange(alertEntry, 772 CarHeadsUpNotificationManager.HeadsUpState.DISMISSED); 773 774 assertThat(mCarHeadsUpNotificationQueue.mCancelInternalNotificationOnStateChange).isTrue(); 775 } 776 777 @Test onStateChange_dismissed_callbackScheduled()778 public void onStateChange_dismissed_callbackScheduled() { 779 mContext.getOrCreateTestableResources().addOverride( 780 R.bool.config_expireHeadsUpWhenParked, /* value= */ false); 781 mCarHeadsUpNotificationQueue = createCarHeadsUpNotificationQueue(); 782 when(mCarHeadsUpNotificationQueueCallback.getActiveHeadsUpNotifications()).thenReturn( 783 new ArrayList<>()); 784 AlertEntry alertEntry = new AlertEntry(generateMockStatusBarNotification( 785 "key1", "msg"), 1000); 786 AlertEntry nextAlertEntry = new AlertEntry(generateMockStatusBarNotification( 787 "key2", "msg"), 2000); 788 mCarHeadsUpNotificationQueue.addToPriorityQueue(nextAlertEntry); 789 790 mCarHeadsUpNotificationQueue.onStateChange(alertEntry, 791 CarHeadsUpNotificationManager.HeadsUpState.DISMISSED); 792 793 verify(mScheduledExecutorService).schedule(any(Runnable.class), anyLong(), 794 any(TimeUnit.class)); 795 } 796 797 @Test onStateChange_removedBySender_callbackScheduled()798 public void onStateChange_removedBySender_callbackScheduled() { 799 mContext.getOrCreateTestableResources().addOverride( 800 R.bool.config_expireHeadsUpWhenParked, /* value= */ false); 801 mCarHeadsUpNotificationQueue = createCarHeadsUpNotificationQueue(); 802 when(mCarHeadsUpNotificationQueueCallback.getActiveHeadsUpNotifications()).thenReturn( 803 new ArrayList<>()); 804 AlertEntry alertEntry = new AlertEntry(generateMockStatusBarNotification( 805 "key1", "msg"), 1000); 806 AlertEntry nextAlertEntry = new AlertEntry(generateMockStatusBarNotification( 807 "key2", "msg"), 2000); 808 mCarHeadsUpNotificationQueue.addToPriorityQueue(nextAlertEntry); 809 810 mCarHeadsUpNotificationQueue.onStateChange(alertEntry, 811 CarHeadsUpNotificationManager.HeadsUpState.REMOVED_BY_SENDER); 812 813 verify(mScheduledExecutorService).schedule(any(Runnable.class), anyLong(), 814 any(TimeUnit.class)); 815 } 816 817 @Test onStateChange_shown_doesNot_showNextNotificationInQueue()818 public void onStateChange_shown_doesNot_showNextNotificationInQueue() { 819 mContext.getOrCreateTestableResources().addOverride( 820 R.bool.config_expireHeadsUpWhenParked, /* value= */ false); 821 mCarHeadsUpNotificationQueue = createCarHeadsUpNotificationQueue(); 822 when(mCarHeadsUpNotificationQueueCallback.getActiveHeadsUpNotifications()).thenReturn( 823 new ArrayList<>()); 824 AlertEntry alertEntry = new AlertEntry(generateMockStatusBarNotification( 825 "key1", "msg"), 1000); 826 AlertEntry nextAlertEntry = new AlertEntry(generateMockStatusBarNotification( 827 "key2", "msg"), 2000); 828 mCarHeadsUpNotificationQueue.addToPriorityQueue(nextAlertEntry); 829 830 mCarHeadsUpNotificationQueue.onStateChange(alertEntry, 831 CarHeadsUpNotificationManager.HeadsUpState.SHOWN); 832 833 verify(mCarHeadsUpNotificationQueueCallback, never()).showAsHeadsUp(any(AlertEntry.class), 834 nullable(NotificationListenerService.RankingMap.class)); 835 } 836 837 @Test onStateChange_removedFromQueue_does_not_showNextNotificationInQueue()838 public void onStateChange_removedFromQueue_does_not_showNextNotificationInQueue() { 839 mContext.getOrCreateTestableResources().addOverride( 840 R.bool.config_expireHeadsUpWhenParked, /* value= */ false); 841 mCarHeadsUpNotificationQueue = createCarHeadsUpNotificationQueue(); 842 when(mCarHeadsUpNotificationQueueCallback.getActiveHeadsUpNotifications()).thenReturn( 843 new ArrayList<>()); 844 AlertEntry alertEntry = new AlertEntry(generateMockStatusBarNotification( 845 "key1", "msg"), 1000); 846 AlertEntry nextAlertEntry = new AlertEntry(generateMockStatusBarNotification( 847 "key2", "msg"), 2000); 848 mCarHeadsUpNotificationQueue.addToPriorityQueue(nextAlertEntry); 849 850 mCarHeadsUpNotificationQueue.onStateChange(alertEntry, 851 CarHeadsUpNotificationManager.HeadsUpState.REMOVED_FROM_QUEUE); 852 853 verify(mCarHeadsUpNotificationQueueCallback, never()).showAsHeadsUp(any(AlertEntry.class), 854 nullable(NotificationListenerService.RankingMap.class)); 855 } 856 857 858 @Test getUserNotificationForExpiredHun_parkState_usesParkNotificationTitle()859 public void getUserNotificationForExpiredHun_parkState_usesParkNotificationTitle() { 860 mContext.getOrCreateTestableResources().addOverride( 861 R.string.hun_suppression_notification_title_park, /* value= */ "test_value"); 862 mCarHeadsUpNotificationQueue = createCarHeadsUpNotificationQueue(); 863 mCarHeadsUpNotificationQueue.setActiveUxRestriction(false); // car is parked 864 865 Notification result = mCarHeadsUpNotificationQueue.getUserNotificationForExpiredHun(); 866 867 assertThat(result.extras.getCharSequence(Notification.EXTRA_TITLE).toString()).isEqualTo( 868 "test_value"); 869 } 870 871 @Test getUserNotificationForExpiredHun_driveState_usesDriveNotificationTitle()872 public void getUserNotificationForExpiredHun_driveState_usesDriveNotificationTitle() { 873 mContext.getOrCreateTestableResources().addOverride( 874 R.string.hun_suppression_notification_title_drive, /* value= */ "test_value"); 875 mCarHeadsUpNotificationQueue = createCarHeadsUpNotificationQueue(); 876 mCarHeadsUpNotificationQueue.setActiveUxRestriction(true); // car is driving 877 878 Notification result = mCarHeadsUpNotificationQueue.getUserNotificationForExpiredHun(); 879 880 assertThat(result.extras.getCharSequence(Notification.EXTRA_TITLE).toString()).isEqualTo( 881 "test_value"); 882 } 883 884 @Test scheduleCallback_schedulesNewTask_whenNoTaskScheduled()885 public void scheduleCallback_schedulesNewTask_whenNoTaskScheduled() { 886 mCarHeadsUpNotificationQueue = createCarHeadsUpNotificationQueue(); 887 AlertEntry alertEntry1 = new AlertEntry(generateMockStatusBarNotification( 888 "key1", "msg"), /* postTime= */ 1000); 889 mCarHeadsUpNotificationQueue.addToPriorityQueue(alertEntry1); 890 when(mCarHeadsUpNotificationQueueCallback.getActiveHeadsUpNotifications()).thenReturn( 891 new ArrayList<>()); 892 mCarHeadsUpNotificationQueue.mScheduledFuture = null; 893 long delay = 10; 894 895 mCarHeadsUpNotificationQueue.scheduleCallback(delay); 896 897 verify(mScheduledExecutorService).schedule(any(Runnable.class), 898 eq(delay), eq(TimeUnit.MILLISECONDS)); 899 } 900 901 @Test scheduleCallback_schedulesNewTask_whenShorterTaskScheduled()902 public void scheduleCallback_schedulesNewTask_whenShorterTaskScheduled() { 903 mCarHeadsUpNotificationQueue = createCarHeadsUpNotificationQueue(); 904 AlertEntry alertEntry1 = new AlertEntry(generateMockStatusBarNotification( 905 "key1", "msg"), /* postTime= */ 1000); 906 mCarHeadsUpNotificationQueue.addToPriorityQueue(alertEntry1); 907 when(mCarHeadsUpNotificationQueueCallback.getActiveHeadsUpNotifications()).thenReturn( 908 new ArrayList<>()); 909 ScheduledFuture<?> mockScheduledFuture = mock(ScheduledFuture.class); 910 when(mockScheduledFuture.getDelay(TimeUnit.MILLISECONDS)).thenReturn(5L); 911 mCarHeadsUpNotificationQueue.mScheduledFuture = mockScheduledFuture; 912 long delay = 10L; 913 914 mCarHeadsUpNotificationQueue.scheduleCallback(delay); 915 916 verify(mScheduledExecutorService).schedule(any(Runnable.class), 917 eq(delay), eq(TimeUnit.MILLISECONDS)); 918 } 919 920 @Test scheduleCallback_cancelsFutureTask_whenShorterTaskScheduled()921 public void scheduleCallback_cancelsFutureTask_whenShorterTaskScheduled() { 922 mCarHeadsUpNotificationQueue = createCarHeadsUpNotificationQueue(); 923 AlertEntry alertEntry1 = new AlertEntry(generateMockStatusBarNotification( 924 "key1", "msg"), /* postTime= */ 1000); 925 mCarHeadsUpNotificationQueue.addToPriorityQueue(alertEntry1); 926 when(mCarHeadsUpNotificationQueueCallback.getActiveHeadsUpNotifications()).thenReturn( 927 new ArrayList<>()); 928 ScheduledFuture<?> mockScheduledFuture = mock(ScheduledFuture.class); 929 when(mockScheduledFuture.getDelay(TimeUnit.MILLISECONDS)).thenReturn(5L); 930 mCarHeadsUpNotificationQueue.mScheduledFuture = mockScheduledFuture; 931 long delay = 10L; 932 933 mCarHeadsUpNotificationQueue.scheduleCallback(delay); 934 935 verify(mockScheduledFuture).cancel(/* mayInterruptIfRunning= */ true); 936 } 937 938 @Test scheduleCallback_doesNotScheduleNewTask_whenLongerTaskScheduled()939 public void scheduleCallback_doesNotScheduleNewTask_whenLongerTaskScheduled() { 940 mCarHeadsUpNotificationQueue = createCarHeadsUpNotificationQueue(); 941 AlertEntry alertEntry1 = new AlertEntry(generateMockStatusBarNotification( 942 "key1", "msg"), /* postTime= */ 1000); 943 mCarHeadsUpNotificationQueue.addToPriorityQueue(alertEntry1); 944 when(mCarHeadsUpNotificationQueueCallback.getActiveHeadsUpNotifications()).thenReturn( 945 new ArrayList<>()); 946 ScheduledFuture<?> mockScheduledFuture = mock(ScheduledFuture.class); 947 when(mockScheduledFuture.getDelay(TimeUnit.MILLISECONDS)).thenReturn(20L); 948 mCarHeadsUpNotificationQueue.mScheduledFuture = mockScheduledFuture; 949 long delay = 10L; 950 951 mCarHeadsUpNotificationQueue.scheduleCallback(delay); 952 953 verify(mScheduledExecutorService, never()).schedule(any(Runnable.class), anyLong(), 954 any(TimeUnit.class)); 955 } 956 generateMockStatusBarNotification(String key, String category)957 private StatusBarNotification generateMockStatusBarNotification(String key, String category) { 958 return generateMockStatusBarNotification(key, category, 959 /* isOngoing= */ false, /* hasFullScreenIntent= */ false); 960 } 961 generateMockStatusBarNotification(String key, String category, boolean isOngoing, boolean hasFullScreenIntent)962 private StatusBarNotification generateMockStatusBarNotification(String key, String category, 963 boolean isOngoing, boolean hasFullScreenIntent) { 964 StatusBarNotification sbn = mock(StatusBarNotification.class); 965 Notification.Builder notificationBuilder = new Notification.Builder(mContext, 966 CHANNEL_ID).setCategory(category).setOngoing(isOngoing); 967 if (hasFullScreenIntent) { 968 notificationBuilder.setFullScreenIntent(mock(PendingIntent.class), 969 /* highPriority= */ true); 970 } 971 when(sbn.getNotification()).thenReturn(notificationBuilder.build()); 972 when(sbn.getKey()).thenReturn(key); 973 return sbn; 974 } 975 generateRunningTaskInfo(String pkg, int displayAreaFeatureId)976 private ActivityManager.RunningTaskInfo generateRunningTaskInfo(String pkg, 977 int displayAreaFeatureId) { 978 ComponentName componentName = mock(ComponentName.class); 979 when(componentName.getPackageName()).thenReturn(pkg); 980 ActivityManager.RunningTaskInfo mockRunningTaskInfo = mock( 981 ActivityManager.RunningTaskInfo.class); 982 mockRunningTaskInfo.baseActivity = componentName; 983 mockRunningTaskInfo.displayAreaFeatureId = displayAreaFeatureId; 984 return mockRunningTaskInfo; 985 } 986 } 987