How to collapse push notifications into one icon? - android

An application receives push notifications. They can be different (payment, order, chat), so I have to show them all, not replacing old with new. A problem is that in old emulators (API 19) all push notifications are shown separately.
In newer emulators they are groupped into one, but (if I'm not mistaken) after 3 received notifications.
Data sent from FCM:
{
"to": "<push token>",
"data": {
"title": "Application name",
"message": "See a message",
"screen": "Main screen",
...
}
}
MyFirebaseMessagingService:
override fun onMessageReceived(remoteMessage: RemoteMessage) {
super.onMessageReceived(remoteMessage)
val soundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
val textStyle = NotificationCompat.BigTextStyle()
val title: String
val body: String
if (remoteMessage.data.isNullOrEmpty()) {
title = remoteMessage.notification?.title ?: ""
body = remoteMessage.notification?.body ?: ""
} else {
title = remoteMessage.data["title"] ?: ""
body = remoteMessage.data["message"] ?: ""
}
val notificationBuilder = NotificationCompat.Builder(this,
getString(R.string.default_notification_channel_id))
.setContentTitle(title)
.setContentText(body)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setStyle(textStyle)
.setSound(soundUri)
.setSmallIcon(R.drawable.ic_notification_icon)
.setAutoCancel(true) // Dismiss the notification on click.
val notificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
// Since android Oreo notification channel is needed.
setupChannels(notificationManager, notificationBuilder)
// Create pending intent, mention the Activity which needs to be triggered
// when user clicks on notification.
val notificationId = Random.nextInt()
navigateToScreen(notificationBuilder, notificationId, remoteMessage)
val notification = notificationBuilder.build()
notificationManager.notify(notificationId, notification)
}
private fun setupChannels(notificationManager: NotificationManager,
notificationBuilder: NotificationCompat.Builder) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channelId = getString(R.string.default_notification_channel_id)
val channel = NotificationChannel(channelId, "title",
NotificationManager.IMPORTANCE_DEFAULT).apply {
description = "body"
enableLights(true)
enableVibration(true)
lightColor = Color.CYAN
lockscreenVisibility = Notification.VISIBILITY_PUBLIC
canShowBadge()
setShowBadge(true)
}
notificationManager.createNotificationChannel(channel)
notificationBuilder.setChannelId(channelId)
}
}
private fun navigateToScreen(notificationBuilder: NotificationCompat.Builder,
notificationId: Int,
remoteMessage: RemoteMessage) {
val intent = getNavigationIntent(remoteMessage)
val pendingIntent = PendingIntent.getActivity(this, notificationId, intent,
PendingIntent.FLAG_UPDATE_CURRENT)
notificationBuilder.setContentIntent(pendingIntent)
}
private fun getNavigationIntent(remoteMessage: RemoteMessage): Intent {
val intent = Intent(this, MainActivity::class.java)
intent.putExtra("screen", remoteMessage.data["screen"])
return intent
}

Related

FCM Foreground and Background notification layout view and PendingIntent

I'm creating notification using Firebase and FCM
class FcmServices: FirebaseMessagingService() {
override fun onMessageReceived(remoteMessage: RemoteMessage) {
sendNotification(remoteMessage)
}
override fun onNewToken(token: String) {
super.onNewToken(token)
}
private fun sendNotification(remoteMessage: RemoteMessage) {
val notifTitle = remoteMessage.notification?.title
val notifBody = remoteMessage.notification?.body
val builder: NotificationCompat.Builder?
val notificationManager =
this.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val channelId = "101"
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val importance = NotificationManager.IMPORTANCE_DEFAULT
val notificationChannel = NotificationChannel(channelId, "notification", importance)
notificationManager.createNotificationChannel(notificationChannel)
builder = NotificationCompat.Builder(applicationContext, notificationChannel.id)
} else {
builder = NotificationCompat.Builder(applicationContext)
}
val intent = Intent(this, NotifikasiActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
val pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
builder.setSmallIcon(R.drawable.logo_bprmsa)
.setLargeIcon(
BitmapFactory.decodeResource(
this.resources,
R.drawable.logo_bprmsa
)
)
.setContentIntent(pendingIntent)
.setContentTitle(notifTitle)
.setContentText(notifBody)
.setDefaults(Notification.DEFAULT_VIBRATE)
var alarmSound: Uri? = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
if (alarmSound == null) {
alarmSound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE)
if (alarmSound == null) {
alarmSound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
}
}
builder.setSound(alarmSound)
builder.setAutoCancel(true)
builder.setWhen(System.currentTimeMillis())
builder.setDefaults(NotificationCompat.DEFAULT_VIBRATE)
builder.priority = NotificationCompat.PRIORITY_HIGH
notificationManager.notify(Random().nextInt(), builder.build())
}
}
I need to open the NotificationActivity after click the notification received from Firebase, but the problem is the PendingIntent is not working while the app is in background state. Is there any different condition between foreground and background state code ?
And for the second, the layout of foreground and background is also different (background state without the icon shown)
https://firebase.google.com/docs/cloud-messaging/concept-options
Based on the official document from Google, it is intentional. The Icon and any other buttons or others you wish to display other than the title and the message won't display if your app is not running foreground.

