Create new Notifcation Channels - android

I am working in a screen where the user is able to customize the notification (for example: Light color, vibration...) and re-customize anytime the user wants to (so the user is able to turn the light off and on anytime he/she wants to).
So far i did this:
private fun createNotification(
title: String,
details: String,
vibration: Boolean,
light: Boolean,
lightColor: Int? = null
) {
val channel = NotificationChannel(
CHANNEL_ID,
CHANNEL_NAME,
NotificationManager.IMPORTANCE_DEFAULT
).apply {
enableVibration(vibration)
vibrationPattern = longArrayOf(0, 400, 200, 400, 200, 400)
enableLights(light)
setLightColor(lightColor ?: Color.GREEN)
}
notificationManager.deleteNotificationChannel(CHANNEL_ID)
notificationManager.createNotificationChannel(channel)
}
But the problem is the deleteNotificationChannel function reads as the following:
If you create a new channel with this same id, the deleted channel will be un-deleted with all of the same settings it had before it was deleted.
How should i approach this situation? Should i store some counter number in the SharedPreferences or what should i do?

Yes, you can concat counter to the channel id, delete the previous one, create with the new one. You can skip SharedPreferences though, from the previous id, extract the count and increment it.

Related

Upload data to server from background through FirebaseMessegingService class

In my app I am using FCM to get notifications. And everything is working when my app is running in foreground but problem starts when app goes in background or if app gets killed from background, in this case I am getting the notification but data is not uploading on the server.
For uploading data to the server I am using a separate class where all the required code is written and I am Just calling it in the FirebaseMessagingService() class.
I tried all possible ways, I tried to make separate service but it didn't work.
But I want to upload data to server When I am getting notification.
here is my FirebaseMessegingService Class
class FirebaseShareDevMessagingService : FirebaseMessagingService() {
private var resultIntent: Intent? = null
override fun onNewToken(p0: String) {
super.onNewToken(p0)
}
var mNotificationManager: NotificationManager? = null
#RequiresApi(Build.VERSION_CODES.O)
override fun onMessageReceived(remoteMessage: RemoteMessage) {
super.onMessageReceived(remoteMessage)
Log.d("remoteMessage", "onMessageReceived: ${remoteMessage.data}")
// playing audio and vibration when user se request
val notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
val r = RingtoneManager.getRingtone(this, notification)
r.play()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
r.isLooping = false
}
val data: MutableMap<String, String> = remoteMessage.data
val deviceId = data["username"]
val deviceName = data["address"]
val notificationType = data["notificationType"]
//codes
....
....
val pendingIntent =
PendingIntent.getActivity(this, 1, resultIntent, PendingIntent.FLAG_UPDATE_CURRENT)
builder.setContentTitle(remoteMessage.notification!!.title)
builder.setContentText(remoteMessage.notification!!.body)
builder.setContentIntent(pendingIntent)
builder.setVibrate(longArrayOf(100, 300, 300, 300))
builder.setStyle(
NotificationCompat.BigTextStyle().bigText(
remoteMessage.notification!!.body
)
)
builder.setAutoCancel(true)
mNotificationManager =
applicationContext.getSystemService(NOTIFICATION_SERVICE) as NotificationManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channelId = "Your_channel_id"
val channel = NotificationChannel(
channelId,
"Channel human readable title",
NotificationManager.IMPORTANCE_DEFAULT
)
mNotificationManager!!.createNotificationChannel(channel)
builder.setChannelId(channelId)
}
mNotificationManager!!.notify(System.currentTimeMillis().toInt(), builder.build())
val cal = TimeStampConverter.getTimeStamp(remoteMessage.sentTime.toString())
Log.d("OnReceivedNotification", "onMessageReceived:$remoteMessage ")
// here i am calling helper class to upload data to
// server.....
BackendServerHelperClass(applicationContext).saveNotification(
System.currentTimeMillis().toInt(),
remoteMessage.notification!!.title!!,
remoteMessage.notification!!.body!!,
FirebaseAuth.getInstance().uid.toString(),
cal.time.toString(),
deviceId,
deviceName,
isAllowedSubLetting.toBoolean(),
notificationType!!.toInt() )
}
}
Please help, if there is any way to achieve this.
Updated
I tried to give permission of Screen overlay but still its not working
Help will be appreciated:)
Thanks
After a lot of research i found solution.
If we want to receive notification in background and want to do some other stuffs in background if notification is received so we should simply sent the "data" section instead "notification" section.
if we sent both "data" and "notification" then we will receive a notification in background but if you are doing some other stuffs in "onMessageReceive" like saving data to ROOM DATABASE or uploading some data to SERVER. , then you should sent only "data" section.
example of data section:
{
"message":{
"token":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
"data":{
"Nick" : "Mario",
"body" : "great match!",
"Room" : "PortugalVSDenmark"
}
}
}
This solved my problem.
You can read more about this on official FCM documentation.
Thanks!! Happy coding:)

