How to implement yearly and monthly repeating alarms? - android

I want to set monthly and yearly alarm in my app.I did this for weekly. AlarmManager.INTERVAL_DAY helped me for that.But i could not find good way to implement monthly and yearly repeat.
Searched so far:
http://www.satyakomatineni.com/akc/display?url=displaynoteimpurl&ownerUserId=satya&reportId=3503
http://groups.google.com/group/android-developers/browse_thread/thread/9f946e40307073c4?pli=1
Is any other way available to do this? Any help appreciated.

I think that, you have two inherent issues with this approach:
AlarmManager will not accept large time intervals because the number of millis will overflow the argument
I do not think Alarms will survive reboots of your phone that will most certainly happen during such a long period of time.
I advice that you store each alarm in a safe place and use a combination of AlarmManager and onBoot receivers to check if one of the alarms from your list must be fired this very day and just reschedule an alarm to wake you up tomorrow if none does.
public class AlarmService extends Service {
//compat to support older devices
#Override
public void onStart(Intent intent, int startId) {
onStartCommand(intent, 0, startId);
}
#Override
public int onStartCommand (Intent intent, int flags, int startId){
//your method to check if an alarm must be fired today
checkForTodayAlarmsAndBehaveAppropriately();
//reschedule me to check again tomorrow
Intent serviceIntent = new Intent(AlarmService.this,AlarmService.class);
PendingIntent restartServiceIntent = PendingIntent.getService(AlarmService.this, 0, serviceIntent,0);
AlarmManager alarms = (AlarmManager)getSystemService(ALARM_SERVICE);
// cancel previous alarm
alarms.cancel(restartServiceIntent);
// schedule alarm for today + 1 day
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DATE, 1);
// schedule the alarm
alarms.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), restartServiceIntent);
}
}
To start your service at boot time use this :
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class serviceAutoLauncher extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
Intent serviceIntent = new Intent(context,AlarmService.class);
context.startService(serviceIntent);
}
}
Finally add this to your manifest to schedule your serviceAutoLauncher to be launched at each boot:
<receiver android:name="serviceAutoLauncher">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"></action>
<category android:name="android.intent.category.HOME"></category>
</intent-filter>
</receiver>

I solved the issue. In my app,multipal alarms were set with different repeating time intervals.So in my broadcast receiver for alarm,i re-scheduled the alarm based on for what time it was scheduled to be repeated.I used calendar object to add month and year accordinly and again set it for next alarm.i used code like this(for scheduling it for next month)-
PendingIntent sender = PendingIntent.getBroadcast(context,Integer.parseInt(Long.toString(id)), intent1, 0);
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(calendar.getTimeInMillis());
calendar.add(Calendar.SECOND, 30);
AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
calendar.add(Calendar.MONTH, 1);
am.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), sender);

To add to Laurent's response. To abstract from calculating recurrence manually I suggest to take a look at this RFC-2445 implementation. It works fine on Android and can save you a lot of pain.

Related

dynamic alarm manager at midnight

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.
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.
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.

My AlarmManager triggers every time service is run, not at set time

I am trying to create a SMS-app which sends out SMS at one set time of day. For this I have created two services SendMessagePeriodicService and SendMessageService.
This is my SendMesagePeriodicService, which is supposed to trigger SendMessageService at a set time of day. The problem is that it triggers every time the onStartCommand-method is called...
public class SendMessagePeriodicService extends Service {
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) { //FIXME
Calendar cal = Calendar.getInstance();
Intent i = new Intent(this, SendMessageService.class);
PendingIntent pintent = PendingIntent.getService(this, 0, i, PendingIntent.FLAG_UPDATE_CURRENT);
cal.set(Calendar.MINUTE, PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getInt("minute", -1));
cal.set(Calendar.HOUR_OF_DAY, PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getInt("hour", -1));
AlarmManager alarm = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
alarm.setRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pintent);
return Service.START_NOT_STICKY;
}
}
I have also checked that the values in the sharedPreferences are correct...
Does anyone have any idea why this is happening?
I would do that by setting the alarmmanager in the activity and than use BroadcastReceiver to send the sms when the alarm fires.
I use the same technique to start a notification on a specific time.
I think I have read somewhere the you use services nowadays only for fileoperations. But I´m not 100%.
It seems that the reason this happens was that the time I set for trigger time was in the past. This means that AlarmManager triggers immediately and not the next time this set time occurs.