Not getting heads up notifications for a while when you swipe up local notifications

When I swipe up after the local notification comes, the application notifications don't appear on the screen for a while. What could be the reason for this? I've read something that when heads up notifications are swiped up, it sends a warning to the system to prevent heads up notifications for a certain period of time, but how can I prevent this? I don't have any problems getting notifications in my app other than that.
fun getChatChannels(): MutableList<NotificationChannel> {
val chatChannels = mutableListOf<NotificationChannel>()
val chatChannel = NotificationChannel(
IM_CHANNEL_ID,
Application.getString(R.string.chat_channel_name),
NotificationManager.IMPORTANCE_HIGH
)
chatChannel.setShowBadge(false)
chatChannel.group = MESSAGE_GROUP_ID
chatChannels.add(chatChannel)
return chatChannels }
private fun getChannelList(): MutableList<NotificationChannel> {
val channelList = mutableListOf<NotificationChannel>()
channelList.addAll(getChatChannels())
return channelList
}
fun createGroupsAndChannels() {
// Create the NotificationChannel, but only on API 26+ because
// the NotificationChannel class is new and not in the support library
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val notificationManager =
Application.instance.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.createNotificationChannelGroups(getGroupList())
notificationManager.createNotificationChannels(getChannelList())
}
Log.d(TAG, "Channels and groups created")
}
fun showChatNotification(destinationAddress: String, messageId: String, message: String) {
ThreadManager.createUITask {
val name = if (domainContact != null) {
"${domainContact.firstName} ${domainContact.lastName}"
} else {
destinationAddress
}
val notificationIntent = Intent(Application.instance, MainActivity::class.java)
notificationIntent.putExtra(
Application.getString(R.string.key_conversation_participant),
destinationAddress
)
notificationIntent.flags = Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
val pendingIntent = PendingIntent.getActivity(
Application.instance,
destinationAddress.hashCode(),
notificationIntent,
PendingIntent.FLAG_UPDATE_CURRENT
)
val notificationId = UUID.randomUUID().hashCode()
val builder = NotificationCompat.Builder(Application.instance, IM_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_notification_message)
.setColor(ContextCompat.getColor(Application.instance, R.color.colorPrimary))
.setContentTitle(name)
.setContentText(message)
.setStyle(
NotificationCompat.BigTextStyle() // Expandable-Collapsible notification
.bigText(message)
)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setDefaults(NotificationCompat.DEFAULT_ALL)
.setCategory(NotificationCompat.CATEGORY_MESSAGE)
.setContentIntent(pendingIntent)
.setVisibility(VISIBILITY_PUBLIC)
.setAutoCancel(true)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
builder.addAction(getChatNotificationReplyAction(destinationAddress, notificationId, messageId))
}
chatNotifications.add(mapOf(destinationAddress to notificationId))
createNotifications(builder, notificationId)
Log.d(TAG, "Chat notification created")
}
private fun createNotifications(builder: NotificationCompat.Builder, notificationId: Int) {
with(NotificationManagerCompat.from(Application.instance)) {
notify(notificationId, builder.build())
}
}

How to dismiss all desired notifications on Android?

