1 package org.robolectric.shadows; 2 3 import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL; 4 import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY; 5 import static com.google.common.truth.Truth.assertThat; 6 import static org.junit.Assert.assertEquals; 7 import static org.junit.Assert.assertNull; 8 import static org.junit.Assert.fail; 9 import static org.robolectric.Shadows.shadowOf; 10 11 import android.app.AutomaticZenRule; 12 import android.app.Notification; 13 import android.app.NotificationChannel; 14 import android.app.NotificationChannelGroup; 15 import android.app.NotificationManager; 16 import android.app.NotificationManager.Policy; 17 import android.content.ComponentName; 18 import android.content.Context; 19 import android.net.Uri; 20 import android.os.Build; 21 import android.service.notification.StatusBarNotification; 22 import androidx.test.core.app.ApplicationProvider; 23 import androidx.test.ext.junit.runners.AndroidJUnit4; 24 import com.google.common.collect.ImmutableList; 25 import java.util.ArrayList; 26 import java.util.List; 27 import org.junit.Before; 28 import org.junit.Test; 29 import org.junit.runner.RunWith; 30 import org.robolectric.annotation.Config; 31 32 @RunWith(AndroidJUnit4.class) 33 public class ShadowNotificationManagerTest { 34 private NotificationManager notificationManager; 35 private Notification notification1 = new Notification(); 36 private Notification notification2 = new Notification(); 37 38 @Before public void setUp() { 39 notificationManager = 40 (NotificationManager) 41 ApplicationProvider.getApplicationContext() 42 .getSystemService(Context.NOTIFICATION_SERVICE); 43 } 44 45 @Test 46 @Config(minSdk = Build.VERSION_CODES.M) 47 public void getCurrentInterruptionFilter() { 48 // Sensible default 49 assertThat(notificationManager.getCurrentInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALL); 50 51 notificationManager.setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY); 52 assertThat(notificationManager.getCurrentInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_PRIORITY); 53 } 54 55 @Test 56 @Config(minSdk = Build.VERSION_CODES.M) 57 public void getNotificationPolicy() { 58 assertThat(notificationManager.getNotificationPolicy()).isNull(); 59 60 final Policy policy = new Policy(0, 0, 0); 61 notificationManager.setNotificationPolicy(policy); 62 assertThat(notificationManager.getNotificationPolicy()).isEqualTo(policy); 63 } 64 65 @Test 66 @Config(minSdk = Build.VERSION_CODES.O) 67 public void createNotificationChannel() { 68 notificationManager.createNotificationChannel(new NotificationChannel("id", "name", 1)); 69 70 assertThat(shadowOf(notificationManager).getNotificationChannels()).hasSize(1); 71 NotificationChannel channel = (NotificationChannel)shadowOf(notificationManager) 72 .getNotificationChannel("id"); 73 assertThat(channel.getName()).isEqualTo("name"); 74 assertThat(channel.getImportance()).isEqualTo(1); 75 } 76 77 @Test 78 @Config(minSdk = Build.VERSION_CODES.O) 79 public void createNotificationChannelGroup() { 80 notificationManager.createNotificationChannelGroup(new NotificationChannelGroup("id", "name")); 81 82 assertThat(shadowOf(notificationManager).getNotificationChannelGroups()).hasSize(1); 83 NotificationChannelGroup group = (NotificationChannelGroup)shadowOf(notificationManager) 84 .getNotificationChannelGroup("id"); 85 assertThat(group.getName()).isEqualTo("name"); 86 } 87 88 @Test 89 @Config(minSdk = Build.VERSION_CODES.O) 90 public void createNotificationChannels() { 91 NotificationChannel channel1 = new NotificationChannel("id", "name", 1); 92 NotificationChannel channel2 = new NotificationChannel("id2", "name2", 1); 93 94 notificationManager.createNotificationChannels(ImmutableList.of(channel1, channel2)); 95 96 assertThat(shadowOf(notificationManager).getNotificationChannels()).hasSize(2); 97 NotificationChannel channel = 98 (NotificationChannel) shadowOf(notificationManager).getNotificationChannel("id"); 99 assertThat(channel.getName()).isEqualTo("name"); 100 assertThat(channel.getImportance()).isEqualTo(1); 101 channel = (NotificationChannel) shadowOf(notificationManager).getNotificationChannel("id2"); 102 assertThat(channel.getName()).isEqualTo("name2"); 103 assertThat(channel.getImportance()).isEqualTo(1); 104 } 105 106 @Test 107 @Config(minSdk = Build.VERSION_CODES.O) 108 public void deleteNotificationChannel() { 109 final String channelId = "channelId"; 110 assertThat(shadowOf(notificationManager).isChannelDeleted(channelId)).isFalse(); 111 notificationManager.createNotificationChannel(new NotificationChannel(channelId, "name", 1)); 112 assertThat(shadowOf(notificationManager).isChannelDeleted(channelId)).isFalse(); 113 notificationManager.deleteNotificationChannel(channelId); 114 assertThat(shadowOf(notificationManager).isChannelDeleted(channelId)).isTrue(); 115 assertThat(notificationManager.getNotificationChannel(channelId)).isNull(); 116 // Per documentation, recreating a deleted channel should have the same settings as the old 117 // deleted channel. 118 notificationManager.createNotificationChannel( 119 new NotificationChannel(channelId, "otherName", 2)); 120 assertThat(shadowOf(notificationManager).isChannelDeleted(channelId)).isFalse(); 121 NotificationChannel channel = notificationManager.getNotificationChannel(channelId); 122 assertThat(channel.getName()).isEqualTo("name"); 123 assertThat(channel.getImportance()).isEqualTo(1); 124 } 125 126 @Test 127 @Config(minSdk = Build.VERSION_CODES.O) 128 public void deleteNotificationChannelGroup() { 129 final String channelId = "channelId"; 130 final String channelGroupId = "channelGroupId"; 131 notificationManager.createNotificationChannelGroup( 132 new NotificationChannelGroup(channelGroupId, "groupName")); 133 NotificationChannel channel = new NotificationChannel(channelId, "channelName", 1); 134 channel.setGroup(channelGroupId); 135 notificationManager.createNotificationChannel(channel); 136 assertThat(shadowOf(notificationManager).isChannelDeleted(channelId)).isFalse(); 137 notificationManager.deleteNotificationChannelGroup(channelGroupId); 138 assertThat(shadowOf(notificationManager).getNotificationChannelGroup(channelGroupId)).isNull(); 139 // Per documentation, deleting a channel group also deletes all associated channels. 140 assertThat(shadowOf(notificationManager).isChannelDeleted(channelId)).isTrue(); 141 } 142 143 @Test 144 @Config(minSdk = Build.VERSION_CODES.N) 145 public void areNotificationsEnabled() { 146 shadowOf(notificationManager).setNotificationsEnabled(true); 147 assertThat(notificationManager.areNotificationsEnabled()).isTrue(); 148 shadowOf(notificationManager).setNotificationsEnabled(false); 149 assertThat(notificationManager.areNotificationsEnabled()).isFalse(); 150 } 151 152 @Test 153 @Config(minSdk = Build.VERSION_CODES.M) 154 public void isNotificationPolicyAccessGranted() { 155 shadowOf(notificationManager).setNotificationPolicyAccessGranted(true); 156 assertThat(notificationManager.isNotificationPolicyAccessGranted()).isTrue(); 157 shadowOf(notificationManager).setNotificationPolicyAccessGranted(false); 158 assertThat(notificationManager.isNotificationPolicyAccessGranted()).isFalse(); 159 } 160 161 @Test 162 @Config(minSdk = Build.VERSION_CODES.N) 163 public void 164 setNotificationPolicyAccessGranted_temporarilyDenyAccess_shouldClearAutomaticZenRules() { 165 shadowOf(notificationManager).setNotificationPolicyAccessGranted(true); 166 AutomaticZenRule rule = 167 new AutomaticZenRule( 168 "name", 169 new ComponentName("pkg", "cls"), 170 Uri.parse("condition://id"), 171 NotificationManager.INTERRUPTION_FILTER_PRIORITY, 172 /* enabled= */ true); 173 String id = notificationManager.addAutomaticZenRule(rule); 174 175 shadowOf(notificationManager).setNotificationPolicyAccessGranted(false); 176 shadowOf(notificationManager).setNotificationPolicyAccessGranted(true); 177 178 assertThat(notificationManager.getAutomaticZenRule(id)).isNull(); 179 assertThat(notificationManager.getAutomaticZenRules()).isEmpty(); 180 } 181 182 @Test 183 @Config(minSdk = Build.VERSION_CODES.N) 184 public void getAutomaticZenRule_notificationAccessDenied_shouldThrowSecurityException() { 185 try { 186 notificationManager.getAutomaticZenRule("some_id"); 187 fail("Should have thrown SecurityException"); 188 } catch (SecurityException expected) { 189 } 190 } 191 192 @Test 193 @Config(minSdk = Build.VERSION_CODES.N) 194 public void getAutomaticZenRule_nonexistentId_shouldReturnNull() { 195 shadowOf(notificationManager).setNotificationPolicyAccessGranted(true); 196 AutomaticZenRule rule = 197 new AutomaticZenRule( 198 "name", 199 new ComponentName("pkg", "cls"), 200 Uri.parse("condition://id"), 201 NotificationManager.INTERRUPTION_FILTER_PRIORITY, 202 /* enabled= */ true); 203 String id = notificationManager.addAutomaticZenRule(rule); 204 205 String nonexistentId = "id_different_from_" + id; 206 assertThat(notificationManager.getAutomaticZenRule(nonexistentId)).isNull(); 207 } 208 209 @Test 210 @Config(minSdk = Build.VERSION_CODES.N) 211 public void getAutomaticZenRules_notificationAccessDenied_shouldThrowSecurityException() { 212 try { 213 notificationManager.getAutomaticZenRules(); 214 fail("Should have thrown SecurityException"); 215 } catch (SecurityException expected) { 216 } 217 } 218 219 @Test 220 @Config(minSdk = Build.VERSION_CODES.N) 221 public void getAutomaticZenRules_noRulesAdded_shouldReturnEmptyMap() { 222 shadowOf(notificationManager).setNotificationPolicyAccessGranted(true); 223 assertThat(notificationManager.getAutomaticZenRules()).isEmpty(); 224 } 225 226 @Test 227 @Config(minSdk = Build.VERSION_CODES.N) 228 public void addAutomaticZenRule_notificationAccessDenied_shouldThrowSecurityException() { 229 AutomaticZenRule rule = 230 new AutomaticZenRule( 231 "name", 232 new ComponentName("pkg", "cls"), 233 Uri.parse("condition://id"), 234 NotificationManager.INTERRUPTION_FILTER_PRIORITY, 235 /* enabled= */ true); 236 try { 237 notificationManager.addAutomaticZenRule(rule); 238 fail("Should have thrown SecurityException"); 239 } catch (SecurityException expected) { 240 } 241 } 242 243 @Test 244 @Config(minSdk = Build.VERSION_CODES.N) 245 public void addAutomaticZenRule_oneRule_shouldAddRuleAndReturnId() { 246 shadowOf(notificationManager).setNotificationPolicyAccessGranted(true); 247 248 AutomaticZenRule rule = 249 new AutomaticZenRule( 250 "name", 251 new ComponentName("pkg", "cls"), 252 Uri.parse("condition://id"), 253 NotificationManager.INTERRUPTION_FILTER_PRIORITY, 254 /* enabled= */ true); 255 String id = notificationManager.addAutomaticZenRule(rule); 256 257 assertThat(id).isNotEmpty(); 258 assertThat(notificationManager.getAutomaticZenRule(id)).isEqualTo(rule); 259 assertThat(notificationManager.getAutomaticZenRules()).containsExactly(id, rule); 260 } 261 262 @Test 263 @Config(minSdk = Build.VERSION_CODES.N) 264 public void addAutomaticZenRule_twoRules_shouldAddBothRulesAndReturnDifferentIds() { 265 shadowOf(notificationManager).setNotificationPolicyAccessGranted(true); 266 267 AutomaticZenRule rule1 = 268 new AutomaticZenRule( 269 "name1", 270 new ComponentName("pkg1", "cls1"), 271 Uri.parse("condition://id1"), 272 NotificationManager.INTERRUPTION_FILTER_PRIORITY, 273 /* enabled= */ true); 274 AutomaticZenRule rule2 = 275 new AutomaticZenRule( 276 "name2", 277 new ComponentName("pkg2", "cls2"), 278 Uri.parse("condition://id2"), 279 NotificationManager.INTERRUPTION_FILTER_ALARMS, 280 /* enabled= */ false); 281 String id1 = notificationManager.addAutomaticZenRule(rule1); 282 String id2 = notificationManager.addAutomaticZenRule(rule2); 283 284 assertThat(id2).isNotEqualTo(id1); 285 assertThat(notificationManager.getAutomaticZenRule(id1)).isEqualTo(rule1); 286 assertThat(notificationManager.getAutomaticZenRule(id2)).isEqualTo(rule2); 287 assertThat(notificationManager.getAutomaticZenRules()).containsExactly(id1, rule1, id2, rule2); 288 } 289 290 @Test 291 @Config(minSdk = Build.VERSION_CODES.N) 292 public void updateAutomaticZenRule_notificationAccessDenied_shouldThrowSecurityException() { 293 AutomaticZenRule rule = 294 new AutomaticZenRule( 295 "name", 296 new ComponentName("pkg", "cls"), 297 Uri.parse("condition://id"), 298 NotificationManager.INTERRUPTION_FILTER_PRIORITY, 299 /* enabled= */ true); 300 try { 301 notificationManager.updateAutomaticZenRule("some_id", rule); 302 fail("Should have thrown SecurityException"); 303 } catch (SecurityException expected) { 304 } 305 } 306 307 @Test 308 @Config(minSdk = Build.VERSION_CODES.N) 309 public void updateAutomaticZenRule_nonexistentId_shouldThrowSecurityException() { 310 shadowOf(notificationManager).setNotificationPolicyAccessGranted(true); 311 AutomaticZenRule rule = 312 new AutomaticZenRule( 313 "name", 314 new ComponentName("pkg", "cls"), 315 Uri.parse("condition://id"), 316 NotificationManager.INTERRUPTION_FILTER_PRIORITY, 317 /* enabled= */ true); 318 String id = notificationManager.addAutomaticZenRule(rule); 319 320 String nonexistentId = "id_different_from_" + id; 321 AutomaticZenRule updatedRule = 322 new AutomaticZenRule( 323 "updated_name", 324 new ComponentName("updated_pkg", "updated_cls"), 325 Uri.parse("condition://updated_id"), 326 NotificationManager.INTERRUPTION_FILTER_ALL, 327 /* enabled= */ false); 328 try { 329 assertThat(notificationManager.updateAutomaticZenRule(nonexistentId, updatedRule)); 330 fail("Should have thrown SecurityException"); 331 } catch (SecurityException expected) { 332 } 333 334 assertThat(notificationManager.getAutomaticZenRule(id)).isEqualTo(rule); 335 assertThat(notificationManager.getAutomaticZenRule(nonexistentId)).isNull(); 336 assertThat(notificationManager.getAutomaticZenRules()).containsExactly(id, rule); 337 } 338 339 @Test 340 @Config(minSdk = Build.VERSION_CODES.N) 341 public void updateAutomaticZenRule_existingId_shouldUpdateRuleAndReturnTrue() { 342 shadowOf(notificationManager).setNotificationPolicyAccessGranted(true); 343 AutomaticZenRule rule1 = 344 new AutomaticZenRule( 345 "name1", 346 new ComponentName("pkg1", "cls1"), 347 Uri.parse("condition://id1"), 348 NotificationManager.INTERRUPTION_FILTER_PRIORITY, 349 /* enabled= */ true); 350 AutomaticZenRule rule2 = 351 new AutomaticZenRule( 352 "name2", 353 new ComponentName("pkg2", "cls2"), 354 Uri.parse("condition://id2"), 355 NotificationManager.INTERRUPTION_FILTER_ALARMS, 356 /* enabled= */ false); 357 String id1 = notificationManager.addAutomaticZenRule(rule1); 358 String id2 = notificationManager.addAutomaticZenRule(rule2); 359 360 AutomaticZenRule updatedRule = 361 new AutomaticZenRule( 362 "updated_name", 363 new ComponentName("updated_pkg", "updated_cls"), 364 Uri.parse("condition://updated_id"), 365 NotificationManager.INTERRUPTION_FILTER_ALL, 366 /* enabled= */ false); 367 assertThat(notificationManager.updateAutomaticZenRule(id2, updatedRule)).isTrue(); 368 369 assertThat(notificationManager.getAutomaticZenRule(id1)).isEqualTo(rule1); 370 assertThat(notificationManager.getAutomaticZenRule(id2)).isEqualTo(updatedRule); 371 assertThat(notificationManager.getAutomaticZenRules()) 372 .containsExactly(id1, rule1, id2, updatedRule); 373 } 374 375 @Test 376 @Config(minSdk = Build.VERSION_CODES.N) 377 public void removeAutomaticZenRule_notificationAccessDenied_shouldThrowSecurityException() { 378 try { 379 notificationManager.removeAutomaticZenRule("some_id"); 380 fail("Should have thrown SecurityException"); 381 } catch (SecurityException expected) { 382 } 383 } 384 385 @Test 386 @Config(minSdk = Build.VERSION_CODES.N) 387 public void removeAutomaticZenRule_nonexistentId_shouldAndReturnFalse() { 388 shadowOf(notificationManager).setNotificationPolicyAccessGranted(true); 389 AutomaticZenRule rule = 390 new AutomaticZenRule( 391 "name", 392 new ComponentName("pkg", "cls"), 393 Uri.parse("condition://id"), 394 NotificationManager.INTERRUPTION_FILTER_PRIORITY, 395 /* enabled= */ true); 396 String id = notificationManager.addAutomaticZenRule(rule); 397 398 String nonexistentId = "id_different_from_" + id; 399 assertThat(notificationManager.removeAutomaticZenRule(nonexistentId)).isFalse(); 400 // The rules stored in NotificationManager should remain unchanged. 401 assertThat(notificationManager.getAutomaticZenRules()).containsExactly(id, rule); 402 } 403 404 @Test 405 @Config(minSdk = Build.VERSION_CODES.N) 406 public void removeAutomaticZenRule_existingId_shouldRemoveRuleAndReturnTrue() { 407 shadowOf(notificationManager).setNotificationPolicyAccessGranted(true); 408 AutomaticZenRule rule1 = 409 new AutomaticZenRule( 410 "name1", 411 new ComponentName("pkg1", "cls1"), 412 Uri.parse("condition://id1"), 413 NotificationManager.INTERRUPTION_FILTER_PRIORITY, 414 /* enabled= */ true); 415 AutomaticZenRule rule2 = 416 new AutomaticZenRule( 417 "name2", 418 new ComponentName("pkg2", "cls2"), 419 Uri.parse("condition://id2"), 420 NotificationManager.INTERRUPTION_FILTER_ALARMS, 421 /* enabled= */ false); 422 String id1 = notificationManager.addAutomaticZenRule(rule1); 423 String id2 = notificationManager.addAutomaticZenRule(rule2); 424 425 assertThat(notificationManager.removeAutomaticZenRule(id1)).isTrue(); 426 427 assertThat(notificationManager.getAutomaticZenRule(id1)).isNull(); 428 assertThat(notificationManager.getAutomaticZenRule(id2)).isEqualTo(rule2); 429 assertThat(notificationManager.getAutomaticZenRules()).containsExactly(id2, rule2); 430 } 431 432 @Test 433 public void testNotify() throws Exception { 434 notificationManager.notify(1, notification1); 435 assertEquals(1, shadowOf(notificationManager).size()); 436 assertEquals(notification1, shadowOf(notificationManager).getNotification(null, 1)); 437 438 notificationManager.notify(31, notification2); 439 assertEquals(2, shadowOf(notificationManager).size()); 440 assertEquals(notification2, shadowOf(notificationManager).getNotification(null, 31)); 441 } 442 443 @Test 444 public void testNotifyReplaces() throws Exception { 445 notificationManager.notify(1, notification1); 446 447 notificationManager.notify(1, notification2); 448 assertEquals(1, shadowOf(notificationManager).size()); 449 assertEquals(notification2, shadowOf(notificationManager).getNotification(null, 1)); 450 } 451 452 @Test 453 public void testNotifyWithTag() throws Exception { 454 notificationManager.notify("a tag", 1, notification1); 455 assertEquals(1, shadowOf(notificationManager).size()); 456 assertEquals(notification1, shadowOf(notificationManager).getNotification("a tag", 1)); 457 } 458 459 @Test 460 public void notifyWithTag_shouldReturnNullForNullTag() throws Exception { 461 notificationManager.notify("a tag", 1, notification1); 462 assertEquals(1, shadowOf(notificationManager).size()); 463 assertNull(shadowOf(notificationManager).getNotification(null, 1)); 464 } 465 466 @Test 467 public void notifyWithTag_shouldReturnNullForUnknownTag() throws Exception { 468 notificationManager.notify("a tag", 1, notification1); 469 assertEquals(1, shadowOf(notificationManager).size()); 470 assertNull(shadowOf(notificationManager).getNotification("unknown tag", 1)); 471 } 472 473 @Test 474 public void testCancel() throws Exception { 475 notificationManager.notify(1, notification1); 476 notificationManager.cancel(1); 477 478 assertEquals(0, shadowOf(notificationManager).size()); 479 assertNull(shadowOf(notificationManager).getNotification(null, 1)); 480 } 481 482 @Test 483 public void testCancelWithTag() throws Exception { 484 notificationManager.notify("a tag", 1, notification1); 485 notificationManager.cancel("a tag", 1); 486 487 assertEquals(0, shadowOf(notificationManager).size()); 488 assertNull(shadowOf(notificationManager).getNotification(null, 1)); 489 assertNull(shadowOf(notificationManager).getNotification("a tag", 1)); 490 } 491 492 @Test 493 public void testCancelAll() throws Exception { 494 notificationManager.notify(1, notification1); 495 notificationManager.notify(31, notification2); 496 notificationManager.cancelAll(); 497 498 assertEquals(0, shadowOf(notificationManager).size()); 499 assertNull(shadowOf(notificationManager).getNotification(null, 1)); 500 assertNull(shadowOf(notificationManager).getNotification(null, 31)); 501 } 502 503 @Test 504 @Config(minSdk = Build.VERSION_CODES.M) 505 public void testGetActiveNotifications() throws Exception { 506 notificationManager.notify(1, notification1); 507 notificationManager.notify(31, notification2); 508 509 StatusBarNotification[] statusBarNotifications = 510 shadowOf(notificationManager).getActiveNotifications(); 511 512 assertThat(asNotificationList(statusBarNotifications)) 513 .containsExactly(notification1, notification2); 514 } 515 516 private static List<Notification> asNotificationList( 517 StatusBarNotification[] statusBarNotifications) { 518 List<Notification> notificationList = new ArrayList<>(statusBarNotifications.length); 519 for (StatusBarNotification statusBarNotification : statusBarNotifications) { 520 notificationList.add(statusBarNotification.getNotification()); 521 } 522 return notificationList; 523 } 524 } 525