1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 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.content.ContextCompat; 33 import android.support.wear.widget.WearableLinearLayoutManager; 34 import android.support.wear.widget.WearableRecyclerView; 35 import android.support.wearable.activity.WearableActivity; 36 import android.util.Log; 37 import android.view.View; 38 import android.widget.FrameLayout; 39 40 import com.example.android.wearable.wear.common.mock.MockDatabase; 41 import com.example.android.wearable.wear.common.util.NotificationUtil; 42 import com.example.android.wearable.wear.wearnotifications.handlers.BigPictureSocialIntentService; 43 import com.example.android.wearable.wear.wearnotifications.handlers.BigPictureSocialMainActivity; 44 import com.example.android.wearable.wear.wearnotifications.handlers.BigTextIntentService; 45 import com.example.android.wearable.wear.wearnotifications.handlers.BigTextMainActivity; 46 import com.example.android.wearable.wear.wearnotifications.handlers.InboxMainActivity; 47 import com.example.android.wearable.wear.wearnotifications.handlers.MessagingIntentService; 48 import com.example.android.wearable.wear.wearnotifications.handlers.MessagingMainActivity; 49 50 51 /** 52 * Demonstrates best practice for {@link NotificationCompat} Notifications created by local 53 * standalone Android Wear apps. All {@link NotificationCompat} examples use 54 * {@link NotificationCompat.Style}. 55 */ 56 public class StandaloneMainActivity extends WearableActivity { 57 58 private static final String TAG = "StandaloneMainActivity"; 59 60 public static final int NOTIFICATION_ID = 888; 61 62 /* 63 * Used to represent each major {@link NotificationCompat.Style} in the 64 * {@link WearableRecyclerView}. These constants are also used in a switch statement when one 65 * of the items is selected to create the appropriate {@link Notification}. 66 */ 67 private static final String BIG_TEXT_STYLE = "BIG_TEXT_STYLE"; 68 private static final String BIG_PICTURE_STYLE = "BIG_PICTURE_STYLE"; 69 private static final String INBOX_STYLE = "INBOX_STYLE"; 70 private static final String MESSAGING_STYLE = "MESSAGING_STYLE"; 71 72 /* 73 Collection of major {@link NotificationCompat.Style} to create {@link CustomRecyclerAdapter} 74 for {@link WearableRecyclerView}. 75 */ 76 private static final String[] NOTIFICATION_STYLES = 77 {BIG_TEXT_STYLE, BIG_PICTURE_STYLE, INBOX_STYLE, MESSAGING_STYLE}; 78 79 private NotificationManagerCompat mNotificationManagerCompat; 80 81 // Needed for {@link SnackBar} to alert users when {@link Notification} are disabled for app. 82 private FrameLayout mMainFrameLayout; 83 private WearableRecyclerView mWearableRecyclerView; 84 private CustomRecyclerAdapter mCustomRecyclerAdapter; 85 86 @Override 87 protected void onCreate(Bundle savedInstanceState) { 88 super.onCreate(savedInstanceState); 89 Log.d(TAG, "onCreate()"); 90 91 setContentView(R.layout.activity_main); 92 setAmbientEnabled(); 93 94 mNotificationManagerCompat = NotificationManagerCompat.from(getApplicationContext()); 95 96 mMainFrameLayout = (FrameLayout) findViewById(R.id.mainFrameLayout); 97 mWearableRecyclerView = (WearableRecyclerView) findViewById(R.id.recycler_view); 98 99 // Aligns the first and last items on the list vertically centered on the screen. 100 mWearableRecyclerView.setEdgeItemsCenteringEnabled(true); 101 102 // Customizes scrolling so items farther away form center are smaller. 103 ScalingScrollLayoutCallback scalingScrollLayoutCallback = 104 new ScalingScrollLayoutCallback(); 105 mWearableRecyclerView.setLayoutManager( 106 new WearableLinearLayoutManager(this, scalingScrollLayoutCallback)); 107 108 // Improves performance because we know changes in content do not change the layout size of 109 // the RecyclerView. 110 mWearableRecyclerView.setHasFixedSize(true); 111 112 // Specifies an adapter (see also next example). 113 mCustomRecyclerAdapter = new CustomRecyclerAdapter( 114 NOTIFICATION_STYLES, 115 // Controller passes selected data from the Adapter out to this Activity to trigger 116 // updates in the UI/Notifications. 117 new Controller(this)); 118 119 mWearableRecyclerView.setAdapter(mCustomRecyclerAdapter); 120 } 121 122 // Called by WearableRecyclerView when an item is selected (check onCreate() for 123 // initialization). 124 public void itemSelected(String data) { 125 126 Log.d(TAG, "itemSelected()"); 127 128 boolean areNotificationsEnabled = mNotificationManagerCompat.areNotificationsEnabled(); 129 130 // If notifications are disabled, allow user to enable. 131 if (!areNotificationsEnabled) { 132 // Because the user took an action to create a notification, we create a prompt to let 133 // the user re-enable notifications for this application again. 134 Snackbar snackbar = Snackbar 135 .make( 136 mMainFrameLayout, 137 "", // Not enough space for both text and action text. 138 Snackbar.LENGTH_LONG) 139 .setAction("Enable Notifications", new View.OnClickListener() { 140 @Override 141 public void onClick(View view) { 142 // Links to this app's notification settings. 143 openNotificationSettingsForApp(); 144 } 145 }); 146 snackbar.show(); 147 return; 148 } 149 150 String notificationStyle = data; 151 152 switch (notificationStyle) { 153 case BIG_TEXT_STYLE: 154 generateBigTextStyleNotification(); 155 break; 156 157 case BIG_PICTURE_STYLE: 158 generateBigPictureStyleNotification(); 159 break; 160 161 case INBOX_STYLE: 162 generateInboxStyleNotification(); 163 break; 164 165 case MESSAGING_STYLE: 166 generateMessagingStyleNotification(); 167 break; 168 169 default: 170 // continue below. 171 } 172 } 173 174 /* 175 * Generates a BIG_TEXT_STYLE Notification that supports both Wear 1.+ and Wear 2.0. 176 * 177 * IMPORTANT NOTE: 178 * This method includes extra code to replicate Notification Styles behavior from Wear 1.+ and 179 * phones on Wear 2.0, i.e., the notification expands on click. To see the specific code in the 180 * method, search for "REPLICATE_NOTIFICATION_STYLE_CODE". 181 * 182 * Notification Styles behave slightly different on Wear 2.0 when they are launched by a 183 * native/local Wear app, i.e., they will NOT expand when the user taps them but will instead 184 * take the user directly into the local app for the richest experience. In contrast, a bridged 185 * Notification launched from the phone will expand with the style details (whether there is a 186 * local app or not). 187 * 188 * If you want to see the new behavior, please review the generateBigPictureStyleNotification() 189 * and generateMessagingStyleNotification() methods. 190 */ 191 private void generateBigTextStyleNotification() { 192 193 Log.d(TAG, "generateBigTextStyleNotification()"); 194 195 // Main steps for building a BIG_TEXT_STYLE notification: 196 // 0. Get your data 197 // 1. Create/Retrieve Notification Channel for O and beyond devices (26+) 198 // 2. Build the BIG_TEXT_STYLE 199 // 3. Set up main Intent for notification 200 // 4. Create additional Actions for the Notification 201 // 5. Build and issue the notification 202 203 // 0. Get your data (everything unique per Notification). 204 MockDatabase.BigTextStyleReminderAppData bigTextStyleReminderAppData = 205 MockDatabase.getBigTextStyleData(); 206 207 // 1. Create/Retrieve Notification Channel for O and beyond devices (26+). 208 String notificationChannelId = 209 NotificationUtil.createNotificationChannel(this, bigTextStyleReminderAppData); 210 211 // 2. Build the BIG_TEXT_STYLE 212 BigTextStyle bigTextStyle = new NotificationCompat.BigTextStyle() 213 // Overrides ContentText in the big form of the template. 214 .bigText(bigTextStyleReminderAppData.getBigText()) 215 // Overrides ContentTitle in the big form of the template. 216 .setBigContentTitle(bigTextStyleReminderAppData.getBigContentTitle()) 217 // Summary line after the detail section in the big form of the template 218 // Note: To improve readability, don't overload the user with info. If Summary Text 219 // doesn't add critical information, you should skip it. 220 .setSummaryText(bigTextStyleReminderAppData.getSummaryText()); 221 222 223 // 3. Set up main Intent for notification. 224 Intent mainIntent = new Intent(this, BigTextMainActivity.class); 225 226 PendingIntent mainPendingIntent = 227 PendingIntent.getActivity( 228 this, 229 0, 230 mainIntent, 231 PendingIntent.FLAG_UPDATE_CURRENT 232 ); 233 234 235 // 4. Create additional Actions (Intents) for the Notification. 236 237 // In our case, we create two additional actions: a Snooze action and a Dismiss action. 238 239 // Snooze Action. 240 Intent snoozeIntent = new Intent(this, BigTextIntentService.class); 241 snoozeIntent.setAction(BigTextIntentService.ACTION_SNOOZE); 242 243 PendingIntent snoozePendingIntent = PendingIntent.getService(this, 0, snoozeIntent, 0); 244 NotificationCompat.Action snoozeAction = 245 new NotificationCompat.Action.Builder( 246 R.drawable.ic_alarm_white_48dp, 247 "Snooze", 248 snoozePendingIntent) 249 .build(); 250 251 // Dismiss Action. 252 Intent dismissIntent = new Intent(this, BigTextIntentService.class); 253 dismissIntent.setAction(BigTextIntentService.ACTION_DISMISS); 254 255 PendingIntent dismissPendingIntent = PendingIntent.getService(this, 0, dismissIntent, 0); 256 NotificationCompat.Action dismissAction = 257 new NotificationCompat.Action.Builder( 258 R.drawable.ic_cancel_white_48dp, 259 "Dismiss", 260 dismissPendingIntent) 261 .build(); 262 263 264 // 5. Build and issue the notification. 265 266 // Because we want this to be a new notification (not updating a previous notification), we 267 // create a new Builder. Later, we use the same global builder to get back the notification 268 // we built here for the snooze action, that is, canceling the notification and relaunching 269 // it several seconds later. 270 271 // Notification Channel Id is ignored for Android pre O (26). 272 NotificationCompat.Builder notificationCompatBuilder = 273 new NotificationCompat.Builder( 274 getApplicationContext(), notificationChannelId); 275 276 GlobalNotificationBuilder.setNotificationCompatBuilderInstance(notificationCompatBuilder); 277 278 notificationCompatBuilder 279 // BIG_TEXT_STYLE sets title and content. 280 .setStyle(bigTextStyle) 281 .setContentTitle(bigTextStyleReminderAppData.getContentTitle()) 282 .setContentText(bigTextStyleReminderAppData.getContentText()) 283 .setSmallIcon(R.drawable.ic_launcher) 284 .setLargeIcon(BitmapFactory.decodeResource( 285 getResources(), 286 R.drawable.ic_alarm_white_48dp)) 287 .setDefaults(NotificationCompat.DEFAULT_ALL) 288 // Set primary color (important for Wear 2.0 Notifications). 289 .setColor(ContextCompat.getColor(getApplicationContext(), R.color.colorPrimary)) 290 291 .setCategory(Notification.CATEGORY_REMINDER) 292 293 // Sets priority for 25 and below. For 26 and above, 'priority' is deprecated for 294 // 'importance' which is set in the NotificationChannel. The integers representing 295 // 'priority' are different from 'importance', so make sure you don't mix them. 296 .setPriority(bigTextStyleReminderAppData.getPriority()) 297 298 // Sets lock-screen visibility for 25 and below. For 26 and above, lock screen 299 // visibility is set in the NotificationChannel. 300 .setVisibility(bigTextStyleReminderAppData.getChannelLockscreenVisibility()) 301 302 // Adds additional actions specified above. 303 .addAction(snoozeAction) 304 .addAction(dismissAction); 305 306 /* REPLICATE_NOTIFICATION_STYLE_CODE: 307 * You can replicate Notification Style functionality on Wear 2.0 (24+) by not setting the 308 * main content intent, that is, skipping the call setContentIntent(). However, you need to 309 * still allow the user to open the native Wear app from the Notification itself, so you 310 * add an action to launch the app. 311 */ 312 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 313 314 // Enables launching app in Wear 2.0 while keeping the old Notification Style behavior. 315 NotificationCompat.Action mainAction = new NotificationCompat.Action.Builder( 316 R.drawable.ic_launcher, 317 "Open", 318 mainPendingIntent) 319 .build(); 320 321 notificationCompatBuilder.addAction(mainAction); 322 323 } else { 324 // Wear 1.+ still functions the same, so we set the main content intent. 325 notificationCompatBuilder.setContentIntent(mainPendingIntent); 326 } 327 328 329 Notification notification = notificationCompatBuilder.build(); 330 331 mNotificationManagerCompat.notify(NOTIFICATION_ID, notification); 332 333 // Close app to demonstrate notification in steam. 334 finish(); 335 } 336 337 /* 338 * Generates a BIG_PICTURE_STYLE Notification that supports both Wear 1.+ and Wear 2.0. 339 * 340 * This example Notification is a social post. It allows updating the notification with 341 * comments/responses via RemoteInput and the BigPictureSocialIntentService on 24+ (N+) and 342 * Android Wear devices. 343 * 344 * IMPORTANT NOTE: 345 * Notification Styles behave slightly different on Wear 2.0 when they are launched by a 346 * native/local Wear app, i.e., they will NOT expand when the user taps them but will instead 347 * take the user directly into the local app for the richest experience. In contrast, a bridged 348 * Notification launched from the phone will expand with the style details (whether there is a 349 * local app or not). 350 * 351 * If you want to enable an action on your Notification without launching the app, you can do so 352 * with the setHintDisplayActionInline() feature (shown below), but this only allows one action. 353 * 354 * If you wish to replicate the original experience of a bridged notification, please review the 355 * generateBigTextStyleNotification() method above to see how. 356 */ 357 private void generateBigPictureStyleNotification() { 358 359 Log.d(TAG, "generateBigPictureStyleNotification()"); 360 361 // Main steps for building a BIG_PICTURE_STYLE notification: 362 // 0. Get your data 363 // 1. Create/Retrieve Notification Channel for O and beyond devices (26+) 364 // 2. Build the BIG_PICTURE_STYLE 365 // 3. Set up main Intent for notification 366 // 4. Set up RemoteInput, so users can input (keyboard and voice) from notification 367 // 5. Build and issue the notification 368 369 // 0. Get your data (everything unique per Notification). 370 MockDatabase.BigPictureStyleSocialAppData bigPictureStyleSocialAppData = 371 MockDatabase.getBigPictureStyleData(); 372 373 // 1. Build the BIG_PICTURE_STYLE. 374 BigPictureStyle bigPictureStyle = new NotificationCompat.BigPictureStyle() 375 // Provides the bitmap for the BigPicture notification. 376 .bigPicture( 377 BitmapFactory.decodeResource( 378 getResources(), 379 bigPictureStyleSocialAppData.getBigImage())) 380 // Overrides ContentTitle in the big form of the template. 381 .setBigContentTitle(bigPictureStyleSocialAppData.getBigContentTitle()) 382 // Summary line after the detail section in the big form of the template. 383 .setSummaryText(bigPictureStyleSocialAppData.getSummaryText()); 384 385 // 2. Create/Retrieve Notification Channel for O and beyond devices (26+). 386 String notificationChannelId = 387 NotificationUtil.createNotificationChannel(this, bigPictureStyleSocialAppData); 388 389 // 3. Set up main Intent for notification. 390 Intent mainIntent = new Intent(this, BigPictureSocialMainActivity.class); 391 392 PendingIntent mainPendingIntent = 393 PendingIntent.getActivity( 394 this, 395 0, 396 mainIntent, 397 PendingIntent.FLAG_UPDATE_CURRENT 398 ); 399 400 // 4. Set up a RemoteInput Action, so users can input (keyboard, drawing, voice) directly 401 // from the notification without entering the app. 402 403 // Create the RemoteInput. 404 String replyLabel = getString(R.string.reply_label); 405 RemoteInput remoteInput = 406 new RemoteInput.Builder(BigPictureSocialIntentService.EXTRA_COMMENT) 407 .setLabel(replyLabel) 408 // List of quick response choices for any wearables paired with the phone. 409 .setChoices(bigPictureStyleSocialAppData.getPossiblePostResponses()) 410 .build(); 411 412 // Create PendingIntent for service that handles input. 413 Intent replyIntent = new Intent(this, BigPictureSocialIntentService.class); 414 replyIntent.setAction(BigPictureSocialIntentService.ACTION_COMMENT); 415 PendingIntent replyActionPendingIntent = PendingIntent.getService(this, 0, replyIntent, 0); 416 417 // Enable action to appear inline on Wear 2.0 (24+). This means it will appear over the 418 // lower portion of the Notification for easy action (only possible for one action). 419 final NotificationCompat.Action.WearableExtender inlineActionForWear2 = 420 new NotificationCompat.Action.WearableExtender() 421 .setHintDisplayActionInline(true) 422 .setHintLaunchesActivity(false); 423 424 NotificationCompat.Action replyAction = 425 new NotificationCompat.Action.Builder( 426 R.drawable.ic_reply_white_18dp, 427 replyLabel, 428 replyActionPendingIntent) 429 .addRemoteInput(remoteInput) 430 // Add WearableExtender to enable inline actions. 431 .extend(inlineActionForWear2) 432 .build(); 433 434 // 5. Build and issue the notification 435 436 // Because we want this to be a new notification (not updating a previous notification), we 437 // create a new Builder. Later, we use the same global builder to get back the notification 438 // we built here for a comment on the post. 439 440 // Notification Channel Id is ignored for Android pre O (26). 441 NotificationCompat.Builder notificationCompatBuilder = 442 new NotificationCompat.Builder( 443 getApplicationContext(), notificationChannelId); 444 445 GlobalNotificationBuilder.setNotificationCompatBuilderInstance(notificationCompatBuilder); 446 447 notificationCompatBuilder 448 // BIG_PICTURE_STYLE sets title and content. 449 .setStyle(bigPictureStyle) 450 .setContentTitle(bigPictureStyleSocialAppData.getContentTitle()) 451 .setContentText(bigPictureStyleSocialAppData.getContentText()) 452 .setSmallIcon(R.drawable.ic_launcher) 453 .setLargeIcon(BitmapFactory.decodeResource( 454 getResources(), 455 R.drawable.ic_person_black_48dp)) 456 .setContentIntent(mainPendingIntent) 457 .setDefaults(NotificationCompat.DEFAULT_ALL) 458 // Set primary color (important for Wear 2.0 Notifications). 459 .setColor(ContextCompat.getColor(getApplicationContext(), R.color.colorPrimary)) 460 461 .setSubText(Integer.toString(1)) 462 .addAction(replyAction) 463 .setCategory(Notification.CATEGORY_SOCIAL) 464 465 // Sets priority for 25 and below. For 26 and above, 'priority' is deprecated for 466 // 'importance' which is set in the NotificationChannel. The integers representing 467 // 'priority' are different from 'importance', so make sure you don't mix them. 468 .setPriority(bigPictureStyleSocialAppData.getPriority()) 469 470 // Sets lock-screen visibility for 25 and below. For 26 and above, lock screen 471 // visibility is set in the NotificationChannel. 472 .setVisibility(bigPictureStyleSocialAppData.getChannelLockscreenVisibility()) 473 // Notifies system that the main launch intent is an Activity. 474 .extend(new NotificationCompat.WearableExtender() 475 .setHintContentIntentLaunchesActivity(true)); 476 477 // If the phone is in "Do not disturb mode, the user will still be notified if 478 // the sender(s) is starred as a favorite. 479 for (String name : bigPictureStyleSocialAppData.getParticipants()) { 480 notificationCompatBuilder.addPerson(name); 481 } 482 483 Notification notification = notificationCompatBuilder.build(); 484 mNotificationManagerCompat.notify(NOTIFICATION_ID, notification); 485 486 // Close app to demonstrate notification in steam. 487 finish(); 488 } 489 490 /* 491 * Generates a INBOX_STYLE Notification that supports both Wear 1.+ and Wear 2.0. 492 */ 493 private void generateInboxStyleNotification() { 494 495 Log.d(TAG, "generateInboxStyleNotification()"); 496 497 498 // Main steps for building a INBOX_STYLE notification: 499 // 0. Get your data 500 // 1. Create/Retrieve Notification Channel for O and beyond devices (26+) 501 // 2. Build the INBOX_STYLE 502 // 3. Set up main Intent for notification 503 // 4. Build and issue the notification 504 505 // 0. Get your data (everything unique per Notification). 506 MockDatabase.InboxStyleEmailAppData inboxStyleEmailAppData = 507 MockDatabase.getInboxStyleData(); 508 509 // 1. Create/Retrieve Notification Channel for O and beyond devices (26+). 510 String notificationChannelId = 511 NotificationUtil.createNotificationChannel(this, inboxStyleEmailAppData); 512 513 // 2. Build the INBOX_STYLE 514 InboxStyle inboxStyle = new NotificationCompat.InboxStyle() 515 // This title is slightly different than regular title, since I know INBOX_STYLE is 516 // available. 517 .setBigContentTitle(inboxStyleEmailAppData.getBigContentTitle()) 518 .setSummaryText(inboxStyleEmailAppData.getSummaryText()); 519 520 // Add each summary line of the new emails, you can add up to 5. 521 for (String summary : inboxStyleEmailAppData.getIndividualEmailSummary()) { 522 inboxStyle.addLine(summary); 523 } 524 525 // 3. Set up main Intent for notification. 526 Intent mainIntent = new Intent(this, InboxMainActivity.class); 527 528 PendingIntent mainPendingIntent = 529 PendingIntent.getActivity( 530 this, 531 0, 532 mainIntent, 533 PendingIntent.FLAG_UPDATE_CURRENT 534 ); 535 536 // 4. Build and issue the notification. 537 538 // Because we want this to be a new notification (not updating a previous notification), we 539 // create a new Builder. However, we don't need to update this notification later, so we 540 // will not need to set a global builder for access to the notification later. 541 542 // Notification Channel Id is ignored for Android pre O (26). 543 NotificationCompat.Builder notificationCompatBuilder = 544 new NotificationCompat.Builder( 545 getApplicationContext(), notificationChannelId); 546 547 GlobalNotificationBuilder.setNotificationCompatBuilderInstance(notificationCompatBuilder); 548 549 notificationCompatBuilder 550 // INBOX_STYLE sets title and content. 551 .setStyle(inboxStyle) 552 .setContentTitle(inboxStyleEmailAppData.getContentTitle()) 553 .setContentText(inboxStyleEmailAppData.getContentText()) 554 .setSmallIcon(R.drawable.ic_launcher) 555 .setLargeIcon(BitmapFactory.decodeResource( 556 getResources(), 557 R.drawable.ic_person_black_48dp)) 558 .setContentIntent(mainPendingIntent) 559 .setDefaults(NotificationCompat.DEFAULT_ALL) 560 // Set primary color (important for Wear 2.0 Notifications). 561 .setColor(ContextCompat.getColor(getApplicationContext(), R.color.colorPrimary)) 562 563 // Sets large number at the right-hand side of the notification for Wear 1.+. 564 .setSubText(Integer.toString(inboxStyleEmailAppData.getNumberOfNewEmails())) 565 566 .setCategory(Notification.CATEGORY_EMAIL) 567 568 // Sets priority for 25 and below. For 26 and above, 'priority' is deprecated for 569 // 'importance' which is set in the NotificationChannel. The integers representing 570 // 'priority' are different from 'importance', so make sure you don't mix them. 571 .setPriority(inboxStyleEmailAppData.getPriority()) 572 573 // Sets lock-screen visibility for 25 and below. For 26 and above, lock screen 574 // visibility is set in the NotificationChannel. 575 .setVisibility(inboxStyleEmailAppData.getChannelLockscreenVisibility()) 576 // Notifies system that the main launch intent is an Activity. 577 .extend(new NotificationCompat.WearableExtender() 578 .setHintContentIntentLaunchesActivity(true)); 579 580 // If the phone is in "Do not disturb mode, the user will still be notified if 581 // the sender(s) is starred as a favorite. 582 for (String name : inboxStyleEmailAppData.getParticipants()) { 583 notificationCompatBuilder.addPerson(name); 584 } 585 586 Notification notification = notificationCompatBuilder.build(); 587 mNotificationManagerCompat.notify(NOTIFICATION_ID, notification); 588 589 // Close app to demonstrate notification in steam. 590 finish(); 591 } 592 593 /* 594 * Generates a MESSAGING_STYLE Notification that supports both Wear 1.+ and Wear 2.0. For 595 * devices on API level 24 (Wear 2.0) and after, displays MESSAGING_STYLE. Otherwise, displays 596 * a basic BIG_TEXT_STYLE. 597 * 598 * IMPORTANT NOTE: 599 * Notification Styles behave slightly different on Wear 2.0 when they are launched by a 600 * native/local Wear app, i.e., they will NOT expand when the user taps them but will instead 601 * take the user directly into the local app for the richest experience. In contrast, a bridged 602 * Notification launched from the phone will expand with the style details (whether there is a 603 * local app or not). 604 * 605 * If you want to enable an action on your Notification without launching the app, you can do so 606 * with the setHintDisplayActionInline() feature (shown below), but this only allows one action. 607 * 608 * If you wish to replicate the original experience of a bridged notification, please review the 609 * generateBigTextStyleNotification() method above to see how. 610 */ 611 private void generateMessagingStyleNotification() { 612 613 Log.d(TAG, "generateMessagingStyleNotification()"); 614 615 // Main steps for building a MESSAGING_STYLE notification: 616 // 0. Get your data 617 // 1. Create/Retrieve Notification Channel for O and beyond devices (26+) 618 // 2. Build the MESSAGING_STYLE 619 // 3. Set up main Intent for notification 620 // 4. Set up RemoteInput (users can input directly from notification) 621 // 5. Build and issue the notification 622 623 // 0. Get your data (everything unique per Notification). 624 MockDatabase.MessagingStyleCommsAppData messagingStyleCommsAppData = 625 MockDatabase.getMessagingStyleData(); 626 627 // 1. Create/Retrieve Notification Channel for O and beyond devices (26+). 628 String notificationChannelId = 629 NotificationUtil.createNotificationChannel(this, messagingStyleCommsAppData); 630 631 // 2. Build the Notification.Style (MESSAGING_STYLE). 632 String contentTitle = messagingStyleCommsAppData.getContentTitle(); 633 634 MessagingStyle messagingStyle = 635 new NotificationCompat.MessagingStyle(messagingStyleCommsAppData.getReplayName()) 636 // You could set a different title to appear when the messaging style 637 // is supported on device (24+) if you wish. In our case, we use the same 638 // title. 639 .setConversationTitle(contentTitle); 640 641 // Adds all Messages. 642 // Note: Messages include the text, timestamp, and sender. 643 for (MessagingStyle.Message message : messagingStyleCommsAppData.getMessages()) { 644 messagingStyle.addMessage(message); 645 } 646 647 // 3. Set up main Intent for notification. 648 Intent notifyIntent = new Intent(this, MessagingMainActivity.class); 649 650 PendingIntent mainPendingIntent = 651 PendingIntent.getActivity( 652 this, 653 0, 654 notifyIntent, 655 PendingIntent.FLAG_UPDATE_CURRENT 656 ); 657 658 659 // 4. Set up a RemoteInput Action, so users can input (keyboard, drawing, voice) directly 660 // from the notification without entering the app. 661 662 // Create the RemoteInput specifying this key. 663 String replyLabel = getString(R.string.reply_label); 664 RemoteInput remoteInput = new RemoteInput.Builder(MessagingIntentService.EXTRA_REPLY) 665 .setLabel(replyLabel) 666 .build(); 667 668 // Create PendingIntent for service that handles input. 669 Intent replyIntent = new Intent(this, MessagingIntentService.class); 670 replyIntent.setAction(MessagingIntentService.ACTION_REPLY); 671 PendingIntent replyActionPendingIntent = PendingIntent.getService(this, 0, replyIntent, 0); 672 673 // Enable action to appear inline on Wear 2.0 (24+). This means it will appear over the 674 // lower portion of the Notification for easy action (only possible for one action). 675 final NotificationCompat.Action.WearableExtender inlineActionForWear2 = 676 new NotificationCompat.Action.WearableExtender() 677 .setHintDisplayActionInline(true) 678 .setHintLaunchesActivity(false); 679 680 NotificationCompat.Action replyAction = 681 new NotificationCompat.Action.Builder( 682 R.drawable.ic_reply_white_18dp, 683 replyLabel, 684 replyActionPendingIntent) 685 .addRemoteInput(remoteInput) 686 // Allows system to generate replies by context of conversation. 687 .setAllowGeneratedReplies(true) 688 // Add WearableExtender to enable inline actions. 689 .extend(inlineActionForWear2) 690 .build(); 691 692 693 // 5. Build and issue the notification. 694 695 // Because we want this to be a new notification (not updating current notification), we 696 // create a new Builder. Later, we update this same notification, so we need to save this 697 // Builder globally (as outlined earlier). 698 699 // Notification Channel Id is ignored for Android pre O (26). 700 NotificationCompat.Builder notificationCompatBuilder = 701 new NotificationCompat.Builder( 702 getApplicationContext(), notificationChannelId); 703 704 GlobalNotificationBuilder.setNotificationCompatBuilderInstance(notificationCompatBuilder); 705 706 notificationCompatBuilder 707 // MESSAGING_STYLE sets title and content for Wear 1.+ and Wear 2.0 devices. 708 .setStyle(messagingStyle) 709 .setContentTitle(contentTitle) 710 .setContentText(messagingStyleCommsAppData.getContentText()) 711 .setSmallIcon(R.drawable.ic_launcher) 712 .setLargeIcon(BitmapFactory.decodeResource( 713 getResources(), 714 R.drawable.ic_person_black_48dp)) 715 .setContentIntent(mainPendingIntent) 716 .setDefaults(NotificationCompat.DEFAULT_ALL) 717 // Set primary color (important for Wear 2.0 Notifications). 718 .setColor(ContextCompat.getColor(getApplicationContext(), R.color.colorPrimary)) 719 720 // Number of new notifications for API <24 (Wear 1.+) devices. 721 .setSubText(Integer.toString(messagingStyleCommsAppData.getNumberOfNewMessages())) 722 723 .addAction(replyAction) 724 .setCategory(Notification.CATEGORY_MESSAGE) 725 726 // Sets priority for 25 and below. For 26 and above, 'priority' is deprecated for 727 // 'importance' which is set in the NotificationChannel. The integers representing 728 // 'priority' are different from 'importance', so make sure you don't mix them. 729 .setPriority(messagingStyleCommsAppData.getPriority()) 730 731 // Sets lock-screen visibility for 25 and below. For 26 and above, lock screen 732 // visibility is set in the NotificationChannel. 733 .setVisibility(messagingStyleCommsAppData.getChannelLockscreenVisibility()); 734 735 // If the phone is in "Do not disturb mode, the user will still be notified if 736 // the sender(s) is starred as a favorite. 737 for (String name : messagingStyleCommsAppData.getParticipants()) { 738 notificationCompatBuilder.addPerson(name); 739 } 740 741 Notification notification = notificationCompatBuilder.build(); 742 mNotificationManagerCompat.notify(NOTIFICATION_ID, notification); 743 744 // Close app to demonstrate notification in steam. 745 finish(); 746 } 747 748 /** 749 * Helper method for the SnackBar action, i.e., if the user has this application's notifications 750 * disabled, this opens up the dialog to turn them back on after the user requests a 751 * Notification launch. 752 * 753 * IMPORTANT NOTE: You should not do this action unless the user takes an action to see your 754 * Notifications like this sample demonstrates. Spamming users to re-enable your notifications 755 * is a bad idea. 756 */ 757 private void openNotificationSettingsForApp() { 758 // Links to this app's notification settings 759 Intent intent = new Intent(); 760 intent.setAction("android.settings.APP_NOTIFICATION_SETTINGS"); 761 intent.putExtra("app_package", getPackageName()); 762 intent.putExtra("app_uid", getApplicationInfo().uid); 763 startActivity(intent); 764 } 765 }