I'm creating a chat app that sends and receives notifications through Firebase Cloud Functions. Let's suppose I have 2 friends, and each friend sends me 5 messages(5 notifications). When I open a chat in my chat list, I want all notifications for that chat to be dismissed and the others not.
Currently I can only dimiss 1 notification. If I get more than one notification the notificationManager.cancel(notificationId) doesn't work. I don't want to use notificationManager.cancelAll() as I just want to dismiss some notifications.
override fun onMessageReceived(remoteMessage: RemoteMessage) {
remoteMessage.notification.let { notification ->
val type = remoteMessage.data[NOTIFICATION_TYPE]
val notificationChannelId = getNotificationChannelId(type)
val soundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
val intent = Intent(this, HomeActivity::class.java)
val notificationIntent = setIntent(remoteMessage.data)
val stackBuilder: TaskStackBuilder = TaskStackBuilder.create(this)
stackBuilder.addParentStack(HomeActivity::class.java)
stackBuilder.addNextIntent(intent)
stackBuilder.addNextIntent(notificationIntent)
val pendingIntent: PendingIntent =
stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
val notificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val notificationBuilder = NotificationCompat.Builder(this, notificationChannelId)
.setContentTitle(notification?.title ?: "")
.setContentText(notification?.body ?: "")
.setStyle(
NotificationCompat.BigTextStyle()
.bigText(notification?.body ?: "")
)
.setSmallIcon(R.drawable.ic_stat)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setSound(soundUri)
.setColor(ContextCompat.getColor(applicationContext, R.color.colorNotification))
.setVibrate(vibrationPattern)
.setContentIntent(pendingIntent)
.setAutoCancel(true)
notify(remoteMessage.data, notificationManager, notificationBuilder)
}
}
private fun notify(
data: Map<String, String>,
notificationManager: NotificationManager,
notificationBuilder: NotificationCompat.Builder
) {
when (data[NOTIFICATION_TYPE]) {
CHAT_MSG -> {
if (data[NotificationUtils.CHAT_ID] != ChatActivity.currentChat) {
notificationManager.notify(
CHAT_MSG_NOTIFICATION_ID,
notificationBuilder.build()
)
}
}
FRIEND_REQUEST -> {
notificationManager.notify(
FRIEND_REQUEST_NOTIFICATION_ID,
notificationBuilder.build()
)
}
}
}
OnStart():
fun dismissNotifications(notificationId: Int) {
val notificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.cancel(notificationId)
}
My thoughts is:
Save all notificationIds in map where:
key is userName/userId in message.
value is List of notificationId
When you need to clear all notifications you just ask you map and clear all that notificationIds for that userName/userId

Notification works in Activity but not in Service

