Android set Exact Repeating Alarms WorkAround - android

I am trying to set repeating alarms in an exact fashion by manually setting an alarm to the next day the moment I receive it in a broadcast. However, I might be missing something and somehow its not working.
The Logic:
When I select the time from a time chooser, I set an exact alarm to the time given and start a pending intent which calls MyBroadCastReceiver.java (extends BroadcastReceiver) when its time. I then forward the intent with another pending intent to AlarmFireActivity. (AlarmFireActivity also has snooze which simply sets a pendingIntent to itself to fire after 5 mins.)
The MyBroadCastReceiver component hence only receives the actual alarms (not snoozes). And its functions are :
a) Get the non-repeating pending intent cancel it, then create another pending intent from the intent and then set it with the milliseconds set to the next day at the same time.
b) Start the AlarmFireActivity and show the alarm.
In AddAlarmActivity
Intent intent = new Intent(context, MyBroadcastReceiver.class);
intent.putExtra(DBHelper.COLUMN_ID,alarm.getId());
AlarmManager alarmManager = (AlarmManager) context.getApplicationContext().getSystemService(Context.ALARM_SERVICE);
if (alarmManager != null) {
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, reminder.getId(), intent, PendingIntent.FLAG_UPDATE_CURRENT);
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, reminder.getHourOfDay());
calendar.set(Calendar.MINUTE, reminder.getMinuteOfHour());
calendar.set(Calendar.SECOND,0);
alarmManager.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
pendingIntent);
BLog("Pending intent added at " + new SimpleDateFormat(utilFunctions.timeFormat).format(calendar.getTime()));
}
Snippet MyBroadCastReceiver.java TaskOne
Logic
Get the intent which MyBroadCastReceiver receives. Reuse the same intent in a new PendingIntent set to the next day at the same time.
Code
AlarmManager alarmManager = (AlarmManager) context.getApplicationContext().getSystemService(Context.ALARM_SERVICE);
if (alarmManager != null) {
PendingIntent p1 = PendingIntent.getBroadcast(context, alarmCurrent.getReminderId(), intent, PendingIntent.FLAG_UPDATE_CURRENT);
if(p1!=null){
alarmManager.cancel(p1);
}
PendingIntent pendingIntent1 = PendingIntent.getBroadcast(context, alarmCurrent.getReminderId(), intent, PendingIntent.FLAG_UPDATE_CURRENT);
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
//for test
calendar.add(Calendar.MINUTE,1);
alarmManager.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
pendingIntent1);
}
MyBroadCastReceiver.java TaskTwo
Logic
If the time when the alarm was set was after the time the alarm is due, do not forward the pending intent to the AlarmFireActivity.
Else forward it to the AlarmFireActivity which shows the screen to dismiss/snooze the alarm.
Code
Calendar timeSet = alarmCurrent.getAlarmReminderSetTime();
Calendar alarmTime = Calendar.getInstance();
if(timeSet!=null && alarmTime.getTimeInMillis() > timeSet.getTimeInMillis()){
Intent intent1 = new Intent(context, AlarmFireActivity.class);
intent1.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent1.putExtra(DBHelper.COLUMN_ID,id);
String ext = extras.getString(DBHelper.TASK_TITLE);
if(ext!=null){
intent1.putExtra(DBHelper.TASK_TITLE,ext);
}
context.startActivity(intent1);
}else{
}
The BroadCastReceiver is doing the second task just fine. However, not the task one. Its not repeating/ resetting a new pending intent. I am guessing I must have messed up the intent/pendingIntent somewhere. I dont know which. Please help/

The solution is restart the phone. I have seen somewhere that there is a bug in a one of the os' 4.3 or so. A pending intent with a request id of 0 might fail at times. And once I restarted the phone it worked. However, there is a more standard solution which is :
Replacing
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, reminder.getId(), intent, PendingIntent.FLAG_UPDATE_CURRENT);
with
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, A_NUMBER_GREATER_THAN_ZERO + reminder.getId(), intent, PendingIntent.FLAG_UPDATE_CURRENT);
so that you are avoiding a pending intent with 0 at all times. Thanks all!

Related

Android scheduling repeating alarms with broadcast receiver