Issue with receiving notifications when app is in background

I am using Firebase FCM service for my android app. Facing the listed down issues in it.
Sometimes when app receives notifications it doesn't sound. Below is the code snippet.
private fun createNotificationChannel(
context: Context, id: String, name: String, description: String,
path: Uri?, dnd: Boolean, importance: Int,
notificationManager: NotificationManager
) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val existingChannel =
notificationManager.getNotificationChannel(id)
if (existingChannel != null) {
notificationManager.deleteNotificationChannel(id)
}
val mAudioManager =
context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
mAudioManager.setStreamVolume(
AudioManager.STREAM_ALARM,
mAudioManager.getStreamMaxVolume(AudioManager.STREAM_ALARM),
0
)
val channel = NotificationChannel(id, name, importance)
channel.description = description
channel.enableLights(true)
channel.setShowBadge(true)
channel.describeContents()
channel.setBypassDnd(dnd)
channel.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
channel.lightColor = R.color.red
channel.enableVibration(true)
if (id == CHANNEL_ID_NORMAL_PRIORITY) {
if (!SharedPrefUtils.getSharedPrefIsAppInForeground(context)) {
channel.setSound(
path,
AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_ALARM)
.build()
)
}
} else if (id == CHANNEL_ID_HIGH_PRIORITY) {
channel.setSound(
path,
AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_ALARM)
.build()
)
}
notificationManager.createNotificationChannel(channel)
}
}
If App is not running for long time then the method onMessageReceived() of firebase messaging service is not getting called. In this case, when the app comes to the foreground, I am receiving notifications.
In push notification, I am sending only data payload because when the app is in background (either running or closed) and receives notification, if we use notification payload it won't set large icon to the banner. Below is code snippet for Push Notification.
AndroidConfig androidConfig = AndroidConfig.builder()
.setTtl(3600 * 1000)
.setPriority(AndroidConfig.Priority.HIGH)
//.setNotification(androidNotification)
.putData("title", title)
.putData("body", StringEscapeUtils.unescapeJava(content))
.putData("icon", icon)
.putData("sound", soundData)
.putData("channelid", channelId)
.build();
Message msg = Message.builder()
.setAndroidConfig(androidConfig)
.setToken(token)
.build();
String response = FirebaseMessaging.getInstance().send(msg);
Just like Micheal pointed out in the comment, this is (unfortunately) the expected behavior of the FCM when using the notification field.
Read more in the Handling messages section of the documentation. And the important bit:
The usual fix for such scenarios is to change the way FCMs are structured:
Do not use notification field (leave it completely unused), but use the data field and build your local push notifications based on that.

How do I make sure my notification sounds play reliably on all devices?

My app sets up two notification channels, each with its own sound effect. But users are saying the sounds aren't working right on some devices, like the Pixel. Here's my setup. Is there anything I could do to improve the channel settings to ensure that the sounds (two .mp3 files kept in res/raw/) play as reliably as possible (as background notifications, not foreground)? Are there better audio settings or formats or configuration that I am missing?
I'm using Firebase Cloud Messaging. Here's the code creating the channels:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val importance = NotificationManager.IMPORTANCE_HIGH
val audioAttributes = AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_ALARM)
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setFlags(FLAG_AUDIBILITY_ENFORCED)
.build()
val newChannel = NotificationChannel(channelId, name, importance)
newChannel.description = description
newChannel.importance = IMPORTANCE_HIGH
newChannel.setSound(sound, audioAttributes)
return newChannel
}
And here's code for obtaining the sound URI:
private fun createSoundUri(soundNum: Int, context: Context):Uri? {
val scheme = ContentResolver.SCHEME_ANDROID_RESOURCE
val packageName = context.packageName
val root = "$scheme://$packageName/"
return when(soundNum) {
0 -> Uri.parse(root + R.raw.sound1)
1 -> Uri.parse(root + R.raw.sound2)
2 -> Settings.System.DEFAULT_NOTIFICATION_URI
else -> null
}
}
I received data using FCM then started an activity initiating the media player in my app.
MediaPlayer mediaPlayer = MediaPlayer.create(this,R.raw.ringtone);
mediaPlayer.setLooping(true);
mediaPlayer.start();
Maybe you could use this idea

How to Properly Create Notification for Firestore Collection Query?

