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 }