I developing an application that receives a push notification when there is an emergency alert and needs to play a sound even when the device is in silent mode.
the sound aslo need to be custom.
I tried all kinds of ways without any success.
How do you do it on Android?
#AndroidEntryPoint
class AlertFirebaseMessagingService : FirebaseMessagingService() {
companion object {
private const val CHANNEL_ID = "HEADS_UP_NOTIFICATION"
}
#Inject
lateinit var dbRepository : DBRepositoryImpl
#Inject
lateinit var networkRepository : RepositoryImpl
override fun onNewToken(token : String) {
super.onNewToken(token)
Log.d("hofitTest", token)
AppSharedPreferences.saveToken(token)
if (AppSharedPreferences.getPhoneNumber().isEmpty().not()) {
sendRegistrationToServer(token)
}
}
// The method is called every time it receives a notification from Firebase.
#RequiresApi(Build.VERSION_CODES.O)
override fun onMessageReceived(
remoteMessage : RemoteMessage) {
Log.d("hofitTest", "From: ${remoteMessage.from}")
if (remoteMessage.data.isNullOrEmpty().not()) {
val type = remoteMessage.data["type"] ?: "1"
sendNotification(
remoteMessage.data["title"] ?: "", remoteMessage.data["body"] ?: "", type)
Log.d("hofitTest", "Message data payload: ${remoteMessage.data}")
}
// } else if(remoteMessage.notification != null){
// //TODO change def 1
// sendNotification(remoteMessage.notification?.title ?: "", remoteMessage.notification?.body ?: "", "1")
// }
super.onMessageReceived(remoteMessage)
}
private fun sendNotification(title : String, body : String, type : String) {
createChannel()
val notification = NotificationCompat.Builder(this, CHANNEL_ID)
notification.setContentTitle(title)
notification.setContentText(body)
notification.priority = NotificationCompat.PRIORITY_HIGH
val intent = Intent(this, SplashActivity::class.java)
val pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE)
notification.setSmallIcon(R.drawable.icon_lifeguard_orange_rounded)
notification.color = ContextCompat.getColor(this, R.color.orange)
notification.setContentIntent(pendingIntent)
notification.setAutoCancel(true)
NotificationManagerCompat.from(this).notify(Random.nextInt(), notification.build())
CoroutineScope(Dispatchers.IO).launch {
dbRepository.saveEvent(
NotificationAlert(name = title ?: "", body = body ?: "", type = type, date = Date().time))
a()
}
}
private fun createChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
CHANNEL_ID, "Heads Up Notification", NotificationManager.IMPORTANCE_HIGH)
channel.enableLights(true)
channel.enableVibration(true)
channel.lightColor = Color.BLUE
channel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
getSystemService(NotificationManager::class.java).createNotificationChannel(channel)
}
}
}
use RingtoneManager - this not works in silent mode
try {
val notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE)
val r = RingtoneManager.getRingtone(LifeguardApplication.context, notification)
r.play()
} catch (e : Exception) {
e.printStackTrace()
}
Related
I'm trying to implement a notification always visible to play and stop a radio. I don't understand what I'm doing wrong. So Here i post my code:
I don't understand if something is missing for the notification to be shown or if I build the notification badly. In any case, the service works correctly if I activate the radio from the button I inserted in the Compose part, and if I close the app everything continues to work correctly, but I would like to understand why there is no notification at the top which allows you to activate or deactivate radio
Manifest
<service
android:name="com.example.appradio.services.MediaSessionService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON"/>
</intent-filter>
</service>
<receiver android:name="androidx.media.session.MediaButtonReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON"/>
</intent-filter>
</receiver>
MainActivity
Inside on create method:
ContextCompat.startForegroundService(
this#MainActivity.applicationContext,
Intent(this#MainActivity.applicationContext, MediaSessionService::class.java)
)
Service
class MediaSessionService : Service() {
var mediaPlayer: MediaPlayer? = null
private var mMediaNotificationManager: MediaNotificationManager? = null
private var mediaSession: MediaSessionCompat? = null
override fun onCreate() {
super.onCreate()
mediaPlayer = MediaPlayer()
mMediaNotificationManager = MediaNotificationManager(this)
mediaSession = MediaSessionCompat(this, "SOME_TAG")
mediaSession!!.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS or MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS)
mediaSession!!.setCallback(object : MediaSessionCompat.Callback() {
override fun onPlay() {
mediaPlayer!!.start()
}
override fun onPause() {
mediaPlayer!!.pause()
}
})
val notification = mMediaNotificationManager!!.getNotification(
metadata, state, mediaSession!!.sessionToken
)
startForeground(NOTIFICATION_ID, notification)
}
val metadata: MediaMetadataCompat
get() {
val builder = MediaMetadataCompat.Builder()
builder.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, "artist")
builder.putString(MediaMetadataCompat.METADATA_KEY_TITLE, "title")
builder.putLong(
MediaMetadataCompat.METADATA_KEY_DURATION, mediaPlayer!!.duration
.toLong()
)
return builder.build()
}
private val state: PlaybackStateCompat
private get() {
val actions =
if (mediaPlayer!!.isPlaying) PlaybackStateCompat.ACTION_PAUSE else PlaybackStateCompat.ACTION_PLAY
val state =
if (mediaPlayer!!.isPlaying) PlaybackStateCompat.STATE_PLAYING else PlaybackStateCompat.STATE_PAUSED
val stateBuilder = PlaybackStateCompat.Builder()
stateBuilder.setActions(actions)
stateBuilder.setState(
state,
mediaPlayer!!.currentPosition.toLong(),
1.0f,
SystemClock.elapsedRealtime()
)
return stateBuilder.build()
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
if ("android.intent.action.MEDIA_BUTTON" == intent.action) {
val keyEvent = intent.extras!!["android.intent.extra.KEY_EVENT"] as KeyEvent?
if (keyEvent!!.keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) {
mediaPlayer!!.pause()
} else {
mediaPlayer!!.start()
}
}
return super.onStartCommand(intent, flags, startId)
}
override fun onBind(intent: Intent): IBinder? {
return null
}
companion object {
const val NOTIFICATION_ID = 888
}
}
Notification Manager
class MediaNotificationManager(private val mService: MediaSessionService) {
private val mPlayAction: NotificationCompat.Action = NotificationCompat.Action(
R.drawable.ic_play,
"play",
MediaButtonReceiver.buildMediaButtonPendingIntent(
mService,
PlaybackStateCompat.ACTION_PLAY
)
)
private val mPauseAction: NotificationCompat.Action = NotificationCompat.Action(
R.drawable.ic_stop,
"pause",
MediaButtonReceiver.buildMediaButtonPendingIntent(
mService,
PlaybackStateCompat.ACTION_PAUSE
)
)
val notificationManager: NotificationManager = mService.getSystemService(Service.NOTIFICATION_SERVICE) as NotificationManager
fun getNotification(
metadata: MediaMetadataCompat,
state: PlaybackStateCompat,
token: MediaSessionCompat.Token
): Notification {
val isPlaying = state.state == PlaybackStateCompat.STATE_PLAYING
val description = metadata.description
val builder = buildNotification(state, token, isPlaying, description)
return builder.build()
}
private fun buildNotification(
state: PlaybackStateCompat,
token: MediaSessionCompat.Token,
isPlaying: Boolean,
description: MediaDescriptionCompat
): NotificationCompat.Builder {
if (isAndroidOOrHigher) {
createChannel()
}
val builder = NotificationCompat.Builder(mService, CHANNEL_ID)
builder.setStyle(
androidx.media.app.NotificationCompat.MediaStyle()
.setMediaSession(token)
.setShowActionsInCompactView(0) // For backwards compatibility with Android L and earlier.
.setShowCancelButton(true)
.setCancelButtonIntent(
MediaButtonReceiver.buildMediaButtonPendingIntent(
mService,
PlaybackStateCompat.ACTION_STOP
)
)
)
.setColor(ContextCompat.getColor(mService, R.color.black))
.setSmallIcon(R.drawable.ic_play) // Pending intent that is fired when user clicks on notification.
.setContentIntent(createContentIntent()) // Title - Usually Song name.
.setContentTitle(description.title) // When notification is deleted (when playback is paused and notification can be
// deleted) fire MediaButtonPendingIntent with ACTION_PAUSE.
.setDeleteIntent(
MediaButtonReceiver.buildMediaButtonPendingIntent(
mService, PlaybackStateCompat.ACTION_PAUSE
)
)
builder.addAction(if (isPlaying) mPauseAction else mPlayAction)
return builder
}
#RequiresApi(Build.VERSION_CODES.O)
private fun createChannel() {
if (notificationManager.getNotificationChannel(CHANNEL_ID) == null) {
val name: CharSequence = "MediaSession"
val description = "MediaSession and MediaPlayer"
val importance = NotificationManager.IMPORTANCE_LOW
val mChannel = NotificationChannel(CHANNEL_ID, name, importance)
mChannel.description = description
mChannel.enableLights(true)
mChannel.lightColor = Color.RED
mChannel.enableVibration(true)
mChannel.vibrationPattern = longArrayOf(100, 200, 300, 400, 500, 400, 300, 200, 400)
notificationManager.createNotificationChannel(mChannel)
Timber.d("createChannel: New channel created")
} else {
Timber.d("createChannel: Existing channel reused")
}
}
private val isAndroidOOrHigher: Boolean
get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
private fun createContentIntent(): PendingIntent {
val openUI = Intent(mService, MainActivity::class.java)
openUI.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
return PendingIntent.getActivity(
mService, REQUEST_CODE, openUI, PendingIntent.FLAG_CANCEL_CURRENT
)
}
companion object {
const val NOTIFICATION_ID = 412
private const val CHANNEL_ID = "com.example.appradio.musicplayer.channel"
private const val REQUEST_CODE = 501
}
init {
notificationManager.cancelAll()
}
}
I am trying to send a data notification to my app and then use the data therein to open a fragment:
override fun onMessageReceived(message: RemoteMessage) {
Timber.d("onMessageReceived")
try {
val data = message.data
if (data != null && (data.containsKey(KEY_MSG) || data.containsKey(KEY_URL))) {
val url = data[KEY_URL]
if (!url.isNullOrEmpty()) {
val clickAction = message.notification?.clickAction
val intent = Intent(clickAction)
intent.putExtra(KEY_URL, url).putUseStateExtra(UseState(UseState.COME_FROM_NOTIFICATION)).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
} else {
sendNotification(data)
}
}
} catch (e: Throwable) {
Timber.e(e, "We didn't send the notification because ${e.message}")
}
}
I then after onMessageReceived() is called I build the notification and send it with the following methods. One to parse out the payload:
private fun sendNotification(data: Map<String, String>) {
Timber.d("Notification sent: $data type: ${data[KEY_CLOUD8_TYPE]}")
if (Interactors.preferences.notificationsEnabled == true) {
Timber.d(data.toString())
val title = data[KEY_TITLE]
val msg = data[KEY_MSG]
var cloud8Type = data[KEY_CLOUD8_TYPE] ?: ""
var notificationType = data[NOTIFICATION_TYPE] ?: ""
val campaignId = (data[KEY_CAMPAIGN_ID] ?: "0")
val url = data[KEY_URL]
if (!url.isNullOrBlank()) {
cloud8Type = Cloud8Type.Link
}
sendNotification(title, msg, cloud8Type, notificationType, campaignId, url)
}
}
One to build the notification:
private fun sendNotification(title: String?, message: String?, cloud8Type: String, notificationType: String, offerId: String, url: String?) {
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val channelId = "Main"
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val notificationChannel = NotificationChannel(channelId, "My Notifications", NotificationManager.IMPORTANCE_HIGH)
// Configure the notification channel.
notificationChannel.description = "Channel description"
notificationChannel.enableVibration(true)
notificationManager.createNotificationChannel(notificationChannel)
}
val pendingIntent = getNotificationIntent(cloud8Type, notificationType, offerId, url)
val defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
val notificationBuilder = NotificationCompat.Builder(this, channelId)
notificationBuilder.setSmallIcon(R.drawable.ic_notification)
notificationBuilder.setContentTitle(title)
notificationBuilder.setContentText(message)
notificationBuilder.setAutoCancel(true)
notificationBuilder.setSound(defaultSoundUri)
notificationBuilder.setContentIntent(pendingIntent)
notificationManager.notify(0, notificationBuilder.build())
}
And two to use the content of the payload to build an intent:
private fun getNotificationIntent(cloud8Type: String, notificationType: String, offerId: String, url: String?): PendingIntent {
Timber.d("Notification type: $cloud8Type}")
val useState = UseState(UseState.COME_FROM_NOTIFICATION)
val intent = getNotificationIntent(this, cloud8Type, notificationType, useState, offerId, url = url)
return PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
}
internal fun getNotificationIntent(
context: Context,
cloud8Type: String?,
notificationType: String,
useState: UseState,
offerId: String?,
url: String?
): Intent {
var intent = Intent()
when (cloud8Type) {
Cloud8Type.NewOffer, Cloud8Type.NewChallengeOffer, Cloud8Type.Link ->
intent = StartActivity.newInstance(context, useState, offerId, url)
Cloud8Type.DailyEarning, Cloud8Type.YouDidIt, Cloud8Type.FundsTransfered, Cloud8Type.OfferPayment, Cloud8Type.OfferDonation -> {
intent = if (Interactors.preferences.the8CloudSdkInfo.showPayoutTab) {
openSponsorTree(context, useState, ASponsorTree.TAB_PAYOUT, null)
} else {
APayoutMain.newIntent(context)
}
}
Cloud8Type.NewOffers ->
intent = openSponsorTree(context, useState, ASponsorTree.TAB_FEED, null)
else -> {
when (notificationType) {
NotificationType.Payment -> intent = openSponsorTree(context, useState, ASponsorTree.TAB_PAYOUT, null)
}
}
}
return intent
}
I'm trying to debug the payload being received when the notification comes, but none of my log statements are showing up when the app is closed. Is there any way to see what is coming back with the RemoteMessage in onMessageReceived()? Is there anyting else I should know about how to accomplish what I want to accomplish?
I added the "notification" attribute to the payload and gave it a click_action, and then intercepted it in my start activity.
Please don't send your payload in the notification attribute. It will only hit when the app is in the foreground state. For getting notification also while in background state you have to send your payload into data attribute, not notification.
Example :
{
"condition": " Better now",
"priority" : "normal",
"time_to_live" : 0,,
"data" : {
"id" : 1,
"text" : "text is here!",
"link" : "www.gmail.com"
}
}
i am using Kotlin Navigation component architecture for my chat app, and i am using Firebase Messaging service to integrate push notification, my requirement is to hide or disable the notifications when i am on User chat screen.Please let me know, how can i achieve this.
this is my code of displaying notification
class MyFirebaseMessagingService : FirebaseMessagingService() {
override fun onMessageReceived(remoteMessage: RemoteMessage?) {
Log.d(TAG, "From: ${remoteMessage?.from}")
remoteMessage?.data?.let {
Log.d(TAG, "data payload: " + remoteMessage.data.toString())
val params =remoteMessage.data.get("body")
val objects = JSONObject(params)
Log.e("JSON OBJECT", objects.toString())
val title = remoteMessage.data.get("title").toString()
sendNotification(messageBody,title, applicationContext)
} }
my notification class is:
fun NotificationManager.sendNotification(messageBody: String, title: String, applicationContext: Context) {
notificationManager = applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
// TODO: Step 1.12 create PendingIntent
if(title.equals("Ride Request")) {
fragmentId = R.id.notificationFragment
}
else if(title.equals("Ride Accepted")) {
fragmentId = R.id.inboxFragment
}
else if(title.equals("New Message")) {
fragmentId = R.id.inboxFragment
}
// ---------- creating navgraph intent to open specific fragment ------------
var contentPendingIntent = NavDeepLinkBuilder(applicationContext)
.setComponentName(HomeActivity::class.java)
.setGraph(R.navigation.home_bottom_navigation)
.setDestination(fragmentId)
.setArguments(bundle)
.createPendingIntent()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
notificationChannel = NotificationChannel(channel_id, description,
NotificationManager.IMPORTANCE_HIGH)
notificationChannel.enableLights(true)
notificationChannel.lightColor = R.color.colorPrimary
notificationChannel.enableVibration(true)
notificationManager.createNotificationChannel(notificationChannel)
builder = NotificationCompat.Builder(applicationContext, channel_id)
.setSmallIcon(R.drawable.icon_car)
.setContentTitle(applicationContext.getString(R.string.app_name))
.setContentText(messageBody)
.setContentIntent(contentPendingIntent)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setAutoCancel(true)
}else {
builder = NotificationCompat.Builder(applicationContext,
applicationContext.getString(R.string.app_name))
.setSmallIcon(R.drawable.icon_car)
.setContentTitle(applicationContext.getString(R.string.app_name))
.setContentText(messageBody)
.setContentIntent(contentPendingIntent)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setAutoCancel(true)
}
notify(NOTIFICATION_ID, builder.build())
}
I don't if this is the perfect but you can try it as well
If the user is in chat screen you can create a sharedPref and set value of the variable to true (that the user is in chat screen)
So in the onMessageReceived() check it the value of the sharedPref is true (which mean the user is in chat screen)
If it's true then don't send notifications if false send notification
To set the sharedPref value
In the onResume of that chat activity set to true
In the onPause set to false
try this
class InboxFragment : Fragment() {
companion object {
var userId: String? = null
}
override fun onStart() {
userId = navArgs.userId
}
override fun onStop() {
userId = null
}
}
class MyFirebaseMessagingService : FirebaseMessagingService() {
override fun onMessageReceived(remoteMessage: RemoteMessage) {
...
if (InboxFragment.userId != remoteMessage.data.get("userId"))
sendNotification(messageBody, title, applicationContext)
}
}
My chat application crashes with the above error when its time to display notifications
class MyFirebaseMessaging() : FirebaseMessagingService() {
lateinit var message:Notification.MessagingStyle.Message
val KEY_TEXT_REPLY = "key_text_reply"
#RequiresApi(Build.VERSION_CODES.KITKAT_WATCH)
override fun onMessageReceived(mRemoteMessage: RemoteMessage) {
Log.d("notif","called 1")
super.onMessageReceived(mRemoteMessage)
val sented = mRemoteMessage.data["sented"]
val user = mRemoteMessage.data["user"]
val sharedPref = getSharedPreferences("PREFS", Context.MODE_PRIVATE)
val currentOnlineUser = sharedPref.getString("currentUser", "none")
val firebaseUser = FirebaseAuth.getInstance().currentUser
if (firebaseUser !== null && sented == firebaseUser.uid) {
if (currentOnlineUser !== user) {
sendOreoNotification(mRemoteMessage)
}
}
}
#SuppressLint("NewApi")
#RequiresApi(Build.VERSION_CODES.KITKAT_WATCH)
private fun sendOreoNotification(mRemoteMessage: RemoteMessage) {
val user = mRemoteMessage.data["user"]
val body = mRemoteMessage.data["body"]
val name = mRemoteMessage.data["name"]
Log.d("notif","called")
val replyLabel = "Enter your reply here"
val remoteInput = RemoteInput.Builder(KEY_TEXT_REPLY)
.setLabel(replyLabel)
.build()
val j = user!!.replace("[\\D]".toRegex(), "").toInt()
val intent = Intent(this, HelperActivity::class.java)
val bundle = Bundle()
val pendingIntent =
PendingIntent.getActivity(this, j, intent, PendingIntent.FLAG_ONE_SHOT)
bundle.putString("userid", user)
intent.putExtras(bundle)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
var i = 0
if (j > 0) {
i = j
}
val oreoNotification = OreoNotification(this)
// if (!appInForeground(applicationContext)) {
val icon_ = Icon.createWithResource(applicationContext,
android.R.drawable.ic_dialog_info)
val replyAction = Notification.Action.Builder(
icon_,
"Reply", pendingIntent)
.addRemoteInput(remoteInput)
.build()
val userP = android.app.Person.Builder().setName(name).setKey(user).setIcon(
Icon.createWithResource(this, R.mipmap.ic_launcher)).build()
message = body?.let {
Notification.MessagingStyle.Message(
it,
Date().time, userP)
}!!
Log.d("notif","recieved :${message.text}")
val style = Notification.MessagingStyle(userP).addMessage(message)
val builder: Notification.Builder =
oreoNotification.getOreoNotification(pendingIntent, replyAction, style)
Log.d("notif","$body")
oreoNotification.getManager?.notify(i, builder.build())
// } else {
// oreoNotification.getManager!!.cancel(i)
// }
}
private fun appInForeground(context: Context): Boolean {
val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
val runningAppProcesses = activityManager.runningAppProcesses ?: return false
return runningAppProcesses.any { it.processName == context.packageName && it.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND }
}
}
OreoNotification.kt file
val getManager :NotificationManager? get() {
if(notificationManager==null){
notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
}
return notificationManager
}
#TargetApi(Build.VERSION_CODES.O)
fun getOreoNotification(pendingIntent: PendingIntent?,replyAction:Notification.Action,style: Notification.Style)
:Notification.Builder{
return Notification.Builder(applicationContext, OreoNotification.CHANNEL_ID)
.setContentIntent(pendingIntent)
.setContentTitle("Vcare")
.setAutoCancel(true)
.addAction(replyAction)
.setStyle(style)
}
Logcat of the crash
2020-09-25 23:54:32.658 7896-8031/com.example.vcare E/AndroidRuntime: FATAL EXCEPTION: Firebase-Messaging-Intent-Handle
Process: com.example.vcare, PID: 7896
java.lang.RuntimeException: User must be valid and have a name.
at android.app.Notification$MessagingStyle.validate(Notification.java:6690)
at android.app.Notification$Builder.build(Notification.java:5573)
at com.example.vcare.notifications.MyFirebaseMessaging.sendOreoNotification(MyFirebaseMessaging.kt:105)
at com.example.vcare.notifications.MyFirebaseMessaging.onMessageReceived(MyFirebaseMessaging.kt:39)
at com.google.firebase.messaging.FirebaseMessagingService.dispatchMessage(com.google.firebase:firebase-messaging##20.2.4:85)
at com.google.firebase.messaging.FirebaseMessagingService.passMessageIntentToSdk(com.google.firebase:firebase-messaging##20.2.4:55)
at com.google.firebase.messaging.FirebaseMessagingService.handleMessageIntent(com.google.firebase:firebase-messaging##20.2.4:34)
at com.google.firebase.messaging.FirebaseMessagingService.handleIntent(com.google.firebase:firebase-messaging##20.2.4:23)
at com.google.firebase.messaging.EnhancedIntentService.lambda$processIntent$0$EnhancedIntentService(com.google.firebase:firebase-messaging##20.2.4:43)
at com.google.firebase.messaging.EnhancedIntentService$$Lambda$0.run(Unknown Source:6)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at com.google.android.gms.common.util.concurrent.zza.run(com.google.android.gms:play-services-basement##17.2.1:6)
at java.lang.Thread.run(Thread.java:764)
This line seems to be erroneous according to logcat (kt:105)
oreoNotification.getManager?.notify(i, builder.build())
the app crashes as soon as i send a message from another device and its time to display notifications.
What is this supposed to mean?
java.lang.RuntimeException: User must be valid and have a name.
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
}