So, I successfully implement notification when a Document added in a Firestore Collection,
this is the code
snip>code updated below
It works perfectly except there is one problem,
Every time I close the App and then Re-Open it, the Notification is appearing again. is there any way to make the notification appear only once after a new document added?
EDIT : I've tried adding timestamp and if condition but it does not work,
val nVoucher = HashMap<String, Any>()
nVoucher["timestamp"] = Timestamp.now().seconds
New Code
db!!.collection("voucher")
.whereGreaterThan("jumlah", 0).addSnapshotListener { documentSnapshots, e ->
if (e != null) {
Log.d(TAG, "Error :" + e.message)
}
if (documentSnapshots != null) {
documentSnapshots.query
for (doc in documentSnapshots.documentChanges) {
if (doc.type == DocumentChange.Type.ADDED) {
run {
val nvoucher = doc.document.toObject<DetilVoucher>(DetilVoucher::class.java)
nvoucher.docID = doc.document.id
voucher!!.add(nvoucher)
val judul = doc.document.get("judul").toString()
val gambar = doc.document.get("gambar").toString()
val docTime = doc.document.get("timestamp")
val timenow = Timestamp.now().seconds
if (timenow == docTime) {
remoteViews!!.setImageViewResource(R.id.remoteview_notification_image, android.R.drawable.ic_dialog_info)
remoteViews!!.setTextViewText(R.id.remoteview_notification_headline, "Voucher Baru")
remoteViews!!.setTextViewText(R.id.remoteview_notification_short_message, judul)
val notifID = 101
val channelID = "com.sample.notification"
val notification = Notification.Builder(context, channelID)
.setContentTitle("Voucher Baru")
.setContentText(judul)
.setSmallIcon(android.R.drawable.ic_dialog_info)
.setChannelId(channelID)
.setCustomContentView(remoteViews)
.setContentIntent(pIntent)
.build()
val notificationTarget: NotificationTarget = NotificationTarget(
context,
R.id.remoteview_notification_image,
remoteViews,
notification,
notifID
)
Glide.with(this#Home.context!!.applicationContext).asBitmap().load(gambar).into(notificationTarget)
notificationManager!!.notify(notifID, notification)}
vouchaerAdapter!!.notifyDataSetChanged()
}
}
}
}
}
notificationManager = this#Home.activity!!.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
createNotificationChannel(
"com.sample.notification",
"Voucher Baru",
"Voucher Promo"
)
private fun createNotificationChannel(id: String, judul: String, detil: String) {
val importance : Int = NotificationManager.IMPORTANCE_LOW
val channel = NotificationChannel(id, judul, importance)
channel.description = detil
channel.enableLights(true)
channel.lightColor = Color.RED
channel.enableVibration(true)
channel.vibrationPattern = longArrayOf(100, 200, 300, 400, 500, 400, 300, 200, 400)
notificationManager!!.createNotificationChannel(channel)
}
When you run the app again, you're re-attaching a listener to the collection/query. And every time you do that, docChanges will fire with doc.type == DocumentChange.Type.ADDED for each document that exists in the collection/query.
If you don't want to take action on documents you've acted on before, you'll have to track that yourself. Typically you do this by storing some data in local storage, or in the database for the user if it needs to work across devices.
The simplest scenario is if the documents have some form of creation/modification date, because then you can just store the timestamp when you last handled the documents.
If there's no timestamp (and you can't add one), you'll have to track which documents you've processed on an individual document level. While that's definitely possible, it's a lot more house keeping, so I'd definitely try the timestamp based approach first.
In your updated code you're comparing the timestamp in the document to the current time, which is unlikely every to be true. You'll want something like:
val timestamp = getTimestampFromSharedPreferences();
val now = Timestamp.now().seconds
for (doc in documentSnapshots.documentChanges) {
val docTimestamp = doc.document.get("timestamp")
if (timestamp == null || timestamp < docTimestamp) {
...
}
setTimestampToSharedPreferences(now);
}

Notifications not working when app is swiped away

