Search code examples
androidalarmmanageralarmandroid-alarms

AlarmManager alarm does not trigger after the phone resets


In my app the user joins a plan, then the next day at noon there is an alarm notification. Here is my code:

First, I set an alarm in AlarmManager like so:

//set alarm to the next day 12:00 noon of the join date
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");  
try {  
    alarm_date = format.parse(join_date);
} catch (ParseException e) {  
    e.printStackTrace();  
}

GregorianCalendar calender = new GregorianCalendar();
calender.setTime(alarm_date);
calender.add(Calendar.DATE, 1);
calender.add(Calendar.HOUR_OF_DAY, 12);
//calender.add(Calendar.HOUR_OF_DAY, 14); //temp testing data
//calender.add(Calendar.MINUTE, 43);

AlarmManager am = (AlarmManager) ctx.getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(ctx, AlarmReceiver.class);
am.set(AlarmManager.RTC_WAKEUP, calender.getTimeInMillis(), PendingIntent.getBroadcast(ctx, 1, i, PendingIntent.FLAG_UPDATE_CURRENT));

Then, at the scheduled time, it triggers the receiver like so:

public class AlarmReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Intent service = new Intent(context, AlarmService.class);
        context.startService(service);
    }
}

Finally, it calls a service to either show notification or bring the app to the front if it is in the background. Here is the code for that:

public class AlarmService extends Service {
    private Context ctx;
    private MyApp gs;
    private SharedPreferences prefs;
    NotificationManager notificationManager;
    Notification myNotification;
    private String joinPlanID;
    private String joinPlanDate;
    private int period;

    public AlarmService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    public void checkPlanPeriod(String planID) {
        if (joinPlanID.equals("1"))
            period = 15;
        else if (joinPlanID.equals("2"))
            period = 30;
        else if (joinPlanID.equals("3"))
            period = 45;
    }

    @Override
    public void onStart(Intent intent, int startId) {
        ctx = getApplicationContext();
        gs = (MyApp) getApplication();
        prefs = PreferenceManager.getDefaultSharedPreferences(ctx);

        if (prefs.getString("joinPlanID", null) != null) {
            Date alarmDate = null;
            joinPlanID = prefs.getString("joinPlanID", null);
            joinPlanDate = prefs.getString("joinPlanDate", null);
            checkPlanPeriod(joinPlanID);

            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");  
            try {  
                alarmDate = format.parse(joinPlanDate);
            } catch (ParseException e) {  
                e.printStackTrace();  
            }

            GregorianCalendar planCalendar = new GregorianCalendar();
            planCalendar.setTime(alarmDate);
            planCalendar.add(Calendar.DATE, period);

            Calendar now = Calendar.getInstance();
            now.add(Calendar.DATE, 1);

            Calendar tomorrowAlarm = Calendar.getInstance();
            tomorrowAlarm.set(Calendar.YEAR, now.get(Calendar.YEAR));
            tomorrowAlarm.set(Calendar.MONTH, now.get(Calendar.MONTH));
            tomorrowAlarm.set(Calendar.DAY_OF_MONTH, now.get(Calendar.DAY_OF_MONTH));
            tomorrowAlarm.set(Calendar.HOUR_OF_DAY, 12);
            tomorrowAlarm.set(Calendar.MINUTE, 0);
            tomorrowAlarm.set(Calendar.SECOND, 0);
            tomorrowAlarm.set(Calendar.MILLISECOND, 0);

            if (planCalendar.compareTo(tomorrowAlarm) != -1) {
                //set the next day alarm
                AlarmManager am = (AlarmManager) ctx.getSystemService(Context.ALARM_SERVICE);
                Intent i = new Intent(ctx, AlarmReceiver.class);
                am.set(AlarmManager.RTC_WAKEUP, tomorrowAlarm.getTimeInMillis(), PendingIntent.getBroadcast(ctx, 1, i, PendingIntent.FLAG_UPDATE_CURRENT));
            }
        }

        if (gs.getIsAppStart()) {
             Intent dialogIntent = new Intent(this, Main.class);
             dialogIntent.putExtra("is_remind", true);
             dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
             this.startActivity(dialogIntent);
        } else {
            NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(ctx)
            .setSmallIcon(R.drawable.ic_launcher)
            .setContentTitle(getResources().getString(R.string.notify_title))
            .setContentText(getResources().getString(R.string.notify_msg));

            Intent toLaunch = new Intent(getApplicationContext(), Main.class);
            toLaunch.putExtra("is_remind", true);
            toLaunch.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
            PendingIntent intentBack = PendingIntent.getActivity(ctx, 0, toLaunch,PendingIntent.FLAG_UPDATE_CURRENT);

            mBuilder.setContentIntent(intentBack);
            NotificationManager mNotificationManager = (NotificationManager) ctx.getSystemService(Context.NOTIFICATION_SERVICE);

            // Send Notification
            Notification primaryNotification = mBuilder.build();
            mNotificationManager.notify(10001, primaryNotification);
        }

    }
}

The problem is, if I reset my device it neither triggers the alarm nor my Receiver gets any thing. How do I fix it?


Solution

  • Add a BootReceiver class

    public class BootReceiver extends BroadcastReceiver {
    
        private static final String BOOT_COMPLETED =
                "android.intent.action.BOOT_COMPLETED";
        private static final String QUICKBOOT_POWERON =
                "android.intent.action.QUICKBOOT_POWERON";
    
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (action.equals(BOOT_COMPLETED) ||
                    action.equals(QUICKBOOT_POWERON)) {
                Intent service = new Intent(context, BootService.class);
                context.startService(service);
            }
        }
    
    }
    

    Add a BootService class

    public class BootService extends IntentService {
    
        public BootService() {
            super("BootService");
        }
    
        private void setAlarm() {
            // Set your alarm here as you do in "1. First I set an alarm in alarm manager"
        }
    
        private void setAlarmsFromDatabase() {
            // Set your alarms from database here
        }
    
        @Override
        protected void onHandleIntent(Intent intent) {
            setAlarm();
            setAlarmsFromDatabase(); // A nice a approach is to store alarms on a database, you may not need it
            Intent service = new Intent(this, BootService.class);
            stopService(service);
        }
    
    }
    

    Then in your AndroidManifest.xml add

    <receiver android:name=".BootReceiver"
              android:enabled="true">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED"/>
             <action android:name="android.intent.action.QUICKBOOT_POWERON" />
        </intent-filter>
    </receiver>
    <service android:name=".BootService"
             android:enabled="true"/>
    

    And also add this permission

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

    Now you can restart your device and the alarm will be set every time your device boots