I have to send 3 different notification(different content) at different time in a day.
Following the official documentation, https://developer.android.com/training/scheduling/alarms.html#type,
I was able to send notification at specified times, but having some trouble with changing the content of each notification. Following is the method to set notifications and is called in onCreate() method of MainActivity.
public void setNotification() {
Intent intent = new Intent(this, MyBroadcastReceiver.class);
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
for (int id = 0; id < 3; id++) {
Calendar calendar = Calendar.getInstance();
// Defining different pendingIntent
switch (id){
case 0:
calendar.set(Calendar.HOUR_OF_DAY, 9);
break;
case 1:
calendar.set(Calendar.HOUR_OF_DAY, 14);
break;
case 2:
calendar.set(Calendar.HOUR_OF_DAY, 20);
break;
}
if(calendar.getTimeInMillis() < System.currentTimeMillis()){
calendar.add(Calendar.DATE, 1);
}
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, id, intent, 0);
// setRepeating() schedules an alarm
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pendingIntent);
}
}
I am aware that I can use Intent.putExtra() features to send data, which can be used to identify the intent, but this data will be lost if app is killed.
I want this to work even if app is not running or app gets killed or device is booted. After some reading I found out that I can use a BOOT_COMPLETE listener to set alarms after device is booted. Because of setting notifications in onCreate() method, notification is appearing everytime I run the app, even though I tried to tackle this by using following code
if(calendar.getTimeInMillis() < System.currentTimeMillis()){
calendar.add(Calendar.DATE, 1);
}
Currently onReceive method looks like this
public void onReceive(Context context, Intent intent) {
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(context)
.setSmallIcon(R.mipmap.app_icon)
.setContentTitle("Title")
.setContentText("Message");
Intent resultIntent = new Intent(context, MainActivity.class);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
stackBuilder.addParentStack(MainActivity.class);
stackBuilder.addNextIntent(resultIntent);
PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
mBuilder.setContentIntent(resultPendingIntent);
NotificationManager mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(0, mBuilder.build());
}
I need help in deciding where should I put setNotification() method so that it registers all alarms once and how to identify which intent is coming so that appropriate actions can be taken. One way to go about it is to define 3 different broadcastReceivers with intent filters and set intent URI while creating them. Is it the correct way or is there any other simple solution?
notification is appearing everytime I run the app,
Keep a check using SharedPreference and set only once if not done.
Also your code
if(calendar.getTimeInMillis() < System.currentTimeMillis()){
calendar.add(Calendar.DATE, 1);
}
should be somewhat like this
Calendar calendar = Calendar.getInstance();
// Get the current time in millis
long currentTime = calendar.getTimeInMillis();
// Set the reminder times
switch(id) {
..
..
}
//Get the reminder time in millis
long intendedTime = calendar.getTimeInMillis();
if (intendedTime < currentTime) {
// Set from next day and to repeat once a day.
// you might consider using calendar.add() for adding one day to the current day
calendar.add(Calendar.DAY_OF_MONTH, 1);
intendedTime = calendar.getTimeInMillis();
alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP,
intendedTime, AlarmManager.INTERVAL_DAY, pendingIntent);
} else {
// Set for today and to repeat once a day.
alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, intendedTime,
AlarmManager.INTERVAL_DAY, pendingIntent);
}
how to identify which intent is coming so that appropriate actions can
be taken
You need to set an action to Intent in order to receive data in BroadcastReceiver
Intent intent = new Intent(this, MyBroadcastReceiver.class);
intent.setAction("THIS_IS_MY_ACTION");
Case of BOOT_COMPLETE
Cancel all the alarm on Boot, and reset them. To cancel an alarm Call cancel() on AlarmManager with an same PendingIntent which you have used while setting an alarm using setRepeating().
Take care of context and INTENT_FLAGS while working with AlarmManager.
Use getApplicationContext() for context and Keep records of request_code as they are going to be used while cancelling an alarm, use PendingIntent.FLAG_UPDATE_CURRENT
This might help:-
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), request_code, intent, PendingIntent.FLAG_UPDATE_CURRENT)

Android SDK - Set multiple alarms with same Intent different extras