I am trying to integrate Firebase Cloud Messages into my app. The code I used in the showNotification function is from the Android User Interface Samples. I tried it in the Activity and it worked but am not sure why it's not working in the service. The println is showing the function is getting called and values are coming as expected. Is there anything am missing from the function?
class MessagingService : FirebaseMessagingService() {
override fun onMessageReceived(remoteMessage: RemoteMessage) {
if (remoteMessage.data.isNotEmpty()) {
val message = Gson().fromJson(remoteMessage.data.toString(), Message.Res::class.java).payload!!
showNotification(message.title, message.body, message.count)
}
}
private fun showNotification(title: String?, body: String?, count: Int = 0) {
println("_print::showNotification(title:$title, body:$body, count:$count)")
val mainPendingIntent = PendingIntent.getActivity(
applicationContext, BuildConfig.REQUEST_CODE,
Intent(applicationContext, MainActivity::class.java),
PendingIntent.FLAG_UPDATE_CURRENT
)
val builder = NotificationCompat.Builder(applicationContext, "channel_email_1")
.setContentTitle(title)
.setContentText(body)
.setSmallIcon(R.mipmap.ic_launcher)
.setLargeIcon(BitmapFactory.decodeResource(resources, R.mipmap.ic_launcher_round))
.setContentIntent(mainPendingIntent)
.setDefaults(NotificationCompat.DEFAULT_ALL)
.setColor(ContextCompat.getColor(applicationContext, R.color.primary))
.setSubText(if (count > 1) "You have $count pending notifications" else title)
.setCategory(Notification.CATEGORY_EMAIL)
.setPriority(1)
.setVisibility(NotificationCompat.VISIBILITY_PRIVATE)
NotificationManagerCompat.from(applicationContext)
.notify(BuildConfig.NOTIFICATION_ID, builder.build())
}
}
Instead of
val mainPendingIntent = PendingIntent.getActivity(
applicationContext, BuildConfig.REQUEST_CODE,
Intent(applicationContext, MainActivity::class.java),
PendingIntent.FLAG_UPDATE_CURRENT
)
NotificationManagerCompat.from(applicationContext)
.notify(BuildConfig.NOTIFICATION_ID, builder.build())
I use:
val notificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
// Since android Oreo notification channel is needed.
setupChannels(notificationManager, builder)
// Create pending intent, mention the Activity which needs to be triggered
// when user clicks on notification.
val notificationId = Random.nextInt()
navigateToScreen(builder, notificationId)
val notification = builder.build()
notificationManager.notify(notificationId, notification)
}
private fun setupChannels(notificationManager: NotificationManager,
builder: NotificationCompat.Builder) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channelId = "channel_email_1"
val channel = NotificationChannel(channelId, "title",
NotificationManager.IMPORTANCE_DEFAULT).apply {
description = "body"
// Add other settings.
lockscreenVisibility = Notification.VISIBILITY_PUBLIC
canShowBadge()
setShowBadge(true)
}
notificationManager.createNotificationChannel(channel)
builder.setChannelId(channelId)
}
}
private fun navigateToScreen(builder: NotificationCompat.Builder,
notificationId: Int) {
val intent = Intent(this, MainActivity::class.java)
val pendingIntent = PendingIntent.getActivity(this, notificationId, intent,
PendingIntent.FLAG_UPDATE_CURRENT)
builder.setContentIntent(pendingIntent)
}

How to send fcm notification when app is not running

I have tried many solutions but none of them are worked. I am able receive FCM notification when app is active, but not getting notification when app is background or killed.
you need to create a service class extending FirebaseMessagingService and override onMessageReceived method in that class to send notification
class MyFirebaseMessagingService : FirebaseMessagingService() {
override fun onMessageReceived(message: RemoteMessage) {
super.onMessageReceived(message)
try {
message.notification?.let {
showNotification(
it.title ?: "",
it.body ?: ""
)
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
you are now getting the notification info from backend and showing it as a notification by using the function showNotification. of course, you must implement the function showNotification. it just a simple function for showing notifications in android
Edit: this the implementation of the function, add this to your class
class MyFirebaseMessagingService : FirebaseMessagingService() {
companion object {
const val channelId = "Channel"
const val channelName = "MyChannel"
const val smallIcon: Int = R.drawable.ic_logo
const val notificationId = 1
}
fun showNotification(myTitle: String, myBody: String) {
val notificationBuilder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Notification.Builder(applicationContext, channelId)
} else {
Notification.Builder(applicationContext)
}
val intent = Intent(applicationContext, HomeActivity::class.java)
val pendingIntent = PendingIntent.getActivity(
applicationContext,
0,
intent,
PendingIntent.FLAG_UPDATE_CURRENT
)
val notificationManager =
applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
when {
Build.VERSION.SDK_INT >= 26 -> {
val channel = NotificationChannel(
channelId,
channelName,
NotificationManager.IMPORTANCE_DEFAULT
)
notificationManager.createNotificationChannel(channel)
notificationBuilder
.setContentIntent(pendingIntent)
.setContentText(myBody)
.setSmallIcon(smallIcon)
.setContentTitle(myTitle)
}
Build.VERSION.SDK_INT >= 24 -> notificationBuilder
.setContentText(myBody)
.setContentTitle(myTitle)
.setSmallIcon(smallIcon)
.setContentIntent(pendingIntent)
else -> notificationBuilder
.setContentText(myBody)
.setContentTitle(myTitle)
.setSmallIcon(smallIcon)
.setContentIntent(pendingIntent)
}
notificationManager.notify(notificationId, notificationBuilder.build())
}
}
If you want to get notification when app is background or killed your json object has to be this like:
{
"data":{
"title" : "your_title",
"body" : "your_body"
},
"to": "device_token",
"priority": "high"
}
You can catch notification onMessageReceived

Categories

Resources