Friday 20 November 2015

Notifications Style in Android

Introduction:

Android Jelly Bean (API level 16) Google has improved a lot of features and introduced new features. One of them is the Notification. Now they have made the notification more versatile by introducing media rich notification. Google has come up with three special style of notification which are mentioned below. Even developer can write his own customized notification style using Remote view. The old Notification class constructor has been deprecated and a brand new and enhanced version of Notification has been introduced.

Notification Type:

  • Basic Notification – Shows simple and short notification with icon.
  • Big Picture Notification – Shows visual content such as bitmap.
  • Big Text Notification – Shows multiline Textview object.
  • Inbox Style Notification – Shows any kind of list, e.g messages, headline etc. 
  • Custom Notification – Shows the custom notification with custom layout.

             Old syntax requires us to create an object of notification but now Android uses builder patter to create the notification object. Notification.Builder class has been introduced to make this task easier. This class returns the builder object which is configurable according to your requirements.
The helper classes have been introduced like Notification.BigPictureStyle, Notification.BigTextStyle, and Notification.InboxStyle. These classes are re-builder classes which take object created by Notification.Builder class and  modify the behavior like so. For the backword compatibility in API Level below 16  there are the classes NotificationCompat.BigPictureStyle, NotificationCompat.BigTextStyle, and NotificationCompat.InboxStyle.
NotificationCompat.Builder is part of the Android Support Library and has a minimum API level of 4 while Notification.Builder is a part of Android and has a minimum API level of 11. By now, there is little benefit in supporting API levels below 16, so compatibility with previous API levels is not a good enough reason to use NotificationCompat.

Notification Priority:
Not all notifications are equally important. A notification’s priority specifies its importance and can take any of these values: Notification.PRIORITY_MIN, Notification.PRIORITY_LOW, Notification.
PRIORITY_DEFAULT, Notification.PRIORITY_HIGH, or Notification.PRIORITY_MAX.


Notification Category:

  1. Lock Screen Notifications : To view notifications prior to Android 5.0, users had to unlock their devices and open the notification drawer.
    In particular, the “when a device is locked” setting can take the following values:
    1. show all notification content: displays all notifications on the lock screen.
    2. hide sensitive notification content: this setting is only available on devices that have a secure lock screen (that is, one that requires a pattern, PIN, or password to unlock). If selected, the lock screen modifies the way certain notifications are displayed, as explained below.
    3. don’t show notifications at all: does not show any notifications on the lock screen.
A notification’s visibility parameter controls how they are displayed on the lock screen when “hide sensitive notification content” is selected:
      Notification.VISIBILITY_PUBLIC (default): shows the entire content of the notification.
      Notification.VISIBILITY_SECRET: completely hides a notification
      Notification.VISIBILITY_PRIVATE: shows a notification but replaces its content with generic or redacted text.

Note that notifications with a visibility of secret would still be displayed on the lock screen if “when a device is locked” is set to “show all notification content”. That is, while apps can specify the sensitivity of notifications, users have the final say as to how they’re displayed on the lock screen.

Implementing Public, Private, and Secret Notifications 
To implement a private or secret notification, simply replace setVisibility's parameter with Notification.VISIBILITY_PRIVATE or Notification.VISIBILITY_SECRET,respectively. The sample project implements notifications with these visibilities in the onPrivateNotificationClick and onSecretNotificationClick methods.
Implementation
1.Create a notification with the desired visibility.
                 Notification notification = new NotificationCompat.Builder(this)
                                         .setContentTitle("Public Notification")
                                         .setContentText("Public content here")
                                        .setSmallIcon(R.drawable.ic_stat_notify)
                                         .setCategory(Notification.CATEGORY_STATUS)
                                         .setVisibility(Notification.VISIBILITY_PUBLIC) 
                                         .build();
2.Issue the notification.
                NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
         notificationManager.notify(NOTIFICATION_ID, notification);
