1 /* 2 * Copyright (C) 2016 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 androidx.core.app; 18 19 import static androidx.core.app.NotificationCompat.DEFAULT_ALL; 20 import static androidx.core.app.NotificationCompat.DEFAULT_LIGHTS; 21 import static androidx.core.app.NotificationCompat.DEFAULT_SOUND; 22 import static androidx.core.app.NotificationCompat.DEFAULT_VIBRATE; 23 import static androidx.core.app.NotificationCompat.GROUP_ALERT_ALL; 24 import static androidx.core.app.NotificationCompat.GROUP_ALERT_CHILDREN; 25 import static androidx.core.app.NotificationCompat.GROUP_ALERT_SUMMARY; 26 27 import static org.junit.Assert.assertEquals; 28 import static org.junit.Assert.assertFalse; 29 import static org.junit.Assert.assertNotNull; 30 import static org.junit.Assert.assertNull; 31 import static org.junit.Assert.assertSame; 32 import static org.junit.Assert.assertTrue; 33 import static org.junit.Assert.fail; 34 35 import android.app.Notification; 36 import android.content.Context; 37 import android.graphics.Color; 38 import android.media.AudioAttributes; 39 import android.media.AudioManager; 40 import android.net.Uri; 41 import android.os.Build; 42 import android.os.Bundle; 43 import android.support.test.filters.SdkSuppress; 44 import android.support.test.filters.SmallTest; 45 import android.support.test.runner.AndroidJUnit4; 46 import android.support.v4.BaseInstrumentationTestCase; 47 48 import androidx.core.app.NotificationCompat.MessagingStyle.Message; 49 50 import org.junit.Before; 51 import org.junit.Test; 52 import org.junit.runner.RunWith; 53 54 import java.util.ArrayList; 55 import java.util.List; 56 57 58 @RunWith(AndroidJUnit4.class) 59 @SmallTest 60 public class NotificationCompatTest extends BaseInstrumentationTestCase<TestActivity> { 61 private static final String TEXT_RESULT_KEY = "text"; 62 private static final String DATA_RESULT_KEY = "data"; 63 private static final String EXTRA_COLORIZED = "android.colorized"; 64 65 Context mContext; 66 67 public NotificationCompatTest() { 68 super(TestActivity.class); 69 } 70 71 @Before 72 public void setup() { 73 mContext = mActivityTestRule.getActivity(); 74 } 75 76 @Test 77 public void testBadgeIcon() throws Throwable { 78 int badgeIcon = NotificationCompat.BADGE_ICON_SMALL; 79 Notification n = new NotificationCompat.Builder(mActivityTestRule.getActivity()) 80 .setBadgeIconType(badgeIcon) 81 .build(); 82 if (Build.VERSION.SDK_INT >= 26) { 83 assertEquals(badgeIcon, NotificationCompat.getBadgeIconType(n)); 84 } else { 85 assertEquals(NotificationCompat.BADGE_ICON_NONE, 86 NotificationCompat.getBadgeIconType(n)); 87 } 88 } 89 90 @Test 91 public void testTimeout() throws Throwable { 92 long timeout = 23552; 93 Notification n = new NotificationCompat.Builder(mActivityTestRule.getActivity()) 94 .setTimeoutAfter(timeout) 95 .build(); 96 if (Build.VERSION.SDK_INT >= 26) { 97 assertEquals(timeout, NotificationCompat.getTimeoutAfter(n)); 98 } else { 99 assertEquals(0, NotificationCompat.getTimeoutAfter(n)); 100 } 101 } 102 103 @Test 104 public void testShortcutId() throws Throwable { 105 String shortcutId = "fgdfg"; 106 Notification n = new NotificationCompat.Builder(mActivityTestRule.getActivity()) 107 .setShortcutId(shortcutId) 108 .build(); 109 if (Build.VERSION.SDK_INT >= 26) { 110 assertEquals(shortcutId, NotificationCompat.getShortcutId(n)); 111 } else { 112 assertEquals(null, NotificationCompat.getShortcutId(n)); 113 } 114 } 115 116 @Test 117 public void testNotificationChannel() throws Throwable { 118 String channelId = "new ID"; 119 Notification n = new NotificationCompat.Builder(mActivityTestRule.getActivity()) 120 .setChannelId(channelId) 121 .build(); 122 if (Build.VERSION.SDK_INT >= 26) { 123 assertEquals(channelId, NotificationCompat.getChannelId(n)); 124 } else { 125 assertNull(NotificationCompat.getChannelId(n)); 126 } 127 } 128 129 @Test 130 public void testNotificationChannel_assignedFromBuilder() throws Throwable { 131 String channelId = "new ID"; 132 Notification n = new NotificationCompat.Builder(mActivityTestRule.getActivity(), channelId) 133 .build(); 134 if (Build.VERSION.SDK_INT >= 26) { 135 assertEquals(channelId, NotificationCompat.getChannelId(n)); 136 } else { 137 assertNull(NotificationCompat.getChannelId(n)); 138 } 139 } 140 141 @Test 142 public void testNotificationActionBuilder_assignsColorized() throws Throwable { 143 Notification n = newNotificationBuilder().setColorized(true).build(); 144 if (Build.VERSION.SDK_INT >= 26) { 145 Bundle extras = NotificationCompat.getExtras(n); 146 assertTrue(Boolean.TRUE.equals(extras.get(EXTRA_COLORIZED))); 147 } 148 } 149 150 @Test 151 public void testNotificationActionBuilder_unassignesColorized() throws Throwable { 152 Notification n = newNotificationBuilder().setColorized(false).build(); 153 if (Build.VERSION.SDK_INT >= 26) { 154 Bundle extras = NotificationCompat.getExtras(n); 155 assertTrue(Boolean.FALSE.equals(extras.get(EXTRA_COLORIZED))); 156 } 157 } 158 159 @Test 160 public void testNotificationActionBuilder_doesntAssignColorized() throws Throwable { 161 Notification n = newNotificationBuilder().build(); 162 if (Build.VERSION.SDK_INT >= 26) { 163 Bundle extras = NotificationCompat.getExtras(n); 164 assertFalse(extras.containsKey(EXTRA_COLORIZED)); 165 } 166 } 167 168 @Test 169 public void testNotificationActionBuilder_copiesRemoteInputs() throws Throwable { 170 NotificationCompat.Action a = newActionBuilder() 171 .addRemoteInput(new RemoteInput("a", "b", null, false, null, null)).build(); 172 173 NotificationCompat.Action aCopy = new NotificationCompat.Action.Builder(a).build(); 174 175 assertSame(a.getRemoteInputs()[0], aCopy.getRemoteInputs()[0]); 176 } 177 178 @Test 179 public void testNotificationActionBuilder_copiesAllowGeneratedReplies() throws Throwable { 180 NotificationCompat.Action a = newActionBuilder() 181 .setAllowGeneratedReplies(true).build(); 182 183 NotificationCompat.Action aCopy = new NotificationCompat.Action.Builder(a).build(); 184 185 assertEquals(a.getAllowGeneratedReplies(), aCopy.getAllowGeneratedReplies()); 186 } 187 188 @SdkSuppress(minSdkVersion = 24) 189 @Test 190 public void testFrameworkNotificationActionBuilder_setAllowGeneratedRepliesTrue() 191 throws Throwable { 192 Notification notif = new Notification.Builder(mContext) 193 .addAction(new Notification.Action.Builder(0, "title", null) 194 .setAllowGeneratedReplies(true).build()).build(); 195 NotificationCompat.Action action = NotificationCompat.getAction(notif, 0); 196 assertTrue(action.getAllowGeneratedReplies()); 197 } 198 199 @Test 200 public void testNotificationActionBuilder_defaultAllowGeneratedRepliesTrue() throws Throwable { 201 NotificationCompat.Action a = newActionBuilder().build(); 202 203 assertTrue(a.getAllowGeneratedReplies()); 204 } 205 206 @Test 207 public void testNotificationActionBuilder_defaultShowsUserInterfaceTrue() { 208 NotificationCompat.Action action = newActionBuilder().build(); 209 210 assertTrue(action.getShowsUserInterface()); 211 } 212 213 @Test 214 public void testNotificationAction_defaultAllowGeneratedRepliesTrue() throws Throwable { 215 NotificationCompat.Action a = new NotificationCompat.Action(0, null, null); 216 217 assertTrue(a.getAllowGeneratedReplies()); 218 } 219 220 @Test 221 public void testNotificationAction_defaultShowsUserInterfaceTrue() { 222 NotificationCompat.Action action = new NotificationCompat.Action(0, null, null); 223 224 assertTrue(action.getShowsUserInterface()); 225 } 226 227 @Test 228 public void testNotificationActionBuilder_setAllowGeneratedRepliesFalse() throws Throwable { 229 NotificationCompat.Action a = newActionBuilder() 230 .setAllowGeneratedReplies(false).build(); 231 232 assertFalse(a.getAllowGeneratedReplies()); 233 } 234 235 @Test 236 public void testNotificationAction_setShowsUserInterfaceFalse() { 237 NotificationCompat.Action action = newActionBuilder() 238 .setShowsUserInterface(false).build(); 239 240 assertFalse(action.getShowsUserInterface()); 241 } 242 243 @SdkSuppress(minSdkVersion = 20) 244 @Test 245 public void testGetActionCompatFromAction_showsUserInterface() { 246 NotificationCompat.Action action = newActionBuilder() 247 .setShowsUserInterface(false).build(); 248 Notification notification = newNotificationBuilder().addAction(action).build(); 249 NotificationCompat.Action result = 250 NotificationCompat.getActionCompatFromAction(notification.actions[0]); 251 252 assertFalse(result.getExtras().getBoolean( 253 NotificationCompat.Action.EXTRA_SHOWS_USER_INTERFACE, true)); 254 assertFalse(result.getShowsUserInterface()); 255 } 256 257 @SdkSuppress(minSdkVersion = 17) 258 @Test 259 public void testNotificationWearableExtenderAction_setAllowGeneratedRepliesTrue() 260 throws Throwable { 261 NotificationCompat.Action a = newActionBuilder() 262 .setAllowGeneratedReplies(true).build(); 263 NotificationCompat.WearableExtender extender = new NotificationCompat.WearableExtender() 264 .addAction(a); 265 Notification notification = newNotificationBuilder().extend(extender).build(); 266 assertTrue(new NotificationCompat.WearableExtender(notification).getActions().get(0) 267 .getAllowGeneratedReplies()); 268 } 269 270 @SdkSuppress(minSdkVersion = 17) 271 @Test 272 public void testNotificationWearableExtenderAction_setAllowGeneratedRepliesFalse() 273 throws Throwable { 274 NotificationCompat.Action a = newActionBuilder() 275 .setAllowGeneratedReplies(false).build(); 276 NotificationCompat.WearableExtender extender = new NotificationCompat.WearableExtender() 277 .addAction(a); 278 Notification notification = newNotificationBuilder().extend(extender).build(); 279 assertFalse(new NotificationCompat.WearableExtender(notification).getActions().get(0) 280 .getAllowGeneratedReplies()); 281 } 282 283 284 @SdkSuppress(maxSdkVersion = 16) 285 @SmallTest 286 @Test 287 public void testNotificationWearableExtenderAction_noActions() 288 throws Throwable { 289 NotificationCompat.Action a = newActionBuilder() 290 .setAllowGeneratedReplies(true).build(); 291 NotificationCompat.WearableExtender extender = new NotificationCompat.WearableExtender() 292 .addAction(a); 293 Notification notification = newNotificationBuilder().extend(extender).build(); 294 assertTrue(new NotificationCompat.WearableExtender(notification).getActions().size() == 0); 295 } 296 297 @Test 298 public void testNotificationActionBuilder_setDataOnlyRemoteInput() throws Throwable { 299 NotificationCompat.Action a = newActionBuilder() 300 .addRemoteInput(newDataOnlyRemoteInput()).build(); 301 RemoteInput[] textInputs = a.getRemoteInputs(); 302 assertTrue(textInputs == null || textInputs.length == 0); 303 verifyRemoteInputArrayHasSingleResult(a.getDataOnlyRemoteInputs(), DATA_RESULT_KEY); 304 } 305 306 @Test 307 public void testNotificationActionBuilder_setTextAndDataOnlyRemoteInput() throws Throwable { 308 NotificationCompat.Action a = newActionBuilder() 309 .addRemoteInput(newDataOnlyRemoteInput()) 310 .addRemoteInput(newTextRemoteInput()) 311 .build(); 312 313 verifyRemoteInputArrayHasSingleResult(a.getRemoteInputs(), TEXT_RESULT_KEY); 314 verifyRemoteInputArrayHasSingleResult(a.getDataOnlyRemoteInputs(), DATA_RESULT_KEY); 315 } 316 317 @Test 318 public void testMessage_setAndGetExtras() throws Throwable { 319 String extraKey = "extra_key"; 320 CharSequence extraValue = "extra_value"; 321 Message m = 322 new Message("text", 0 /*timestamp */, "sender"); 323 m.getExtras().putCharSequence(extraKey, extraValue); 324 assertEquals(extraValue, m.getExtras().getCharSequence(extraKey)); 325 326 ArrayList<Message> messages = new ArrayList<>(1); 327 messages.add(m); 328 Bundle[] bundleArray = 329 Message.getBundleArrayForMessages(messages); 330 assertEquals(1, bundleArray.length); 331 Message fromBundle = 332 Message.getMessageFromBundle(bundleArray[0]); 333 assertEquals(extraValue, fromBundle.getExtras().getCharSequence(extraKey)); 334 } 335 336 @Test 337 public void testGetGroupAlertBehavior() throws Throwable { 338 Notification n = new NotificationCompat.Builder(mActivityTestRule.getActivity()) 339 .setGroupAlertBehavior(GROUP_ALERT_CHILDREN) 340 .build(); 341 if (Build.VERSION.SDK_INT >= 26) { 342 assertEquals(GROUP_ALERT_CHILDREN, NotificationCompat.getGroupAlertBehavior(n)); 343 } else { 344 assertEquals(GROUP_ALERT_ALL, NotificationCompat.getGroupAlertBehavior(n)); 345 } 346 } 347 348 @Test 349 public void testGroupAlertBehavior_mutesGroupNotifications() throws Throwable { 350 // valid between api 20, when groups were added, and api 25, the last to use sound 351 // and vibration from the notification itself 352 353 Notification n = new NotificationCompat.Builder(mActivityTestRule.getActivity()) 354 .setGroupAlertBehavior(GROUP_ALERT_CHILDREN) 355 .setVibrate(new long[] {235}) 356 .setSound(Uri.EMPTY) 357 .setDefaults(DEFAULT_ALL) 358 .setGroup("grouped") 359 .setGroupSummary(true) 360 .build(); 361 362 Notification n2 = new NotificationCompat.Builder(mActivityTestRule.getActivity()) 363 .setGroupAlertBehavior(GROUP_ALERT_SUMMARY) 364 .setVibrate(new long[] {235}) 365 .setSound(Uri.EMPTY) 366 .setDefaults(DEFAULT_ALL) 367 .setGroup("grouped") 368 .setGroupSummary(false) 369 .build(); 370 371 if (Build.VERSION.SDK_INT >= 20 && !(Build.VERSION.SDK_INT >= 26)) { 372 assertNull(n.sound); 373 assertNull(n.vibrate); 374 assertTrue((n.defaults & DEFAULT_LIGHTS) != 0); 375 assertTrue((n.defaults & DEFAULT_SOUND) == 0); 376 assertTrue((n.defaults & DEFAULT_VIBRATE) == 0); 377 378 assertNull(n2.sound); 379 assertNull(n2.vibrate); 380 assertTrue((n2.defaults & DEFAULT_LIGHTS) != 0); 381 assertTrue((n2.defaults & DEFAULT_SOUND) == 0); 382 assertTrue((n2.defaults & DEFAULT_VIBRATE) == 0); 383 } else if (Build.VERSION.SDK_INT < 20) { 384 assertNotNull(n.sound); 385 assertNotNull(n.vibrate); 386 assertTrue((n.defaults & DEFAULT_LIGHTS) != 0); 387 assertTrue((n.defaults & DEFAULT_SOUND) != 0); 388 assertTrue((n.defaults & DEFAULT_VIBRATE) != 0); 389 390 assertNotNull(n2.sound); 391 assertNotNull(n2.vibrate); 392 assertTrue((n2.defaults & DEFAULT_LIGHTS) != 0); 393 assertTrue((n2.defaults & DEFAULT_SOUND) != 0); 394 assertTrue((n2.defaults & DEFAULT_VIBRATE) != 0); 395 } 396 } 397 398 @Test 399 public void testGroupAlertBehavior_doesNotMuteIncorrectGroupNotifications() throws Throwable { 400 Notification n = new NotificationCompat.Builder(mActivityTestRule.getActivity()) 401 .setGroupAlertBehavior(GROUP_ALERT_SUMMARY) 402 .setVibrate(new long[] {235}) 403 .setSound(Uri.EMPTY) 404 .setDefaults(DEFAULT_ALL) 405 .setGroup("grouped") 406 .setGroupSummary(true) 407 .build(); 408 409 Notification n2 = new NotificationCompat.Builder(mActivityTestRule.getActivity()) 410 .setGroupAlertBehavior(GROUP_ALERT_CHILDREN) 411 .setVibrate(new long[] {235}) 412 .setSound(Uri.EMPTY) 413 .setDefaults(DEFAULT_ALL) 414 .setGroup("grouped") 415 .setGroupSummary(false) 416 .build(); 417 418 Notification n3 = new NotificationCompat.Builder(mActivityTestRule.getActivity()) 419 .setVibrate(new long[] {235}) 420 .setSound(Uri.EMPTY) 421 .setDefaults(DEFAULT_ALL) 422 .setGroup("grouped") 423 .setGroupSummary(false) 424 .build(); 425 426 if (Build.VERSION.SDK_INT >= 20 && !(Build.VERSION.SDK_INT >= 26)) { 427 assertNotNull(n.sound); 428 assertNotNull(n.vibrate); 429 assertTrue((n.defaults & DEFAULT_LIGHTS) != 0); 430 assertTrue((n.defaults & DEFAULT_SOUND) != 0); 431 assertTrue((n.defaults & DEFAULT_VIBRATE) != 0); 432 433 assertNotNull(n2.sound); 434 assertNotNull(n2.vibrate); 435 assertTrue((n2.defaults & DEFAULT_LIGHTS) != 0); 436 assertTrue((n2.defaults & DEFAULT_SOUND) != 0); 437 assertTrue((n2.defaults & DEFAULT_VIBRATE) != 0); 438 439 assertNotNull(n3.sound); 440 assertNotNull(n3.vibrate); 441 assertTrue((n3.defaults & DEFAULT_LIGHTS) != 0); 442 assertTrue((n3.defaults & DEFAULT_SOUND) != 0); 443 assertTrue((n3.defaults & DEFAULT_VIBRATE) != 0); 444 } 445 } 446 447 @Test 448 public void testGroupAlertBehavior_doesNotMuteNonGroupNotifications() throws Throwable { 449 Notification n = new NotificationCompat.Builder(mActivityTestRule.getActivity()) 450 .setGroupAlertBehavior(GROUP_ALERT_CHILDREN) 451 .setVibrate(new long[] {235}) 452 .setSound(Uri.EMPTY) 453 .setDefaults(DEFAULT_ALL) 454 .setGroup(null) 455 .setGroupSummary(false) 456 .build(); 457 if (!(Build.VERSION.SDK_INT >= 26)) { 458 assertNotNull(n.sound); 459 assertNotNull(n.vibrate); 460 assertTrue((n.defaults & DEFAULT_LIGHTS) != 0); 461 assertTrue((n.defaults & DEFAULT_SOUND) != 0); 462 assertTrue((n.defaults & DEFAULT_VIBRATE) != 0); 463 } 464 } 465 466 @Test 467 @SdkSuppress(minSdkVersion = 21) 468 public void testHasAudioAttributesFrom21() throws Throwable { 469 Notification n = new NotificationCompat.Builder(mActivityTestRule.getActivity()) 470 .setSound(Uri.EMPTY) 471 .build(); 472 assertNotNull(n.audioAttributes); 473 assertEquals(-1, n.audioStreamType); 474 assertEquals(Uri.EMPTY, n.sound); 475 476 n = new NotificationCompat.Builder(mActivityTestRule.getActivity()) 477 .setSound(Uri.EMPTY, AudioManager.STREAM_RING) 478 .build(); 479 assertNotNull(n.audioAttributes); 480 assertEquals(AudioAttributes.CONTENT_TYPE_SONIFICATION, 481 n.audioAttributes.getContentType()); 482 assertEquals(-1, n.audioStreamType); 483 assertEquals(Uri.EMPTY, n.sound); 484 } 485 486 @Test 487 @SdkSuppress(maxSdkVersion = 20) 488 public void testHasStreamTypePre21() throws Throwable { 489 Notification n = new NotificationCompat.Builder(mActivityTestRule.getActivity()) 490 .setSound(Uri.EMPTY, 34) 491 .build(); 492 assertEquals(34, n.audioStreamType); 493 assertEquals(Uri.EMPTY, n.sound); 494 } 495 496 @SdkSuppress(minSdkVersion = 26) 497 @Test 498 public void testClearAlertingFieldsIfUsingChannels() throws Throwable { 499 long[] vibration = new long[]{100}; 500 501 // stripped if using channels 502 Notification n = new NotificationCompat.Builder(mActivityTestRule.getActivity(), "test") 503 .setSound(Uri.EMPTY) 504 .setDefaults(Notification.DEFAULT_ALL) 505 .setVibrate(vibration) 506 .setLights(Color.BLUE, 100, 100) 507 .build(); 508 assertNull(n.sound); 509 assertEquals(0, n.defaults); 510 assertNull(n.vibrate); 511 assertEquals(0, n.ledARGB); 512 assertEquals(0, n.ledOnMS); 513 assertEquals(0, n.ledOffMS); 514 515 // left intact if not using channels 516 n = new NotificationCompat.Builder(mActivityTestRule.getActivity()) 517 .setSound(Uri.EMPTY) 518 .setDefaults(Notification.DEFAULT_ALL) 519 .setVibrate(vibration) 520 .setLights(Color.BLUE, 100, 100) 521 .build(); 522 assertEquals(Uri.EMPTY, n.sound); 523 assertNotNull(n.audioAttributes); 524 assertEquals(Notification.DEFAULT_ALL, n.defaults); 525 assertEquals(vibration, n.vibrate); 526 assertEquals(Color.BLUE, n.ledARGB); 527 assertEquals(100, n.ledOnMS); 528 assertEquals(100, n.ledOffMS); 529 } 530 531 @Test 532 public void testMessagingStyle_nullPerson() { 533 NotificationCompat.MessagingStyle messagingStyle = 534 new NotificationCompat.MessagingStyle("self name"); 535 messagingStyle.addMessage("text", 200, (Person) null); 536 537 Notification notification = new NotificationCompat.Builder(mContext, "test id") 538 .setSmallIcon(1) 539 .setContentTitle("test title") 540 .setStyle(messagingStyle) 541 .build(); 542 543 List<Message> result = NotificationCompat.MessagingStyle 544 .extractMessagingStyleFromNotification(notification) 545 .getMessages(); 546 547 assertEquals(1, result.size()); 548 assertEquals("text", result.get(0).getText()); 549 assertEquals(200, result.get(0).getTimestamp()); 550 assertNull(result.get(0).getPerson()); 551 assertNull(result.get(0).getSender()); 552 } 553 554 @Test 555 public void testMessagingStyle_message() { 556 NotificationCompat.MessagingStyle messagingStyle = 557 new NotificationCompat.MessagingStyle("self name"); 558 Person person = new Person.Builder().setName("test name").setKey("key").build(); 559 Person person2 = new Person.Builder() 560 .setName("test name 2").setKey("key 2").setImportant(true).build(); 561 messagingStyle.addMessage("text", 200, person); 562 messagingStyle.addMessage("text2", 300, person2); 563 564 Notification notification = new NotificationCompat.Builder(mContext, "test id") 565 .setSmallIcon(1) 566 .setContentTitle("test title") 567 .setStyle(messagingStyle) 568 .build(); 569 570 List<Message> result = NotificationCompat.MessagingStyle 571 .extractMessagingStyleFromNotification(notification) 572 .getMessages(); 573 574 assertEquals(2, result.size()); 575 assertEquals("text", result.get(0).getText()); 576 assertEquals(200, result.get(0).getTimestamp()); 577 assertEquals("test name", result.get(0).getPerson().getName()); 578 assertEquals("key", result.get(0).getPerson().getKey()); 579 assertEquals("text2", result.get(1).getText()); 580 assertEquals(300, result.get(1).getTimestamp()); 581 assertEquals("test name 2", result.get(1).getPerson().getName()); 582 assertEquals("key 2", result.get(1).getPerson().getKey()); 583 assertTrue(result.get(1).getPerson().isImportant()); 584 } 585 586 @Test 587 public void testMessagingStyle_requiresNonEmptyUserName() { 588 try { 589 new NotificationCompat.MessagingStyle(new Person.Builder().build()); 590 fail("Expected IllegalArgumentException about a non-empty user name."); 591 } catch (IllegalArgumentException e) { 592 // Expected 593 } 594 } 595 596 @Test 597 public void testMessagingStyle_isGroupConversation() { 598 mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.P; 599 NotificationCompat.MessagingStyle messagingStyle = 600 new NotificationCompat.MessagingStyle("self name") 601 .setGroupConversation(true) 602 .setConversationTitle("test conversation title"); 603 Notification notification = new NotificationCompat.Builder(mContext, "test id") 604 .setSmallIcon(1) 605 .setContentTitle("test title") 606 .setStyle(messagingStyle) 607 .build(); 608 609 NotificationCompat.MessagingStyle result = 610 NotificationCompat.MessagingStyle 611 .extractMessagingStyleFromNotification(notification); 612 613 assertTrue(result.isGroupConversation()); 614 } 615 616 @Test 617 public void testMessagingStyle_isGroupConversation_noConversationTitle() { 618 mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.P; 619 NotificationCompat.MessagingStyle messagingStyle = 620 new NotificationCompat.MessagingStyle("self name") 621 .setGroupConversation(true) 622 .setConversationTitle(null); 623 Notification notification = new NotificationCompat.Builder(mContext, "test id") 624 .setSmallIcon(1) 625 .setContentTitle("test title") 626 .setStyle(messagingStyle) 627 .build(); 628 629 NotificationCompat.MessagingStyle result = 630 NotificationCompat.MessagingStyle 631 .extractMessagingStyleFromNotification(notification); 632 633 assertTrue(result.isGroupConversation()); 634 } 635 636 @Test 637 public void testMessagingStyle_isGroupConversation_withConversationTitle_legacy() { 638 // In legacy (version < P), isGroupConversation is controlled by conversationTitle. 639 mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.O; 640 NotificationCompat.MessagingStyle messagingStyle = 641 new NotificationCompat.MessagingStyle("self name") 642 .setConversationTitle("test conversation title"); 643 Notification notification = new NotificationCompat.Builder(mContext, "test id") 644 .setSmallIcon(1) 645 .setContentTitle("test title") 646 .setStyle(messagingStyle) 647 .build(); 648 649 NotificationCompat.MessagingStyle result = 650 NotificationCompat.MessagingStyle 651 .extractMessagingStyleFromNotification(notification); 652 653 assertTrue(result.isGroupConversation()); 654 } 655 656 @Test 657 public void testMessagingStyle_isGroupConversation_withoutConversationTitle_legacy() { 658 // In legacy (version < P), isGroupConversation is controlled by conversationTitle. 659 mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.O; 660 NotificationCompat.MessagingStyle messagingStyle = 661 new NotificationCompat.MessagingStyle("self name") 662 .setConversationTitle(null); 663 Notification notification = new NotificationCompat.Builder(mContext, "test id") 664 .setSmallIcon(1) 665 .setContentTitle("test title") 666 .setStyle(messagingStyle) 667 .build(); 668 669 NotificationCompat.MessagingStyle result = 670 NotificationCompat.MessagingStyle 671 .extractMessagingStyleFromNotification(notification); 672 673 assertFalse(result.isGroupConversation()); 674 } 675 676 @Test 677 public void testMessagingStyle_isGroupConversation_withConversationTitle_legacyWithOverride() { 678 // #setGroupConversation should always take precedence over legacy behavior, so a non-null 679 // title shouldn't affect #isGroupConversation. 680 mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.O; 681 NotificationCompat.MessagingStyle messagingStyle = 682 new NotificationCompat.MessagingStyle("self name") 683 .setGroupConversation(false) 684 .setConversationTitle("test conversation title"); 685 Notification notification = new NotificationCompat.Builder(mContext, "test id") 686 .setSmallIcon(1) 687 .setContentTitle("test title") 688 .setStyle(messagingStyle) 689 .build(); 690 691 NotificationCompat.MessagingStyle result = 692 NotificationCompat.MessagingStyle 693 .extractMessagingStyleFromNotification(notification); 694 695 assertFalse(result.isGroupConversation()); 696 } 697 698 @Test 699 public void testMessagingStyle_isGroupConversation_withoutTitle_legacyWithOverride() { 700 // #setGroupConversation should always take precedence over legacy behavior, so a null 701 // title shouldn't affect #isGroupConversation. 702 mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.O; 703 NotificationCompat.MessagingStyle messagingStyle = 704 new NotificationCompat.MessagingStyle("self name") 705 .setGroupConversation(true) 706 .setConversationTitle(null); 707 Notification notification = new NotificationCompat.Builder(mContext, "test id") 708 .setSmallIcon(1) 709 .setContentTitle("test title") 710 .setStyle(messagingStyle) 711 .build(); 712 713 NotificationCompat.MessagingStyle result = 714 NotificationCompat.MessagingStyle 715 .extractMessagingStyleFromNotification(notification); 716 717 assertTrue(result.isGroupConversation()); 718 } 719 720 @SdkSuppress(minSdkVersion = 28) 721 @Test 722 public void testMessagingStyle_applyNoTitleAndNotGroup() { 723 NotificationCompat.MessagingStyle messagingStyle = 724 new NotificationCompat.MessagingStyle("self name") 725 .setGroupConversation(false) 726 .addMessage( 727 new Message( 728 "body", 729 1, 730 new Person.Builder().setName("example name").build())) 731 .addMessage(new Message("body 2", 2, (Person) null)); 732 733 Notification resultNotification = new NotificationCompat.Builder(mContext, "test id") 734 .setStyle(messagingStyle) 735 .build(); 736 NotificationCompat.MessagingStyle resultCompatMessagingStyle = 737 NotificationCompat.MessagingStyle 738 .extractMessagingStyleFromNotification(resultNotification); 739 740 // SDK >= 28 applies no title when none is provided to MessagingStyle. 741 assertNull(resultCompatMessagingStyle.getConversationTitle()); 742 assertFalse(resultCompatMessagingStyle.isGroupConversation()); 743 } 744 745 @SdkSuppress(minSdkVersion = 24, maxSdkVersion = 27) 746 @Test 747 public void testMessagingStyle_applyNoTitleAndNotGroup_legacy() { 748 NotificationCompat.MessagingStyle messagingStyle = 749 new NotificationCompat.MessagingStyle("self name") 750 .setGroupConversation(false) 751 .addMessage( 752 new Message( 753 "body", 754 1, 755 new Person.Builder().setName("example name").build())) 756 .addMessage(new Message("body 2", 2, (Person) null)); 757 758 Notification resultNotification = new NotificationCompat.Builder(mContext, "test id") 759 .setStyle(messagingStyle) 760 .build(); 761 NotificationCompat.MessagingStyle resultCompatMessagingStyle = 762 NotificationCompat.MessagingStyle 763 .extractMessagingStyleFromNotification(resultNotification); 764 765 // SDK [24, 27] applies first incoming message sender name as Notification content title. 766 assertEquals("example name", NotificationCompat.getContentTitle(resultNotification)); 767 assertNull(resultCompatMessagingStyle.getConversationTitle()); 768 assertFalse(resultCompatMessagingStyle.isGroupConversation()); 769 } 770 771 @SdkSuppress(minSdkVersion = 28) 772 @Test 773 public void testMessagingStyle_applyConversationTitleAndNotGroup() { 774 NotificationCompat.MessagingStyle messagingStyle = 775 new NotificationCompat.MessagingStyle("self name") 776 .setGroupConversation(false) 777 .setConversationTitle("test title"); 778 779 Notification resultNotification = new NotificationCompat.Builder(mContext, "test id") 780 .setStyle(messagingStyle) 781 .build(); 782 NotificationCompat.MessagingStyle resultMessagingStyle = 783 NotificationCompat.MessagingStyle 784 .extractMessagingStyleFromNotification(resultNotification); 785 786 // SDK >= 28 applies provided title to MessagingStyle. 787 assertEquals("test title", resultMessagingStyle.getConversationTitle()); 788 assertFalse(resultMessagingStyle.isGroupConversation()); 789 } 790 791 @SdkSuppress(minSdkVersion = 19, maxSdkVersion = 27) 792 @Test 793 public void testMessagingStyle_applyConversationTitleAndNotGroup_legacy() { 794 NotificationCompat.MessagingStyle messagingStyle = 795 new NotificationCompat.MessagingStyle("self name") 796 .setGroupConversation(false) 797 .setConversationTitle("test title"); 798 799 Notification resultNotification = new NotificationCompat.Builder(mContext, "test id") 800 .setStyle(messagingStyle) 801 .build(); 802 NotificationCompat.MessagingStyle resultMessagingStyle = 803 NotificationCompat.MessagingStyle 804 .extractMessagingStyleFromNotification(resultNotification); 805 806 // SDK <= 27 applies MessagingStyle title as Notification content title. 807 assertEquals("test title", NotificationCompat.getContentTitle(resultNotification)); 808 assertEquals("test title", resultMessagingStyle.getConversationTitle()); 809 assertFalse(resultMessagingStyle.isGroupConversation()); 810 } 811 812 @Test 813 public void testMessagingStyle_restoreFromCompatExtras() { 814 NotificationCompat.MessagingStyle messagingStyle = 815 new NotificationCompat.MessagingStyle( 816 new Person.Builder().setName("test name").build()) 817 .setGroupConversation(true); 818 Bundle bundle = new Bundle(); 819 messagingStyle.addCompatExtras(bundle); 820 821 NotificationCompat.MessagingStyle resultMessagingStyle = 822 new NotificationCompat.MessagingStyle(new Person.Builder().setName("temp").build()); 823 resultMessagingStyle.restoreFromCompatExtras(bundle); 824 825 assertTrue(resultMessagingStyle.isGroupConversation()); 826 assertEquals("test name", resultMessagingStyle.getUser().getName()); 827 } 828 829 @Test 830 public void testMessagingStyleMessage_bundle_legacySender() { 831 Bundle legacyBundle = new Bundle(); 832 legacyBundle.putCharSequence(Message.KEY_TEXT, "message"); 833 legacyBundle.putLong(Message.KEY_TIMESTAMP, 100); 834 legacyBundle.putCharSequence(Message.KEY_SENDER, "sender"); 835 836 Message result = Message.getMessageFromBundle(legacyBundle); 837 assertEquals("sender", result.getPerson().getName()); 838 } 839 840 @Test 841 public void action_builder_hasDefault() { 842 NotificationCompat.Action action = 843 new NotificationCompat.Action.Builder(0, "Test Title", null).build(); 844 assertEquals(NotificationCompat.Action.SEMANTIC_ACTION_NONE, action.getSemanticAction()); 845 } 846 847 @Test 848 public void action_builder_setSemanticAction() { 849 NotificationCompat.Action action = 850 new NotificationCompat.Action.Builder(0, "Test Title", null) 851 .setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_REPLY) 852 .build(); 853 assertEquals(NotificationCompat.Action.SEMANTIC_ACTION_REPLY, action.getSemanticAction()); 854 } 855 856 @Test 857 @SdkSuppress(minSdkVersion = 20) 858 public void action_semanticAction_toAndFromNotification() { 859 NotificationCompat.Action action = 860 new NotificationCompat.Action.Builder(0, "Test Title", null) 861 .setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_REPLY) 862 .build(); 863 Notification notification = newNotificationBuilder().addAction(action).build(); 864 NotificationCompat.Action result = NotificationCompat.getAction(notification, 0); 865 866 assertEquals(NotificationCompat.Action.SEMANTIC_ACTION_REPLY, result.getSemanticAction()); 867 } 868 869 private static final NotificationCompat.Action TEST_INVISIBLE_ACTION = 870 new NotificationCompat.Action.Builder(0, "Test Title", null) 871 .setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_MUTE) 872 .setShowsUserInterface(false) 873 .build(); 874 875 @Test 876 @SdkSuppress(minSdkVersion = 21) 877 public void getInvisibleActions() { 878 Notification notification = 879 newNotificationBuilder().addInvisibleAction(TEST_INVISIBLE_ACTION).build(); 880 verifyInvisibleActionExists(notification); 881 } 882 883 @Test 884 @SdkSuppress(minSdkVersion = 21) 885 public void getInvisibleActions_withCarExtender() { 886 NotificationCompat.CarExtender carExtender = new NotificationCompat.CarExtender(); 887 Notification notification = newNotificationBuilder() 888 .addInvisibleAction(TEST_INVISIBLE_ACTION) 889 .extend(carExtender) 890 .build(); 891 verifyInvisibleActionExists(notification); 892 } 893 894 @Test 895 @SdkSuppress(minSdkVersion = 19) 896 public void getContentTitle() { 897 Notification notification = new NotificationCompat.Builder(mContext, "test channel") 898 .setContentTitle("example title") 899 .build(); 900 901 assertEquals("example title", NotificationCompat.getContentTitle(notification)); 902 } 903 904 private static void verifyInvisibleActionExists(Notification notification) { 905 List<NotificationCompat.Action> result = 906 NotificationCompat.getInvisibleActions(notification); 907 assertTrue("Expecting 1 result, got " + result.size(), result.size() == 1); 908 NotificationCompat.Action resultAction = result.get(0); 909 assertEquals(resultAction.getIcon(), TEST_INVISIBLE_ACTION.getIcon()); 910 assertEquals(resultAction.getTitle(), TEST_INVISIBLE_ACTION.getTitle()); 911 assertEquals( 912 resultAction.getShowsUserInterface(), 913 TEST_INVISIBLE_ACTION.getShowsUserInterface()); 914 assertEquals(resultAction.getSemanticAction(), TEST_INVISIBLE_ACTION.getSemanticAction()); 915 } 916 917 private static RemoteInput newDataOnlyRemoteInput() { 918 return new RemoteInput.Builder(DATA_RESULT_KEY) 919 .setAllowFreeFormInput(false) 920 .setAllowDataType("mimeType", true) 921 .build(); 922 } 923 924 private static RemoteInput newTextRemoteInput() { 925 return new RemoteInput.Builder(TEXT_RESULT_KEY).build(); // allowFreeForm defaults to true 926 } 927 928 private static void verifyRemoteInputArrayHasSingleResult( 929 RemoteInput[] remoteInputs, String expectedResultKey) { 930 assertTrue(remoteInputs != null && remoteInputs.length == 1); 931 assertEquals(expectedResultKey, remoteInputs[0].getResultKey()); 932 } 933 934 private static NotificationCompat.Action.Builder newActionBuilder() { 935 return new NotificationCompat.Action.Builder(0, "title", null); 936 } 937 938 private NotificationCompat.Builder newNotificationBuilder() { 939 return new NotificationCompat.Builder(mContext) 940 .setSmallIcon(0) 941 .setContentTitle("title") 942 .setContentText("text"); 943 } 944 } 945