I'm writing a simple app that sets up to 8 random, repeating alarms, sends a notification, and then generates a quote when the user taps on the notification. This all seems to work properly when the app is running but when the app is swiped away from the recent apps, or force closed, the notifications don't work.
I've poured over my research from the last several days and can't find a solution that's current or fixes my problem. The most common thing I've seen is to use onReceive to set up a service, but my reading has shown me that this no longer works with Oreo and is outdated advice. I've also seen some stuff about foreground services, but I'm really not looking to have a persistent notification bothering the user.
I've also seen some people say to do some work in onDestroy, but that hasn't worked for me either. A lot of the stuff I've found has said that this kind of behavior is "expected behavior", as the system assumes that if an app is swiped away, the user no longer wants it doing anything. I don't want this happening and there must be some way around it, since reminders and notifications from other apps are able to come through.
Any help would be greatly appreciated, I've been struggling with this for a long time. I'll post my code for setting alarms below, as well as the code for setting up the notification channels and the BroadcastReceiver class.
By the way, my test device is a Pixel 2XL with Android 9.
//method to save preferences when the user clicks "SAVE"
private fun saveData() {
if (NOTIFICATIONS_PER_DAY > 0) {
setAlarms()
} else {
clearAlarms() //clearing if the user is removing notifications
}
val sharedPreferences = activity!!.getSharedPreferences(SHARED_PREFS, MODE_PRIVATE)
val editor = sharedPreferences.edit()
editor.putString(THEME_PREF, THEME_SELECTED)
editor.putInt(NOTS_PREF, NOTIFICATIONS_PER_DAY)
editor.apply()
Toast.makeText(context, "Settings saved", Toast.LENGTH_SHORT).show()
}//saveData method
//method to set repeating notification alarms (random times)
private fun setAlarms() {
//clearing any previously saved alarms to prevent tons of extra
clearAlarms()
calList.clear()
var hour: Int
var minute: Int
for (i in 0 until (NOTIFICATIONS_PER_DAY)) {
val hourRand = (0..23).random()
val minuteRand = (0..59).random()
hour = hourRand
minute = minuteRand
val cal = Calendar.getInstance()
cal.set(Calendar.HOUR_OF_DAY, hour)
cal.set(Calendar.MINUTE, minute)
cal.set(Calendar.SECOND, 0)
calList.add(cal)
}//for
var i = 0
for (cal in calList) {
val alarmManager = context!!.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val intent = Intent(context, AlertReceiver::class.java)
val pendingIntent = PendingIntent.getBroadcast(context, i, intent, 0)
alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, cal.timeInMillis, AlarmManager.INTERVAL_DAY, pendingIntent)
println(i)
i++
}//for
}//setAlarms method
class BetterDays : Application() {
override fun onCreate() {
super.onCreate()
createNotificationChannels()
}
private fun createNotificationChannels() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel1 = NotificationChannel(CHANNEL_1_ID, "Quote Channel", NotificationManager.IMPORTANCE_DEFAULT).apply { description = "New quotes notification" }
channel1.enableLights(true)
channel1.enableVibration(true)
//channel1.description = "New quotes notification"
/* val channel2 = NotificationChannel(CHANNEL_2_ID, "New Quote!", NotificationManager.IMPORTANCE_DEFAULT)
channel2.enableLights(true)
channel2.enableVibration(true)
channel2.description = "New quotes notification" */
val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
manager.createNotificationChannel(channel1)
//manager.createNotificationChannel(channel2)
}
}//createNotificationChannels method
companion object {
val CHANNEL_1_ID = "quotes notification"
val CHANNEL_2_ID = "quotes notification 2"
}
}
class AlertReceiver : BroadcastReceiver() {
private var notificationManager: NotificationManagerCompat? = null
private var theContext: Context? = null
override fun onReceive(context: Context, intent: Intent) {
notificationManager = NotificationManagerCompat.from(context)
theContext = context
sendOnChannel1()
}//onReceive method
private fun sendOnChannel1() {
val title = "New Quote Available"
val message = "Come check it out!"
var index: Int = 0
if(quotesList.size != 0) {
index = Random.nextInt(quotesList.size)
}//if
quoteText = quotesList[index]
speakerText = speakersList[index]
quoteTextView?.text = quotesList[index]
speakerTextView?.text = speakersList[index]
val intent = Intent(theContext!!, MainActivity::class.java)
intent.putExtra("From", "quotesFragment")
val pendingIntent: PendingIntent = PendingIntent.getActivity(theContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
val notification = NotificationCompat.Builder(theContext!!, CHANNEL_1_ID)
.setSmallIcon(R.drawable.ic_quotes)
.setContentTitle(title)
.setContentText(message)
.setAutoCancel(true)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setCategory(NotificationCompat.CATEGORY_MESSAGE)
.setContentIntent(pendingIntent)
.build()
val id = createID()
notificationManager!!.notify(id, notification)
}//sendOnChannel1 method
/* //for future functionality
fun sendOnChannel2() {
val title = "Title"
val message = "Message"
val notification = NotificationCompat.Builder(theContext!!, CHANNEL_2_ID)
.setSmallIcon(R.drawable.ic_quotes)
.setContentTitle(title)
.setContentText(message)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setCategory(NotificationCompat.CATEGORY_MESSAGE)
.build()
notificationManager!!.notify(2, notification)
}//sendOnChannel2 method*/
//method to generate a unique ID
private fun createID(): Int{
val now = Date()
val id = Integer.parseInt(SimpleDateFormat("ddHHmmss", Locale.US).format(now))
return id
}//createID method
}//AlertReceiver class
Some chinese device with their own modified android system so when the apps are swiped from the recent app tray your app gets terminated (similar to Force Stop). And due to this every task running in the background like Services, Jobs gets killed with the app. Even High priority FCM doesn’t see the daylight in Chinese ROMs.
you can read in here : https://medium.com/mindorks/enable-background-services-in-chinese-roms-32e73dfba1a6
maybe can help ;)

Categories

Resources