I'm trying to have to have 2 repeating alarms triggering same Intent but with different extras:
// first alarm # 2AM
Calendar calendar1 = Calendar.getInstance();
calendar1.set(Calendar.HOUR_OF_DAY, 2);
calendar1.set(Calendar.MINUTE, 0);
calendar1.set(Calendar.SECOND, 0);
// second alarm # 3AM
Calendar calendar2 = Calendar.getInstance();
calendar2.set(Calendar.HOUR_OF_DAY, 3);
calendar2.set(Calendar.MINUTE, 0);
calendar2.set(Calendar.SECOND, 0);
// Set both intents with differing extras
Intent intent1 = new Intent(context, Receiver.class);
intent1.putExtra("status", true);
Intent intent2 = new Intent(context, Receiver.class);
intent2.putExtra("status", false);
// Set both pending Intents with differint ids
PendingIntent pendingIntent1 = PendingIntent.getBroadcast(context, 1, intent1, 0);
PendingIntent pendingIntent2 = PendingIntent.getBroadcast(context, 2, intent2, 0);
AlarmManager alarm1 = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarm1.setRepeating(AlarmManager.RTC_WAKEUP, calendar1.getTimeInMillis(),
AlarmManager.INTERVAL_DAY, pendingIntent1);
AlarmManager alarm2 = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarm2.setRepeating(AlarmManager.RTC_WAKEUP, calendar2.getTimeInMillis(),
AlarmManager.INTERVAL_DAY, pendingIntent2);
And here is what is triggered by the Intent
public class Receiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.d("MyApp", "Received intent with status : " + intent.getBooleanExtra("status", false));
}
.....
}
But for a strange reason both intents are triggered at approximately 3AM :
Received intent with status : false
Received intent with status : true
What am I doing wrong?
Problem is that AlarmManager.setRepeative is not accurate as written in the official doc.
I was starting my program at 2:50AM so with the above code, first alarm should have been started 50 minutes beforeso it needs to be triggered but since another alarm is scheduled at 31M, the Android OS decides to wait for the 3AM alarm to trigger both (to lower battery consumption I guess).
Solution is to add 24h to the alarms if needed in order to be sure they are scheduled in the future and not in the past and to use AlarmManager.setExact which is exact but not repeative. Then Reschedule a new AlarmManager.setExact every day.
I would suggest to never use AlarmManager.setRepeative if you need better accuracy than 1 hour.

Create alarm service in android for multiple times

in my task application, i want to create alarm service for multiple items. Here is the code i followed to create service. Lets assume, if there are more than 5 items present in list. then we will sent alarm service for 5 different times.
What if the user comes and edits the time?, how to update the system and make sure it triggers at new instance?. What if the user delete the task, how to quickly removes the registered alram for the deleted task?
What about uninstalling the app?, how to clear all the entries?, what if the system re-boots?
This is the code i'm following to register the task in alram services
Calendar Calendar_Object = Calendar.getInstance();
Calendar_Object.set(Calendar.MONTH, 8);
Calendar_Object.set(Calendar.YEAR, 2012);
Calendar_Object.set(Calendar.DAY_OF_MONTH, 6);
Calendar_Object.set(Calendar.HOUR_OF_DAY, 14);
Calendar_Object.set(Calendar.MINUTE, 25);
Calendar_Object.set(Calendar.SECOND, 0);
Step 2:
We need to create an alarm which help us to invoke the BroadCastReceiver on our specified time.
// MyView is my current Activity, and AlarmReceiver is the BoradCastReceiver
Intent myIntent = new Intent(MyView.this, AlarmReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
MyView.this, 0, myIntent, 0);
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
/*
The following sets the Alarm in the specific time by getting the long value of the alarm date time which is in calendar object by calling the getTimeInMillis(). Since Alarm supports only long value , we're using this method.
*/
alarmManager.set(AlarmManager.RTC, Calendar_Object.getTimeInMillis(),
pendingIntent);
Use this method to set Alarm :
public static void startAlarm(Context context, int alarm_code, String time) {
String[] arrTime = time.split(":");
intent = new Intent(context, AlarmReceiver.class);
intent.putExtra("CODE", alarm_code);
PendingIntent mAlarmSender = PendingIntent.getBroadcast(context,
alarm_code, intent, 0);
Calendar cal_alarm = Calendar.getInstance();
cal_alarm.setTimeZone(TimeZone.getDefault());
cal_alarm.set(Calendar.HOUR_OF_DAY, Integer.parseInt(arrTime[0]));
cal_alarm.set(Calendar.MINUTE, Integer.parseInt(arrTime[1]));
AlarmManager am = (AlarmManager) context
.getSystemService(Context.ALARM_SERVICE);
am.setRepeating(AlarmManager.RTC_WAKEUP, cal_alarm.getTimeInMillis(),
AlarmManager.INTERVAL_DAY, mAlarmSender);
}
Here,"int alarm_code" is your unique code for particular item.
And for update alarm first cancel the existing alarm and set new alarm for new time.For that use this method :
public static void cancelAlarm(Context context, int alarm_code) {
AlarmManager am = (AlarmManager) context
.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, AlarmReceiver.class);
intent.putExtra("CODE", alarm_code);
am.cancel(PendingIntent.getBroadcast(context, alarm_code, intent, 0));
}
Here,"int alarm_code" will be your unique code which you have used to set alarm.So that alarm with particular alarm_id will be cancel.You can also delete alarm with this method.
Same way you can clear all the alarms passing alarm_code to the given function.
You have to register receiver for system reboot and in onReceive of register you have to set all alarms again.
Hope this will help you.

