I'm making an application in which I want to cancel an alarm(not set by my application) after few seconds.
What is the way to cancel alarm which may be set by any other application?
What I have is, notification posted on Android Notification Center when alarm triggers.
I read from android documentation that I need PendingIntent to cancel the triggered alarm. But how can I get PendingIntent in this case?
I noticed that I can get contentIntent from alarm notification,posted to the Android Notification Center. I tried to cancel alarm from this PendingIntent but did not succeed.
Any way to get PendingIntent of triggered alarm? Or/And to cancel alarm?
This trick is a little bit old, but it saves many developers.
Suppose in ActivityOne we start an AlarmManager like:
AlarmManager mgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(context, OnServiceReceiver.class);
PendingIntent pi = PendingIntent.getBroadcast(context, 5290, i, 0);
mgr.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() + 60000, LOCAL_SERVICE_PERIOD, pi);
To cancel this AlarmManager in any other Activity/Broadcastreceiver/Service we have to remember some of its informations.
1: Context: The context used by the AlarmManager followed by its PendingIntent.
2: PendingIntent ID: getBroadcast(context, 5290, i, 0); .It makes the Pi unique which is mostly important.
So we have to save the PendingIntent id in a SharedPreference to confirm at the time of canceling.
Now the Context used by AlarmManager .
In same Activity( ActivityOne) we have to create a global Context which holds the original one. like:
//Define it globaly in ActivityOne
private static Context mContext;
//create a public static method which holds the current context and share
public static Context getActivityOneContext() {
return ActivityOne.mContext;
}
//initialize it by assigning application context in onCreate() method
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.xxx);
mContext = getApplicationContext();
//Or if this is a BroadCastReceiver ..use the current context and do same in onReceive()
//OnBootReceiver.mContext = context.getApplicationContext();
Now you can cancel the AlarmManager anywhere of the application..
AlarmManager mgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent stopIntent = new Intent(ActivityOne.getActivityOneContext, OnServiceReceiver.class);
PendingIntent stopPI = PendingIntent.getBroadcast(ActivityOne.getActivityOneContext, 5290, stopIntent, 0);
mgr.cancel(stopPI);
Related
Let's see if someone found this problem and can help me to solve the mistery. I have the following situation. The app is a simple to-do list:
public class NewToDo extends Activity {
private PendingIntent pendingIntent;
private AlarmManager manager;
...
When the user creates a to-do with a notification, I create it in one of the methods of this class like this:
int interval = 30000;
Intent alarmIntent = new Intent(this, AlarmReceiver.class);
/*Let's let the putExtras apart, as documentation says they're not considered when comparing intents, but I add some of them*/
manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
pendingIntent = PendingIntent.getBroadcast(getBaseContext(), _id, alarmIntent, alarmIntent.FILL_IN_DATA);
/*Printing _id we see 3, for instance, but is a unique identifier to associate the notification with this to-do item*/
/*I use the flag alarmIntent.FILL_IN_DATA because I want to send extras to create a custom notification*/
manager.setInexactRepeating(AlarmManager.RTC_WAKEUP,
System.currentTimeMillis() + interval, interval,
pendingIntent);
I quit from the app (but no difference if I don't quit).Then the notification arrives correctly each 30 seconds, and selecting it the app launchs on the detailed view of the item. An actionbar button allows editting it, calling again the same class, NewToDo.
While editting I can cancel the notifications associated with the to-do, and my code makes that like this -in the NewToDo class-:
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
Intent i = new Intent(this, AlarmReceiver.class); //creating a matching Intent
Log.v(getString(R.string.app_name), "_id: " + _id); //the 3 aforementioned
PendingIntent displayIntent = PendingIntent.getActivity(getBaseContext(), _id, i, alarmIntent.FILL_IN_DATA);
alarmManager.cancel(displayIntent);
But the notifications keeps appearing each 30 seconds, so they're not cancelled.
I saw the following suggestion in another post:
"Intent intentYouWantToCancel = new Intent(this, AlarmReceive.class);
intentYouWantToCancel.setAction("aRelevantString");
when you set the alarmmanager as well as when you want to delete it. "
So I put in both intents as 'aRelevantString' a cast to String of the unique identifier. But same problem.
Also changed the getBaseContext() for this, just in case, but no difference.
No errors, just the notifications keep repeating ignoring my cancel. Any idea?
PS: I restart the AVD each time I run it. I realised that the way I was using the print of the filterEquals had no sense at all (too many hours with this), but that doesn't affect the rest of the question.
I finally solved it like this. To create the alarm:
public class NewToDo extends Activity {
private PendingIntent pendingIntent;
private AlarmManager manager;
...
int interval = 30000;
manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent alarmIntent = new Intent(this, AlarmReceiver.class);
alarmIntent.putExtra("id", _id);
pendingIntent = PendingIntent.getBroadcast(this, _id, alarmIntent,0);
manager.setInexactRepeating(AlarmManager.RTC_WAKEUP,
System.currentTimeMillis() + interval, interval,
pendingIntent);
To cancel that concrete alarm:
manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(getBaseContext(), AlarmReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
getApplicationContext(), _id, i, 0);
manager.cancel(pendingIntent);
pendingIntent.cancel();
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.
I have the problem removing the alarm which I already set. I used two activities to "ADD" or "EDIT" alarms. When I delete an alarm, I want to use the activity which is made to show list of alarms. However, It doesn't delete the PendingIntent I set.
Here's the part of my source code in List Activity.
private void removeAlarm(int number)
{
final int alarmCode = AlarmCodeCreator.CreateNum(1, clickedPosition, 0);
AlarmManager alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(ListMainActivity.this,
PushMainActivity.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
getApplicationContext(), alarmCode, intent, 0);
alarmManager.cancel(pendingIntent);
System.out.println(alarmCode);
}
You should use the PendingIntent.FLAG_UPDATE_CURRENT flag and make sure to create the PendingIntent the same way it was originally created.
Same answer here.
The PendingIntent cancel() API doc says:
Cancel a currently active PendingIntent. Only the original application owning a PendingIntent can cancel it.
I not sure about the meaning of this. If I set the AlarmManager event from activity x like this:
PendingIntent pendingIntent;
Intent myIntent = new Intent(x.this, AlarmReciever.class);
myIntent.putExtra("task_uuid", task_uuid);
pendingIntent = PendingIntent.getBroadcast(x.this, 0, myIntent,0);
AlarmManager alarmManager = (AlarmManager)getSystemService(ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC, dateTime.getTimeInMillis(), pendingIntent);
My question is: can I cancel the pending intent from activity y using:
PendingIntent pendingIntent;
Intent myIntent = new Intent(y.this, AlarmReciever.class);
myIntent.putExtra("task_uuid", task_uuid);
pendingIntent = PendingIntent.getBroadcast(y.this, 0, myIntent,0);
AlarmManager alarmManager = (AlarmManager)getSystemService(ALARM_SERVICE);
alarmManager.cancel(pendingIntent);
When you set alarm you need to pass one key value in PendingIntent it will differentiate no of alarms, should be like this
pendingIntent = PendingIntent.getBroadcast(x.this, key_value, myIntent,0);
SharedPreferences settings = context.getSharedPreferences("alarm", 0);
SharedPreferences.Editor editor = settings.edit();
editor.putBoolean(key, false);
editor.commit();
to cancel same alarm you need to save that key_values some where, you can use Shared preferences. get the same key and then cancel alarm like this
SharedPreferences settings = context.getSharedPreferences("alarm", 0);
Map<String,?> allNotifyIdsMap = settings.getAll();
if(allNotifyIdsMap!=null&&allNotifyIdsMap.isEmpty()==false)
{
for(String notifyId: allNotifyIdsMap.keySet())
{
boolean isCleared = settings.getBoolean(notifyId, false);
if(isCleared==false)
{
pendingIntent = PendingIntent.getBroadcast(y.this, key_value, myIntent,0);
AlarmManager alarmManager = (AlarmManager)getSystemService(ALARM_SERVICE);
alarmManager.cancel(pendingIntent);
}
}
}
Brief Overview
AlarmManager basically schedules a time based operation by invoking the PendingIntent at the scheduled time. So inorder to cancel the Scheduled Alarm you need access to that PendingIntent.
Always note the two parameters while creating a Pending Intent- Request Code and Flag
PendingIntent.getBroadcast(context,REQUEST_CODE,intent, PendingIntent.FLAG_UPDATE_CURRENT);
Request Code - Acts as the unique identifier
Flag - Defines the behavior of PendingIntent
This brief overview is not enough to understand AlarmManager. Read more about working of AlarmManager and PendingIntent here
Solution
You can cancel the scheduled Alarm from anywhere inside your Application even if dont have access to the same PendingIntent object used while scheduling the Alarm. You can create a new pending intent with the same request code and FLAG_NO_CREATE and it will return the same PendingIntent object.
/*
With FLAG_NO_CREATE it will return null if the PendingIntent doesnt already
exist. If it already exists it returns
reference to the existing PendingIntent
*/
PendingIntent pendingIntent=PendingIntent.getBroadcast(this,REQUEST_CODE,intent,PendingIntent.FLAG_NO_CREATE);
if (pendingIntent!=null)
alarmManager.cancel(pendingIntent);
The code snippet below....
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
///////////Do something////////////////////////
showtext.startScan();
//SEt Alarm
Intent intent = new Intent(this, TextReceiver.class);
PendingIntent pi = PendingIntent.getBroadcast(this, 0, intent, 0);
AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()+9000, pi);}
And my Receiver :
TextReceiver extends BroadcastReceiver{
public void onReceive(Context context, Intent intent) {
///Show text/////
}
}
The thing is that when I run the program after 9sn, I am getting an error that "The app stopped unexpectedly". Why I get this error?
My goal is to show the text every 9sn. What is the correct usage of AlarmManager in the main activity
OR Should I set alarm in the BroadcastReceiver ?
Which one does makes sense: am.setRepeating or am.set in terms of my goal?
**Edit: How can I change my alarm code to run in the Broadcast Receiver ? **
//try this
AlarmManager am=(AlarmManager)getApplicationContext getSystemService(Context.ALARM_SERVICE);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
am.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),(9 * 1000), pendingIntent);
Never, ever use FLAG_CANCEL_CURRENT with PendingIntents that are set as alarms.
What happens is that you wind up canceling the validity of the PendingIntent currently held by the alarm manager, and this means that the alarm manager can no longer tell that any newly-set alarm matches that old PendingIntent. You wind up with the old (invalid) alarm still registered along with your new one. If you keep doing this you can wind up with hundreds (or more!) stale alarms registered in the system, none of which will actually do anything but which are taking up memory and CPU.