1 /* 2 * Copyright (C) 2008 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 android.app.cts; 18 19 import static android.app.Notification.FLAG_BUBBLE; 20 import static android.graphics.drawable.Icon.TYPE_ADAPTIVE_BITMAP; 21 import static android.graphics.drawable.Icon.TYPE_RESOURCE; 22 23 import android.app.Notification; 24 import android.app.Notification.Action.Builder; 25 import android.app.Notification.MessagingStyle; 26 import android.app.Notification.MessagingStyle.Message; 27 import android.app.NotificationChannel; 28 import android.app.NotificationManager; 29 import android.app.PendingIntent; 30 import android.app.Person; 31 import android.app.RemoteInput; 32 import android.content.Context; 33 import android.content.Intent; 34 import android.graphics.Bitmap; 35 import android.graphics.Canvas; 36 import android.graphics.drawable.Icon; 37 import android.net.Uri; 38 import android.os.Build; 39 import android.os.Parcel; 40 import android.os.Parcelable; 41 import androidx.annotation.Nullable; 42 43 import android.os.StrictMode; 44 import android.test.AndroidTestCase; 45 import android.widget.RemoteViews; 46 47 import java.util.ArrayList; 48 import java.util.List; 49 import java.util.function.Consumer; 50 51 public class NotificationTest extends AndroidTestCase { 52 private static final String TEXT_RESULT_KEY = "text"; 53 private static final String DATA_RESULT_KEY = "data"; 54 private static final String DATA_AND_TEXT_RESULT_KEY = "data and text"; 55 56 private Notification.Action mAction; 57 private Notification mNotification; 58 private Context mContext; 59 60 private static final String TICKER_TEXT = "tickerText"; 61 private static final String CONTENT_TITLE = "contentTitle"; 62 private static final String CONTENT_TEXT = "contentText"; 63 private static final String URI_STRING = "uriString"; 64 private static final String ACTION_TITLE = "actionTitle"; 65 private static final int BUBBLE_HEIGHT = 300; 66 private static final int BUBBLE_HEIGHT_RESID = 31415; 67 private static final int TOLERANCE = 200; 68 private static final long TIMEOUT = 4000; 69 private static final NotificationChannel CHANNEL = new NotificationChannel("id", "name", 70 NotificationManager.IMPORTANCE_HIGH); 71 private static final String SHORTCUT_ID = "shortcutId"; 72 private static final String SETTING_TEXT = "work chats"; 73 private static final boolean ALLOW_SYS_GEN_CONTEXTUAL_ACTIONS = false; 74 75 @Override 76 protected void setUp() throws Exception { 77 super.setUp(); 78 mContext = getContext(); 79 mNotification = new Notification(); 80 } 81 82 public void testConstructor() { 83 mNotification = null; 84 mNotification = new Notification(); 85 assertNotNull(mNotification); 86 assertTrue(System.currentTimeMillis() - mNotification.when < TOLERANCE); 87 88 mNotification = null; 89 final int notificationTime = 200; 90 mNotification = new Notification(0, TICKER_TEXT, notificationTime); 91 assertEquals(notificationTime, mNotification.when); 92 assertEquals(0, mNotification.icon); 93 assertEquals(TICKER_TEXT, mNotification.tickerText); 94 assertEquals(0, mNotification.number); 95 } 96 97 public void testBuilderConstructor() { 98 mNotification = new Notification.Builder(mContext, CHANNEL.getId()).build(); 99 assertEquals(CHANNEL.getId(), mNotification.getChannelId()); 100 assertEquals(Notification.BADGE_ICON_NONE, mNotification.getBadgeIconType()); 101 assertNull(mNotification.getShortcutId()); 102 assertEquals(Notification.GROUP_ALERT_ALL, mNotification.getGroupAlertBehavior()); 103 assertEquals((long) 0, mNotification.getTimeoutAfter()); 104 } 105 106 public void testDescribeContents() { 107 final int expected = 0; 108 mNotification = new Notification(); 109 assertEquals(expected, mNotification.describeContents()); 110 } 111 112 public void testCategories() { 113 assertNotNull(Notification.CATEGORY_ALARM); 114 assertNotNull(Notification.CATEGORY_CALL); 115 assertNotNull(Notification.CATEGORY_EMAIL); 116 assertNotNull(Notification.CATEGORY_ERROR); 117 assertNotNull(Notification.CATEGORY_EVENT); 118 assertNotNull(Notification.CATEGORY_MESSAGE); 119 assertNotNull(Notification.CATEGORY_NAVIGATION); 120 assertNotNull(Notification.CATEGORY_PROGRESS); 121 assertNotNull(Notification.CATEGORY_PROMO); 122 assertNotNull(Notification.CATEGORY_RECOMMENDATION); 123 assertNotNull(Notification.CATEGORY_REMINDER); 124 assertNotNull(Notification.CATEGORY_SERVICE); 125 assertNotNull(Notification.CATEGORY_SOCIAL); 126 assertNotNull(Notification.CATEGORY_STATUS); 127 assertNotNull(Notification.CATEGORY_SYSTEM); 128 assertNotNull(Notification.CATEGORY_TRANSPORT); 129 } 130 131 public void testWriteToParcel() { 132 Notification.BubbleMetadata bubble = makeBubbleMetadata(); 133 mNotification = new Notification.Builder(mContext, CHANNEL.getId()) 134 .setBadgeIconType(Notification.BADGE_ICON_SMALL) 135 .setShortcutId(SHORTCUT_ID) 136 .setTimeoutAfter(TIMEOUT) 137 .setSettingsText(SETTING_TEXT) 138 .setGroupAlertBehavior(Notification.GROUP_ALERT_CHILDREN) 139 .setBubbleMetadata(bubble) 140 .setAllowSystemGeneratedContextualActions(ALLOW_SYS_GEN_CONTEXTUAL_ACTIONS) 141 .build(); 142 mNotification.icon = 0; 143 mNotification.number = 1; 144 final Intent intent = new Intent(); 145 final PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0); 146 mNotification.contentIntent = pendingIntent; 147 final Intent deleteIntent = new Intent(); 148 final PendingIntent delPendingIntent = PendingIntent.getBroadcast( 149 mContext, 0, deleteIntent, 0); 150 mNotification.deleteIntent = delPendingIntent; 151 mNotification.tickerText = TICKER_TEXT; 152 153 final RemoteViews contentView = new RemoteViews(mContext.getPackageName(), 154 android.R.layout.simple_list_item_1); 155 mNotification.contentView = contentView; 156 mNotification.defaults = 0; 157 mNotification.flags = 0; 158 final Uri uri = Uri.parse(URI_STRING); 159 mNotification.sound = uri; 160 mNotification.audioStreamType = 0; 161 final long[] longArray = { 1l, 2l, 3l }; 162 mNotification.vibrate = longArray; 163 mNotification.ledARGB = 0; 164 mNotification.ledOnMS = 0; 165 mNotification.ledOffMS = 0; 166 mNotification.iconLevel = 0; 167 168 Parcel parcel = Parcel.obtain(); 169 mNotification.writeToParcel(parcel, 0); 170 parcel.setDataPosition(0); 171 // Test Notification(Parcel) 172 Notification result = new Notification(parcel); 173 assertEquals(mNotification.icon, result.icon); 174 assertEquals(mNotification.when, result.when); 175 assertEquals(mNotification.number, result.number); 176 assertNotNull(result.contentIntent); 177 assertNotNull(result.deleteIntent); 178 assertEquals(mNotification.tickerText, result.tickerText); 179 assertNotNull(result.contentView); 180 assertEquals(mNotification.defaults, result.defaults); 181 assertEquals(mNotification.flags, result.flags); 182 assertNotNull(result.sound); 183 assertEquals(mNotification.audioStreamType, result.audioStreamType); 184 assertEquals(mNotification.vibrate[0], result.vibrate[0]); 185 assertEquals(mNotification.vibrate[1], result.vibrate[1]); 186 assertEquals(mNotification.vibrate[2], result.vibrate[2]); 187 assertEquals(mNotification.ledARGB, result.ledARGB); 188 assertEquals(mNotification.ledOnMS, result.ledOnMS); 189 assertEquals(mNotification.ledOffMS, result.ledOffMS); 190 assertEquals(mNotification.iconLevel, result.iconLevel); 191 assertEquals(mNotification.getShortcutId(), result.getShortcutId()); 192 assertEquals(mNotification.getBadgeIconType(), result.getBadgeIconType()); 193 assertEquals(mNotification.getTimeoutAfter(), result.getTimeoutAfter()); 194 assertEquals(mNotification.getChannelId(), result.getChannelId()); 195 assertEquals(mNotification.getSettingsText(), result.getSettingsText()); 196 assertEquals(mNotification.getGroupAlertBehavior(), result.getGroupAlertBehavior()); 197 assertNotNull(result.getBubbleMetadata()); 198 assertEquals(mNotification.getAllowSystemGeneratedContextualActions(), 199 result.getAllowSystemGeneratedContextualActions()); 200 201 mNotification.contentIntent = null; 202 parcel = Parcel.obtain(); 203 mNotification.writeToParcel(parcel, 0); 204 parcel.setDataPosition(0); 205 result = new Notification(parcel); 206 assertNull(result.contentIntent); 207 208 mNotification.deleteIntent = null; 209 parcel = Parcel.obtain(); 210 mNotification.writeToParcel(parcel, 0); 211 parcel.setDataPosition(0); 212 result = new Notification(parcel); 213 assertNull(result.deleteIntent); 214 215 mNotification.tickerText = null; 216 parcel = Parcel.obtain(); 217 mNotification.writeToParcel(parcel, 0); 218 parcel.setDataPosition(0); 219 result = new Notification(parcel); 220 assertNull(result.tickerText); 221 222 mNotification.contentView = null; 223 parcel = Parcel.obtain(); 224 mNotification.writeToParcel(parcel, 0); 225 parcel.setDataPosition(0); 226 result = new Notification(parcel); 227 assertNull(result.contentView); 228 229 mNotification.sound = null; 230 parcel = Parcel.obtain(); 231 mNotification.writeToParcel(parcel, 0); 232 parcel.setDataPosition(0); 233 result = new Notification(parcel); 234 assertNull(result.sound); 235 } 236 237 public void testColorizeNotification() { 238 mNotification = new Notification.Builder(mContext, "channel_id") 239 .setSmallIcon(1) 240 .setContentTitle(CONTENT_TITLE) 241 .setColorized(true) 242 .build(); 243 244 assertTrue(mNotification.extras.getBoolean(Notification.EXTRA_COLORIZED)); 245 } 246 247 public void testBuilder() { 248 final Intent intent = new Intent(); 249 final PendingIntent contentIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0); 250 Notification.BubbleMetadata bubble = makeBubbleMetadata(); 251 mNotification = new Notification.Builder(mContext, CHANNEL.getId()) 252 .setSmallIcon(1) 253 .setContentTitle(CONTENT_TITLE) 254 .setContentText(CONTENT_TEXT) 255 .setContentIntent(contentIntent) 256 .setBadgeIconType(Notification.BADGE_ICON_SMALL) 257 .setShortcutId(SHORTCUT_ID) 258 .setTimeoutAfter(TIMEOUT) 259 .setSettingsText(SETTING_TEXT) 260 .setGroupAlertBehavior(Notification.GROUP_ALERT_SUMMARY) 261 .setBubbleMetadata(bubble) 262 .setAllowSystemGeneratedContextualActions(ALLOW_SYS_GEN_CONTEXTUAL_ACTIONS) 263 .build(); 264 assertEquals(CONTENT_TEXT, mNotification.extras.getString(Notification.EXTRA_TEXT)); 265 assertEquals(CONTENT_TITLE, mNotification.extras.getString(Notification.EXTRA_TITLE)); 266 assertEquals(1, mNotification.icon); 267 assertEquals(contentIntent, mNotification.contentIntent); 268 assertEquals(CHANNEL.getId(), mNotification.getChannelId()); 269 assertEquals(Notification.BADGE_ICON_SMALL, mNotification.getBadgeIconType()); 270 assertEquals(SHORTCUT_ID, mNotification.getShortcutId()); 271 assertEquals(TIMEOUT, mNotification.getTimeoutAfter()); 272 assertEquals(SETTING_TEXT, mNotification.getSettingsText()); 273 assertEquals(Notification.GROUP_ALERT_SUMMARY, mNotification.getGroupAlertBehavior()); 274 assertEquals(bubble, mNotification.getBubbleMetadata()); 275 assertEquals(ALLOW_SYS_GEN_CONTEXTUAL_ACTIONS, 276 mNotification.getAllowSystemGeneratedContextualActions()); 277 } 278 279 public void testBuilder_getStyle() { 280 MessagingStyle ms = new MessagingStyle(new Person.Builder().setName("Test name").build()); 281 Notification.Builder builder = new Notification.Builder(mContext, CHANNEL.getId()); 282 283 builder.setStyle(ms); 284 285 assertEquals(ms, builder.getStyle()); 286 } 287 288 public void testActionBuilder() { 289 final Intent intent = new Intent(); 290 final PendingIntent actionIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0); 291 mAction = null; 292 mAction = new Notification.Action.Builder(0, ACTION_TITLE, actionIntent).build(); 293 assertEquals(ACTION_TITLE, mAction.title); 294 assertEquals(actionIntent, mAction.actionIntent); 295 assertEquals(true, mAction.getAllowGeneratedReplies()); 296 } 297 298 public void testNotification_addPerson() { 299 String name = "name"; 300 String key = "key"; 301 String uri = "name:name"; 302 Person person = new Person.Builder() 303 .setName(name) 304 .setIcon(Icon.createWithResource(mContext, 1)) 305 .setKey(key) 306 .setUri(uri) 307 .build(); 308 mNotification = new Notification.Builder(mContext, CHANNEL.getId()) 309 .setSmallIcon(1) 310 .setContentTitle(CONTENT_TITLE) 311 .addPerson(person) 312 .build(); 313 314 ArrayList<Person> restoredPeople = mNotification.extras.getParcelableArrayList( 315 Notification.EXTRA_PEOPLE_LIST); 316 assertNotNull(restoredPeople); 317 Person restoredPerson = restoredPeople.get(0); 318 assertNotNull(restoredPerson); 319 assertNotNull(restoredPerson.getIcon()); 320 assertEquals(name, restoredPerson.getName()); 321 assertEquals(key, restoredPerson.getKey()); 322 assertEquals(uri, restoredPerson.getUri()); 323 } 324 325 public void testNotification_MessagingStyle_people() { 326 String name = "name"; 327 String key = "key"; 328 String uri = "name:name"; 329 Person user = new Person.Builder() 330 .setName(name) 331 .setIcon(Icon.createWithResource(mContext, 1)) 332 .setKey(key) 333 .setUri(uri) 334 .build(); 335 Person participant = new Person.Builder().setName("sender").build(); 336 Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle(user) 337 .addMessage("text", 0, participant) 338 .addMessage(new Message("text 2", 0, participant)); 339 mNotification = new Notification.Builder(mContext, CHANNEL.getId()) 340 .setSmallIcon(1) 341 .setStyle(messagingStyle) 342 .build(); 343 344 Person restoredPerson = mNotification.extras.getParcelable( 345 Notification.EXTRA_MESSAGING_PERSON); 346 assertNotNull(restoredPerson); 347 assertNotNull(restoredPerson.getIcon()); 348 assertEquals(name, restoredPerson.getName()); 349 assertEquals(key, restoredPerson.getKey()); 350 assertEquals(uri, restoredPerson.getUri()); 351 assertNotNull(mNotification.extras.getParcelableArray(Notification.EXTRA_MESSAGES)); 352 } 353 354 355 public void testMessagingStyle_historicMessages() { 356 Message referenceMessage = new Message("historic text", 0, "historic sender"); 357 Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle("self name") 358 .addMessage("text", 0, "sender") 359 .addMessage(new Message("image", 0, "sender") 360 .setData("image/png", Uri.parse("http://example.com/image.png"))) 361 .addHistoricMessage(referenceMessage) 362 .setConversationTitle("title"); 363 mNotification = new Notification.Builder(mContext, CHANNEL.getId()) 364 .setSmallIcon(1) 365 .setContentTitle(CONTENT_TITLE) 366 .setStyle(messagingStyle) 367 .build(); 368 369 List<Message> historicMessages = messagingStyle.getHistoricMessages(); 370 assertNotNull(historicMessages); 371 assertEquals(1, historicMessages.size()); 372 Message message = historicMessages.get(0); 373 assertEquals(referenceMessage, message); 374 375 assertNotNull( 376 mNotification.extras.getParcelableArray(Notification.EXTRA_HISTORIC_MESSAGES)); 377 } 378 379 public void testMessagingStyle_isGroupConversation() { 380 mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.P; 381 Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle("self name") 382 .setGroupConversation(true) 383 .setConversationTitle("test conversation title"); 384 Notification notification = new Notification.Builder(mContext, "test id") 385 .setSmallIcon(1) 386 .setContentTitle("test title") 387 .setStyle(messagingStyle) 388 .build(); 389 390 assertTrue(messagingStyle.isGroupConversation()); 391 assertTrue(notification.extras.getBoolean(Notification.EXTRA_IS_GROUP_CONVERSATION)); 392 } 393 394 public void testMessagingStyle_isGroupConversation_noConversationTitle() { 395 mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.P; 396 Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle("self name") 397 .setGroupConversation(true) 398 .setConversationTitle(null); 399 Notification notification = new Notification.Builder(mContext, "test id") 400 .setSmallIcon(1) 401 .setContentTitle("test title") 402 .setStyle(messagingStyle) 403 .build(); 404 405 assertTrue(messagingStyle.isGroupConversation()); 406 assertTrue(notification.extras.getBoolean(Notification.EXTRA_IS_GROUP_CONVERSATION)); 407 } 408 409 public void testMessagingStyle_isGroupConversation_withConversationTitle_legacy() { 410 // In legacy (version < P), isGroupConversation is controlled by conversationTitle. 411 mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.O; 412 Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle("self name") 413 .setGroupConversation(false) 414 .setConversationTitle("test conversation title"); 415 Notification notification = new Notification.Builder(mContext, "test id") 416 .setSmallIcon(1) 417 .setContentTitle("test title") 418 .setStyle(messagingStyle) 419 .build(); 420 421 assertTrue(messagingStyle.isGroupConversation()); 422 assertFalse(notification.extras.getBoolean(Notification.EXTRA_IS_GROUP_CONVERSATION)); 423 } 424 425 public void testMessagingStyle_isGroupConversation_withoutConversationTitle_legacy() { 426 // In legacy (version < P), isGroupConversation is controlled by conversationTitle. 427 mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.O; 428 Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle("self name") 429 .setGroupConversation(true) 430 .setConversationTitle(null); 431 Notification notification = new Notification.Builder(mContext, "test id") 432 .setSmallIcon(1) 433 .setContentTitle("test title") 434 .setStyle(messagingStyle) 435 .build(); 436 437 assertFalse(messagingStyle.isGroupConversation()); 438 assertTrue(notification.extras.getBoolean(Notification.EXTRA_IS_GROUP_CONVERSATION)); 439 } 440 441 public void testMessagingStyle_getUser() { 442 Person user = new Person.Builder().setName("Test name").build(); 443 444 MessagingStyle messagingStyle = new MessagingStyle(user); 445 446 assertEquals(user, messagingStyle.getUser()); 447 } 448 449 public void testMessagingStyle_getConversationTitle() { 450 final String title = "test conversation title"; 451 Person user = new Person.Builder().setName("Test name").build(); 452 MessagingStyle messagingStyle = new MessagingStyle(user).setConversationTitle(title); 453 454 Notification notification = new Notification.Builder(mContext, CHANNEL.getId()) 455 .setSmallIcon(1) 456 .setContentTitle(CONTENT_TITLE) 457 .setStyle(messagingStyle) 458 .build(); 459 460 assertEquals(title, messagingStyle.getConversationTitle()); 461 assertEquals(title, notification.extras.getString(Notification.EXTRA_CONVERSATION_TITLE)); 462 } 463 464 public void testMessage() { 465 String senderName = "Test name"; 466 Person sender = new Person.Builder().setName(senderName).build(); 467 String text = "Test message"; 468 long timestamp = 400; 469 470 Message message = new Message(text, timestamp, sender); 471 472 assertEquals(text, message.getText()); 473 assertEquals(timestamp, message.getTimestamp()); 474 assertEquals(sender, message.getSenderPerson()); 475 assertEquals(senderName, message.getSender()); 476 } 477 478 public void testMessageData() { 479 Person sender = new Person.Builder().setName("Test name").build(); 480 String text = "Test message"; 481 long timestamp = 400; 482 Message message = new Message(text, timestamp, sender); 483 484 String mimeType = "image/png"; 485 Uri uri = Uri.parse("http://example.com/image.png"); 486 message.setData(mimeType, uri); 487 488 assertEquals(mimeType, message.getDataMimeType()); 489 assertEquals(uri, message.getDataUri()); 490 } 491 492 public void testToString() { 493 mNotification = new Notification(); 494 assertNotNull(mNotification.toString()); 495 mNotification = null; 496 } 497 498 public void testNotificationActionBuilder_setDataOnlyRemoteInput() throws Throwable { 499 Notification.Action a = newActionBuilder() 500 .addRemoteInput(newDataOnlyRemoteInput()).build(); 501 RemoteInput[] textInputs = a.getRemoteInputs(); 502 assertTrue(textInputs == null || textInputs.length == 0); 503 verifyRemoteInputArrayHasSingleResult(a.getDataOnlyRemoteInputs(), DATA_RESULT_KEY); 504 } 505 506 public void testNotificationActionBuilder_setTextAndDataOnlyRemoteInput() throws Throwable { 507 Notification.Action a = newActionBuilder() 508 .addRemoteInput(newDataOnlyRemoteInput()) 509 .addRemoteInput(newTextRemoteInput()) 510 .build(); 511 512 verifyRemoteInputArrayHasSingleResult(a.getRemoteInputs(), TEXT_RESULT_KEY); 513 verifyRemoteInputArrayHasSingleResult(a.getDataOnlyRemoteInputs(), DATA_RESULT_KEY); 514 } 515 516 public void testNotificationActionBuilder_setTextAndDataOnlyAndBothRemoteInput() 517 throws Throwable { 518 Notification.Action a = newActionBuilder() 519 .addRemoteInput(newDataOnlyRemoteInput()) 520 .addRemoteInput(newTextRemoteInput()) 521 .addRemoteInput(newTextAndDataRemoteInput()) 522 .build(); 523 524 assertTrue(a.getRemoteInputs() != null && a.getRemoteInputs().length == 2); 525 assertEquals(TEXT_RESULT_KEY, a.getRemoteInputs()[0].getResultKey()); 526 assertFalse(a.getRemoteInputs()[0].isDataOnly()); 527 assertEquals(DATA_AND_TEXT_RESULT_KEY, a.getRemoteInputs()[1].getResultKey()); 528 assertFalse(a.getRemoteInputs()[1].isDataOnly()); 529 530 verifyRemoteInputArrayHasSingleResult(a.getDataOnlyRemoteInputs(), DATA_RESULT_KEY); 531 assertTrue(a.getDataOnlyRemoteInputs()[0].isDataOnly()); 532 } 533 534 public void testAction_builder_hasDefault() { 535 Notification.Action action = makeNotificationAction(null); 536 assertEquals(Notification.Action.SEMANTIC_ACTION_NONE, action.getSemanticAction()); 537 } 538 539 public void testAction_builder_setSemanticAction() { 540 Notification.Action action = makeNotificationAction( 541 builder -> builder.setSemanticAction(Notification.Action.SEMANTIC_ACTION_REPLY)); 542 assertEquals(Notification.Action.SEMANTIC_ACTION_REPLY, action.getSemanticAction()); 543 } 544 545 public void testAction_builder_contextualAction_nullIcon() { 546 PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0); 547 Notification.Action.Builder builder = 548 new Notification.Action.Builder(null /* icon */, "title", pendingIntent) 549 .setContextual(true); 550 try { 551 builder.build(); 552 fail("Creating a semantic Action with a null icon should cause a NullPointerException"); 553 } catch (NullPointerException e) { 554 // Expected 555 } 556 } 557 558 public void testAction_builder_contextualAction_nullIntent() { 559 Notification.Action.Builder builder = 560 new Notification.Action.Builder(0 /* icon */, "title", null /* intent */) 561 .setContextual(true); 562 try { 563 builder.build(); 564 fail("Creating a semantic Action with a null PendingIntent should cause a " 565 + "NullPointerException"); 566 } catch (NullPointerException e) { 567 // Expected 568 } 569 } 570 571 public void testAction_parcel() { 572 Notification.Action action = writeAndReadParcelable( 573 makeNotificationAction(builder -> { 574 builder.setSemanticAction(Notification.Action.SEMANTIC_ACTION_ARCHIVE); 575 builder.setAllowGeneratedReplies(true); 576 })); 577 578 assertEquals(Notification.Action.SEMANTIC_ACTION_ARCHIVE, action.getSemanticAction()); 579 assertTrue(action.getAllowGeneratedReplies()); 580 } 581 582 public void testAction_clone() { 583 Notification.Action action = makeNotificationAction( 584 builder -> builder.setSemanticAction(Notification.Action.SEMANTIC_ACTION_DELETE)); 585 assertEquals( 586 Notification.Action.SEMANTIC_ACTION_DELETE, 587 action.clone().getSemanticAction()); 588 } 589 590 public void testBuildStrictMode() { 591 try { 592 StrictMode.setThreadPolicy( 593 new StrictMode.ThreadPolicy.Builder().detectAll().penaltyDeath().build()); 594 Notification.Action a = newActionBuilder() 595 .addRemoteInput(newDataOnlyRemoteInput()) 596 .addRemoteInput(newTextRemoteInput()) 597 .addRemoteInput(newTextAndDataRemoteInput()) 598 .build(); 599 Notification.Builder b = new Notification.Builder(mContext, "id") 600 .setStyle(new Notification.BigTextStyle().setBigContentTitle("Big content")) 601 .setContentTitle("title") 602 .setActions(a); 603 604 b.build(); 605 } finally { 606 StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().permitAll().build()); 607 } 608 } 609 610 public void testGetAllowSystemGeneratedContextualActions_trueByDefault() { 611 Notification notification = new Notification.Builder(mContext, CHANNEL.getId()).build(); 612 assertTrue(notification.getAllowSystemGeneratedContextualActions()); 613 } 614 615 public void testGetAllowSystemGeneratedContextualActions() { 616 Notification notification = new Notification.Builder(mContext, CHANNEL.getId()) 617 .setAllowSystemGeneratedContextualActions(false) 618 .build(); 619 assertFalse(notification.getAllowSystemGeneratedContextualActions()); 620 } 621 622 public void testBubbleMetadataBuilder() { 623 PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0); 624 PendingIntent deleteIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0); 625 Icon icon = Icon.createWithResource(mContext, 1); 626 Notification.BubbleMetadata.Builder metadataBuilder = 627 new Notification.BubbleMetadata.Builder() 628 .setDesiredHeight(BUBBLE_HEIGHT) 629 .setIcon(icon) 630 .setIntent(bubbleIntent) 631 .setDeleteIntent(deleteIntent); 632 633 Notification.BubbleMetadata data = metadataBuilder.build(); 634 assertEquals(BUBBLE_HEIGHT, data.getDesiredHeight()); 635 assertEquals(icon, data.getIcon()); 636 assertEquals(bubbleIntent, data.getIntent()); 637 assertEquals(deleteIntent, data.getDeleteIntent()); 638 assertFalse(data.isNotificationSuppressed()); 639 assertFalse(data.getAutoExpandBubble()); 640 } 641 642 public void testBubbleMetadata_parcel() { 643 PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0); 644 PendingIntent deleteIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0); 645 Icon icon = Icon.createWithResource(mContext, 1); 646 Notification.BubbleMetadata metadata = 647 new Notification.BubbleMetadata.Builder() 648 .setDesiredHeight(BUBBLE_HEIGHT) 649 .setAutoExpandBubble(true) 650 .setSuppressNotification(true) 651 .setIcon(icon) 652 .setIntent(bubbleIntent) 653 .setDeleteIntent(deleteIntent) 654 .build(); 655 656 writeAndReadParcelable(metadata); 657 assertEquals(BUBBLE_HEIGHT, metadata.getDesiredHeight()); 658 assertEquals(icon, metadata.getIcon()); 659 assertEquals(bubbleIntent, metadata.getIntent()); 660 assertEquals(deleteIntent, metadata.getDeleteIntent()); 661 assertTrue(metadata.getAutoExpandBubble()); 662 assertTrue(metadata.isNotificationSuppressed()); 663 } 664 665 public void testBubbleMetadata_parcelResId() { 666 PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0); 667 Icon icon = Icon.createWithResource(mContext, 1); 668 Notification.BubbleMetadata metadata = 669 new Notification.BubbleMetadata.Builder() 670 .setDesiredHeightResId(BUBBLE_HEIGHT_RESID) 671 .setIcon(icon) 672 .setIntent(bubbleIntent) 673 .build(); 674 writeAndReadParcelable(metadata); 675 assertEquals(BUBBLE_HEIGHT_RESID, metadata.getDesiredHeightResId()); 676 assertEquals(icon, metadata.getIcon()); 677 assertEquals(bubbleIntent, metadata.getIntent()); 678 assertFalse(metadata.getAutoExpandBubble()); 679 assertFalse(metadata.isNotificationSuppressed()); 680 } 681 682 public void testBubbleMetadataBuilder_throwForNoIntent() { 683 Icon icon = Icon.createWithResource(mContext, 1); 684 Notification.BubbleMetadata.Builder metadataBuilder = 685 new Notification.BubbleMetadata.Builder() 686 .setDesiredHeight(BUBBLE_HEIGHT) 687 .setIcon(icon); 688 try { 689 metadataBuilder.build(); 690 fail("Should have thrown IllegalStateException, no pending intent"); 691 } catch (IllegalStateException e) { 692 // expected 693 } 694 } 695 696 public void testBubbleMetadataBuilder_throwForNoIcon() { 697 PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0); 698 Notification.BubbleMetadata.Builder metadataBuilder = 699 new Notification.BubbleMetadata.Builder() 700 .setDesiredHeight(BUBBLE_HEIGHT) 701 .setIntent(bubbleIntent); 702 try { 703 metadataBuilder.build(); 704 fail("Should have thrown IllegalStateException, no icon"); 705 } catch (IllegalStateException e) { 706 // expected 707 } 708 } 709 710 public void testBubbleMetadataBuilder_throwForBitmapIcon() { 711 Bitmap b = Bitmap.createBitmap(50, 25, Bitmap.Config.ARGB_8888); 712 new Canvas(b).drawColor(0xffff0000); 713 Icon icon = Icon.createWithBitmap(b); 714 715 PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0); 716 try { 717 Notification.BubbleMetadata.Builder metadataBuilder = 718 new Notification.BubbleMetadata.Builder() 719 .setIcon(icon) 720 .setIntent(bubbleIntent); 721 fail("Should have thrown IllegalArgumentException, invalid icon type (bitmap)"); 722 } catch (IllegalArgumentException e) { 723 // expected 724 } 725 } 726 727 public void testBubbleMetadataBuilder_noThrowForAdaptiveBitmapIcon() { 728 Bitmap b = Bitmap.createBitmap(50, 25, Bitmap.Config.ARGB_8888); 729 new Canvas(b).drawColor(0xffff0000); 730 Icon icon = Icon.createWithAdaptiveBitmap(b); 731 732 PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0); 733 Notification.BubbleMetadata.Builder metadataBuilder = 734 new Notification.BubbleMetadata.Builder() 735 .setIcon(icon) 736 .setIntent(bubbleIntent); 737 Notification.BubbleMetadata metadata = metadataBuilder.build(); 738 assertNotNull(metadata.getIcon()); 739 assertEquals(TYPE_ADAPTIVE_BITMAP, metadata.getIcon().getType()); 740 } 741 742 public void testBubbleMetadataBuilder_noThrowForNonBitmapIcon() { 743 Icon icon = Icon.createWithResource(mContext, R.drawable.ic_android); 744 745 PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0); 746 Notification.BubbleMetadata.Builder metadataBuilder = 747 new Notification.BubbleMetadata.Builder() 748 .setIcon(icon) 749 .setIntent(bubbleIntent); 750 Notification.BubbleMetadata metadata = metadataBuilder.build(); 751 assertNotNull(metadata.getIcon()); 752 assertEquals(TYPE_RESOURCE, metadata.getIcon().getType()); 753 } 754 755 public void testBubbleMetadataBuilder_replaceHeightRes() { 756 PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0); 757 PendingIntent deleteIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0); 758 Icon icon = Icon.createWithResource(mContext, 1); 759 Notification.BubbleMetadata.Builder metadataBuilder = 760 new Notification.BubbleMetadata.Builder() 761 .setDesiredHeight(BUBBLE_HEIGHT) 762 .setDesiredHeightResId(BUBBLE_HEIGHT_RESID) 763 .setIcon(icon) 764 .setIntent(bubbleIntent) 765 .setDeleteIntent(deleteIntent); 766 767 Notification.BubbleMetadata data = metadataBuilder.build(); 768 // Desired height should be cleared 769 assertEquals(0, data.getDesiredHeight()); 770 // Res id should be used 771 assertEquals(BUBBLE_HEIGHT_RESID, data.getDesiredHeightResId()); 772 } 773 774 public void testBubbleMetadataBuilder_replaceHeightDp() { 775 PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0); 776 PendingIntent deleteIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0); 777 Icon icon = Icon.createWithResource(mContext, 1); 778 Notification.BubbleMetadata.Builder metadataBuilder = 779 new Notification.BubbleMetadata.Builder() 780 .setDesiredHeightResId(BUBBLE_HEIGHT_RESID) 781 .setDesiredHeight(BUBBLE_HEIGHT) 782 .setIcon(icon) 783 .setIntent(bubbleIntent) 784 .setDeleteIntent(deleteIntent); 785 786 Notification.BubbleMetadata data = metadataBuilder.build(); 787 // Desired height should be used 788 assertEquals(BUBBLE_HEIGHT, data.getDesiredHeight()); 789 // Res id should be cleared 790 assertEquals(0, data.getDesiredHeightResId()); 791 } 792 793 public void testFlagBubble() { 794 Notification n = new Notification(); 795 assertFalse((n.flags & FLAG_BUBBLE) != 0); 796 n.flags |= FLAG_BUBBLE; 797 assertTrue((n.flags & FLAG_BUBBLE) != 0); 798 } 799 800 private static RemoteInput newDataOnlyRemoteInput() { 801 return new RemoteInput.Builder(DATA_RESULT_KEY) 802 .setAllowFreeFormInput(false) 803 .setAllowDataType("mimeType", true) 804 .build(); 805 } 806 807 private static RemoteInput newTextAndDataRemoteInput() { 808 return new RemoteInput.Builder(DATA_AND_TEXT_RESULT_KEY) 809 .setAllowDataType("mimeType", true) 810 .build(); // allowFreeForm defaults to true 811 } 812 813 private static RemoteInput newTextRemoteInput() { 814 return new RemoteInput.Builder(TEXT_RESULT_KEY).build(); // allowFreeForm defaults to true 815 } 816 817 private static void verifyRemoteInputArrayHasSingleResult( 818 RemoteInput[] remoteInputs, String expectedResultKey) { 819 assertTrue(remoteInputs != null && remoteInputs.length == 1); 820 assertEquals(expectedResultKey, remoteInputs[0].getResultKey()); 821 } 822 823 private static Notification.Action.Builder newActionBuilder() { 824 return new Notification.Action.Builder(0, "title", null); 825 } 826 827 /** 828 * Writes an arbitrary {@link Parcelable} into a {@link Parcel} using its writeToParcel 829 * method before reading it out again to check that it was sent properly. 830 */ 831 private static <T extends Parcelable> T writeAndReadParcelable(T original) { 832 Parcel p = Parcel.obtain(); 833 p.writeParcelable(original, /* flags */ 0); 834 p.setDataPosition(0); 835 return p.readParcelable(/* classLoader */ null); 836 } 837 838 /** 839 * Creates a Notification.Action by mocking initial dependencies and then applying 840 * transformations if they're defined. 841 */ 842 private Notification.Action makeNotificationAction( 843 @Nullable Consumer<Builder> transformation) { 844 Notification.Action.Builder actionBuilder = 845 new Notification.Action.Builder(null, "Test Title", null); 846 if (transformation != null) { 847 transformation.accept(actionBuilder); 848 } 849 return actionBuilder.build(); 850 } 851 852 private Notification.BubbleMetadata makeBubbleMetadata() { 853 PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0, new Intent(), 0); 854 855 return new Notification.BubbleMetadata.Builder() 856 .setIntent(bubbleIntent) 857 .setIcon(Icon.createWithResource(mContext, 1)) 858 .setDesiredHeight(BUBBLE_HEIGHT) 859 .build(); 860 } 861 } 862