Android AlarmManager Constantly goes off

I am using AlarmManager for my Timer. I have 2 classes.
In the first class (MainActivity) I am starting my alarm with the next code:
public void startAlarm(long Seconds) {
Intent myIntent = new Intent(MainActivity.this, MyAlarmService.class);
pendingIntent = PendingIntent.getService(MainActivity.this, 13141337,
myIntent, 0);
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.add(Calendar.SECOND, (int) Seconds);
alarmManager.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
pendingIntent);
}
In another class (AlarmSound), where the alarm goes off and the phone vibrates, I'm calling my cancel Alarm method.
public void stopAlarm(){
Intent intentstop = new Intent(AlarmSound.this, MyAlarmService.class);
PendingIntent senderstop = PendingIntent.getBroadcast(AlarmSound.this,
13141337, intentstop, 0);
AlarmManager myalarm = (AlarmManager) getSystemService(ALARM_SERVICE);
myalarm.cancel(senderstop);
Log.w("Karl","stopAlarm?");
}
Is this the correct way to do this? Because my alarm constantly goes off on android 4.1.
Thanks in advance!
Your Cancel wont work. The pending Intents have to match - since they have a different context (MainActivity and AlarmSound) this cancel wont work. You have to also use getService on both.
You can
a) try to recreate a matching pending Intent
b) get the pendingIntent that started the Service in the Service and use that to cancel
But usually every Alarm just goes off once if you dont use setRepeating().
Are you sure you are terminating your Service correctly with stopself() and are giving it the right flag in the onStartCommand() Method?

AlarmManager set to same BroadcastReceiver doesn't work

I want to be able to register two alarms to the same BroadcastReceiver. However, the first alarm never gets fired. How can I make this work?
Calendar now = Calendar.getInstance();
now.set(Calendar.SECOND, now.get(Calendar.SECOND) + 5);
long trigger1 = now.getTimeInMillis();
now.set(Calendar.SECOND, now.get(Calendar.SECOND) + 10);
long trigger2 = now.getTimeInMillis();
Intent startIntent = new Intent(AlarmStartReceiver.START_ALARM);
startIntent.putExtra(AlarmStartReceiver.EXTRA_ALARM_ID, 4);
PendingIntent startPIntent = PendingIntent.getBroadcast(context, 0, startIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Intent startIntent2 = new Intent(AlarmStartReceiver.START_ALARM);
startIntent2.putExtra(AlarmStartReceiver.EXTRA_ALARM_ID, 5);
PendingIntent startPIntent2 = PendingIntent.getBroadcast(context, 0, startIntent2, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarm.set(AlarmManager.RTC_WAKEUP, trigger1, startPIntent);
alarm.set(AlarmManager.RTC_WAKEUP, trigger2, startPIntent2);
Only the second one goes off. How can I make them both go off?
EDIT FOR ANSWER: Set the requestCode to something unique. The second param of the PendingIntent.getBroadcast) method
android pending intent notification problem
Set the requestCode to something unique. The second param of the PendingIntent.getBroadcast) method android pending intent notification problem
Are you looking for separate notification event for each alarm you are setting? Or it has to be same notification with number of alarm events showing on the status bar icon?
Look at this post on how you can use "setData()" to the intent to create separate alarms.
Alarm Manager - Scheduling multiple Non-repeating events

Categories

Resources