Implementing Private Notifications with a Public Version
When “hide sensitive notification content” is selected, the content of private notifications on the lock screen is hidden. In particular, the notification’s content title is replaced with the name of the activity that triggered the notification, and its content text is replaced with the text “Contents hidden.” Notifications can override this default behavior by providing an alternate “public version” that is shown on the lock screen. For instance, a chat notification could provide a public version that indicates how many messages have been received as opposed to showing the content of the messages.
Implementation
                        1.Create a public version for the notification.
                                        Notification publicNotification = new NotificationCompat.Builder(this)
                                                      .setContentTitle("Public Version Notification")
                                                      .setContentText("Redacted private content here")
                                                      .setSmallIcon(R.drawable.ic_stat_notify)
                                                      .setCategory(Notification.CATEGORY_STATUS)
                                                      .build();
                        2.Create the private notification and set the notification from step 1 as its public version.
                                        Notification notification = new NotificationCompat.Builder(this)
                                                      .setContentTitle("Private Notification")
                                                      .setContentText("Sensitive or private content here")
                                                      .setSmallIcon(R.drawable.ic_stat_notify)
                                                      .setCategory(Notification.CATEGORY_STATUS)
                                                      .setVisibility(Notification.VISIBILITY_PRIVATE)
                                                      .setPublicVersion(publicNotification)
                                                      .build();
                     3.Issue the private notification.
                                     NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
                                     notificationManager.notify(NOTIFICATION_ID+2, notification);

2. Heads-up Notifications:  An interruptive priority is either Notification.PRIORITY_HIGH or Notification.MAX. A notification that has both an interruptive priority and an alert is called an interruptive notification. When users receive an interruptive notification in Android 5.0, it initially appears as a heads-up notification, which is a small floating window that allows users to immediately view and interact with notifications without having to check the notification drawer.
Implementation
             1.Create a notification with an interruptive priority and an alert.
                                    Notification notification = new NotificationCompat.Builder(this)
                                                     .setContentTitle("High priority")
                                                     .setContentText("Important message here")
                                                     .setSmallIcon(R.drawable.ic_stat_notify)
                                                     .setPriority(Notification.PRIORITY_HIGH)
                                                     .setDefaults(Notification.DEFAULT_ALL)
                                                     .setCategory(Notification.CATEGORY_STATUS)
                                                     .build();
          2.Issue the notification.
                         NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
                         notificationManager.notify(NOTIFICATION_ID, notification);
     3.Rich Notifications: Rich notifications, which were introduced with Android Jellybean (API 16), let you create notifications with large content areas, images, and buttons. These rich notification features 
            let you display a lot more information in a notification than you are able to display in a standard notification. Rich notifications have two states: a normal state and an expanded state. To expand
           a notification, users must swipe down on the notification with two fingers. Additionally, the notification may appear expanded by default if there is enough available room.
               Chat applications such as Hangouts deliver a notification every time you receive one or more messages. While only short messages can be displayed in standard notifications, rich notifications can display messages with long text and pictures.

      4. BigTextStyle Notification: Some chat messages are very long, but a standard notification’s content text can only show a single line of text. Before rich notifications were available, the user would have to tap the notification to open the app and see the rest of the chat message. A type of rich notification called a BigTextStyle notification shows multiple lines of text when the notification is expanded. When it’s not expanded, the notification shows the same content as a standard notification.
Implementation
                    1.Create an instance of NotificationCompat.BigTextStyle.
                            String longText = "Without BigTextStyle, only a single line of text would be visible. " +"Any additional text would not appear directly on the notification. " +"The entire first line would not even be on the notification if it were too long! " +"Text that doesn't fit in a standard notification becomes ellipsized. " +"That is, the characters that don't fit are removed and replaced by ellipsis.";
                           NotificationCompat.BigTextStyle bigTextStyle = new NotificationCompat.BigTextStyle().bigText(longText);
                         
                  2. Create a notification and set its style to bigTextStyle.
                                    Notification bigTextStyleNotification = new NotificationCompat.Builder(this)
                                              .setContentTitle("All Hail BigTextStyle")
                                              .setContentText(longText)
                                              .setSmallIcon(R.drawable.ic_stat_notify)
                                              .setContentIntent(activityPendingIntent)
                                              .setCategory(Notification.CATEGORY_STATUS)
                                              .setStyle(bigTextStyle)
                                              .build();
                3. Issue the notification.
                                    NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
                                    notificationManager.notify(NOTIFICATION_ID, bigTextStyleNotification);

The notification should be able to stand by itself regardless of whether or not it’s expanded.That is, users will not necessarily see both the normal and the expanded notification. Your app should continue to work without any major disruptions no matter which version of the notification the user views.

       5.BigPictureStyle Notification:  If you receive a picture as a chat message, you can display it in an expanded notification with BigPictureStyle. If the notification is not expanded, it will look just like a standard notification.
 Implementation
                       1.Create an instance of NotificationCompat.BigPictureStyle.
                     Bitmap bigPicture = BitmapFactory.decodeResource(getResources(), R.drawable.mandrill);
                     String contentText = "A classic image processing test image.";
                     String summaryText = "This mandrill is often used as a test image.";
                     NotificationCompat.BigPictureStyle bigPictureStyle = new NotificationCompat.BigPictureStyle()
                                      .setSummaryText(summaryText)
                                      .bigPicture(bigPicture);