How to Issue a notification according to objects in a listview

I have in my MainActivity a listView containing "Entry" objects, In my entry object I have a function getDaysLeft() that returns int,
I want my application to issue a notification whenever the getDaysLeft() of any item in the list becomes less than a set threshold.
How do I have the application check for that in the background and issue a notification whenever this happens ?
You'll need to use AlarmManager and BroadcastReceiver.
Checking for timing condition in background is bad idea, it's way better to tell Android "wake up my app tomorrow at 10 AM".
To set up one-time alarm that wakes your app up:
AlarmManager AM = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent("com.yourapp.package.SOME_ACTION");
PendingIntent pending = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(cal.getTimeInMillis() 24 * 60 * 60 * 1000); //24h from now
AM.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), pending);
Your .package.MyAlarmReceiver class:
public class MyAlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
//Show a notification here
}
}
Also, you'll need to register your MyAlarmReceiver in AndroidManifest.xml:
<receiver android:name=".package.MyAlarmReceiver">
<intent-filter>
<action android:name="com.yourapp.package.SOME_ACTION" />
</intent-filter>
</receiver>
In case you've got lot of Entries schedule alarm for nearest notification or two, then in onReceive method schedule alarm for next notification and so on, to avoid cluttering AlarmManager.

AlarmManager fires alarms at wrong time

I managed everything all right to create a notification service used to fire a notification as a result of an alarm. Unfortunately, setting the alarm using AlarmManager doesn't work right. It fires several minutes later (not exactly hours, which would indicate a timezone problem). The recurring period is 1 week, so I used the constant INTERVAL_DAY and multiplied it with 7. In order to make sure that one PendingIntent doesn't replace the other, I pass the dayOfWeek as second parameter to PendingIntent.getService(). I check the correctness of the time for the alarm to fire by logging it:
Log.d(TAG, "next alarm " + df.format(cal.getTime()));
Is there really no way to list all alarms set - at least those from my own app? I believe this is the only way to track down the error.
My code:
cal.setTimeInMillis(System.currentTimeMillis());
cal.add(Calendar.DATE, 1);
cal.set(Calendar.HOUR_OF_DAY, hour);
cal.set(Calendar.MINUTE, minute);
Log.d(TAG, "next alarm " + df.format(cal.getTime()));
Intent showNotificationIntent = new Intent(context, NotificationService.class);
dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);
alarmIntent = PendingIntent.getService(context, dayOfWeek, showNotificationIntent, 0);
getAlarmManager(context).setInexactRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(),
INTERVAL_WEEK, alarmIntent);
I want to offer to have an alarm every day, but at various times, which can be set by the user. So I use up to 7 alarms, which should fire on a weekly basis.
Even after reading the numerous answers to similar questions (I don't intend to create a duplicate question), I haven't managed to find the problem.
For api levels below 19 you should use AlarmManager.setRepeating() and your alarms will trigger exactly at specified time. Thou on api levels 19 and above this will no longer work. There was change in android so that all repeating alarms are inexact. So if you would like to achieve exact repeating alarm you should schedule alarm with AlarmManager.setExact() and then when alarm triggers do it again for next week and so on every week.
Because of setInexactRepeating. Use setRepeating and it will be processed at the right time.
Instead of:
setInexactRepeating
use
setRepeating
setInexactRepeating, is OS and battery friendly, it batches together all the work to be done on Alarm receive and works through one by one, while as setRepeating instantly fires the alarm
Also a note: Alarms are wiped off once phone is rebooted, you might have to implement a boot broadcast receiver to make it persistent. Make sure you dont do that runtime, you need to implement it in the Manifest else when your app is not in background you will not receive any broadcasts.
A small example:
This is working code. It wakes CPU every 10 minutes until the phone turns off.
Add to Manifest.xml:
...
<uses-permission android:name="android.permission.WAKE_LOCK"></uses-permission>
...
<receiver android:process=":remote" android:name="Alarm"></receiver>
...
Code:
package YourPackage;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.PowerManager;
import android.widget.Toast;
public class Alarm extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "");
wl.acquire();
// Put here YOUR code.
Toast.makeText(context, "Alarm !!!!!!!!!!", Toast.LENGTH_LONG).show(); // For example
wl.release();
}
public void SetAlarm(Context context)
{
AlarmManager am=(AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(context, Alarm.class);
PendingIntent pi = PendingIntent.getBroadcast(context, 0, i, 0);
am.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 1000 * 60 * 10, pi); // Millisec * Second * Minute
}
public void CancelAlarm(Context context)
{
Intent intent = new Intent(context, Alarm.class);
PendingIntent sender = PendingIntent.getBroadcast(context, 0, intent, 0);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.cancel(sender);
}
}
Set Alarm from Service:
package YourPackage;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
public class YourService extends Service
{
Alarm alarm = new Alarm();
public void onCreate()
{
super.onCreate();
}
public void onStart(Context context,Intent intent, int startId)
{
alarm.SetAlarm(context);
}
#Override
public IBinder onBind(Intent intent)
{
return null;
}
}
If you want set alarm repeating at phone boot time:
Add permission to Manifest.xml:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"></uses-permission>
...
<receiver android:name=".AutoStart">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"></action>
</intent-filter>
</receiver>
...
And create new class:
package YourPackage;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class AutoStart extends BroadcastReceiver
{
Alarm alarm = new Alarm();
#Override
public void onReceive(Context context, Intent intent)
{
if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED"))
{
alarm.SetAlarm(context);
}
}
}

