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