The BigPictureStyle summary text replaces the content text while the notification is expanded. Also note that the maximum height of the big picture is 256dp.
                     2. Create a Notification and set its style to bigPictureStyle.
                     PendingIntent activityPendingIntent = getActivityPendingIntent();
                     Notification bigPictureStyleNotification = new NotificationCompat.Builder(this)
                                          .setContentTitle("Rando Fact")
                                          .setContentText(contentText)
                                          .setSmallIcon(R.drawable.ic_stat_notify)
                                          .setContentIntent(activityPendingIntent)
                                          .setCategory(Notification.CATEGORY_STATUS)
                                          .setStyle(bigPictureStyle)
                                          .build();
                    3.Issue the notification.
                               NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
                     notificationManager.notify(NOTIFICATION_ID, bigPictureStyleNotification);

      6. InboxStyle Notification: With InboxStyle, you can preview multiple messages in the expanded notification, one per line.If the notification is not expanded, it behaves like a standard notification.
Implementation
                     1.Create a helper method that formats a single line of the expanded notification.
                 private Spannable formatInboxStyleLine(String username, String message) {
                           Spannable spannable = new SpannableString(username + " " + message);
                           int color = getResources().getColor(R.color.notification_title);
                           spannable.setSpan(new ForegroundColorSpan(color), 0, username.length(),
                           Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);.
                           return spannable;
                    }
               2.Create an instance of NotificationCompat.InboxStyle.
                NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle()
                                .addLine(formatInboxStyleLine("Alice", "hey there"))
                                .addLine(formatInboxStyleLine("Bob", "what are you doing?"))
                                .addLine(formatInboxStyleLine("Eve", "give me a call when you get a chance"))
                                .addLine(formatInboxStyleLine("Trudy", "I like potatoes"))
                                .addLine(formatInboxStyleLine("Mallory", "Dinner tomorrow?"));
                       The addLine method takes a CharSequence to display in the expanded notification.A CharSequence is the superclass of String, so you could pass a String into addLine. In this
case, however, we want to use the formatted Spannables from the first step.
              3.Create a Notification and set its style to inboxStyle.
                     PendingIntent activityPendingIntent = getActivityPendingIntent();
                     Notification inboxStyleNotification = new NotificationCompat.Builder(this)
                                          .setContentTitle("5 messages received")
                                          .setContentText("Alice, Bob, Eve, Trudy, Mallory")
                                          .setSmallIcon(R.drawable.ic_stat_notify)
                                          .setPriority(Notification.PRIORITY_HIGH)
                                          .setContentIntent(activityPendingIntent)
                                          .setCategory(Notification.CATEGORY_MESSAGE)
                                          .setStyle(inboxStyle)
                                          .build();
           4. Issue the notification.
                               NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
                               notificationManager.notify(NOTIFICATION_ID, inboxStyleNotification);
      
         7.Custom Notifications: 

            This notification has a few interesting features:
    • The normal (unexpanded) notification has buttons.
    • The expanded notification has three lines of text, and
    • both versions have a “close” button at the right.

These features cannot be implemented without custom layouts. Regular notifications can’t use custom layouts and they can’t display any actions when unexpanded. Custom notifications are more complicated to implement than regular notifications and should be used sparingly. If you can display all the content you deem essential in regular notifications, then you should not use a custom notification. “But custom layouts can make my notifications stand out, man!” you say. I completely agree. However, when users look at notifications, they won’t be wowed by your out-of-the-box thinking, but rather distracted
and annoyed.
Implementation
1.Create the layouts for your custom notifications.
                  The standard (that is, unexpanded) view and the expanded view are two different layouts. In this example, we’ll borrow these layouts directly from the Play Music app (and modify them
only slightly). You can find them in the book’s example source code. The standard layout is in res ➤ layout-v11 ➤ statusbar.xml and the expanded layout is in res ➤ layout-v16 ➤
statusbar_expanded.xml.
2.Create the PendingIntents that are triggered by actions. 
           PendingIntent pausePendingIntent =getMediaCommandPendingIntent(MediaCommandService.ACTION_PAUSE);
           PendingIntent nextPendingIntent =getMediaCommandPendingIntent(MediaCommandService.ACTION_NEXT);
           PendingIntent prevPendingIntent =getMediaCommandPendingIntent(MediaCommandService.ACTION_PREV);
           PendingIntent closePendingIntent =getMediaCommandPendingIntent(MediaCommandService.ACTION_CLOSE);



