How to Properly Create Notification for Firestore Collection Query? - android

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);
}

Related

Android Bad notification posted Crash

I'm new to coding. I have made a net speed indicator app for fun. It displays the network speed as a status bar icon. It also allows users to set data usage limits (like 100 GB per month) and sends a notification when they reach the limit.
The final output looks like this:
I followed this answer to display the status bar icon.
Here is the Notifications class I use:
class Notifications(private val context: Context) {
private val notificationChannelPrimary =
"Primary Channel Notification" // This is the persistent channel that shows live data usage
val notificationPersistentChannelID = 15 // To send notifications of persistent channel
private val notificationChannelDataLimit = "Data Limit Warning Notifications"
private val notificationDataLimitID = 10
private val intentNotification = Intent(context, MainActivity::class.java)
private val notificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
private var mNotificationBuilder: Notification.Builder? = null
private var tempValues = arrayOf(String())
// The following variables are for creating status bar icon
private val bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888)
private val canvas = Canvas(bitmap)
private val paintSpeed = Paint() // This is for speed value
private val paintUnit = Paint() // This is for speed unit
init {
// Set the paint styles
paintSpeed.color = Color.WHITE
paintSpeed.isAntiAlias = true
paintSpeed.textSize = 60f
paintSpeed.textAlign = Paint.Align.CENTER
paintSpeed.typeface = Typeface.DEFAULT_BOLD
paintUnit.color = Color.WHITE
paintUnit.isAntiAlias = true
paintUnit.textSize = 40f
paintUnit.textAlign = Paint.Align.CENTER
paintUnit.typeface = Typeface.DEFAULT_BOLD
createNotificationChannel()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
mNotificationBuilder =
getNotificationBuilder()
}
}
// This method sets the speed to zero when the device is offline
fun setZeroSpeed() {
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)
canvas.drawText("0", 48f, 52f, paintSpeed)
canvas.drawText("Kb/s", 48f, 95f, paintUnit)
try {
mNotificationBuilder?.setSmallIcon(Icon.createWithBitmap(bitmap))
notificationManager.notify(notificationPersistentChannelID, mNotificationBuilder?.build())
} catch (e: Exception) {
e.printStackTrace()
}
}
// This method shows the speed + data usage on the notification
// It is called every second (from a worker thread)
fun updateDataUsage(
dataSpeed: Long,
remainingData: String?,
planDetails: String?
) {
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)
// BytesConversion.convertSpeed() method takes long value and returns speed with unit array (like 2 Mb/s)
tempValues = BytesConversion.convertSpeed(dataSpeed)
canvas.drawText(tempValues[0], 50f, 50f, paintSpeed)
canvas.drawText(tempValues[1], 50f, 95f, paintUnit)
// If the argument of setContentTitle is null, it's not displaying anything in the title
mNotificationBuilder?.setContentTitle(remainingData)
mNotificationBuilder?.setContentText(planDetails)
try {
mNotificationBuilder?.setSmallIcon(Icon.createWithBitmap(bitmap))
notificationManager.notify(notificationPersistentChannelID, mNotificationBuilder?.build())
} catch (e: Exception) {
}
}
// This method sends notifications when the data usage limit is reached
// You don't need to check supported or unsupported
// because it works in all the devices
// It's just a normal notification
fun sendDataLimitWarning(dataUsageTitle: String?, dataUsageDescription: String?) {
val notificationDataLimit = getNotificationBuilderDataLimit()
notificationDataLimit.setContentText(dataUsageDescription)
notificationDataLimit.setContentTitle(dataUsageTitle)
notificationManager.notify(
notificationDataLimitID,
notificationDataLimit.build()
)
}
// Notification channel is only available from Android Oreo
// So, check the condition
private fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val notificationChannels = mutableListOf<NotificationChannel>()
val notificationChannel1 = NotificationChannel(
notificationChannelPrimary, "Persistent Notification",
NotificationManager.IMPORTANCE_HIGH
)
notificationChannel1.description = "Notification that shows data usage and speed"
notificationChannel1.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
val notificationChannel2 = NotificationChannel(
notificationChannelDataLimit, "Data Limit Warning",
NotificationManager.IMPORTANCE_HIGH
)
notificationChannel2.description = "Notification that shows data limit warnings"
notificationChannel2.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
notificationChannel2.enableVibration(true)
notificationChannel2.enableLights(true)
notificationChannels.add(notificationChannel1)
notificationChannels.add(notificationChannel2)
notificationManager.createNotificationChannels(notificationChannels)
}
}
// This is for sending data limit warnings
private fun getNotificationBuilderDataLimit(): NotificationCompat.Builder {
val pendingIntentDataLimit = PendingIntent.getActivity(
context,
notificationDataLimitID,
intentNotification,
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)
return NotificationCompat.Builder(
context,
notificationChannelDataLimit
)
.setContentTitle("Data Limit Warning")
.setSmallIcon(R.drawable.icon_notification)
.setColor(ContextCompat.getColor(context, R.color.primary_color))
.setContentIntent(pendingIntentDataLimit)
.setDefaults(NotificationCompat.DEFAULT_ALL)
.setPriority(NotificationManager.IMPORTANCE_HIGH)
}
// This is for sending data + speed notification
// Speed indicator requires Notification.Builder
#RequiresApi(Build.VERSION_CODES.O)
fun getNotificationBuilder(
): Notification.Builder {
val pendingIntentNotification = PendingIntent.getActivity(
context,
notificationPersistentChannelID,
intentNotification,
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)
return Notification.Builder(
context,
notificationChannelPrimary
)
.setSmallIcon(R.drawable.icon_notification)
.setContentIntent(pendingIntentNotification)
.setAutoCancel(false)
.setStyle(Notification.BigTextStyle())
.setOnlyAlertOnce(true)
.setColor(ContextCompat.getColor(context, R.color.primary_color))
}
}
The problem is that I'm getting the following crashes a lot.
Fatal Exception: android.app.RemoteServiceException: Bad notification(tag=null, id=15) posted from package [package_name], crashing app(uid=10769, pid=16295): Couldn't inflate contentViewsjava.util.ConcurrentModificationException
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1894)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7156)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:494)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:975)
and
Fatal Exception: android.app.RemoteServiceException: Bad notification(tag=null, id=15) posted from package [package_name], crashing app(uid=10337, pid=27920): Couldn't inflate contentViewsjava.lang.ArrayIndexOutOfBoundsException: src.length=8 srcPos=0 dst.length=8 dstPos=2 length=8
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2168)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:227)
at android.app.ActivityThread.main(ActivityThread.java:7822)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1026)
I searched for "Bad notification posted crash", I found that mNotificationBuilder.setSmallIcon() argument must be a PNG icon (I'm calling it in updateDataUsage() method). Can someone please tell me how to convert the bitmap to PNG and pass it to setSmallIcon()?

Missing notifications using AlarmManager

I am facing a problem when scheduling multiple (up to 14) notifications using the AlarmManager. The problem is that the notifications are randomly missing (especially when the debug cable isn’t plugged). For example the first three notifications are triggered and after these notifications none of the scheduled notifications get triggered anymore, until you reschedule the notifications. It seems it’s because of the amount of notifications, but I am not 100% sure.
Below you can find my functions for scheduling notifications (alarm and bedtime notifications);
private fun createNotificationChannel(name: String, descriptionText: String, id: String, setSound:Boolean)
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
val importance = NotificationManager.IMPORTANCE_DEFAULT
val channel = NotificationChannel(id, name, importance).apply {
description = descriptionText
}
val audioAttributes = AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_NOTIFICATION)
.build()
if (setSound)
{
channel.setSound(Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + m_context.packageName + "/" + m_ringtone), audioAttributes)
}
(m_context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).createNotificationChannel(channel)
}
}
private fun createBedTimeNotificationChannel()
{
createNotificationChannel(BEDTIME_NOTIFICATION_CHANNEL_NAME, BEDTIME_NOTIFICATION_CHANNEL_DESCRIPTION, BEDTIME_NOTIFICATION_CHANNEL, false)
}
private fun createWakeUpNotificationChannel()
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
for (channel in (m_context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).notificationChannels)
{
if (channel.id.contains(WAKE_UP_ALARM_NOTIFICATION_CHANNEL))
{
(m_context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager).deleteNotificationChannel(channel.id)
}
}
m_randomNumber = Random().nextLong()
createNotificationChannel(WAKE_UP_NOTIFICATION_CHANNEL_NAME, WAKE_UP_NOTIFICATION_CHANNEL_DESCRIPTION, WAKE_UP_ALARM_NOTIFICATION_CHANNEL + m_randomNumber, true)
}
}
fun configureAlarmNotification(ringtone: Int, alarmTime: Int, snooze: Boolean, days: BooleanArray)
{
m_ringtone = ringtone
createWakeUpNotificationChannel()
for ((index, alarmOn) in days.withIndex())
{
if (alarmOn)
{
val builder = getWakeUpAlarmBuilder()
val intent = Intent(m_context, CMainActivity::class.java)
val activity = PendingIntent.getActivity(m_context, WAKE_UP_NOTIFICATION_ID_START + index + 1, intent, PendingIntent.FLAG_UPDATE_CURRENT)
builder.setContentIntent(activity)
if (snooze)
{
val snoozeIntent = Intent(m_context, CSleepPlannerService::class.java).apply {
putExtra(ACTION, ACTION_SNOOZE)
putExtra(NOTIFICATION_ID, WAKE_UP_NOTIFICATION_ID_START + index + 1)
putExtra(NOTIFICATION, builder.build())
}
val snoozePendingIntent: PendingIntent = PendingIntent.getBroadcast(m_context, SNOOZE_PRESSED_NOTIFICATION_ID + index + 1, snoozeIntent, 0)
builder.addAction(R.drawable.alarm_bel_icon, m_context.getString(R.string.snooze), snoozePendingIntent)
}
val notification = builder.build()
val calendar = Calendar.getInstance()
val currentTimeInMillis = calendar.timeInMillis
calendar[Calendar.DAY_OF_WEEK] = convertWeekday(index)
calendar[Calendar.HOUR_OF_DAY] = alarmTime / 60
calendar[Calendar.MINUTE] = alarmTime % 60
calendar[Calendar.SECOND] = 0
if (calendar.timeInMillis <= currentTimeInMillis)
{
calendar.timeInMillis += NUMBER_OF_MILLIS_IN_WEEK
}
val pendingIntent = getPendingIntent(calendar.timeInMillis, WAKE_UP_NOTIFICATION_ID_START + index + 1, notification, ACTION_NOTIFICATION, PendingIntent.FLAG_UPDATE_CURRENT)
(m_context.getSystemService(ALARM_SERVICE) as AlarmManager).setExact(AlarmManager.RTC_WAKEUP, calendar.timeInMillis, pendingIntent)
}
}
val calendar = Calendar.getInstance()
m_alarmWithSoundEnabledWithinADay.postValue((m_preferences.isAlarmWithSoundEnabled() && isAlarmEnabled(calendar[Calendar.HOUR_OF_DAY] * 60 + calendar[Calendar.MINUTE], m_preferences.getAlarmTime(), calendar[Calendar.DAY_OF_WEEK])))
}
fun configureBedTimeNotification(bedTime: Int, bedTimeSetting: Int, days: BooleanArray)
{
for ((index, alarmOn) in days.withIndex())
{
if (alarmOn)
{
val builder = NotificationCompat.Builder(m_context, BEDTIME_NOTIFICATION_CHANNEL).apply {
setContentTitle(m_context.getString(R.string.bedtime_notification_tile))
when (BedTimeNotificationSetting_e.fromInt(bedTimeSetting))
{
BedTimeNotificationSetting_e.AT_BED_TIME -> setContentText(m_context.getString(R.string.bedtime_notification_at_bedtime))
BedTimeNotificationSetting_e.TEN_MINUTES_BEFORE -> setContentText(m_context.getString(
R.string.bedtime_notification_description_ten
))
BedTimeNotificationSetting_e.THIRTY_MINUTES_BEFORE -> setContentText(m_context.getString(R.string.bedtime_notification_description_thirty))
BedTimeNotificationSetting_e.FORTYFIVE_MINUTES_BEFORE -> setContentText(m_context.getString(R.string.bedtime_notification_description_fortyfive))
BedTimeNotificationSetting_e.SIXTY_MINUTES_BEFORE -> setContentText(m_context.getString(R.string.bedtime_notification_description_sixty))
}
setAutoCancel(true)
setSmallIcon(R.drawable.alarm_icon)
priority = NotificationCompat.PRIORITY_HIGH
}
val intent = Intent(m_context, CMainActivity::class.java)
val activity = PendingIntent.getActivity(m_context, BEDTIME_NOTIFICATION_ID_START + index + 1, intent, PendingIntent.FLAG_UPDATE_CURRENT)
builder.setContentIntent(activity)
val notification = builder.build()
val calendar = Calendar.getInstance()
val currentTimeInMillis = calendar.timeInMillis
calendar[Calendar.DAY_OF_WEEK] = convertWeekday(index)
calendar[Calendar.HOUR_OF_DAY] = bedTime / 60
calendar[Calendar.MINUTE] = bedTime % 60
calendar[Calendar.SECOND] = 0
var newBedTime = calendar.timeInMillis - (bedTimeSetting * 60 * 1000)
if (newBedTime <= currentTimeInMillis)
{
newBedTime += NUMBER_OF_MILLIS_IN_WEEK
}
val pendingIntent = getPendingIntent(newBedTime, BEDTIME_NOTIFICATION_ID_START + index + 1, notification, ACTION_NOTIFICATION, PendingIntent.FLAG_UPDATE_CURRENT)
(m_context.getSystemService(ALARM_SERVICE) as AlarmManager).setExact(AlarmManager.RTC_WAKEUP, newBedTime, pendingIntent)
}
}
}
private fun getPendingIntent(calendarTime: Long = 0, notificationId: Int, notification: Notification? = null, action: String, flag: Int) : PendingIntent?
{
val notificationIntent = Intent(m_context, this::class.java)
notificationIntent.putExtra(ACTION, action)
notificationIntent.putExtra(NOTIFICATION_ID, notificationId)
notificationIntent.putExtra(NOTIFICATION, notification)
notificationIntent.putExtra(ALARM_TIME, calendarTime)
return PendingIntent.getBroadcast(m_context, notificationId, notificationIntent, flag)
}
What could be the cause of this?
It is difficult to know exactly what could be wrong. Depending on the version of the system, Android might decide to wait / delay / group notifications. The amount of battery left also impact the decision to show a notif.
I see that you use setPriority on the notification builder with a HIGH value. This should work for android < api 26. For newer versions, it is the setImportance in the notification channel that will be taken in account
see https://developer.android.com/reference/androidx/core/app/NotificationCompat.Builder?hl=en#setPriority(int)
In the creation of the channel, try using High importance
val importance = NotificationManager.IMPORTANCE_HIGH // edit your code like this in createNotificationChannel, you currently use DEFAULT
make sure that there is no option like battery saver or whatever in the phone you use to test.
I fixed the issue for setting the wakeup notifications by using the function setAlarmClock from AlarmManager.

