How to schedule reminder for daily habits

30 Views Asked by At

I'm creating a Habit Tracker with Firebase that has the option of remembering an hour and minute of the day to show a notification of the habit you created, the problem is when you create a new habit the notification replaces the old hour and minute warning.

I don't want multiple notifications, but use a single notification to show a message to check the habit.

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    private lateinit var notificationHelper: NotificationHelper

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        Thread.sleep(2000)
        installSplashScreen()
        notificationHelper = NotificationHelper(applicationContext)
        notificationHelper.createNotificationChannel()//create notification

HabitReminderReceiver

class HabitReminderReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {

        val notification = buildNotification(context, intent)

        val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        manager.notify(DataBaseConstants.NOTIFICATION.notificationID, notification)
    }

    private fun buildNotification(context: Context, intent: Intent): Notification {
        val message = intent.getStringExtra(DataBaseConstants.NOTIFICATION.messageExtra)
        return Notification.Builder(context, DataBaseConstants.NOTIFICATION.channelID)
            .setSmallIcon(R.drawable.baseline_add_alert_24)
            .setContentText(message)
            .build()
    }
}

NotificationHelper to create channel in MainActivity and scheduleNotification

class NotificationHelper(private val context: Context) {

    fun createNotificationChannel() {
        Log.i("NotificationHelper", "Notification Created ")
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel(
                DataBaseConstants.NOTIFICATION.channelID,
                DataBaseConstants.NOTIFICATION.channelName,
                NotificationManager.IMPORTANCE_DEFAULT
            )
            val notificationManager =
                context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
            notificationManager.createNotificationChannel(channel)
        }
    }

    fun scheduleNotification(context: Context, intent: Intent) {
        
        //get hour and minute from reminder of habit
        val hour = intent.getIntExtra(DataBaseConstants.NOTIFICATION.HOUR, -1)
        val minute = intent.getIntExtra(DataBaseConstants.NOTIFICATION.MINUTE, -1)

        Log.i("NotificationHelper", "$hour: $minute")

        val pendingIntent = PendingIntent.getBroadcast(
            context,
            DataBaseConstants.NOTIFICATION.notificationID,//Use the unique notification ID as the requestCode
            intent,
            PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
        )

        val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager

        val calendar = Calendar.getInstance().apply {
            set(Calendar.HOUR_OF_DAY, hour)
            set(Calendar.MINUTE, minute)
            set(Calendar.SECOND, 0)
        }

        val time = calendar.timeInMillis // timeInMillis with reminder hour and minute
        Log.i(DataBaseConstants.TAG.ADD_HABIT_FORM_ACTIVITY, "Time in millis: $time")

        alarmManager.setRepeating(
            AlarmManager.RTC_WAKEUP, time, AlarmManager.INTERVAL_DAY, pendingIntent
        )
    }

}

Class addHabitFormActivity who add new habit and use NotificationHelper to schedule reminder

private lateinit var notificationHelper: NotificationHelper

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    notificationHelper = NotificationHelper(context = applicationContext)

    buttonCreateHabit.setOnClickListener {

    //class habit to pass name, user id, icon, reminder with hour and minute...
    habit = Habit(
      "",//id empty
     editName.text.toString(),
     editDesc.text.toString(),
     FirebaseAuthManager.getCurrentUser?.uid.toString(),
     reminder,
     dataEnd,
     icon,
     rangeOptionEnumClass,
     goalType,
     durationTimer,
     arrayListOf()
      )
      
     habit.save()//save into firebase the habit
    
      Toast.makeText(
      applicationContext,
      getString(R.string.save_success),
      Toast.LENGTH_SHORT).show()//Toast saved
    
     if (!editReminderTimepick.text.isEmpty()) {
                 
     //add hour and minute from class Reminder and name of the habit
     var intent = Intent(applicationContext,HabitReminderReceiver::class.java)

        intent.putExtra(DataBaseConstants.NOTIFICATION.HOUR, reminder.hour)
        intent.putExtra(DataBaseConstants.NOTIFICATION.MINUTE, reminder.minute)
        intent.putExtra(DataBaseConstants.NOTIFICATION.messageExtra, habit.name)

        notificationHelper.scheduleNotification(this, intent)
    
        }
      finish() // finish addHabitFormActivity
}


class DataBaseConstants private constructor() {
    object NOTIFICATION {

        const val notificationID = 1
        const val channelID = "channel1"
        const val channelName = "channelName"
        const val titleExtra = "titleExtra"
        const val messageExtra = "messageExtra"
        const val HOUR = "hour"
        const val MINUTE = "minute"
    }
}

class Reminder {
    var enable: Boolean? = false
    var hour: Int? = null
    var minute: Int? = null
    var days: Weekday = Weekday()
}

Manifest

<receiver android:name=".utils.HabitReminderReceiver"
            android:exported="false">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED"/>
            </intent-filter>
</receiver>
1

There are 1 best solutions below

0
Braian Silva On

I found how to solve this:

In the HabitReminderReceiver in onReceive I create a variable that is always show 1 notification fixed.

class HabitReminderReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {

        val habitId = intent.getIntExtra(DataBaseConstants.NOTIFICATION.habitId, -1)

        val notification = buildNotification(context, intent)

        val notificationFixed = 1//fixed notification

        val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        manager.notify(notificationFixed, notification)
    }

In NotificationHelper a variable habitId that has a hashCode of the Firebase habit id and when the hour and minute reminder is repeated it uses the same notification set in HabiReminderReceive to display, and this does not create multiple notification and updates the same notification with new habit.

val hour = intent.getIntExtra(DataBaseConstants.NOTIFICATION.HOUR, -1)
        val minute = intent.getIntExtra(DataBaseConstants.NOTIFICATION.MINUTE, -1)
        val habitId = intent.getIntExtra(DataBaseConstants.NOTIFICATION.habitId, -1)

        Log.i("NotificationHelper", "$hour: $minute: $habitId")

        val pendingIntent = PendingIntent.getBroadcast(
            context,
            habitId,//add every habit id with hashcode
            intent,//load each , hour, minute to show
            PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE
        )