dynamic alarm manager at midnight

582 Views Asked by At

my mean goal is to run a task periodically at midnight (00:00:00)

but the user can set the period based on the interval (daily, weekly , monthly) let's assume that this job is a backup Scheduling. this task will triggred at midnight but based on the user preference (midnight everyday, every week , or monthly ), and if the phone was in the doze mode or even off , wait untill the next start and start backup.

when i start implementing the code , i started with JobService and JobScheduler , but unfortunately i learned that i can set the repetitive but i can't set the exact time, so the last solution was to work with alarmmanager.

i use this code for triggering the alarm :

 public static void setTheTimeToStartBackup(Context context,String periode) {

    int DATA_FETCHER_RC = 123;
    AlarmManager mAlarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
    
    Calendar calendar = Calendar.getInstance();


    Intent intent = new Intent(context, BackUpAlarmRecevier.class);
    PendingIntent pendingIntent = PendingIntent.getBroadcast(context, DATA_FETCHER_RC,intent, PendingIntent.FLAG_UPDATE_CURRENT);


    long interval = 0;
    switch (periode){
        case "never":
            return;
        case "daily":
            alarmStartTime.set(Calendar.HOUR_OF_DAY, 0);
            interval = AlarmManager.INTERVAL_DAY;
            break;
        case "weekly":
            alarmStartTime.set(Calendar.DAY_OF_WEEK, 1);
            interval = AlarmManager.INTERVAL_DAY*7;
            break;

        case "monthly":
            alarmStartTime.set(Calendar.WEEK_OF_MONTH, 1);
            interval = AlarmManager.INTERVAL_DAY*30;
            break;
    }

    alarmStartTime.set(Calendar.HOUR_OF_DAY, 0);
    alarmStartTime.set(Calendar.MINUTE, 0);
    alarmStartTime.set(Calendar.SECOND, 0);

    mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP,calendar.getTimeInMillis(),interval, pendingIntent);

    Log.e("Alarm","Set for midnight");

}

this is my receiver :

public class BackUpAlarmRecevier extends BroadcastReceiver {

SharedPreferences preferences;
@Override
public void onReceive(Context context, Intent intent) {
    Log.e("BackUpAlarmReciver","Triggred");

    PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
    PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "TAG:APP");
    wl.acquire();

    sendNotification(context);// simple notification...
    Toast.makeText(context, "Alarm !!", Toast.LENGTH_LONG).show(); 
    startBackupProcess();

    wl.release();

}}

the problem is task never start.

so i went to test it with less time interval (15min as the minimum possible as i read ), so i change my first function setTheTimeToStartBackup to this :

public static void setTheTimeToStartBackup(Context context,String periode) {

    int DATA_FETCHER_RC = 123;
    AlarmManager mAlarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);

    Calendar calendar = Calendar.getInstance();

    calendar.set(Calendar.SECOND, 55);

    Intent intent = new Intent(context, BackUpAlarmRecevier.class);
    PendingIntent pendingIntent = PendingIntent.getBroadcast(context, DATA_FETCHER_RC,intent, PendingIntent.FLAG_UPDATE_CURRENT);

    mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP,calendar.getTimeInMillis(),AlarmManager.INTERVAL_FIFTEEN_MINUTES, pendingIntent);

    Log.e("Alarm","Set for midnight");

}

and exactly the same problem , nothing started, no log , no notification , nothing.

and i already set the Receiver in my manifest with all permission like that :

        <receiver android:name=".job.BackUpAlarmRecevier"
        android:enabled="true"
        android:process=":remote"
        android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
        </intent-filter>
    </receiver>

what im doing wrong in both cases ? and if it work , this code will persist for ever or i need to set it again each time ?

thanks :)

EDIT: i call my function setTheTimeToStartBackup in the MainActivity.

2

There are 2 best solutions below

2
MitchHS On

You could set it to occur at midnight if you did the appropriate time calculations. Dynamically get the current time and date, calculate when to register the broadcast for the alarm manager. Customize the onReceive method to set another alarm at 12pm again.

Either way you can trigger a broadcast receiver by registering your receiver manually.

Broadcast receiver class:

public class AlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
    System.out.println("Alarm received!! ");
    // Register alarm again here.
    }
}

Code to register a receiver with a custom intent filter.

 @Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    AlarmManager mAlarmManager = (AlarmManager)getApplicationContext().getSystemService(Context.ALARM_SERVICE);


    getApplicationContext().registerReceiver(new AlarmReceiver(), new IntentFilter("AlarmAction"));
    PendingIntent broadcast = PendingIntent.getBroadcast(this, 0, new Intent("AlarmAction"), 0);

   // Add dynamic time calculations. For testing just +100 milli.
   mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + 100, broadcast);
    ;

}

You could achieve what you wanted through a background service.

1
Simon On

My suggestion would be to turn the problem around a bit. Create three topics on Firebase (daily, weekly, monthly). Subscribe users to appropriate topics. Have a Firebase function that is triggered by CRON job which sends the data notification down to the device, this data notification should schedule one-time WorkManager job for the update. This way you can control everything from server-side, if the phone is off, it will still execute as soon as it turns on and you don't need to manually take care of catching the Boot completed with alarm manager etc.