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