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