Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Notify your users, the right way

Notify your users, the right way

Notifications have been one of the core features in the Android UI since the first public release of the platform.
In this talk, presented at Droidcon Turin 2018, I gave an introduction showing the evolution of notifications in Android and how developers can customise them to give users the best way of accessing important information and perform quick actions at a glance.

danybony

April 19, 2018
Tweet

More Decks by danybony

Other Decks in Programming

Transcript

  1. A notification is a message that Android displays outside your

    app's UI to provide the user with reminders, communication from other people, or other timely information from your app
  2. Create a Notification val notification = Notification.Builder(context) .setSmallIcon(R.drawable.ic_notification) .setContentTitle("Title") .setContentText("Content")

    .build() val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager notificationManager.notify(NOTIFICATION_ID, notification)
  3. Channels val channel = NotificationChannel( CHANNEL_ID, "Status updates", NotificationManager.IMPORTANCE_DEFAULT )

    channel.description = description channel.lightColor = Color.RED val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager notificationManager.createNotificationChannel(channel)
  4. Channels val channel = NotificationChannel( CHANNEL_ID, "Status updates", NotificationManager.IMPORTANCE_DEFAULT )

    channel.description = description channel.lightColor = Color.RED val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager notificationManager.createNotificationChannel(channel)
  5. Channels val channel = NotificationChannel( CHANNEL_ID, "Status updates", NotificationManager.IMPORTANCE_DEFAULT )

    channel.description = description channel.lightColor = Color.RED val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager notificationManager.createNotificationChannel(channel)
  6. Big text style val notification = NotificationCompat.Builder(context, CHANNEL_ID) .setSmallIcon(R.drawable.new_mail) .setContentTitle(emailSender)

    .setContentText(emailSubject) .setLargeIcon(emailSenderAvatar) .setStyle(NotificationCompat.BigTextStyle() .bigText(emailSubjectAndSnippet)) .build()
  7. Big text style val notification = NotificationCompat.Builder(context, CHANNEL_ID) .setSmallIcon(R.drawable.new_mail) .setContentTitle(emailSender)

    .setContentText(emailSubject) .setLargeIcon(emailSenderAvatar) .setStyle(NotificationCompat.BigTextStyle() .bigText(emailSubjectAndSnippet)) .build()
  8. Large image style val notification = NotificationCompat.Builder(context, CHANNEL_ID) .setSmallIcon(R.drawable.new_image) .setContentTitle(imageTitle)

    .setContentText(imageDescription) .setLargeIcon(largeBitmap) .setStyle(NotificationCompat.BigPictureStyle() .bigPicture(largeBitmap) .bigLargeIcon(null)) .build()
  9. Large image style val notification = NotificationCompat.Builder(context, CHANNEL_ID) .setSmallIcon(R.drawable.new_image) .setContentTitle(imageTitle)

    .setContentText(imageDescription) .setLargeIcon(largeBitmap) .setStyle(NotificationCompat.BigPictureStyle() .bigPicture(largeBitmap) .bigLargeIcon(null)) .build()
  10. Inbox style val notification = NotificationCompat.Builder(this, CHANNEL_ID) .setSmallIcon(R.drawable.ic_inbox) .setContentTitle("New mails")

    .setContentText("You have 3 new emails") .setLargeIcon(largeBitmap) .setStyle(NotificationCompat.InboxStyle() .addLine(getSubjectAndMessage(...)) .addLine(getSubjectAndMessage(...)) .addLine(getSubjectAndMessage(...)) ) .build()
  11. Inbox style val notification = NotificationCompat.Builder(this, CHANNEL_ID) .setSmallIcon(R.drawable.ic_inbox) .setContentTitle("New mails")

    .setContentText("You have 3 new emails") .setLargeIcon(largeBitmap) .setStyle(NotificationCompat.InboxStyle() .addLine(getSubjectAndMessage(...)) .addLine(getSubjectAndMessage(...)) .addLine(getSubjectAndMessage(...)) ) .build()
  12. Messaging style val message1 = NotificationCompat.MessagingStyle.Message( messages[0].getText(), messages[0].getTime(), messages[0].getSender() )

    val message2 = NotificationCompat.MessagingStyle.Message( messages[1].getText(), messages[1].getTime(), messages[1].getSender() ) val notification = NotificationCompat.Builder(context, CHANNEL_ID) .setSmallIcon(R.drawable.new_message) .setStyle(NotificationCompat.MessagingStyle(disaplayedUserName) .addMessage(message1) .addMessage(message2)) .build()
  13. Media style val notification = NotificationCompat.Builder(context, CHANNEL_ID) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) .setSmallIcon(R.drawable.ic_stat_player) .addAction(R.drawable.ic_thumb_down,

    "Dislike", thumbDownPendingIntent) .addAction(R.drawable.ic_prev, "Previous", prevPendingIntent) .addAction(R.drawable.ic_pause, "Pause", pausePendingIntent) .addAction(R.drawable.ic_next, "Next", nextPendingIntent) .addAction(R.drawable.ic_thumb_up, “Like", thumbDownPendingIntent) .setStyle(NotificationCompat.MediaStyle() .setShowActionsInCompactView(1, 2, 3) .setMediaSession(mediaSession.getSessionToken())) .setContentTitle("Wonderful song") .setContentText("Awesome Band") .setLargeIcon(albumArtBitmap) .build()
  14. val notification = NotificationCompat.Builder(context, CHANNEL_ID) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) .setSmallIcon(R.drawable.ic_stat_player) .addAction(R.drawable.ic_thumb_down, "Dislike", thumbDownPendingIntent)

    .addAction(R.drawable.ic_prev, "Previous", prevPendingIntent) .addAction(R.drawable.ic_pause, "Pause", pausePendingIntent) .addAction(R.drawable.ic_next, "Next", nextPendingIntent) .addAction(R.drawable.ic_thumb_up, “Like", thumbDownPendingIntent) .setStyle(NotificationCompat.MediaStyle() .setShowActionsInCompactView(1, 2, 3) .setMediaSession(mediaSession.getSessionToken())) .setContentTitle("Wonderful song") .setContentText("Awesome Band") .setLargeIcon(albumArtBitmap) .build() Media style
  15. val notification = NotificationCompat.Builder(context, CHANNEL_ID) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) .setSmallIcon(R.drawable.ic_stat_player) .addAction(R.drawable.ic_thumb_down, "Dislike", thumbDownPendingIntent)

    .addAction(R.drawable.ic_prev, "Previous", prevPendingIntent) .addAction(R.drawable.ic_pause, "Pause", pausePendingIntent) .addAction(R.drawable.ic_next, "Next", nextPendingIntent) .addAction(R.drawable.ic_thumb_up, “Like", thumbDownPendingIntent) .setStyle(NotificationCompat.MediaStyle() .setShowActionsInCompactView(1, 2, 3) .setMediaSession(mediaSession.getSessionToken())) .setContentTitle("Wonderful song") .setContentText("Awesome Band") .setLargeIcon(albumArtBitmap) .build() Media style
  16. Remote views val remoteViews = RemoteViews(packageName, R.layout.custom_notification) val bigRemoteViews =

    RemoteViews(packageName, R.layout.custom_notification_big) val notification = NotificationCompat.Builder(context, CHANNEL_ID) .setStyle(NotificationCompat.DecoratedCustomViewStyle()) .setSmallIcon(R.drawable.ic_notification) .setCustomContentView(remoteViews) .setCustomBigContentView(bigRemoteViews) .build()
  17. Remote views val remoteViews = RemoteViews(packageName, R.layout.custom_notification) val bigRemoteViews =

    RemoteViews(packageName, R.layout.custom_notification_big) val notification = NotificationCompat.Builder(context, CHANNEL_ID) .setStyle(NotificationCompat.DecoratedCustomViewStyle()) .setSmallIcon(R.drawable.ic_notification) .setCustomContentView(remoteViews) .setCustomBigContentView(bigRemoteViews) .build()
  18. Spannables val builder = SpannableStringBuilder() val subjectSpannable = SpannableString(subject) val

    boldSpan = StyleSpan(Typeface.BOLD) subjectSpannable.setSpan(boldSpan, 0, subject.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) builder.append(subjectSpannable).append(' ') builder.append(message)
  19. Notification tap action val intent = Intent(context, MainActivity::class.java) intent.flags =

    Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK val pendingIntent = PendingIntent.getActivity(context, REQUEST_CODE, intent, 0) val notification = NotificationCompat.Builder(context, CHANNEL_ID) .setSmallIcon(R.drawable.ic_notification) .setContentTitle("Title") .setContentText("Content") .setContentIntent(pendingIntent) .setAutoCancel(true) .build()
  20. Notification tap action val intent = Intent(context, MainActivity::class.java) intent.flags =

    Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK val pendingIntent = PendingIntent.getActivity(context, REQUEST_CODE, intent, 0) val notification = NotificationCompat.Builder(context, CHANNEL_ID) .setSmallIcon(R.drawable.ic_notification) .setContentTitle("Title") .setContentText("Content") .setContentIntent(pendingIntent) .setAutoCancel(true) .build()
  21. Additional actions val intent = Intent(context, MainActivity::class.java) intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK

    or Intent.FLAG_ACTIVITY_CLEAR_TASK val pendingIntent = PendingIntent.getActivity(context, REQUEST_CODE, intent, 0) val shareIntent = Intent(context, NotificationBroadcastReceiver::class.java) shareIntent.action = ACTION_SHARE shareIntent.putExtra(EXTRA_NOTIFICATION_ID, NOTIFICATION_ID) val sharePendingIntent = PendingIntent.getBroadcast(context, 0, shareIntent, 0) val notification = NotificationCompat.Builder(context, CHANNEL_ID) .setSmallIcon(R.drawable.ic_notification) .setContentTitle("My notification") .setContentText("Hello World!") .setPriority(NotificationCompat.PRIORITY_DEFAULT) .setContentIntent(pendingIntent) .setAutoCancel(true) .addAction(R.drawable.ic_share, "Share", sharePendingIntent) .build()
  22. Additional actions val intent = Intent(context, MainActivity::class.java) intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK

    or Intent.FLAG_ACTIVITY_CLEAR_TASK val pendingIntent = PendingIntent.getActivity(context, REQUEST_CODE, intent, 0) val shareIntent = Intent(context, NotificationBroadcastReceiver::class.java) shareIntent.action = ACTION_SHARE shareIntent.putExtra(EXTRA_NOTIFICATION_ID, NOTIFICATION_ID) val sharePendingIntent = PendingIntent.getBroadcast(context, 0, shareIntent, 0) val notification = NotificationCompat.Builder(context, CHANNEL_ID) .setSmallIcon(R.drawable.ic_notification) .setContentTitle("My notification") .setContentText("Hello World!") .setPriority(NotificationCompat.PRIORITY_DEFAULT) .setContentIntent(pendingIntent) .setAutoCancel(true) .addAction(R.drawable.ic_share, "Share", sharePendingIntent) .build()
  23. Additional actions - dismiss val intent = Intent(context, MainActivity::class.java) intent.flags

    = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK val pendingIntent = PendingIntent.getActivity(context, REQUEST_CODE, intent, 0) val shareIntent = Intent(context, NotificationBroadcastReceiver::class.java) shareIntent.action = ACTION_SHARE shareIntent.putExtra(EXTRA_NOTIFICATION_ID, NOTIFICATION_ID) val sharePendingIntent = PendingIntent.getBroadcast(context, 0, shareIntent, 0) val notification = NotificationCompat.Builder(context, CHANNEL_ID) .setSmallIcon(R.drawable.ic_notification) .setContentTitle("My notification") .setContentText("Hello World!") .setPriority(NotificationCompat.PRIORITY_DEFAULT) .setContentIntent(pendingIntent) .setAutoCancel(true) .addAction(R.drawable.ic_share, "Share", sharePendingIntent) .build() override fun onReceive(context: Context, intent: Intent) { val manager = context.getSystemService(NOTIFICATION_SERVICE) as NotificationManager val notificationId = intent.getIntExtra(Notification.EXTRA_NOTIFICATION_ID, -1) manager.cancel(notificationId) }
  24. Direct replies val remoteInput = RemoteInput.Builder(KEY_TEXT_REPLY) .setLabel("Your reply") .build() val

    replyIntent = Intent(context, NotificationBroadcastReceiver::class.java) replyIntent.action = ACTION_REPLY replyIntent.putExtra(EXTRA_NOTIFICATION_ID, NOTIFICATION_ID) val replyPendingIntent = PendingIntent.getBroadcast(context, ACTION_REQUEST_CODE, replyIntent, 0) val action = NotificationCompat.Action.Builder( R.drawable.ic_reply, "Reply", replyPendingIntent) .addRemoteInput(remoteInput) .build()
  25. Direct replies val remoteInput = RemoteInput.Builder(KEY_TEXT_REPLY) .setLabel("Your reply") .build() val

    replyIntent = Intent(context, NotificationBroadcastReceiver::class.java) replyIntent.action = ACTION_REPLY replyIntent.putExtra(EXTRA_NOTIFICATION_ID, NOTIFICATION_ID) val replyPendingIntent = PendingIntent.getBroadcast(context, ACTION_REQUEST_CODE, replyIntent, 0) val action = NotificationCompat.Action.Builder( R.drawable.ic_reply, "Reply", replyPendingIntent) .addRemoteInput(remoteInput) .build() val notification = NotificationCompat.Builder(context, CHANNEL_ID) .setSmallIcon(R.drawable.ic_notification) .setContentTitle("Message from Bob") .setContentText("Hey there, how are you doing?") .addAction(action) .build()
  26. Direct replies private fun getReplyText(intent: Intent): CharSequence? { val remoteInput

    = RemoteInput.getResultsFromIntent(intent) return remoteInput?.getCharSequence(KEY_TEXT_REPLY) }
  27. Wear-only actions val actionExtender = NotificationCompat.Action.WearableExtender() .setHintLaunchesActivity(true) .setHintDisplayActionInline(true); val action

    = NotificationCompat.Action.Builder(R.drawable.ic_reply, "Reply", pendingIntent) .addRemoteInput(remoteInput) .extend(actionExtender) .build() val wearableExtender = NotificationCompat.WearableExtender() wearableExtender.addAction(action) val notification = NotificationCompat.Builder(context, CHANNEL_ID) .extend(wearableExtender) .build()
  28. Remote input smart reply val remoteInput = RemoteInput.Builder(KEY_TEXT_REPLY) .setLabel("Your reply")

    .build() val action = NotificationCompat.Action.Builder(R.drawable.ic_reply, "Reply", pendingIntent) .addRemoteInput(remoteInput) .build()
  29. val remoteInput = RemoteInput.Builder(KEY_TEXT_REPLY) .setLabel("Your reply") .setChoices(arrayOf("Yes", "No", "Maybe")) .build()

    val action = NotificationCompat.Action.Builder(R.drawable.ic_reply, "Reply", pendingIntent) .addRemoteInput(remoteInput) .build() Remote input smart reply
  30. val remoteInput = RemoteInput.Builder(KEY_TEXT_REPLY) .setLabel("Your reply") .setChoices(arrayOf("Yes", "No", "Maybe")) .build()

    val action = NotificationCompat.Action.Builder(R.drawable.ic_reply, "Reply", pendingIntent) .addRemoteInput(remoteInput) .setAllowGeneratedReplies(true) .build() Remote input smart reply
  31. Image in MessagingStyle val message = NotificationCompat.MessagingStyle.Message( messages[0].getText(), messages[0].getTime(), messages[0].getSender()

    ) val notification = NotificationCompat.Builder(context, CHANNEL_ID) .setSmallIcon(R.drawable.new_message .setStyle(NotificationCompat.MessagingStyle(disaplayedUserName) .addMessage(message) .addMessage(message2)) .build()
  32. Image in MessagingStyle val message = NotificationCompat.MessagingStyle.Message( messages[0].getText(), messages[0].getTime(), messages[0].getSender()

    ).setData("image/png", imageUri) val notification = NotificationCompat.Builder(context, CHANNEL_ID) .setSmallIcon(R.drawable.new_message .setStyle(NotificationCompat.MessagingStyle(disaplayedUserName) .addMessage(message) .addMessage(message2)) .build()
  33. More info Wearable features for Notifications https://developer.android.com/training/wearables/notifications/index.html Notifications Design Guidelines

    https://material.io/guidelines/patterns/notifications.html Notifications overview https://developer.android.com/guide/topics/ui/notifiers/notifications.html