Share
Embed
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
Transcript
70 slides
-
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 -
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) -
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) -
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) -
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) -
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() -
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() -
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() -
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() -
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() -
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() -
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() -
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() -
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 -
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 -
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() -
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() -
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) -
Timeout val notification = NotificationCompat.Builder(this, CHANNEL_ID) .setSmallIcon(R.drawable.ic_notification) .setContentTitle("Title") .setContentText("Notification content")
.setTimeoutAfter(TimeUnit.MINUTES.toMillis(10)) .setPriority(NotificationCompat.PRIORITY_DEFAULT) .build() -
Timeout val notification = NotificationCompat.Builder(this, CHANNEL_ID) .setSmallIcon(R.drawable.ic_notification) .setContentTitle("Title") .setContentText("Notification content")
.setTimeoutAfter(TimeUnit.MINUTES.toMillis(10)) .setPriority(NotificationCompat.PRIORITY_DEFAULT) .build() -
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() -
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() -
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() -
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() -
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) } -
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() -
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() -
Direct replies private fun getReplyText(intent: Intent): CharSequence? { val remoteInput
= RemoteInput.getResultsFromIntent(intent) return remoteInput?.getCharSequence(KEY_TEXT_REPLY) } -
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() -
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() -
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 -
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 -
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() -
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() -
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