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