I am making an app where the user can set an alarm based on GPS location. I only want 1 alarm to be active at any one time. So, when the user sets a 2nd alarm, I want to cancel the notification for the 1st alarm (then set a new notification for the 2nd alarm).
Right now, my notifications continue to stack up (as in I can't delete them, so they are all active). Here is my code where I am trying to delete the alarm and notification(s):
// Stop the location alarm activity
Intent intentAlarmService_delete = new Intent(v.getContext(), AlarmService.class);
stopService(intentAlarmService_delete); // I think this calls onDestroy() in AlarmService class ...
mNtf = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
mNtf.cancelAll();
Intent alarmIntent2 = new Intent(getApplicationContext(), OneTimeAlarmReceiver.class);
PendingIntent pendingIntentAlarm = PendingIntent.getBroadcast(getApplicationContext(), PENDING_INTENT_REQUEST_CODE1,
alarmIntent2, PendingIntent.FLAG_CANCEL_CURRENT);
pendingIntentAlarm.cancel();
This is the onDestroy() function in my AlarmService.class (I'm not really sure when this is called...)
public void onDestroy(){
super.onDestroy();
mNtf.cancel(NOTIFICATION_ID1);
Intent alarmIntent = new Intent(getApplicationContext(), OneTimeAlarmReceiver.class);
PendingIntent pendingIntentAlarm = PendingIntent.getBroadcast(getApplicationContext(), PENDING_INTENT_REQUEST_CODE1,
alarmIntent, PendingIntent.FLAG_CANCEL_CURRENT);
pendingIntentAlarm.cancel();
Intent intentAlarmService = new Intent(getApplicationContext(), AlarmService.class);
stopService(intentAlarmService);
mNtf.cancel(NOTIFICATION_ID1);
mNtf.cancelAll();
}
Then, this is how I am setting a new alarm and notification:
Intent intentAlarmService2 = new Intent(v.getContext(), AlarmService.class);
startService(intentAlarmService2);
By the way, my AlarmService.class is working for sure.
Thanks in advance.
First, get rid of getApplicationContext(). You almost never need it and it is frequently the wrong choice. Replace it with this, since whatever you are calling getApplicationContext() on is a Context.
In the code you have listed, you never raise a Notification. Hence, it is difficult to help you figure out why you are getting more than one. Calling cancelAll() on the NotificationManager should get rid of all outstanding notifications from your application.
My best guess is that onDestroy() is not being called on your service. That would occur if something else is keeping the service in memory (e.g., you have an active bound connection to it via bindService()). Or, possibly, you have something strange in your <service> element in the manifest (e.g., an unnecessary android:process attribute) that is fouling up the NotificationManager cancel() operation.
You need to make sure you are always referencing the same instance of your NotificationManager. Different instances will produce different notifications. I'd recommend using a service to manage notifications.
http://developer.android.com/guide/topics/fundamentals.html
Related
I am listening to DetectedActivity, FusedLocation, GoogleFit etc. updates using a pending intent. I pass over
PendingIntent.FLAG_UPDATE_CURRENT when calling: pendingIntent = PendingIntent.getService(this, 12, intentService, PendingIntent.FLAG_UPDATE_CURRENT);
However, I still get duplicate data.
I tried using a different request id for every time I listen to certain updates (different request id for listening to FusedLocation updates, different request id for listening to GoogleFit updates etc.) but it still gives me duplicate data. I also tried adding different actionIds to the intentService
Inside my Service, I have the following code:
intentService = new Intent(this, SensorsIntentService.class); //in onCreate
pendingIntent = PendingIntent.getService(this, 12, intentService, PendingIntent.FLAG_UPDATE_CURRENT);
I expect only one set of data every time and no duplicates.
Any help would be appreciated.
So, answering my own question, apparently my problem was not about the pending intent flag, but about the onStartCommand
inside my service. Apparently it was called twice (second time because of me moving the service to the foreground by calling:
startForeground(id, notification)
(correct me if I am wrong regards to the reason) so the listeners were registering again and so data was being sent twice. Instead of starting this in the onStartCommand I now start the foregroundService in the onBind.
The only thing I didn't figure out is: I had to create a BroadcastReceiver instead of my IntentService to handle the pendingIntent and in my Service implementation I now use
pendingIntent = PendingIntent.getBroadcast(this, 12, intentService, PendingIntent.FLAG_UPDATE_CURRENT);
instead of
pendingIntent = PendingIntent.getService(this, 12, intentService, PendingIntent.FLAG_UPDATE_CURRENT);
or else the pendingIntent is never caught.
Anyone have an idea why?
I have followed some tutorials from SO to generate a local notification using AlertManager some time after my app closes by broadcasting to a custom BroadcastReceiver impl of mine. It works well, I can see the notification come up in the notification area of the device.
However, it only works if the app's main activity is paused. If the app is stopped (via Settings->apps), there is no notification, the broadcast receiver is never called. I had the impression that AlertManager registers my notification request in some OS service - not related to my app, that's the whole point, to have some sort of notification through which the user can restart my app. I am testing on Android 4.2.1 BTW. Any chance I am simply doing something wrong and there is actually a way to get AlertManager to successfully broadcast something out?
Here is my AlertManager code, called from my main activity's onPause (set to 10 seconds, just for testing). 'ctx' is the main activity
Calendar cal = Calendar.getInstance();
cal.add(Calendar.SECOND, 10);
Intent intent = new Intent(ctx, MyAlarmReceiver.class);
intent.putExtra("alarm_message", "hey, wake up this app!");
// note: 192837 is just some test ID:
PendingIntent sender = PendingIntent.getBroadcast(ctx, 192837, intent, PendingIntent.FLAG_UPDATE_CURRENT);
// Get the AlarmManager service
AlarmManager am = (AlarmManager) ctx.getSystemService(Context.ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), sender);
And here is MyAlarmReceiver.onReceive(context, intent):
try {
Bundle bundle = intent.getExtras();
String message = bundle.getString("alarm_message");
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(context)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle("Title!!!")
.setContentText(message);
Intent resultIntent = new Intent(context, MainActivity.class);
PendingIntent resultPendingIntent =
PendingIntent.getActivity(context, 192838, resultIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
mBuilder.setContentIntent(resultPendingIntent);
NotificationManager mNotificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(123423, mBuilder.build());
} catch (Exception e) {
Toast.makeText(context, "There was an error somewhere, but we still received an alarm", Toast.LENGTH_SHORT).show();
e.printStackTrace();
}
To summarize, Force closing your app means that the user explicitly said that he
didn't want to run your app anymore
Starting with 3.1 when applications are installed they are in a “stopped” state so they will not be able to run until the user explicitly launches them. Pressing Force Stop will return them to this state, so if the user force-stops your application, all the components of your app (BroadcastReceivers, Services, AlarmManager...) will no longer work again until the user manually run your application again. And this is documented at the 3.1 release notes here.
Although the documentation for AlarmManager states that
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.
It will not work also after your application is force closed and this is not a bug it's a feature.
This behavior is confirmed by the Android framework developers https://groups.google.com/forum/?fromgroups=#!topic/android-developers/anUoem0qrxU
I have a situation in my application. I am calling a service using an alarm. The alarm wakes up every 5 min and calls the service.
But it may so happen that the user might close my app and I am allowing the functionality of the service to work even if the user is not using my app.
To stop the service there are two ways either the user comes back to the app and presses a button which will cancel the alarm OR the second way is say after x time I want to stop the service i.e cancel the alarm from a broadcast receiver.
Now how can I do the second way ? When I tried to get reference of the AlarmManager it is giving me error of Null Pointer. (I am accessing this alarm manager from a broadcast receiver)
Can anyone give me suggestion on how to cancel repeating alarms from outside the activity context ?
Thanks :)
You can stop your service like this
context.stopService(new Intent(context, YourService.class))
Also in order to cancel the alarm you can do this
Intent intent = new Intent(this, YourClass.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 1253, intent, PendingIntent.FLAG_UPDATE_CURRENT| Intent.FILL_IN_DATA);
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmManager.cancel(pendingIntent);
I hope this might help you
In your BroadcastReceiver try this:
public void onReceive(Context context, Intent intent) {
context.stopService(new Intent(context, YourService.class));
}
I have implemented an Alarm class wich should set a new pending Intent and always overriwrite the old one. (I would rather stop/delete all old ones but I dont know how to)
private void startAlarm(){
Intent intent = new Intent(source, Alarm_Activity.class);
// 10000 should be the ID of Intent
PendingIntent pendingIntent = PendingIntent.getActivity(source, 10000, intent, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager am = (AlarmManager)source.getSystemService(Context.ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(),pendingIntent);
}
Unfortionatly I create this class multiple times and from different activities. I think this is the reason why it doesnt cancel the last intent (Flag_cancel_current). How can I make the Flag work throughout multiple instances of this class?
Given your code, so long as all places are using the same Intent (pointing to Alarm_Activity.class) and are using the same PendingIntent ID (10000 in your sample), your code will cancel any current PendingIntent.
This does not cancel any current alarms.
To cancel an alarm, call cancel() on AlarmManager. In particular, if you do this, get rid of PendingIntent.FLAG_CANCEL_CURRENT, so your cancelling of the old PendingIntent does not somehow interfere with your cancelling of the old alarm tied to that PendingIntent.
I am writing an Android application where the user can choose several stocks to watch and gets alerted if an predefined alert condition is matched. The stock data is saved to 5 objects of a custom Parcelable class "alert" (one object per stock and condition). The periodic data update is done via a service started by an AlarmManager. The alert objects are passed to the service via putting them into the Intent which is put into the PendingIntent of the AlarmManager.
Intent intent = new Intent(this, UpdateService.class);
Bundle b = new Bundle();
saveAlertsToBundle(b);
intent.putExtras(b);
intent.setData(Uri.parse("updateManager"));
PendingIntent pendIntent = PendingIntent.getService(this,0,intent,0);
// 1min intervall
long intervall = DateUtils.MINUTE_IN_MILLIS * 1;
// time of first start
long firstStartDelay = DateUtils.SECOND_IN_MILLIS * 30;
long firstStart = System.currentTimeMillis() + firstStartDelay;
AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
// kill running
am.cancel(pendIntent);
//start new
am.setRepeating(AlarmManager.RTC_WAKEUP,firstStart,intervall,pendIntent);
My problem is:
When starting the service for the first time when there is only one object of alert passed to the service everything works fine. As soon as there are more alerts objects existing they also need to be passed to the service but this does not work with the code above. The service does not receive the updated intent with the additional alert objects , but only the initial one with only one alert object. The code above correctly creates an Intent holding the additional alert object, but they never get to the service.
So my question is, how to pass the updated intent to the already running AlarmManager.
I already tried stopping the AlarmManager (the line at the // kill running comment) and restarting it, but this does not work. Perhaps because of the intent not holding the same alert objects as at the time when he was created ? I tried to fix this by setting an uri in the data part of the intent but this also did not help.
Thanks for help.
Your problem is the way PendingIntent works. The system manages a pool of PengingIntents. When your code does:
PendingIntent pendIntent = PendingIntent.getService(this,0,intent,0);
This causes the system to search for a PendingIntent that matches the parameters you've passed in (in this case, your Intent. However, the matching algorithm that PendingIntent uses only compares certain fields of the Intent to determine if it is the one that you are looking for. In particular, it does not compare extras. So this means after you've created the first PendingIntent, the call to PendingIntent.getService() will always return the same PendingIntent from the pool (and not create a new one, which is what you want).
In order to make the call to PendingIntent.getService() create a new PendingIntent every time you call it, try making the parameters you pass to the call unique, like this:
int requestCode = (int) System.currentTimeMillis(); // Create unique request code
PendingIntent pendIntent = PendingIntent.getService(this, requestCode, intent, 0);
Since requestCode will be different for each call to PendingIntent.getService(), this should solve your problem.
EDIT Based on OP's comments below
You want to cancel the existing alarm and create a new one with new data. In that case you don't need to use unique identifiers because you only want to have a single PendingIntent in the pool. But, you want to change the data for that. Try this:
// Create a PendingIntent (or update the existing PendingIntent with new values
PendingIntent pendIntent = PendingIntent.getService(this, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
// cancel any pending alarms
am.cancel(pendIntent);
//start new
am.setRepeating(AlarmManager.RTC_WAKEUP,firstStart,intervall,pendIntent);