Search code examples
javaandroidnotificationsalarmmanager

Scheduling a notification to a specific date and time


I'm the developer of Call Manager, and I'm trying to implement a notification reminder feature to my app. The idea is that the user can set themselves a reminder to call a specific person in the list. At first, when implementing this feature, the notification would show up immediately without being scheduled - this was due to an incorrect setting of values that resulted in negative milliseconds. However, now that I have corrected that, notifications do not get scheduled at all, even though I have the correct millisecond value to give the AlarmManager.

The method that schedules the notification is below:

public void scheduleReminder(Notification notification, String date, String time){
        String[] dateArray = date.split("/");
        String[] timeArray = time.split(":|\\s+");
        Date currentDate = new Date();
        int notHour;

        Calendar cal = Calendar.getInstance();
        cal.setTimeInMillis(System.currentTimeMillis());
        cal.clear();
        if(timeArray[2].equals("PM")){
            notHour = Integer.parseInt(timeArray[0]);
            notHour = notHour + 12;
            cal.set(Calendar.YEAR, Integer.parseInt(dateArray[2]));
            cal.set(Calendar.MONTH, Integer.parseInt(dateArray[0]) - 1);
            cal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(dateArray[1]));
            cal.set(Calendar.HOUR_OF_DAY, notHour);
            cal.set(Calendar.MINUTE, Integer.parseInt(timeArray[1]));
        }
        else{
            cal.set(Calendar.YEAR, Integer.parseInt(dateArray[2]));
            cal.set(Calendar.MONTH, Integer.parseInt(dateArray[0]) - 1);
            cal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(dateArray[1]));
            cal.set(Calendar.HOUR_OF_DAY, Integer.parseInt(timeArray[0]));
            cal.set(Calendar.MINUTE, Integer.parseInt(timeArray[1]));
        }

        Date reminderDate = cal.getTime();
        long diffInMillis  = reminderDate.getTime() - currentDate.getTime();

        Intent notificationIntent = new Intent(this, NotificationPublisher.class);
        notificationIntent.putExtra(NotificationPublisher.NOTIFICATION_ID, 1);
        notificationIntent.putExtra(NotificationPublisher.NOTIFICATION, notification);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);

        AlarmManager alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
        alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + diffInMillis, pendingIntent);
    }

My Broadcast receiver class is as follows:

package groovinchip.com.callmanager;

import android.app.Notification;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

public class NotificationPublisher extends BroadcastReceiver {

    public static String NOTIFICATION_ID = "notification-id";
    public static String NOTIFICATION = "notification";

    @Override
    public void onReceive(Context context, Intent intent) {
        NotificationManager notificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);

        Notification notification  = intent.getParcelableExtra(NOTIFICATION);
        int id = intent.getIntExtra(NOTIFICATION_ID, 0);
        notificationManager.notify(id, notification);
    }
}

And the relevant Android Manifest declarations are as follows:

<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />

<receiver android:name="groovinchip.com.callmanager.NotificationPublisher"
     android:enabled="true">
</receiver>

I'm stumped as to why this isn't working - I've pored over my Google results trying to figure it out. I've switched between: 1) Setting AlarmManager with System.ELAPSED_REALTIME_WAKEUP 2) Passing SystemClock.elapsedRealTime() + diffInMillies 3) Only passing diffInMillis 4) passing an Integer value representing only a few seconds instead of diffInMillis to see if it would work at all

Anyone able to help with this? Thank you so much!


Solution

  • When you create your Notification are you setting a channel id? If you are testing on API 26 the alarm will not go off if there isn't one set, as well as a a channel in the broadcast receiver.

    I have two methods that create and set the reminder from a timepicker and an alarm that the user chooses. Here is the source code for them

    private void createReminder(Notification notification) {
        Intent notificationIntent = new Intent(this, NotificationPublisher.class);
        notificationIntent.putExtra(NotificationPublisher.NOTIFICATION_ID, 1);
        notificationIntent.putExtra(NotificationPublisher.NOTIFICATION, notification);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
        long delay = alarmCalendar.getTimeInMillis() - Calendar.getInstance().getTimeInMillis();
        long futureInMillis = SystemClock.elapsedRealtime() + delay;
        AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
        alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, futureInMillis, pendingIntent);
    }
    
    private Notification getNotification() {
        String channelId = "Reminders";
        PendingIntent newEntryActivityPendingIntent = PendingIntent.getActivity(this, 1, new Intent(this, NewEntryActivity.class), PendingIntent.FLAG_UPDATE_CURRENT);
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this, channelId)
                .setContentTitle(getString(R.string.app_name))
                .setContentText(getString(R.string.reminder_content))
                .setTicker(getString(R.string.app_name))
                .setSmallIcon(R.drawable.notebook_notification_white)
                .setDefaults(Notification.DEFAULT_SOUND)
                .setAutoCancel(true)
                .setContentIntent(newEntryActivityPendingIntent);
        Log.i(TAG, "notification built");
        return builder.build();
    }
    

    In my app I have a notification reminder and I have a seperate class for my broadcast reciever similar to you and this how mine looks

        public class NotificationPublisher extends BroadcastReceiver {
    
        private static final String TAG = "NotificationPublisher";
        public static String NOTIFICATION_ID = "notification-id";
        public static String NOTIFICATION = "notification";
    
        @Override
        public void onReceive(Context context, Intent intent) {
            NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
            if (Build.VERSION.SDK_INT >= 26) {
                NotificationChannel channel = new NotificationChannel("Reminders", "Reminders", NotificationManager.IMPORTANCE_DEFAULT);
                notificationManager.createNotificationChannel(channel);
            }
            Notification notification = intent.getParcelableExtra(NOTIFICATION);
            int id = intent.getIntExtra(NOTIFICATION_ID, 0);
            Log.i(TAG, "notification sent");
            notificationManager.notify(id, notification);
        }
    }
    

    Almost exactly like yours, from the looks of it.

    And this works well for me. Let me know if I can help any other way.