1 /* 2 * Copyright (C) 2013 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.cts.verifier.notifications; 18 19 import android.annotation.SuppressLint; 20 import android.app.Activity; 21 import android.app.Notification; 22 import android.app.NotificationChannel; 23 import android.app.NotificationManager; 24 import android.graphics.Bitmap; 25 import android.graphics.BitmapFactory; 26 import android.os.Bundle; 27 import android.provider.Settings.Secure; 28 import android.service.notification.StatusBarNotification; 29 import android.support.v4.app.NotificationCompat; 30 import android.util.Log; 31 import android.view.View; 32 import android.view.ViewGroup; 33 34 import com.android.cts.verifier.R; 35 36 import org.json.JSONException; 37 import org.json.JSONObject; 38 39 import java.util.ArrayList; 40 import java.util.HashSet; 41 import java.util.List; 42 import java.util.Set; 43 import java.util.UUID; 44 45 import static com.android.cts.verifier.notifications.MockListener.*; 46 47 import static junit.framework.Assert.assertNotNull; 48 49 public class NotificationListenerVerifierActivity extends InteractiveVerifierActivity 50 implements Runnable { 51 private static final String TAG = "NoListenerVerifier"; 52 private static final String NOTIFICATION_CHANNEL_ID = TAG; 53 54 private String mTag1; 55 private String mTag2; 56 private String mTag3; 57 private int mIcon1; 58 private int mIcon2; 59 private int mIcon3; 60 private int mId1; 61 private int mId2; 62 private int mId3; 63 private long mWhen1; 64 private long mWhen2; 65 private long mWhen3; 66 private int mFlag1; 67 private int mFlag2; 68 private int mFlag3; 69 70 @Override 71 int getTitleResource() { 72 return R.string.nls_test; 73 } 74 75 @Override 76 int getInstructionsResource() { 77 return R.string.nls_info; 78 } 79 80 // Test Setup 81 82 @Override 83 protected List<InteractiveTestCase> createTestItems() { 84 List<InteractiveTestCase> tests = new ArrayList<>(17); 85 tests.add(new IsEnabledTest()); 86 tests.add(new ServiceStartedTest()); 87 tests.add(new NotificationReceivedTest()); 88 tests.add(new DataIntactTest()); 89 tests.add(new DismissOneTest()); 90 tests.add(new DismissOneWithReasonTest()); 91 tests.add(new DismissAllTest()); 92 tests.add(new SnoozeNotificationForTimeTest()); 93 tests.add(new SnoozeNotificationForTimeCancelTest()); 94 tests.add(new GetSnoozedNotificationTest()); 95 tests.add(new EnableHintsTest()); 96 tests.add(new RequestUnbindTest()); 97 tests.add(new RequestBindTest()); 98 tests.add(new MessageBundleTest()); 99 tests.add(new EnableHintsTest()); 100 tests.add(new IsDisabledTest()); 101 tests.add(new ServiceStoppedTest()); 102 tests.add(new NotificationNotReceivedTest()); 103 return tests; 104 } 105 106 private void createChannel() { 107 NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, 108 NOTIFICATION_CHANNEL_ID, NotificationManager.IMPORTANCE_LOW); 109 mNm.createNotificationChannel(channel); 110 } 111 112 private void deleteChannel() { 113 mNm.deleteNotificationChannel(NOTIFICATION_CHANNEL_ID); 114 } 115 116 @SuppressLint("NewApi") 117 private void sendNotifications() { 118 mTag1 = UUID.randomUUID().toString(); 119 Log.d(TAG, "Sending " + mTag1); 120 mTag2 = UUID.randomUUID().toString(); 121 Log.d(TAG, "Sending " + mTag2); 122 mTag3 = UUID.randomUUID().toString(); 123 Log.d(TAG, "Sending " + mTag3); 124 125 mWhen1 = System.currentTimeMillis() + 1; 126 mWhen2 = System.currentTimeMillis() + 2; 127 mWhen3 = System.currentTimeMillis() + 3; 128 129 mIcon1 = R.drawable.ic_stat_alice; 130 mIcon2 = R.drawable.ic_stat_bob; 131 mIcon3 = R.drawable.ic_stat_charlie; 132 133 mId1 = NOTIFICATION_ID + 1; 134 mId2 = NOTIFICATION_ID + 2; 135 mId3 = NOTIFICATION_ID + 3; 136 137 mPackageString = "com.android.cts.verifier"; 138 139 Notification n1 = new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 140 .setContentTitle("ClearTest 1") 141 .setContentText(mTag1) 142 .setSmallIcon(mIcon1) 143 .setWhen(mWhen1) 144 .setDeleteIntent(makeIntent(1, mTag1)) 145 .setOnlyAlertOnce(true) 146 .build(); 147 mNm.notify(mTag1, mId1, n1); 148 mFlag1 = Notification.FLAG_ONLY_ALERT_ONCE; 149 150 Notification n2 = new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 151 .setContentTitle("ClearTest 2") 152 .setContentText(mTag2) 153 .setSmallIcon(mIcon2) 154 .setWhen(mWhen2) 155 .setDeleteIntent(makeIntent(2, mTag2)) 156 .setAutoCancel(true) 157 .build(); 158 mNm.notify(mTag2, mId2, n2); 159 mFlag2 = Notification.FLAG_AUTO_CANCEL; 160 161 Notification n3 = new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 162 .setContentTitle("ClearTest 3") 163 .setContentText(mTag3) 164 .setSmallIcon(mIcon3) 165 .setWhen(mWhen3) 166 .setDeleteIntent(makeIntent(3, mTag3)) 167 .setAutoCancel(true) 168 .setOnlyAlertOnce(true) 169 .build(); 170 mNm.notify(mTag3, mId3, n3); 171 mFlag3 = Notification.FLAG_ONLY_ALERT_ONCE | Notification.FLAG_AUTO_CANCEL; 172 } 173 174 // Tests 175 176 private class NotificationReceivedTest extends InteractiveTestCase { 177 @Override 178 View inflate(ViewGroup parent) { 179 return createAutoItem(parent, R.string.nls_note_received); 180 181 } 182 183 @Override 184 void setUp() { 185 createChannel(); 186 sendNotifications(); 187 status = READY; 188 } 189 190 @Override 191 void tearDown() { 192 mNm.cancelAll(); 193 MockListener.getInstance().resetData(); 194 deleteChannel(); 195 } 196 197 @Override 198 void test() { 199 List<String> result = new ArrayList<>(MockListener.getInstance().mPosted); 200 if (result.size() > 0 && result.contains(mTag1)) { 201 status = PASS; 202 } else { 203 logFail(); 204 status = FAIL; 205 } 206 } 207 } 208 209 private class DataIntactTest extends InteractiveTestCase { 210 @Override 211 View inflate(ViewGroup parent) { 212 return createAutoItem(parent, R.string.nls_payload_intact); 213 } 214 215 @Override 216 void setUp() { 217 createChannel(); 218 sendNotifications(); 219 status = READY; 220 } 221 222 @Override 223 void test() { 224 List<JSONObject> result = new ArrayList<>(MockListener.getInstance().getPosted()); 225 226 Set<String> found = new HashSet<String>(); 227 if (result.size() == 0) { 228 status = FAIL; 229 return; 230 } 231 boolean pass = true; 232 for (JSONObject payload : result) { 233 try { 234 pass &= checkEquals(mPackageString, 235 payload.getString(JSON_PACKAGE), 236 "data integrity test: notification package (%s, %s)"); 237 String tag = payload.getString(JSON_TAG); 238 if (mTag1.equals(tag)) { 239 found.add(mTag1); 240 pass &= checkEquals(mIcon1, payload.getInt(JSON_ICON), 241 "data integrity test: notification icon (%d, %d)"); 242 pass &= checkFlagSet(mFlag1, payload.getInt(JSON_FLAGS), 243 "data integrity test: notification flags (%d, %d)"); 244 pass &= checkEquals(mId1, payload.getInt(JSON_ID), 245 "data integrity test: notification ID (%d, %d)"); 246 pass &= checkEquals(mWhen1, payload.getLong(JSON_WHEN), 247 "data integrity test: notification when (%d, %d)"); 248 } else if (mTag2.equals(tag)) { 249 found.add(mTag2); 250 pass &= checkEquals(mIcon2, payload.getInt(JSON_ICON), 251 "data integrity test: notification icon (%d, %d)"); 252 pass &= checkFlagSet(mFlag2, payload.getInt(JSON_FLAGS), 253 "data integrity test: notification flags (%d, %d)"); 254 pass &= checkEquals(mId2, payload.getInt(JSON_ID), 255 "data integrity test: notification ID (%d, %d)"); 256 pass &= checkEquals(mWhen2, payload.getLong(JSON_WHEN), 257 "data integrity test: notification when (%d, %d)"); 258 } else if (mTag3.equals(tag)) { 259 found.add(mTag3); 260 pass &= checkEquals(mIcon3, payload.getInt(JSON_ICON), 261 "data integrity test: notification icon (%d, %d)"); 262 pass &= checkFlagSet(mFlag3, payload.getInt(JSON_FLAGS), 263 "data integrity test: notification flags (%d, %d)"); 264 pass &= checkEquals(mId3, payload.getInt(JSON_ID), 265 "data integrity test: notification ID (%d, %d)"); 266 pass &= checkEquals(mWhen3, payload.getLong(JSON_WHEN), 267 "data integrity test: notification when (%d, %d)"); 268 } else { 269 pass = false; 270 logFail("unexpected notification tag: " + tag); 271 } 272 } catch (JSONException e) { 273 pass = false; 274 Log.e(TAG, "failed to unpack data from mocklistener", e); 275 } 276 } 277 278 pass &= found.size() == 3; 279 status = pass ? PASS : FAIL; 280 } 281 282 @Override 283 void tearDown() { 284 mNm.cancelAll(); 285 MockListener.getInstance().resetData(); 286 deleteChannel(); 287 } 288 } 289 290 private class DismissOneTest extends InteractiveTestCase { 291 @Override 292 View inflate(ViewGroup parent) { 293 return createAutoItem(parent, R.string.nls_clear_one); 294 } 295 296 @Override 297 void setUp() { 298 createChannel(); 299 sendNotifications(); 300 status = READY; 301 } 302 303 @Override 304 void test() { 305 if (status == READY) { 306 MockListener.getInstance().cancelNotification( 307 MockListener.getInstance().getKeyForTag(mTag1)); 308 status = RETEST; 309 } else { 310 List<String> result = new ArrayList<>(MockListener.getInstance().mRemoved); 311 if (result.size() != 0 312 && result.contains(mTag1) 313 && !result.contains(mTag2) 314 && !result.contains(mTag3)) { 315 status = PASS; 316 } else { 317 logFail(); 318 status = FAIL; 319 } 320 } 321 } 322 323 @Override 324 void tearDown() { 325 mNm.cancelAll(); 326 deleteChannel(); 327 MockListener.getInstance().resetData(); 328 } 329 } 330 331 private class DismissOneWithReasonTest extends InteractiveTestCase { 332 int mRetries = 3; 333 334 @Override 335 View inflate(ViewGroup parent) { 336 return createAutoItem(parent, R.string.nls_clear_one_reason); 337 } 338 339 @Override 340 void setUp() { 341 createChannel(); 342 sendNotifications(); 343 status = READY; 344 } 345 346 @Override 347 void test() { 348 if (status == READY) { 349 MockListener.getInstance().cancelNotification( 350 MockListener.getInstance().getKeyForTag(mTag1)); 351 status = RETEST; 352 } else { 353 List<JSONObject> result = 354 new ArrayList<>(MockListener.getInstance().mRemovedReason.values()); 355 boolean pass = false; 356 for (JSONObject payload : result) { 357 try { 358 pass |= (checkEquals(mTag1, 359 payload.getString(JSON_TAG), 360 "data dismissal test: notification tag (%s, %s)") 361 && checkEquals(REASON_LISTENER_CANCEL, 362 payload.getInt(JSON_REASON), 363 "data dismissal test: reason (%d, %d)")); 364 if(pass) { 365 break; 366 } 367 } catch (JSONException e) { 368 e.printStackTrace(); 369 } 370 } 371 if (pass) { 372 status = PASS; 373 } else { 374 if (--mRetries > 0) { 375 sleep(100); 376 status = RETEST; 377 } else { 378 status = FAIL; 379 } 380 } 381 } 382 } 383 384 @Override 385 void tearDown() { 386 mNm.cancelAll(); 387 deleteChannel(); 388 MockListener.getInstance().resetData(); 389 } 390 } 391 392 private class DismissAllTest extends InteractiveTestCase { 393 @Override 394 View inflate(ViewGroup parent) { 395 return createAutoItem(parent, R.string.nls_clear_all); 396 } 397 398 @Override 399 void setUp() { 400 createChannel(); 401 sendNotifications(); 402 status = READY; 403 } 404 405 @Override 406 void test() { 407 if (status == READY) { 408 MockListener.getInstance().cancelAllNotifications(); 409 status = RETEST; 410 } else { 411 List<String> result = new ArrayList<>(MockListener.getInstance().mRemoved); 412 if (result.size() != 0 413 && result.contains(mTag1) 414 && result.contains(mTag2) 415 && result.contains(mTag3)) { 416 status = PASS; 417 } else { 418 logFail(); 419 status = FAIL; 420 } 421 } 422 } 423 424 @Override 425 void tearDown() { 426 mNm.cancelAll(); 427 deleteChannel(); 428 MockListener.getInstance().resetData(); 429 } 430 } 431 432 private class IsDisabledTest extends InteractiveTestCase { 433 @Override 434 View inflate(ViewGroup parent) { 435 return createNlsSettingsItem(parent, R.string.nls_disable_service); 436 } 437 438 @Override 439 boolean autoStart() { 440 return true; 441 } 442 443 @Override 444 void test() { 445 String listeners = Secure.getString(getContentResolver(), 446 ENABLED_NOTIFICATION_LISTENERS); 447 if (listeners == null || !listeners.contains(LISTENER_PATH)) { 448 status = PASS; 449 } else { 450 status = WAIT_FOR_USER; 451 } 452 } 453 454 @Override 455 void tearDown() { 456 MockListener.getInstance().resetData(); 457 } 458 } 459 460 private class ServiceStoppedTest extends InteractiveTestCase { 461 @Override 462 View inflate(ViewGroup parent) { 463 return createAutoItem(parent, R.string.nls_service_stopped); 464 } 465 466 @Override 467 void test() { 468 if (mNm.getEffectsSuppressor() == null) { 469 status = PASS; 470 } else { 471 status = FAIL; 472 } 473 } 474 } 475 476 private class NotificationNotReceivedTest extends InteractiveTestCase { 477 @Override 478 View inflate(ViewGroup parent) { 479 return createAutoItem(parent, R.string.nls_note_missed); 480 481 } 482 483 @Override 484 void setUp() { 485 createChannel(); 486 sendNotifications(); 487 status = READY; 488 } 489 490 @Override 491 void test() { 492 List<String> result = new ArrayList<>(MockListener.getInstance().mPosted); 493 if (result.size() == 0) { 494 status = PASS; 495 } else { 496 logFail(); 497 status = FAIL; 498 } 499 next(); 500 } 501 502 @Override 503 void tearDown() { 504 mNm.cancelAll(); 505 deleteChannel(); 506 MockListener.getInstance().resetData(); 507 } 508 } 509 510 private class RequestUnbindTest extends InteractiveTestCase { 511 int mRetries = 5; 512 513 @Override 514 View inflate(ViewGroup parent) { 515 return createAutoItem(parent, R.string.nls_snooze); 516 517 } 518 519 @Override 520 void setUp() { 521 status = READY; 522 MockListener.getInstance().requestInterruptionFilter( 523 MockListener.HINT_HOST_DISABLE_CALL_EFFECTS); 524 } 525 526 @Override 527 void test() { 528 if (status == READY) { 529 MockListener.getInstance().requestUnbind(); 530 status = RETEST; 531 } else { 532 if (mNm.getEffectsSuppressor() == null && !MockListener.getInstance().isConnected) { 533 status = PASS; 534 } else { 535 if (--mRetries > 0) { 536 status = RETEST; 537 } else { 538 logFail(); 539 status = FAIL; 540 } 541 } 542 next(); 543 } 544 } 545 } 546 547 private class RequestBindTest extends InteractiveTestCase { 548 int mRetries = 5; 549 550 @Override 551 View inflate(ViewGroup parent) { 552 return createAutoItem(parent, R.string.nls_unsnooze); 553 554 } 555 556 @Override 557 void test() { 558 if (status == READY) { 559 MockListener.requestRebind(MockListener.COMPONENT_NAME); 560 status = RETEST; 561 } else { 562 if (MockListener.getInstance().isConnected) { 563 status = PASS; 564 next(); 565 } else { 566 if (--mRetries > 0) { 567 status = RETEST; 568 next(); 569 } else { 570 logFail(); 571 status = FAIL; 572 } 573 } 574 } 575 } 576 } 577 578 private class EnableHintsTest extends InteractiveTestCase { 579 @Override 580 View inflate(ViewGroup parent) { 581 return createAutoItem(parent, R.string.nls_hints); 582 583 } 584 585 @Override 586 void test() { 587 if (status == READY) { 588 MockListener.getInstance().requestListenerHints( 589 MockListener.HINT_HOST_DISABLE_CALL_EFFECTS); 590 status = RETEST; 591 } else { 592 int result = MockListener.getInstance().getCurrentListenerHints(); 593 if (result == MockListener.HINT_HOST_DISABLE_CALL_EFFECTS) { 594 status = PASS; 595 next(); 596 } else { 597 logFail(); 598 status = FAIL; 599 } 600 } 601 } 602 } 603 604 private class SnoozeNotificationForTimeTest extends InteractiveTestCase { 605 final static int READY_TO_SNOOZE = 0; 606 final static int SNOOZED = 1; 607 final static int READY_TO_CHECK_FOR_UNSNOOZE = 2; 608 int state = -1; 609 long snoozeTime = 3000; 610 611 @Override 612 View inflate(ViewGroup parent) { 613 return createAutoItem(parent, R.string.nls_snooze_one_time); 614 } 615 616 @Override 617 void setUp() { 618 createChannel(); 619 sendNotifications(); 620 status = READY; 621 state = READY_TO_SNOOZE; 622 delay(); 623 } 624 625 @Override 626 void test() { 627 status = RETEST; 628 if (state == READY_TO_SNOOZE) { 629 MockListener.getInstance().snoozeNotification( 630 MockListener.getInstance().getKeyForTag(mTag1), snoozeTime); 631 state = SNOOZED; 632 } else if (state == SNOOZED) { 633 List<JSONObject> result = 634 new ArrayList<>(MockListener.getInstance().mRemovedReason.values()); 635 boolean pass = false; 636 for (JSONObject payload : result) { 637 try { 638 pass |= (checkEquals(mTag1, 639 payload.getString(JSON_TAG), 640 "data dismissal test: notification tag (%s, %s)") 641 && checkEquals(MockListener.REASON_SNOOZED, 642 payload.getInt(JSON_REASON), 643 "data dismissal test: reason (%d, %d)")); 644 if (pass) { 645 break; 646 } 647 } catch (JSONException e) { 648 e.printStackTrace(); 649 } 650 } 651 if (!pass) { 652 logFail(); 653 status = FAIL; 654 next(); 655 return; 656 } else { 657 state = READY_TO_CHECK_FOR_UNSNOOZE; 658 } 659 } else { 660 List<String> result = new ArrayList<>(MockListener.getInstance().mPosted); 661 if (result.size() > 0 && result.contains(mTag1)) { 662 status = PASS; 663 } else { 664 logFail(); 665 status = FAIL; 666 } 667 } 668 } 669 670 @Override 671 void tearDown() { 672 mNm.cancelAll(); 673 deleteChannel(); 674 MockListener.getInstance().resetData(); 675 delay(); 676 } 677 } 678 679 /** 680 * Posts notifications, snoozes one of them. Verifies that it is snoozed. Cancels all 681 * notifications and reposts them. Confirms that the original notification is still snoozed. 682 */ 683 private class SnoozeNotificationForTimeCancelTest extends InteractiveTestCase { 684 685 final static int READY_TO_SNOOZE = 0; 686 final static int SNOOZED = 1; 687 final static int READY_TO_CHECK_FOR_SNOOZE = 2; 688 int state = -1; 689 long snoozeTime = 10000; 690 private String tag; 691 692 @Override 693 View inflate(ViewGroup parent) { 694 return createAutoItem(parent, R.string.nls_snooze_one_time); 695 } 696 697 @Override 698 void setUp() { 699 createChannel(); 700 sendNotifications(); 701 tag = mTag1; 702 status = READY; 703 state = READY_TO_SNOOZE; 704 delay(); 705 } 706 707 @Override 708 void test() { 709 status = RETEST; 710 if (state == READY_TO_SNOOZE) { 711 MockListener.getInstance().snoozeNotification( 712 MockListener.getInstance().getKeyForTag(tag), snoozeTime); 713 state = SNOOZED; 714 } else if (state == SNOOZED) { 715 List<String> result = getSnoozed(); 716 if (result.size() >= 1 717 && result.contains(tag)) { 718 // cancel and repost 719 sendNotifications(); 720 state = READY_TO_CHECK_FOR_SNOOZE; 721 } else { 722 logFail(); 723 status = FAIL; 724 } 725 } else { 726 List<String> result = getSnoozed(); 727 if (result.size() >= 1 728 && result.contains(tag)) { 729 status = PASS; 730 } else { 731 logFail(); 732 status = FAIL; 733 } 734 } 735 } 736 737 private List<String> getSnoozed() { 738 List<String> result = new ArrayList<>(); 739 StatusBarNotification[] snoozed = MockListener.getInstance().getSnoozedNotifications(); 740 for (StatusBarNotification sbn : snoozed) { 741 result.add(sbn.getTag()); 742 } 743 return result; 744 } 745 746 @Override 747 void tearDown() { 748 mNm.cancelAll(); 749 deleteChannel(); 750 MockListener.getInstance().resetData(); 751 } 752 } 753 754 private class GetSnoozedNotificationTest extends InteractiveTestCase { 755 final static int READY_TO_SNOOZE = 0; 756 final static int SNOOZED = 1; 757 final static int READY_TO_CHECK_FOR_GET_SNOOZE = 2; 758 int state = -1; 759 long snoozeTime = 30000; 760 761 @Override 762 View inflate(ViewGroup parent) { 763 return createAutoItem(parent, R.string.nls_get_snoozed); 764 } 765 766 @Override 767 void setUp() { 768 createChannel(); 769 sendNotifications(); 770 status = READY; 771 state = READY_TO_SNOOZE; 772 } 773 774 @Override 775 void test() { 776 status = RETEST; 777 if (state == READY_TO_SNOOZE) { 778 MockListener.getInstance().snoozeNotification( 779 MockListener.getInstance().getKeyForTag(mTag1), snoozeTime); 780 MockListener.getInstance().snoozeNotification( 781 MockListener.getInstance().getKeyForTag(mTag2), snoozeTime); 782 state = SNOOZED; 783 } else if (state == SNOOZED) { 784 List<JSONObject> result = 785 new ArrayList<>(MockListener.getInstance().mRemovedReason.values()); 786 if (result.size() == 0) { 787 status = FAIL; 788 return; 789 } 790 boolean pass = false; 791 for (JSONObject payload : result) { 792 try { 793 pass |= (checkEquals(mTag1, 794 payload.getString(JSON_TAG), 795 "data dismissal test: notification tag (%s, %s)") 796 && checkEquals(MockListener.REASON_SNOOZED, 797 payload.getInt(JSON_REASON), 798 "data dismissal test: reason (%d, %d)")); 799 if (pass) { 800 break; 801 } 802 } catch (JSONException e) { 803 e.printStackTrace(); 804 } 805 } 806 if (!pass) { 807 logFail(); 808 status = FAIL; 809 } else { 810 state = READY_TO_CHECK_FOR_GET_SNOOZE; 811 } 812 } else { 813 List<String> result = new ArrayList<>(); 814 StatusBarNotification[] snoozed = 815 MockListener.getInstance().getSnoozedNotifications(); 816 for (StatusBarNotification sbn : snoozed) { 817 result.add(sbn.getTag()); 818 } 819 if (result.size() >= 2 820 && result.contains(mTag1) 821 && result.contains(mTag2)) { 822 status = PASS; 823 } else { 824 logFail(); 825 status = FAIL; 826 } 827 } 828 } 829 830 @Override 831 void tearDown() { 832 mNm.cancelAll(); 833 deleteChannel(); 834 MockListener.getInstance().resetData(); 835 delay(); 836 } 837 } 838 839 /** Tests that the extras {@link Bundle} in a MessagingStyle#Message is preserved. */ 840 private class MessageBundleTest extends InteractiveTestCase { 841 private final String extrasKey1 = "extras_key_1"; 842 private final CharSequence extrasValue1 = "extras_value_1"; 843 private final String extrasKey2 = "extras_key_2"; 844 private final CharSequence extrasValue2 = "extras_value_2"; 845 846 @Override 847 View inflate(ViewGroup parent) { 848 return createAutoItem(parent, R.string.msg_extras_preserved); 849 } 850 851 @Override 852 void setUp() { 853 createChannel(); 854 sendMessagingNotification(); 855 status = READY; 856 } 857 858 @Override 859 void tearDown() { 860 mNm.cancelAll(); 861 deleteChannel(); 862 delay(); 863 } 864 865 private void sendMessagingNotification() { 866 mTag1 = UUID.randomUUID().toString(); 867 mNm.cancelAll(); 868 mWhen1 = System.currentTimeMillis() + 1; 869 mIcon1 = R.drawable.ic_stat_alice; 870 mId1 = NOTIFICATION_ID + 1; 871 872 Notification.MessagingStyle.Message msg1 = 873 new Notification.MessagingStyle.Message("text1", 0 /* timestamp */, "sender1"); 874 msg1.getExtras().putCharSequence(extrasKey1, extrasValue1); 875 876 Notification.MessagingStyle.Message msg2 = 877 new Notification.MessagingStyle.Message("text2", 1 /* timestamp */, "sender2"); 878 msg2.getExtras().putCharSequence(extrasKey2, extrasValue2); 879 880 Notification.MessagingStyle style = new Notification.MessagingStyle("display_name"); 881 style.addMessage(msg1); 882 style.addMessage(msg2); 883 884 Notification n1 = new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 885 .setContentTitle("ClearTest 1") 886 .setContentText(mTag1.toString()) 887 .setPriority(Notification.PRIORITY_LOW) 888 .setSmallIcon(mIcon1) 889 .setWhen(mWhen1) 890 .setDeleteIntent(makeIntent(1, mTag1)) 891 .setOnlyAlertOnce(true) 892 .setStyle(style) 893 .build(); 894 mNm.notify(mTag1, mId1, n1); 895 mFlag1 = Notification.FLAG_ONLY_ALERT_ONCE; 896 } 897 898 // Returns true on success. 899 private boolean verifyMessage( 900 NotificationCompat.MessagingStyle.Message message, 901 String extrasKey, 902 CharSequence extrasValue) { 903 return message.getExtras() != null 904 && message.getExtras().getCharSequence(extrasKey) != null 905 && message.getExtras().getCharSequence(extrasKey).equals(extrasValue); 906 } 907 908 @Override 909 void test() { 910 List<Notification> result = 911 new ArrayList<>(MockListener.getInstance().mPostedNotifications); 912 if (result.size() != 1 || result.get(0) == null) { 913 logFail(); 914 status = FAIL; 915 next(); 916 return; 917 } 918 // Can only read in MessaginStyle using the compat class. 919 NotificationCompat.MessagingStyle readStyle = 920 NotificationCompat.MessagingStyle 921 .extractMessagingStyleFromNotification( 922 result.get(0)); 923 if (readStyle == null || readStyle.getMessages().size() != 2) { 924 status = FAIL; 925 logFail(); 926 next(); 927 return; 928 } 929 930 if (!verifyMessage(readStyle.getMessages().get(0), extrasKey1, 931 extrasValue1) 932 || !verifyMessage( 933 readStyle.getMessages().get(1), extrasKey2, extrasValue2)) { 934 status = FAIL; 935 logFail(); 936 next(); 937 return; 938 } 939 940 status = PASS; 941 } 942 } 943 } 944