1 /* 2 Copyright 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 package com.example.android.wearable.wear.wearnotifications; 17 18 import android.app.Notification; 19 import android.app.PendingIntent; 20 import android.content.Intent; 21 import android.graphics.BitmapFactory; 22 import android.os.Build; 23 import android.os.Bundle; 24 import android.support.design.widget.Snackbar; 25 import android.support.v4.app.NotificationCompat; 26 import android.support.v4.app.NotificationCompat.BigPictureStyle; 27 import android.support.v4.app.NotificationCompat.BigTextStyle; 28 import android.support.v4.app.NotificationCompat.InboxStyle; 29 import android.support.v4.app.NotificationCompat.MessagingStyle; 30 import android.support.v4.app.NotificationManagerCompat; 31 import android.support.v4.app.RemoteInput; 32 import android.support.v4.app.TaskStackBuilder; 33 import android.support.v4.content.ContextCompat; 34 import android.support.v7.app.AppCompatActivity; 35 import android.util.Log; 36 import android.view.View; 37 import android.widget.AdapterView; 38 import android.widget.ArrayAdapter; 39 import android.widget.RelativeLayout; 40 import android.widget.Spinner; 41 import android.widget.TextView; 42 43 import com.example.android.wearable.wear.common.mock.MockDatabase; 44 import com.example.android.wearable.wear.common.util.NotificationUtil; 45 import com.example.android.wearable.wear.wearnotifications.handlers.BigPictureSocialIntentService; 46 import com.example.android.wearable.wear.wearnotifications.handlers.BigPictureSocialMainActivity; 47 import com.example.android.wearable.wear.wearnotifications.handlers.BigTextIntentService; 48 import com.example.android.wearable.wear.wearnotifications.handlers.BigTextMainActivity; 49 import com.example.android.wearable.wear.wearnotifications.handlers.InboxMainActivity; 50 import com.example.android.wearable.wear.wearnotifications.handlers.MessagingIntentService; 51 import com.example.android.wearable.wear.wearnotifications.handlers.MessagingMainActivity; 52 53 /** 54 * The Activity demonstrates several popular Notification.Style examples along with their best 55 * practices (include proper Wear support when you don't have a dedicated Wear app). 56 */ 57 public class MainActivity extends AppCompatActivity implements AdapterView.OnItemSelectedListener { 58 59 public static final String TAG = "MainActivity"; 60 61 public static final int NOTIFICATION_ID = 888; 62 63 // Used for Notification Style array and switch statement for Spinner selection. 64 private static final String BIG_TEXT_STYLE = "BIG_TEXT_STYLE"; 65 private static final String BIG_PICTURE_STYLE = "BIG_PICTURE_STYLE"; 66 private static final String INBOX_STYLE = "INBOX_STYLE"; 67 private static final String MESSAGING_STYLE = "MESSAGING_STYLE"; 68 69 // Collection of notification styles to back ArrayAdapter for Spinner. 70 private static final String[] NOTIFICATION_STYLES = 71 {BIG_TEXT_STYLE, BIG_PICTURE_STYLE, INBOX_STYLE, MESSAGING_STYLE}; 72 73 private static final String[] NOTIFICATION_STYLES_DESCRIPTION = 74 { 75 "Demos reminder type app using BIG_TEXT_STYLE", 76 "Demos social type app using BIG_PICTURE_STYLE + inline notification response", 77 "Demos email type app using INBOX_STYLE", 78 "Demos messaging app using MESSAGING_STYLE + inline notification responses" 79 }; 80 81 private NotificationManagerCompat mNotificationManagerCompat; 82 83 private int mSelectedNotification = 0; 84 85 // RelativeLayout required for SnackBars to alert users when Notifications are disabled for app. 86 private RelativeLayout mMainRelativeLayout; 87 private Spinner mSpinner; 88 private TextView mNotificationDetailsTextView; 89 90 @Override 91 protected void onCreate(Bundle savedInstanceState) { 92 93 super.onCreate(savedInstanceState); 94 setContentView(R.layout.activity_main); 95 96 mMainRelativeLayout = (RelativeLayout) findViewById(R.id.mainRelativeLayout); 97 mNotificationDetailsTextView = (TextView) findViewById(R.id.notificationDetails); 98 mSpinner = (Spinner) findViewById(R.id.spinner); 99 100 mNotificationManagerCompat = NotificationManagerCompat.from(getApplicationContext()); 101 102 // Create an ArrayAdapter using the string array and a default spinner layout. 103 ArrayAdapter<CharSequence> adapter = 104 new ArrayAdapter( 105 this, 106 android.R.layout.simple_spinner_item, 107 NOTIFICATION_STYLES); 108 // Specify the layout to use when the list of choices appears. 109 adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 110 // Apply the adapter to the spinner. 111 mSpinner.setAdapter(adapter); 112 mSpinner.setOnItemSelectedListener(this); 113 } 114 115 @Override 116 public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { 117 Log.d(TAG, "onItemSelected(): position: " + position + " id: " + id); 118 119 mSelectedNotification = position; 120 121 mNotificationDetailsTextView.setText( 122 NOTIFICATION_STYLES_DESCRIPTION[mSelectedNotification]); 123 } 124 @Override 125 public void onNothingSelected(AdapterView<?> parent) { 126 // Required 127 } 128 129 public void onClick(View view) { 130 131 Log.d(TAG, "onClick()"); 132 133 boolean areNotificationsEnabled = mNotificationManagerCompat.areNotificationsEnabled(); 134 135 if (!areNotificationsEnabled) { 136 // Because the user took an action to create a notification, we create a prompt to let 137 // the user re-enable notifications for this application again. 138 Snackbar snackbar = Snackbar 139 .make( 140 mMainRelativeLayout, 141 "You need to enable notifications for this app", 142 Snackbar.LENGTH_LONG) 143 .setAction("ENABLE", new View.OnClickListener() { 144 @Override 145 public void onClick(View view) { 146 // Links to this app's notification settings 147 openNotificationSettingsForApp(); 148 } 149 }); 150 snackbar.show(); 151 return; 152 } 153 154 String notificationStyle = NOTIFICATION_STYLES[mSelectedNotification]; 155 156 switch (notificationStyle) { 157 case BIG_TEXT_STYLE: 158 generateBigTextStyleNotification(); 159 break; 160 161 case BIG_PICTURE_STYLE: 162 generateBigPictureStyleNotification(); 163 break; 164 165 case INBOX_STYLE: 166 generateInboxStyleNotification(); 167 break; 168 169 case MESSAGING_STYLE: 170 generateMessagingStyleNotification(); 171 break; 172 173 default: 174 // continue below 175 } 176 } 177 178 /* 179 * Generates a BIG_TEXT_STYLE Notification that supports both phone/tablet and wear. For devices 180 * on API level 16 (4.1.x - Jelly Bean) and after, displays BIG_TEXT_STYLE. Otherwise, displays 181 * a basic notification. 182 */ 183 private void generateBigTextStyleNotification() { 184 185 Log.d(TAG, "generateBigTextStyleNotification()"); 186 187 // Main steps for building a BIG_TEXT_STYLE notification: 188 // 0. Get your data 189 // 1. Create/Retrieve Notification Channel for O and beyond devices (26+) 190 // 2. Build the BIG_TEXT_STYLE 191 // 3. Set up main Intent for notification 192 // 4. Create additional Actions for the Notification 193 // 5. Build and issue the notification 194 195 // 0. Get your data (everything unique per Notification). 196 MockDatabase.BigTextStyleReminderAppData bigTextStyleReminderAppData = 197 MockDatabase.getBigTextStyleData(); 198 199 // 1. Create/Retrieve Notification Channel for O and beyond devices (26+). 200 String notificationChannelId = 201 NotificationUtil.createNotificationChannel(this, bigTextStyleReminderAppData); 202 203 204 // 2. Build the BIG_TEXT_STYLE. 205 BigTextStyle bigTextStyle = new NotificationCompat.BigTextStyle() 206 // Overrides ContentText in the big form of the template. 207 .bigText(bigTextStyleReminderAppData.getBigText()) 208 // Overrides ContentTitle in the big form of the template. 209 .setBigContentTitle(bigTextStyleReminderAppData.getBigContentTitle()) 210 // Summary line after the detail section in the big form of the template. 211 // Note: To improve readability, don't overload the user with info. If Summary Text 212 // doesn't add critical information, you should skip it. 213 .setSummaryText(bigTextStyleReminderAppData.getSummaryText()); 214 215 216 // 3. Set up main Intent for notification. 217 Intent notifyIntent = new Intent(this, BigTextMainActivity.class); 218 219 // When creating your Intent, you need to take into account the back state, i.e., what 220 // happens after your Activity launches and the user presses the back button. 221 222 // There are two options: 223 // 1. Regular activity - You're starting an Activity that's part of the application's 224 // normal workflow. 225 226 // 2. Special activity - The user only sees this Activity if it's started from a 227 // notification. In a sense, the Activity extends the notification by providing 228 // information that would be hard to display in the notification itself. 229 230 // For the BIG_TEXT_STYLE notification, we will consider the activity launched by the main 231 // Intent as a special activity, so we will follow option 2. 232 233 // For an example of option 1, check either the MESSAGING_STYLE or BIG_PICTURE_STYLE 234 // examples. 235 236 // For more information, check out our dev article: 237 // https://developer.android.com/training/notify-user/navigation.html 238 239 // Sets the Activity to start in a new, empty task 240 notifyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); 241 242 PendingIntent notifyPendingIntent = 243 PendingIntent.getActivity( 244 this, 245 0, 246 notifyIntent, 247 PendingIntent.FLAG_UPDATE_CURRENT 248 ); 249 250 251 // 4. Create additional Actions (Intents) for the Notification. 252 253 // In our case, we create two additional actions: a Snooze action and a Dismiss action. 254 // Snooze Action. 255 Intent snoozeIntent = new Intent(this, BigTextIntentService.class); 256 snoozeIntent.setAction(BigTextIntentService.ACTION_SNOOZE); 257 258 PendingIntent snoozePendingIntent = PendingIntent.getService(this, 0, snoozeIntent, 0); 259 NotificationCompat.Action snoozeAction = 260 new NotificationCompat.Action.Builder( 261 R.drawable.ic_alarm_white_48dp, 262 "Snooze", 263 snoozePendingIntent) 264 .build(); 265 266 267 // Dismiss Action. 268 Intent dismissIntent = new Intent(this, BigTextIntentService.class); 269 dismissIntent.setAction(BigTextIntentService.ACTION_DISMISS); 270 271 PendingIntent dismissPendingIntent = PendingIntent.getService(this, 0, dismissIntent, 0); 272 NotificationCompat.Action dismissAction = 273 new NotificationCompat.Action.Builder( 274 R.drawable.ic_cancel_white_48dp, 275 "Dismiss", 276 dismissPendingIntent) 277 .build(); 278 279 280 // 5. Build and issue the notification. 281 282 // Because we want this to be a new notification (not updating a previous notification), we 283 // create a new Builder. Later, we use the same global builder to get back the notification 284 // we built here for the snooze action, that is, canceling the notification and relaunching 285 // it several seconds later. 286 287 // Notification Channel Id is ignored for Android pre O (26). 288 NotificationCompat.Builder notificationCompatBuilder = 289 new NotificationCompat.Builder( 290 getApplicationContext(), notificationChannelId); 291 292 GlobalNotificationBuilder.setNotificationCompatBuilderInstance(notificationCompatBuilder); 293 294 Notification notification = notificationCompatBuilder 295 // BIG_TEXT_STYLE sets title and content for API 16 (4.1 and after). 296 .setStyle(bigTextStyle) 297 // Title for API <16 (4.0 and below) devices. 298 .setContentTitle(bigTextStyleReminderAppData.getContentTitle()) 299 // Content for API <24 (7.0 and below) devices. 300 .setContentText(bigTextStyleReminderAppData.getContentText()) 301 .setSmallIcon(R.drawable.ic_launcher) 302 .setLargeIcon(BitmapFactory.decodeResource( 303 getResources(), 304 R.drawable.ic_alarm_white_48dp)) 305 .setContentIntent(notifyPendingIntent) 306 .setDefaults(NotificationCompat.DEFAULT_ALL) 307 // Set primary color (important for Wear 2.0 Notifications). 308 .setColor(ContextCompat.getColor(getApplicationContext(), R.color.colorPrimary)) 309 310 // SIDE NOTE: Auto-bundling is enabled for 4 or more notifications on API 24+ (N+) 311 // devices and all Wear devices. If you have more than one notification and 312 // you prefer a different summary notification, set a group key and create a 313 // summary notification via 314 // .setGroupSummary(true) 315 // .setGroup(GROUP_KEY_YOUR_NAME_HERE) 316 317 .setCategory(Notification.CATEGORY_REMINDER) 318 319 // Sets priority for 25 and below. For 26 and above, 'priority' is deprecated for 320 // 'importance' which is set in the NotificationChannel. The integers representing 321 // 'priority' are different from 'importance', so make sure you don't mix them. 322 .setPriority(bigTextStyleReminderAppData.getPriority()) 323 324 // Sets lock-screen visibility for 25 and below. For 26 and above, lock screen 325 // visibility is set in the NotificationChannel. 326 .setVisibility(bigTextStyleReminderAppData.getChannelLockscreenVisibility()) 327 328 // Adds additional actions specified above. 329 .addAction(snoozeAction) 330 .addAction(dismissAction) 331 332 .build(); 333 334 mNotificationManagerCompat.notify(NOTIFICATION_ID, notification); 335 } 336 337 /* 338 * Generates a BIG_PICTURE_STYLE Notification that supports both phone/tablet and wear. For 339 * devices on API level 16 (4.1.x - Jelly Bean) and after, displays BIG_PICTURE_STYLE. 340 * Otherwise, displays a basic notification. 341 * 342 * This example Notification is a social post. It allows updating the notification with 343 * comments/responses via RemoteInput and the BigPictureSocialIntentService on 24+ (N+) and 344 * Wear devices. 345 */ 346 private void generateBigPictureStyleNotification() { 347 348 Log.d(TAG, "generateBigPictureStyleNotification()"); 349 350 // Main steps for building a BIG_PICTURE_STYLE notification: 351 // 0. Get your data 352 // 1. Create/Retrieve Notification Channel for O and beyond devices (26+) 353 // 2. Build the BIG_PICTURE_STYLE 354 // 3. Set up main Intent for notification 355 // 4. Set up RemoteInput, so users can input (keyboard and voice) from notification 356 // 5. Build and issue the notification 357 358 // 0. Get your data (everything unique per Notification). 359 MockDatabase.BigPictureStyleSocialAppData bigPictureStyleSocialAppData = 360 MockDatabase.getBigPictureStyleData(); 361 362 // 1. Create/Retrieve Notification Channel for O and beyond devices (26+). 363 String notificationChannelId = 364 NotificationUtil.createNotificationChannel(this, bigPictureStyleSocialAppData); 365 366 // 2. Build the BIG_PICTURE_STYLE. 367 BigPictureStyle bigPictureStyle = new NotificationCompat.BigPictureStyle() 368 // Provides the bitmap for the BigPicture notification. 369 .bigPicture( 370 BitmapFactory.decodeResource( 371 getResources(), 372 bigPictureStyleSocialAppData.getBigImage())) 373 // Overrides ContentTitle in the big form of the template. 374 .setBigContentTitle(bigPictureStyleSocialAppData.getBigContentTitle()) 375 // Summary line after the detail section in the big form of the template. 376 .setSummaryText(bigPictureStyleSocialAppData.getSummaryText()); 377 378 // 3. Set up main Intent for notification. 379 Intent mainIntent = new Intent(this, BigPictureSocialMainActivity.class); 380 381 // When creating your Intent, you need to take into account the back state, i.e., what 382 // happens after your Activity launches and the user presses the back button. 383 384 // There are two options: 385 // 1. Regular activity - You're starting an Activity that's part of the application's 386 // normal workflow. 387 388 // 2. Special activity - The user only sees this Activity if it's started from a 389 // notification. In a sense, the Activity extends the notification by providing 390 // information that would be hard to display in the notification itself. 391 392 // Even though this sample's MainActivity doesn't link to the Activity this Notification 393 // launches directly, i.e., it isn't part of the normal workflow, a social app generally 394 // always links to individual posts as part of the app flow, so we will follow option 1. 395 396 // For an example of option 2, check out the BIG_TEXT_STYLE example. 397 398 // For more information, check out our dev article: 399 // https://developer.android.com/training/notify-user/navigation.html 400 401 TaskStackBuilder stackBuilder = TaskStackBuilder.create(this); 402 // Adds the back stack. 403 stackBuilder.addParentStack(BigPictureSocialMainActivity.class); 404 // Adds the Intent to the top of the stack. 405 stackBuilder.addNextIntent(mainIntent); 406 // Gets a PendingIntent containing the entire back stack. 407 PendingIntent mainPendingIntent = 408 PendingIntent.getActivity( 409 this, 410 0, 411 mainIntent, 412 PendingIntent.FLAG_UPDATE_CURRENT 413 ); 414 415 // 4. Set up RemoteInput, so users can input (keyboard and voice) from notification. 416 417 // Note: For API <24 (M and below) we need to use an Activity, so the lock-screen presents 418 // the auth challenge. For API 24+ (N and above), we use a Service (could be a 419 // BroadcastReceiver), so the user can input from Notification or lock-screen (they have 420 // choice to allow) without leaving the notification. 421 422 // Create the RemoteInput. 423 String replyLabel = getString(R.string.reply_label); 424 RemoteInput remoteInput = 425 new RemoteInput.Builder(BigPictureSocialIntentService.EXTRA_COMMENT) 426 .setLabel(replyLabel) 427 // List of quick response choices for any wearables paired with the phone 428 .setChoices(bigPictureStyleSocialAppData.getPossiblePostResponses()) 429 .build(); 430 431 // Pending intent = 432 // API <24 (M and below): activity so the lock-screen presents the auth challenge 433 // API 24+ (N and above): this should be a Service or BroadcastReceiver 434 PendingIntent replyActionPendingIntent; 435 436 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 437 Intent intent = new Intent(this, BigPictureSocialIntentService.class); 438 intent.setAction(BigPictureSocialIntentService.ACTION_COMMENT); 439 replyActionPendingIntent = PendingIntent.getService(this, 0, intent, 0); 440 441 } else { 442 replyActionPendingIntent = mainPendingIntent; 443 } 444 445 NotificationCompat.Action replyAction = 446 new NotificationCompat.Action.Builder( 447 R.drawable.ic_reply_white_18dp, 448 replyLabel, 449 replyActionPendingIntent) 450 .addRemoteInput(remoteInput) 451 .build(); 452 453 // 5. Build and issue the notification. 454 455 // Because we want this to be a new notification (not updating a previous notification), we 456 // create a new Builder. Later, we use the same global builder to get back the notification 457 // we built here for a comment on the post. 458 459 NotificationCompat.Builder notificationCompatBuilder = 460 new NotificationCompat.Builder(getApplicationContext(), notificationChannelId); 461 462 GlobalNotificationBuilder.setNotificationCompatBuilderInstance(notificationCompatBuilder); 463 464 notificationCompatBuilder 465 // BIG_PICTURE_STYLE sets title and content for API 16 (4.1 and after). 466 .setStyle(bigPictureStyle) 467 // Title for API <16 (4.0 and below) devices. 468 .setContentTitle(bigPictureStyleSocialAppData.getContentTitle()) 469 // Content for API <24 (7.0 and below) devices. 470 .setContentText(bigPictureStyleSocialAppData.getContentText()) 471 .setSmallIcon(R.drawable.ic_launcher) 472 .setLargeIcon(BitmapFactory.decodeResource( 473 getResources(), 474 R.drawable.ic_person_black_48dp)) 475 .setContentIntent(mainPendingIntent) 476 .setDefaults(NotificationCompat.DEFAULT_ALL) 477 // Set primary color (important for Wear 2.0 Notifications). 478 .setColor(ContextCompat.getColor(getApplicationContext(), R.color.colorPrimary)) 479 480 // SIDE NOTE: Auto-bundling is enabled for 4 or more notifications on API 24+ (N+) 481 // devices and all Wear devices. If you have more than one notification and 482 // you prefer a different summary notification, set a group key and create a 483 // summary notification via 484 // .setGroupSummary(true) 485 // .setGroup(GROUP_KEY_YOUR_NAME_HERE) 486 487 .setSubText(Integer.toString(1)) 488 .addAction(replyAction) 489 .setCategory(Notification.CATEGORY_SOCIAL) 490 491 // Sets priority for 25 and below. For 26 and above, 'priority' is deprecated for 492 // 'importance' which is set in the NotificationChannel. The integers representing 493 // 'priority' are different from 'importance', so make sure you don't mix them. 494 .setPriority(bigPictureStyleSocialAppData.getPriority()) 495 496 // Sets lock-screen visibility for 25 and below. For 26 and above, lock screen 497 // visibility is set in the NotificationChannel. 498 .setVisibility(bigPictureStyleSocialAppData.getChannelLockscreenVisibility()); 499 500 // If the phone is in "Do not disturb mode, the user will still be notified if 501 // the sender(s) is starred as a favorite. 502 for (String name : bigPictureStyleSocialAppData.getParticipants()) { 503 notificationCompatBuilder.addPerson(name); 504 } 505 506 Notification notification = notificationCompatBuilder.build(); 507 508 mNotificationManagerCompat.notify(NOTIFICATION_ID, notification); 509 } 510 511 /* 512 * Generates a INBOX_STYLE Notification that supports both phone/tablet and wear. For devices 513 * on API level 16 (4.1.x - Jelly Bean) and after, displays INBOX_STYLE. Otherwise, displays a 514 * basic notification. 515 */ 516 private void generateInboxStyleNotification() { 517 518 Log.d(TAG, "generateInboxStyleNotification()"); 519 520 521 // Main steps for building a INBOX_STYLE notification: 522 // 0. Get your data 523 // 1. Create/Retrieve Notification Channel for O and beyond devices (26+) 524 // 2. Build the INBOX_STYLE 525 // 3. Set up main Intent for notification 526 // 4. Build and issue the notification 527 528 // 0. Get your data (everything unique per Notification). 529 MockDatabase.InboxStyleEmailAppData inboxStyleEmailAppData = 530 MockDatabase.getInboxStyleData(); 531 532 // 1. Create/Retrieve Notification Channel for O and beyond devices (26+). 533 String notificationChannelId = 534 NotificationUtil.createNotificationChannel(this, inboxStyleEmailAppData); 535 536 // 2. Build the INBOX_STYLE. 537 InboxStyle inboxStyle = new NotificationCompat.InboxStyle() 538 // This title is slightly different than regular title, since I know INBOX_STYLE is 539 // available. 540 .setBigContentTitle(inboxStyleEmailAppData.getBigContentTitle()) 541 .setSummaryText(inboxStyleEmailAppData.getSummaryText()); 542 543 // Add each summary line of the new emails, you can add up to 5. 544 for (String summary : inboxStyleEmailAppData.getIndividualEmailSummary()) { 545 inboxStyle.addLine(summary); 546 } 547 548 // 3. Set up main Intent for notification. 549 Intent mainIntent = new Intent(this, InboxMainActivity.class); 550 551 // When creating your Intent, you need to take into account the back state, i.e., what 552 // happens after your Activity launches and the user presses the back button. 553 554 // There are two options: 555 // 1. Regular activity - You're starting an Activity that's part of the application's 556 // normal workflow. 557 558 // 2. Special activity - The user only sees this Activity if it's started from a 559 // notification. In a sense, the Activity extends the notification by providing 560 // information that would be hard to display in the notification itself. 561 562 // Even though this sample's MainActivity doesn't link to the Activity this Notification 563 // launches directly, i.e., it isn't part of the normal workflow, a eamil app generally 564 // always links to individual emails as part of the app flow, so we will follow option 1. 565 566 // For an example of option 2, check out the BIG_TEXT_STYLE example. 567 568 // For more information, check out our dev article: 569 // https://developer.android.com/training/notify-user/navigation.html 570 571 TaskStackBuilder stackBuilder = TaskStackBuilder.create(this); 572 // Adds the back stack. 573 stackBuilder.addParentStack(InboxMainActivity.class); 574 // Adds the Intent to the top of the stack. 575 stackBuilder.addNextIntent(mainIntent); 576 // Gets a PendingIntent containing the entire back stack. 577 PendingIntent mainPendingIntent = 578 PendingIntent.getActivity( 579 this, 580 0, 581 mainIntent, 582 PendingIntent.FLAG_UPDATE_CURRENT 583 ); 584 585 // 4. Build and issue the notification. 586 587 // Because we want this to be a new notification (not updating a previous notification), we 588 // create a new Builder. However, we don't need to update this notification later, so we 589 // will not need to set a global builder for access to the notification later. 590 591 NotificationCompat.Builder notificationCompatBuilder = 592 new NotificationCompat.Builder(getApplicationContext(), notificationChannelId); 593 594 GlobalNotificationBuilder.setNotificationCompatBuilderInstance(notificationCompatBuilder); 595 596 notificationCompatBuilder 597 598 // INBOX_STYLE sets title and content for API 16+ (4.1 and after) when the 599 // notification is expanded. 600 .setStyle(inboxStyle) 601 602 // Title for API <16 (4.0 and below) devices and API 16+ (4.1 and after) when the 603 // notification is collapsed. 604 .setContentTitle(inboxStyleEmailAppData.getContentTitle()) 605 606 // Content for API <24 (7.0 and below) devices and API 16+ (4.1 and after) when the 607 // notification is collapsed. 608 .setContentText(inboxStyleEmailAppData.getContentText()) 609 .setSmallIcon(R.drawable.ic_launcher) 610 .setLargeIcon(BitmapFactory.decodeResource( 611 getResources(), 612 R.drawable.ic_person_black_48dp)) 613 .setContentIntent(mainPendingIntent) 614 .setDefaults(NotificationCompat.DEFAULT_ALL) 615 // Set primary color (important for Wear 2.0 Notifications). 616 .setColor(ContextCompat.getColor(getApplicationContext(), R.color.colorPrimary)) 617 618 // SIDE NOTE: Auto-bundling is enabled for 4 or more notifications on API 24+ (N+) 619 // devices and all Wear devices. If you have more than one notification and 620 // you prefer a different summary notification, set a group key and create a 621 // summary notification via 622 // .setGroupSummary(true) 623 // .setGroup(GROUP_KEY_YOUR_NAME_HERE) 624 625 // Sets large number at the right-hand side of the notification for API <24 devices. 626 .setSubText(Integer.toString(inboxStyleEmailAppData.getNumberOfNewEmails())) 627 628 .setCategory(Notification.CATEGORY_EMAIL) 629 630 // Sets priority for 25 and below. For 26 and above, 'priority' is deprecated for 631 // 'importance' which is set in the NotificationChannel. The integers representing 632 // 'priority' are different from 'importance', so make sure you don't mix them. 633 .setPriority(inboxStyleEmailAppData.getPriority()) 634 635 // Sets lock-screen visibility for 25 and below. For 26 and above, lock screen 636 // visibility is set in the NotificationChannel. 637 .setVisibility(inboxStyleEmailAppData.getChannelLockscreenVisibility()); 638 639 // If the phone is in "Do not disturb mode, the user will still be notified if 640 // the sender(s) is starred as a favorite. 641 for (String name : inboxStyleEmailAppData.getParticipants()) { 642 notificationCompatBuilder.addPerson(name); 643 } 644 645 Notification notification = notificationCompatBuilder.build(); 646 647 mNotificationManagerCompat.notify(NOTIFICATION_ID, notification); 648 } 649 650 /* 651 * Generates a MESSAGING_STYLE Notification that supports both phone/tablet and wear. For 652 * devices on API level 24 (7.0 - Nougat) and after, displays MESSAGING_STYLE. Otherwise, 653 * displays a basic BIG_TEXT_STYLE. 654 */ 655 private void generateMessagingStyleNotification() { 656 657 Log.d(TAG, "generateMessagingStyleNotification()"); 658 659 // Main steps for building a MESSAGING_STYLE notification: 660 // 0. Get your data 661 // 1. Create/Retrieve Notification Channel for O and beyond devices (26+) 662 // 2. Build the MESSAGING_STYLE 663 // 3. Set up main Intent for notification 664 // 4. Set up RemoteInput (users can input directly from notification) 665 // 5. Build and issue the notification 666 667 // 0. Get your data (everything unique per Notification) 668 MockDatabase.MessagingStyleCommsAppData messagingStyleCommsAppData = 669 MockDatabase.getMessagingStyleData(); 670 671 // 1. Create/Retrieve Notification Channel for O and beyond devices (26+). 672 String notificationChannelId = 673 NotificationUtil.createNotificationChannel(this, messagingStyleCommsAppData); 674 675 // 2. Build the Notification.Style (MESSAGING_STYLE). 676 String contentTitle = messagingStyleCommsAppData.getContentTitle(); 677 678 MessagingStyle messagingStyle = 679 new NotificationCompat.MessagingStyle(messagingStyleCommsAppData.getReplayName()) 680 // This could be the user-created name of the group or, if it doesn't have 681 // a specific name, a list of the participants in the conversation. Do not 682 // set a conversation title for one-on-one chats, since platforms use the 683 // existence of this field as a hint that the conversation is a group. 684 // 685 // In our case, we use the same title. 686 .setConversationTitle(contentTitle); 687 688 // Adds all Messages. 689 // Note: Messages include the text, timestamp, and sender. 690 for (MessagingStyle.Message message : messagingStyleCommsAppData.getMessages()) { 691 messagingStyle.addMessage(message); 692 } 693 694 // 3. Set up main Intent for notification. 695 Intent notifyIntent = new Intent(this, MessagingMainActivity.class); 696 697 // When creating your Intent, you need to take into account the back state, i.e., what 698 // happens after your Activity launches and the user presses the back button. 699 700 // There are two options: 701 // 1. Regular activity - You're starting an Activity that's part of the application's 702 // normal workflow. 703 704 // 2. Special activity - The user only sees this Activity if it's started from a 705 // notification. In a sense, the Activity extends the notification by providing 706 // information that would be hard to display in the notification itself. 707 708 // Even though this sample's MainActivity doesn't link to the Activity this Notification 709 // launches directly, i.e., it isn't part of the normal workflow, a chat app generally 710 // always links to individual conversations as part of the app flow, so we will follow 711 // option 1. 712 713 // For an example of option 2, check out the BIG_TEXT_STYLE example. 714 715 // For more information, check out our dev article: 716 // https://developer.android.com/training/notify-user/navigation.html 717 718 TaskStackBuilder stackBuilder = TaskStackBuilder.create(this); 719 // Adds the back stack 720 stackBuilder.addParentStack(MessagingMainActivity.class); 721 // Adds the Intent to the top of the stack 722 stackBuilder.addNextIntent(notifyIntent); 723 // Gets a PendingIntent containing the entire back stack 724 PendingIntent mainPendingIntent = 725 PendingIntent.getActivity( 726 this, 727 0, 728 notifyIntent, 729 PendingIntent.FLAG_UPDATE_CURRENT 730 ); 731 732 733 // 4. Set up RemoteInput, so users can input (keyboard and voice) from notification. 734 735 // Note: For API <24 (M and below) we need to use an Activity, so the lock-screen present 736 // the auth challenge. For API 24+ (N and above), we use a Service (could be a 737 // BroadcastReceiver), so the user can input from Notification or lock-screen (they have 738 // choice to allow) without leaving the notification. 739 740 // Create the RemoteInput specifying this key. 741 String replyLabel = getString(R.string.reply_label); 742 RemoteInput remoteInput = new RemoteInput.Builder(MessagingIntentService.EXTRA_REPLY) 743 .setLabel(replyLabel) 744 .build(); 745 746 // Pending intent = 747 // API <24 (M and below): activity so the lock-screen presents the auth challenge. 748 // API 24+ (N and above): this should be a Service or BroadcastReceiver. 749 PendingIntent replyActionPendingIntent; 750 751 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 752 Intent intent = new Intent(this, MessagingIntentService.class); 753 intent.setAction(MessagingIntentService.ACTION_REPLY); 754 replyActionPendingIntent = PendingIntent.getService(this, 0, intent, 0); 755 756 } else { 757 replyActionPendingIntent = mainPendingIntent; 758 } 759 760 NotificationCompat.Action replyAction = 761 new NotificationCompat.Action.Builder( 762 R.drawable.ic_reply_white_18dp, 763 replyLabel, 764 replyActionPendingIntent) 765 .addRemoteInput(remoteInput) 766 // Allows system to generate replies by context of conversation. 767 .setAllowGeneratedReplies(true) 768 .build(); 769 770 771 // 5. Build and issue the notification. 772 773 // Because we want this to be a new notification (not updating current notification), we 774 // create a new Builder. Later, we update this same notification, so we need to save this 775 // Builder globally (as outlined earlier). 776 777 NotificationCompat.Builder notificationCompatBuilder = 778 new NotificationCompat.Builder(getApplicationContext(), notificationChannelId); 779 780 GlobalNotificationBuilder.setNotificationCompatBuilderInstance(notificationCompatBuilder); 781 782 notificationCompatBuilder 783 // MESSAGING_STYLE sets title and content for API 16 and above devices. 784 .setStyle(messagingStyle) 785 // Title for API < 16 devices. 786 .setContentTitle(contentTitle) 787 // Content for API < 16 devices. 788 .setContentText(messagingStyleCommsAppData.getContentText()) 789 .setSmallIcon(R.drawable.ic_launcher) 790 .setLargeIcon(BitmapFactory.decodeResource( 791 getResources(), 792 R.drawable.ic_person_black_48dp)) 793 .setContentIntent(mainPendingIntent) 794 .setDefaults(NotificationCompat.DEFAULT_ALL) 795 // Set primary color (important for Wear 2.0 Notifications). 796 .setColor(ContextCompat.getColor(getApplicationContext(), R.color.colorPrimary)) 797 798 // SIDE NOTE: Auto-bundling is enabled for 4 or more notifications on API 24+ (N+) 799 // devices and all Wear devices. If you have more than one notification and 800 // you prefer a different summary notification, set a group key and create a 801 // summary notification via 802 // .setGroupSummary(true) 803 // .setGroup(GROUP_KEY_YOUR_NAME_HERE) 804 805 // Number of new notifications for API <24 (M and below) devices. 806 .setSubText(Integer.toString(messagingStyleCommsAppData.getNumberOfNewMessages())) 807 808 .addAction(replyAction) 809 .setCategory(Notification.CATEGORY_MESSAGE) 810 811 // Sets priority for 25 and below. For 26 and above, 'priority' is deprecated for 812 // 'importance' which is set in the NotificationChannel. The integers representing 813 // 'priority' are different from 'importance', so make sure you don't mix them. 814 .setPriority(messagingStyleCommsAppData.getPriority()) 815 816 // Sets lock-screen visibility for 25 and below. For 26 and above, lock screen 817 // visibility is set in the NotificationChannel. 818 .setVisibility(messagingStyleCommsAppData.getChannelLockscreenVisibility()); 819 820 // If the phone is in "Do not disturb mode, the user will still be notified if 821 // the sender(s) is starred as a favorite. 822 for (String name : messagingStyleCommsAppData.getParticipants()) { 823 notificationCompatBuilder.addPerson(name); 824 } 825 826 Notification notification = notificationCompatBuilder.build(); 827 mNotificationManagerCompat.notify(NOTIFICATION_ID, notification); 828 } 829 830 /** 831 * Helper method for the SnackBar action, i.e., if the user has this application's notifications 832 * disabled, this opens up the dialog to turn them back on after the user requests a 833 * Notification launch. 834 * 835 * IMPORTANT NOTE: You should not do this action unless the user takes an action to see your 836 * Notifications like this sample demonstrates. Spamming users to re-enable your notifications 837 * is a bad idea. 838 */ 839 private void openNotificationSettingsForApp() { 840 // Links to this app's notification settings. 841 Intent intent = new Intent(); 842 intent.setAction("android.settings.APP_NOTIFICATION_SETTINGS"); 843 intent.putExtra("app_package", getPackageName()); 844 intent.putExtra("app_uid", getApplicationInfo().uid); 845 startActivity(intent); 846 } 847 }