I'm trying to set several alarms with my app, and cancel them upon request.
I've read a lot of topics about the same, and went through the documentation, but it seems like it doesn't work.
Documentation says that cancel will try to find an intent that matches the one I provide with 'filterEquals'('Determine if two intents are the same for the purposes of intent resolution (filtering). That is, if their action, data, type, class, and categories are the same. This does not compare any extra data included in the intents.')
I'm providing same action, data, type, class, and category, so why does it not work?
Create alarm:
AlarmManager manager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
int interval = 30000;
Intent intent = new Intent(getApplicationContext(), Broadcast.class);
intent.putExtra("alarmTime",alarmTime);
intent.putExtra("reminder1",reminder1);
intent.putExtra("reminder2",reminder2);
intent.putExtra("title",title);
intent.putExtra("message",message);
intent.putExtra("vibrate",vibrate);
intent.putExtra("sound",sound);
intent.putExtra("name",name);
//Create _id from data input. is unique
int _id = 0;
for (char i: name.toCharArray()){
_id += Character.getNumericValue(i);
}
for (char i:title.toCharArray()){
_id += Character.getNumericValue(i);
}
for (char i:message.toCharArray()){
_id += Character.getNumericValue(i);
}
_id += (int) alarmTime.getTime();
_id += (int) reminder1.getTime();
_id += (int) reminder2.getTime();
Log.e("ALARMS", "Creating alarm with id: "+_id);
intent.setData(Uri.parse("custom://" + _id));
intent.setAction(String.valueOf(_id));
PendingIntent pendingUpdateIntent = PendingIntent.getBroadcast(getApplicationContext(), _id, intent, 0);
manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + interval, pendingUpdateIntent);
Cancel alarm
AlarmManager manager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(getApplicationContext(), Broadcast.class);
intent.putExtra("alarmTime",alarmTime);
intent.putExtra("reminder1",reminder1);
intent.putExtra("reminder2",reminder2);
intent.putExtra("title",title);
intent.putExtra("message",message);
intent.putExtra("vibrate",vibrate);
intent.putExtra("sound",sound);
intent.putExtra("name",name);
int _id = 0;
for (char i:name.toCharArray()){
_id += Character.getNumericValue(i);
}
for (char i:title.toCharArray()){
_id += Character.getNumericValue(i);
}
for (char i:message.toCharArray()){
_id += Character.getNumericValue(i);
}
_id += (int) alarmTime.getTime();
_id += (int) reminder1.getTime();
_id += (int) reminder2.getTime();
intent.setData(Uri.parse("custom://" + _id));
intent.setAction(String.valueOf(_id));
PendingIntent pendingUpdateIntent = PendingIntent.getBroadcast(getApplicationContext(), _id, intent, 0);
Log.e("ALARMS", "Cancelling alarm with id: "+_id);
// Cancel alarms
try {
manager.cancel(pendingUpdateIntent);
} catch (Exception e) {
Log.e("ALARMS", "AlarmManager update was not canceled. " + e.toString());
}
So I call 3 times the create alarm with different parameters, so my _id is different each time.
Then I call 3 times the cancel alarm and the ids generated match for each alarm upon create and cancel.
So the cancel runs but my alarms still get fired.
My alarms trigger a broadcast receiver that will output a notification per alarm.
I forgot to mention that I tried with different flags for the PendingIntent but none work (FLAG_CANCEL_CURRENT, FLAG_UPDATE_CURRENT)
Seems like I was feeding different dates to the alarms, and the _id's were different by a couple of numbers (start and finish were the same), this resulted on calling a cancel on _id values that were different and not working.
Changed the way I generate the _id to exclude the dates (which I generated with new Date()) and not it works
By bad, I didn't see it.
Related
I have this app release in Android that notifies the user everyday at specific time (for example 10:00 pm). I didn't use setRepeating as it was not recommended on higher APIs from 21 up. The following was the pseudo code I used to keep resetting the alarm.
AlarmController (set alarm)
Once time was met send to alarm receiver
Alarm receiver would call the Alarm Controller and Increase the day by 1 and set it again
At the same time Alarm receiver will fire up the Intentservice for notification and set notified **true** in DataBase
In theory it was supposed to work. I am using Joda Time here to easily increase the day by 1. But the problem was, the notification kept firing up at a random time, sometimes it will fire up 6, after it was last fire up, or most often every 1 hour or 30 mins. I don't get it. Now the pseudo code I provided was the basic one. On my real code I have two alarms, one for firing up the notification and set Notified to true and the other was to reset the notified to false once the day change.
My app has been released. I never expected this scenario to happen because the way I debug this was on genymotion, I move the time manually and everything seems to be working alright. I had to fire a log on fabric IO but it seems that the log would only show once you get an error. Anyone thanks
Here is my bare minimum code that I was using exactly:
Alarm Controller
public static void setAdaptiveReminder(Context context, long ALARM_ID, DateTime dateTime, boolean shouldsetAlarm) {
Intent myIntent = new Intent(context, AdaptiveReminderReceiver.class);
myIntent.putExtra("reminder", shouldsetAlarm);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, (int) ALARM_ID, myIntent, PendingIntent.FLAG_UPDATE_CURRENT);
ALARMMANAGER = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Log.d(TAG, "setAdaptiveReminder: " + dateTime.toLocalTime().toString() + " " + dateTime.toLocalDate().toString());
CrashUtility.LogErrorReportToFabric(TAG + " setAdaptiveReminder", dateTime.toLocalTime().toString() + " " + dateTime.toLocalDate().toString());
if (Utility.GetBuildVersion() >= 19 && Utility.GetBuildVersion() < 23) { // if lollipop
//ALARMMANAGER.setRepeating(AlarmManager.RTC_WAKEUP,System.currentTimeMillis() + 5000,1000,pendingIntent);
ALARMMANAGER.setExact(AlarmManager.RTC_WAKEUP, dateTime.toDate().getTime(), pendingIntent);
//ALARMMANAGER.setRepeating(AlarmManager.RTC_WAKEUP,dateTime.toDate().getTime(), AlarmManager.INTERVAL_DAY,pendingIntent);
} else if (Utility.GetBuildVersion() >= 23 || Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
//Log.d(TAG, "setTimeSinceLastUseReminder: android M and UP");
ALARMMANAGER.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, dateTime.toDate().getTime(), pendingIntent);
//ALARMMANAGER.setrep(AlarmManager.RTC_WAKEUP,dateTime.toDate().getTime(), AlarmManager.INTERVAL_DAY,pendingIntent);
}
}
AlarmPresenter
public void setAdaptiveReminder() {
//AlarmController.setAdaptiveReminder(context,778,d);
DateTime dateTime = TimeUtility.SetCorrectTimeInCorrectDate(settingsRepository.getAdaptiveReminderTime());
boolean isNotified = settingsRepository.isNotifiedReminder();
//Toast.makeText(context, "" + dateTime.toLocalTime().toString(), Toast.LENGTH_SHORT).show();
Log.d(TAG, "setAdaptiveReminder: gggfss " + TimeUtility.DateIsToday(dateTime) + " " + dateTime.toLocalTime().toString());
Log.d(TAG, "setAdaptiveReminder: " + isNotified);
if (TimeUtility.DateIsToday(dateTime) && !isNotified) {
dateTime = dateTime.plusDays(1);
CrashUtility.LogErrorReportToFabric(TAG + " setAdaptiveReminder", dateTime.toLocalTime().toString() + " " + dateTime.toLocalDate().toString());
AlarmController.setAdaptiveReminder(context, 778, dateTime, true);
Log.d(TAG, "setAdaptiveReminder: " + dateTime.toLocalDate() + " " + dateTime.toLocalTime().toString());
}
}
Receiver
public void onReceive(Context context, Intent intent) {
// TODO: This method is called when the BroadcastReceiver is receiving
// an Intent broadcast.
this.context = context;
alarmControllerPresenter = new AlarmControllerPresenter(context,settingsRepository,habitRepository);
alarmControllerPresenter.setAdaptiveReminder();
}
So the code I have provided was the exact same code I am using to set the alarm every day. I just did some cleaning up and that was about it.
I am currently stuck with the question, if creating a new intent inside a for loop is good or bad. I have the following situation:
1.
public static void reactivateReminders(Schedule schedule) {
ArrayList<Lecture> allLectures = schedule.getAllLectures();
for(Lecture lecture : allLectures) {
...
// Set up various things for the reminder
...
Intent intent = new Intent(getApplicationContext(), ReminderReceiver.class);
String at = getResources().getString(R.string.at);
String with = getResources().getString(R.string.with);
String beginH = ScheduleHelper.formatNumber(changedBeginH);
String beginM = ScheduleHelper.formatNumber(changedBeginM);
String room = lecture.getRoom();
intent.putExtra("contentText", at + " " + beginH + ":" + beginM + " in " + room + " " + with + " " + lecture.getLecturer());
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), lecture.getAlarmId(), intent, 0);//PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarm = (AlarmManager) getSystemService(ALARM_SERVICE);
if(lecture.getBeginH() != beginH || lecture.getBeginM() != beginM)
alarm.cancel(pendingIntent);
alarm.setInexactRepeating(AlarmManager.RTC, calendar.getTimeInMillis() + offset, 1000 * 60 * 60 * 24 * 7, pendingIntent);
}
}
2.
public static void reactivateReminders(Schedule schedule) {
ArrayList<Lecture> allLectures = schedule.getAllLectures();
Intent intent = new Intent(getApplicationContext(), ReminderReceiver.class);
for(Lecture lecture : allLectures) {
...
// Set up various things for the reminder
...
String at = getResources().getString(R.string.at);
String with = getResources().getString(R.string.with);
String beginH = ScheduleHelper.formatNumber(changedBeginH);
String beginM = ScheduleHelper.formatNumber(changedBeginM);
String room = lecture.getRoom();
intent.putExtra("contentText", at + " " + beginH + ":" + beginM + " in " + room + " " + with + " " + lecture.getLecturer());
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), lecture.getAlarmId(), intent, 0);//PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarm = (AlarmManager) getSystemService(ALARM_SERVICE);
if(lecture.getBeginH() != beginH || lecture.getBeginM() != beginM)
alarm.cancel(pendingIntent);
alarm.setInexactRepeating(AlarmManager.RTC, calendar.getTimeInMillis() + offset, 1000 * 60 * 60 * 24 * 7, pendingIntent);
}
}
Which option is the better one? I'm not too familiar with Java so I don't know how Java handles either one. Maybe is doesn't make a difference at all but since I'm programming in C++ normally, creating new objects inside a loop worries me.
Thanks for any help in advance.
Edit: Conclusion:
As mentioned by Alex Shutov, it better to not set all reminders at once. The user probably only needs the next one that is coming up.
To achieve this, you should set the earliest reminder somewhere in the app and store the other reminders (or rather the data you use for it) sorted in some place outside the app (XML, SQL, ...) so that your service can, after the earliest reminder set off, read the file to load the next one.
By doing this, you don't burden the system with reminders that the user doesn't even need yet. I will try to implement this idea sometime but for now I will use my approach.
Regarding my code:
A better approach for my posted code is to create the new intent once outside the loop. Since the extra I put in it has the same key, it will overwrite every time and you don't have to create a new intent. Other variables like my "at" and "with", which are constant, can be placed outside of the loop too. The variables "beginH, beginM, room" can be removed and you can just call the functions directly in the putExtra parameter. You can also place the PendingIntent and the AlarmManager line outside the loop.
I would post the code but I think my post will be too big then.
Thanks for the fast help :)
It is a bad idea, because you overload system with unneccessary tasks, you should instead schedule nearest event, in IntentService schedule next event
I have managed to pass through the "main" information into a calendar intent...
however when I try to add attendees to the intent, they are not inserted. Here is the code
startCalIntent = new Intent(Intent.ACTION_EDIT);
startCalIntent.setType("vnd.android.cursor.item/event");
startCalIntent.putExtra(Events.TITLE, title);
startCalIntent.putExtra(Events.EVENT_LOCATION, location);
startCalIntent.putExtra(Events.DESCRIPTION, details);
startCalIntent.putExtra(Events.ORGANIZER, organiser);
startCalIntent.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, splitDateTime(date, startTime));
startCalIntent.putExtra(CalendarContract.EXTRA_EVENT_END_TIME, splitDateTime(date, endTime));
startCalIntent.putExtra(Events.EVENT_TIMEZONE, "Europe/London");
startCalIntent.putExtra(Attendees.HAS_ATTENDEE_DATA, "1");
startCalIntent.putExtra(Attendees.ATTENDEE_NAME, "DAVE");//<---NOT WORKING
startActivity(startCalIntent);
You cannot add attendee during creating event. You need Event_ID to proceed another update on event like adding remainders, or attendees.
Note: See how this example captures the event ID after the event is
created. This is the easiest way to get an event ID. You often need
the event ID to perform other calendar operations—for example, to add
attendees or reminders to an event.
source: Android developer
you can use this code as provided in Android developer:
long eventID = 202;
...
ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
values.put(Attendees.ATTENDEE_NAME, "Trevor");
values.put(Attendees.ATTENDEE_EMAIL, "trevor#example.com");
values.put(Attendees.ATTENDEE_RELATIONSHIP, Attendees.RELATIONSHIP_ATTENDEE);
values.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_OPTIONAL);
values.put(Attendees.ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_INVITED);
values.put(Attendees.EVENT_ID, eventID);
Uri uri = cr.insert(Attendees.CONTENT_URI, values);
Hope that may help;
Try this ..
ContentValues values = new ContentValues();
values.put("calendar_id", 1);
values.put("title", "event Name");
values.put("allDay", 0);
values.put("dtstart", cal.getTimeInMillis() + diffInhrs*60*1000); // event starts at date specified in datepicker
values.put("dtend", cal.getTimeInMillis()+ end_diff *60*1000); // ends 60 minutes from selected date
values.put("description", "event desc");
values.put("visibility", 0);
values.put("hasAlarm", 1);
Uri event = cr.insert(EVENTS_URI, values);
For more explaination plz go through this CLICK HERE
I have been developing an Android application and I need to execute 1 task every hour. I uses the following code for it:
private static final long ALARM_PERIOD = 1000L;
public static void initAlarmManager(Context context) {
Editor editor=PreferenceManager.getDefaultSharedPreferences(context).edit();
editor.putBoolean(context.getString(R.string.terminate_key), true).commit();
AlarmManager manager = (AlarmManager) context.getSystemService(context.ALARM_SERVICE);
Intent i = new Intent(context, AlarmEventReceiver.class);
PendingIntent receiver = PendingIntent.getBroadcast(context, 0, i, 0);
manager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime(), ALARM_PERIOD, receiver);
}
It works for me, but my client tells me that the task works only 1 time and won't work 1 hour. Where have I made a mistake? Please, tell me. Thank you.
According to your code, ALARM_PERIOD is 1000L, as repeating interval. So I doubt the alarm will set of in every 1000 milliseconds.
if you are setting repeating interval for every hour, it should be 3600000L.
And take note that if the phone is restarted, your alarm manager will no longer work unless you start again.
Here is the my Code:
private void setAlarmManager() {
Intent intent = new Intent(this, AlarmReceiver.class);
PendingIntent sender = PendingIntent.getBroadcast(this, 2, intent, 0);
AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
long l = new Date().getTime();
if (l < new Date().getTime()) {
l += 86400000; // start at next 24 hour
}
am.setRepeating(AlarmManager.RTC_WAKEUP, l, 86400000, sender); // 86400000
}
Have you added receiver tag in application tag in manifest.xml
<receiver android:name=".AlarmReceiver" android:process=":remote"/>
Instead of Alram-Manager I recommended you to use Android-TimerTask
The TimerTask class represents a task to run at a specified time. The task may be run once or repeatedly. Its perfect suits for your requirements.
Try by modifying your code by changing your setRepeating() method like this
manager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,SystemClock.elapsedRealtime(), SystemClock.elapsedRealtime()+(60*60*1000), receiver);
OR
Test this it is repeating for every minute
manager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,Calendar.getInstance().getTimeInMillis(), Calendar.getInstance().getTimeInMillis()+(1*60*1000), receiver);
What I'm doing is adding different proximity alerts with a unique ID as an extra and it's not working -
for (int i = 0; i < latArray.size(); i++)
{
Bundle extra = new Bundle();
extra.putInt("UID", i);
Intent intent = new Intent(IntentToFire);
intent.putExtra("Blob", extra);
PendingIntent proximityIntent = PendingIntent.getBroadcast(this,-1 , intent, 0);
LocationManager locationManager =
Log.i("Picture:","Location img:"+GetLocation.imgArray.get(i));
Log.i("Potatoo:", "Lat :"+latArray.get(i)+" Lng :"+lngArray.get(i));
locationManager.addProximityAlert(Double.valueOf(latArray.get(i)), Double.valueOf(lngArray.get(i)), radius,expiration,proximityIntent);
}
And then on the broadcast receiver I'm putting this code -
flag = intent.getBundleExtra("blob").getInt("UID");
Every time I got to print flag, I just get an error. An ideas?
Try to use the FLAG_UPDATE_CURRENT:
PendingIntent.getBroadcast(this,-1 , intent, PendingIntent.FLAG_UPDATE_CURRENT);
I found it easiest is to encode data in the data-uri with proximity alerts on the Intent that is included in the pending intent, e.x.:
geo:<lat>,<lon>?id=<your id>
You can use your own protocol part though (geo is used by google maps AFAIK). No caching problems for me (seems you get a cached/old/wrong PendingIntent).