3.Create an instance of RemoteViews and initialize it with the standard layout (that is, the one in statusbar_expanded.xml).
           RemoteViews contentView = new RemoteViews(getApplicationContext().getPackageName(),R.layout.statusbar);
                      contentView.setImageViewResource(R.id.albumart, R.drawable.bg_default_album_art);
                      contentView.setTextViewText(R.id.trackname, "Song Name");
                      contentView.setTextViewText(R.id.artistalbum, "Artist Name");
                      contentView.setOnClickPendingIntent(R.id.playpause, pausePendingIntent);
                      contentView.setOnClickPendingIntent(R.id.next, nextPendingIntent);
                      contentView.setOnClickPendingIntent(R.id.veto, closePendingIntent);
RemoteViews is essentially a view that can be displayed in another process. This requirement limits the types of views that can be used in layouts for RemoteViews.

4.Create the Notification and set contentView as its content.
          PendingIntent activityPendingIntent = getActivityPendingIntent();
          Notification customNotification = new NotificationCompat.Builder(this)
                    .setContentTitle("Song Name")
                    .setContentText("Artist Name")
                    .setSmallIcon(R.drawable.ic_stat_notify)
                    .setPriority(Notification.PRIORITY_HIGH)
                    .setContentIntent(activityPendingIntent)
                    .setCategory(Notification.CATEGORY_TRANSPORT)
                    .setContent(contentView)
                    .setOngoing(true)
                    .build();

The call to setOngoing(true) prevents the user from swiping the notification away. Since the user is always aware of whether a music app is running (even if it’s in the background), an ongoing notification guarantees that the user always gets proper feedback. That is, it guarantees that a notification will always be present when there is music playing. The user can still dismiss the notification by pressing the “close” button. In a real implementation,pressing close would also stop the music.
We’ll create the expanded view in the next few steps. 
5.create a helper method that returns true if running Jellybean (API level 16) or above.
            private boolean isJellybeanOrAbove() {
              return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
            }
6.Set the expanded content view only if running Jellybean (API 16) or above. Trying to set the expanded view on any previous version of Android would result in a crash.
         if(isJellybeanOrAbove()) {
               RemoteViews expandedNotificationView =new RemoteViews(getApplicationContext().getPackageName(),R.layout.statusbar_expanded);
               expandedNotificationView.setImageViewResource(R.id.albumart,R.drawable.bg_default_album_art);
               expandedNotificationView.setTextViewText(R.id.trackname, "Song Name");
               expandedNotificationView.setTextViewText(R.id.artist, "Artist Name");
               expandedNotificationView.setTextViewText(R.id.album, "Album Name");
               expandedNotificationView.setOnClickPendingIntent(R.id.playpause, pausePendingIntent);
               expandedNotificationView.setOnClickPendingIntent(R.id.prev, prevPendingIntent);
               expandedNotificationView.setOnClickPendingIntent(R.id.next, nextPendingIntent);
               expandedNotificationView.setOnClickPendingIntent(R.id.veto, closePendingIntent);
               customNotification.bigContentView = expandedNotificationView;
          }

Note that the expanded content view must be set directly on the notification and not on the Builder.
7.Issue the notification.
             NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
             notificationManager.notify(NOTIFICATION_ID, customNotification);

Reference:
  1. http://developer.android.com/training/notify-user/index.html
  2. http://developer.android.com/reference/android/support/v4/app/NotificationCompat.Builder.html
  3. http://www.javacodegeeks.com/2013/10/android-notificationlistenerservice-example.html
  4. http://stackoverflow.com/questions/15350998/determine-addaction-click-for-android-notifications
  5. http://www.programcreek.com/java-api-examples/index.php?api=android.support.v4.app.NotificationCompat
  6. http://code4reference.com/2012/07/android-jelly-bean-notification/
  7. http://www.scriptscoop.net/t/2269661ee9bc/notification-bar-icon-turns-white-in-android-5-lollipop.html
  8. https://books.google.co.in/books?id=OOkmCAAAQBAJ&pg=PA33&lpg=PA33&dq=set+NotificationCompat.Builder+set+Public+Version+in+android&source=bl&ots=UScY9hHQYi&sig=X22xLSF3IF6-50upr9n0ZWgrLDI&hl=en&sa=X&ved=0CEEQ6AEwBWoVChMI9-abpNDiyAIVYZSmCh2FxQFp#v=onepage&q&f=false