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