service doing a periodic job

I am developing an Android app which must perform 2 periodic tasks in background:
download files from server every 24 hours.
perform file operations each week on phone sd card.
How do i do this?
Firstly you need to use AlarmManager. When the registered alarm e.g. 24 hours case, will trigger, you can call service from the broadcast receiver of AlarmManager. You need to study a bit about AlarmManager if you don't already know. For further help you can get some idea from code below:
Calendar cur_cal = new GregorianCalendar();
cur_cal.setTimeInMillis(System.currentTimeMillis());
Calendar cal = new GregorianCalendar();
cal.set(Calendar.DAY_OF_YEAR, cal.get(Calendar.DAY_OF_YEAR) + 1); //+1 For Next day (24 hours or so...)
cal.set(Calendar.HOUR_OF_DAY, hour);
cal.set(Calendar.MINUTE, minute);
cal.set(Calendar.SECOND, cur_cal.get(Calendar.SECOND));
cal.set(Calendar.MILLISECOND, cur_cal.get(Calendar.MILLISECOND));
cal.set(Calendar.DATE, cur_cal.get(Calendar.DATE));
cal.set(Calendar.MONTH, cur_cal.get(Calendar.MONTH));
AlarmManager am = (AlarmManager)getApplicationContext().getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(getApplicationContext(), AlarmReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, intent, PendingIntent.FLAG_ONE_SHOT);
am.cancel(pendingIntent);
am.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), pendingIntent);
Here is how you can set your Alarm. Now when Alarm will be triggered, you will call your service like this:
public class AlarmReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context arg0, Intent arg1)
{
// Call you service or any task here
}
}
Last thing, don't forget to mention your broadcast receiver and service in AndroidManifest.xml
<receiver android:name=".AlarmReceiver">
</receiver>
<service android:name=".MyService"/>
Here's what official android docs says about AlarmManager
Note: The Alarm Manager is intended for cases where you want to have
your application code run at a specific time, even if your application
is not currently running. For normal timing operations (ticks, timeouts, etc)
it is easier and much more efficient to use Handler.
Handler documentation.

Categories

Resources