Android workmanager job does not run after application close (samsung s9+)

I'm trying to run a one time job with WorkManager. I don't know why but if I close my application from recent app menu job does not start until I start the application :/. Tried with samsung galaxy s9+. Android 9 Pie. Any help would be appreciated. As far as I know workmanager should work even when phone is restarted.
val constraints = Constraints.Builder().setRequiredNetworkType(NetworkType.NOT_REQUIRED).build()
val request = OneTimeWorkRequestBuilder<NotificationJob>()
.addTag(item.uniqueId)
.setInputData(data)
.setConstraints(constraints)
//TODO revert
// .setInitialDelay(warrantyItem.reminderDate - System.currentTimeMillis(), TimeUnit.MILLISECONDS)
.setInitialDelay(1, TimeUnit.MINUTES)
.build()
WorkManager.getInstance(this#MainActivity).enqueue(request)
job is pretty simple. Just notification:
override fun doWork(): Result {
// Get the input
val uniqueId = inputData.getString(JOB_KEY)
if (uniqueId != null) {
sendNotification(uniqueId, "title", "subtitle")
}
return Result.success()
}
private fun sendNotification(uniqueId: String, title: String, subtitle: String) {
val intent = Intent(applicationContext, MainActivity::class.java)
intent.flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK
intent.putExtra(NOTIFICATION_ID, id)
val notificationManager =
applicationContext.getSystemService(NOTIFICATION_SERVICE) as NotificationManager
val bitmap = applicationContext.vectorToBitmap(R.drawable.ic_warranty_icon)
val titleNotification = "$title"
val subtitleNotification = subtitle
val pendingIntent = getActivity(applicationContext, 0, intent, 0)
val notification = NotificationCompat.Builder(applicationContext, NOTIFICATION_CHANNEL)
.setLargeIcon(bitmap).setSmallIcon(R.drawable.ic_warranty_icon)
.setContentTitle(titleNotification).setContentText(subtitleNotification)
.setDefaults(DEFAULT_ALL).setContentIntent(pendingIntent).setAutoCancel(true)
notification.priority = PRIORITY_MAX
if (SDK_INT >= O) {
notification.setChannelId(NOTIFICATION_CHANNEL)
val ringtoneManager = getDefaultUri(TYPE_NOTIFICATION)
val audioAttributes = AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE)
.setContentType(CONTENT_TYPE_SONIFICATION).build()
val channel =
NotificationChannel(NOTIFICATION_CHANNEL, NOTIFICATION_NAME, IMPORTANCE_HIGH)
channel.enableLights(true)
channel.lightColor = RED
channel.enableVibration(true)
channel.vibrationPattern = longArrayOf(100, 200, 300, 400, 500, 400, 300, 200, 400)
channel.setSound(ringtoneManager, audioAttributes)
notificationManager.createNotificationChannel(channel)
}
with(NotificationManagerCompat.from(applicationContext)) {
// notificationId is a unique int for each notification that you must define
notify(uniqueId, ID, notification.build())
}
}

