Thursday, 20 September 2018

Android Notification - How to create a system based on model properties

I've been trying to implement Notifications in my app without effectively succeeding. The following are my observations:

Requirements:

  1. After scheduling a Notification it has to be repeated on a monthly basis.
  2. The notification will be inflated with some model object data, so the object needs to be passed to the notification builder.

How I tackled the problem so far:

I created a NotificationScheduler object that basically creates a PendingIntent for the notification to be fired.

object NotificationScheduler {
    fun scheduleNotification(context: Context, model: Model, cal: Calendar) {
        cal.set(Calendar.HOUR_OF_DAY, 11)
        val notificationIntent = Intent(context, NotificationPublisher::class.java)
        notificationIntent.putExtra("model", Utils.parcelToBytes(model))

        val pendingIntent = PendingIntent.getBroadcast(context, 1, notificationIntent,
                PendingIntent.FLAG_UPDATE_CURRENT)
        val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
        alarmManager.set(AlarmManager.RTC_WAKEUP, cal.timeInMillis, pendingIntent)
    }
}

Then I have a NotificationPublisher class that should show the notification and set the next one by calling NotificationScheduler again.

class NotificationPublisher: BroadcastReceiver() {
    var model: Model? = null

    override fun onReceive(context: Context?, intent: Intent?) {
        val modelBytes = intent?.getByteArrayExtra("model")

        if (modelBytes != null) {
            model = Utils.bytesToParcel(modelBytes, Model.CREATOR)
        }

        val notificationBuilder = NotificationBuilder(context!!, model!!)
        val notification = notificationBuilder.buildNotification()

        val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        notificationManager.notify(1, notification)

        //schedules the next notification

NotificationScheduler.scheduleNotification(context, model!!, Calendar.getInstance())
        }
    }

Where the NotificationBuilder class just takes the context and the model and builds the notification

class NotificationBuilder(val context: Context, val model: Model) {

    fun buildNotification(): Notification {
        generateNotificationChannel(context)

        val builder = NotificationCompat.Builder(context, "ID")
                .setSmallIcon(R.drawable.sub)
                .setContentTitle(model.title)
                .setPriority(NotificationCompat.PRIORITY_HIGH)

        return builder.build()
    }

    private fun generateNotificationChannel(context: Context) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val name = "Test"
            val description = "description"
            val importance = NotificationManager.IMPORTANCE_DEFAULT

            val channel = NotificationChannel("ID", name, importance)
            channel.description = description

            val notificationManager = context.getSystemService(NotificationManager::class.java)
            notificationManager?.createNotificationChannel(channel)
        }
    }

My problem with this approach:

It works if I purposely display the notification as soon as it is created, but I get a NullPointerException whenever the user, for example, reboots his device or kills the process. While debugging I found out that the data I store in the notificationIntent in NotificationScheduler, once the device is rebooted, isn't available anymore in the onReceive() of the NotificationPublisher and therefore will generate a NPE.

I think I am doing something conceptually wrong with this approach, and I'd like you to help me figure out how to achieve the above mentioned requirements.



from Android Notification - How to create a system based on model properties

No comments:

Post a Comment