Is it possible to navigate to page from native code onFlutter?

I want to navigate to my inbox page when clicking the notification. I handle notification on native code. I want to do this when clicking the notification. Navigator.pushNamed(context,'/inboxPage')
Android Native
val intent = Intent(this, MainActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
val pendingIntent = PendingIntent.getActivity(this, 0, intent,
PendingIntent.FLAG_ONE_SHOT)
val channelId = getString(R.string.default_notification_channel_id)
val notificationBuilder = NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.drawable.app_icon)
.setContentTitle(messageTitle)
.setContentText(messageBody)
.setAutoCancel(true)
.setContentIntent(pendingIntent)
IOS Native
let notificationContent = UNMutableNotificationContent()
notificationContent.title = userInfo["title"] as! String
notificationContent.body = userInfo["body"] as! String
notificationContent.sound = UNNotificationSound.default()
let request = UNNotificationRequest(identifier: UUID.init().uuidString, content: notificationContent, trigger: nil)
let center = UNUserNotificationCenter.current()
center.add(request) { (error: Error?) in
if let theError = error {
print("error \(theError)")
}
}
define a global var for a listener function
Function kNotificationClicked;
instantiate/init a method channel
const platform = MethodChannel('com.myapp.etc');
platform.setMethodCallHandler((call) async {
if (call.method.compareTo('notification-clicked') == 0) {
if(kNotificationClicked!=null){
kNotificationClicked(call.arguments);
}
}
});
in any widget (mostly while init or even better while the app init) set the callback
kNotificationClicked = (args){
//go to the route
}
on native side(kotlin) instantiate a method channel
private val CHANNEL = "com.myapp.etc"
protected lateinit var platform: MethodChannel;
fun configureFEngine() {
platform = MethodChannel(flutterView, CHANNEL)
platform.setMethodCallHandler { call, result ->
}
}
invoke when you wish
fun notificationClicked() {
platform.invokeMethod("notification-clicked", args)
}
I've not tested the code, but have shown you how would i approach this

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