Duplicate data when using PendingIntent - android

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?

Related

Android widget change alarm interval

I have created a configure activity for my widget, where the user can choose from various update frequencies.. Until now I started the alarm in the OnEnabled() method, like this:
Intent intent = new Intent(CLOCK_WIDGET_UPDATE);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.setRepeating(AlarmManager.RTC, System.currentTimeMillis(), 1000 * 60,
pendingIntent);
The settings are saved in shared preferences with a unique name (widgetId) and in this OnEnabled() method I can't retrieve the settings here because I can't get the widgetId yet.
There's an another problem, the user can change the frequency anytime, but this method is called just once, at the beginning. So I think I need to start the alarm in OnUpdate(), but I don't know how to do it, I don't want to make multiple instances of an alarm accidentally so I would like to ask for some advice.
To answer your second problem, calling setRepeating multiple times will not create multiple alarm as far as you provide same PendingIntent and same request code along with PendingIntent.FLAG_UPDATE_CURRENT flag. I would also suggest to use setInexactRepeating instead of setRepeating. So you can use the same code in OnUpdate() too with new frequency. Go through docs of FLAG_UPDATE_CURRENT and setInexactRepeating for more detials.

PendingIntent throughout multiple Instances

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.

pendingAlarmIntent.cancel() or AlarmManager.cancel(pendingAlarmIntent)?

I'm just looking at how to cancel an alarm and I came across these two methods. Which one should be used in what situation and why? Are they both the same?
I am currently doing this:
Intent alarmIntent = new Intent(ChangeAlarmActivity.this, AlarmReceiver.class);
PendingIntent pendingAlarmIntent = PendingIntent.getBroadcast(ChangeAlarmActivity.this, (int)alarm.getID(),
alarmIntent, 0);
pendingAlarmIntent.cancel();
How is that different to this below?
Intent alarmIntent = new Intent(ChangeAlarmActivity.this, AlarmReceiver.class);
PendingIntent pendingAlarmIntent = PendingIntent.getBroadcast(ChangeAlarmActivity.this, (int)alarm.getID(),
alarmIntent, 0);
AlarmManager alarmManager = (AlarmManager)getSystemService(ALARM_SERVICE);
alarmManager.cancel(pendingAlarmIntent);
Are they both the same?
No.
If you want to cancel an alarm, call cancel() on AlarmManager.
cancel() on PendingIntent probably will appear to have the same effect -- whatever your alarm events were supposed to trigger no longer happens. However, you are then assuming that AlarmManager will detect this and clean things up on its side, which is not guaranteed. Particularly for _WAKEUP alarms, this could result in the device being woken up for no good reason, wasting battery life.
Which one should be used in what situation and why?
I am sure that cancel() on PendingIntent has use cases. I cannot name any concrete ones, as I have never seen it used. Typically when you are using a PendingIntent, any "cancel" semantics are on the use of the PendingIntent (e.g., you cancel() the alarm via AlarmManager, you cancel() the notification via NotificationManager), not on the PendingIntent itself.
One place for cancel() on PendingIntent, therefore, would be some place where you pass off a PendingIntent and there is no "cancel" to revert that, or you explicitly wish to use cancel() as the revert mechanism. For example, if you are creating some sort of plugin mechanism, and the plugin sends a PendingIntent to the host app, the plugin might use cancel() to say "stop using that PendingIntent", which the host app would find out about the next time it tried to send() the PendingIntent and got the exception. Personally, I'm not a huge fan of this -- much along the AlarmManager lines, you don't know what resources might be used by the host app if it fails to properly handle this scenario. But, used properly, it could certainly work.
How is that different to this below?
The "below" one is what I would recommend that you use.

AlarmManager.set(...) behavior not matching documentation. Am I doing something wrong?

I'm setting up alarms using this code
//in onCreate()
mAlarmManager = (AlarmManager) getApplicationContext()
.getSystemService(ALARM_SERVICE);
//called for each timer I schedule
Intent intent = new Intent (Intents.MY_INTENT_ACTION);
PendingIntent pendIntent = PendingIntent.getBroadcast(
getApplicationContext(), alert.getID(),
intent, PendingIntent.FLAG_ONE_SHOT);
long delay = 1000 * alert.getDuration();
Calendar cal = Calendar.getInstance();
mAlarmManager.set(AlarmManager.RTC_WAKEUP,
cal.getTimeInMillis() + delay, pendIntent);
But the behavior I'm seeing doesn't match what I should see in the documtation1,
public void set(int type, long triggerAtTime, PendingIntent operation)
If there is already an alarm scheduled for the same IntentSender, it will first be canceled...If there is already an alarm for this Intent scheduled (with the equality of two intents being defined by filterEquals(Intent)), then it will be removed and replaced by this one...
which suggests that calling set(int type, long triggetAtTime, PendingIntent operation) for an already alarmed intent should replace the old alarm for that intent. I'm not seeing any alarms get dropped. Instead, every alarm i set fires, despite the fact that the intents that are fired by the pending intents should all match (by filterEquals(intent)), since all I've set on each intent is an identical action.
Am I doing something wrong, or is the API not behaving as documented?
Note: changing the PendingIntent instantiation to
PendingIntent pendIntent = PendingIntent.getBroadcast(
getApplicationContext(), CONSTANT_ID,
intent, PendingIntent.FLAG_ONE_SHOT);
Behaves as expected, dropping any already set alarm, and replacing it with a new alarm.
Maybe it is because you are giving each alarm a different ID (Does alert.getID() give different ID's or not?). By the documentation, it shouldn't matter but yet you should still try.
If it doesn't work too, hold a reference for your last set alarm, and when you need it to be canceled, cancel it yourself then set the next one.
Have you tried with PendingIntent flag : PendingIntent.FLAG_UPDATE_CURRENT intead of PendingIntent.FLAG_ONE_SHOT?
It appears the consensus is that the documentation for AlarmManager.set(), as well as other AlarmManager methods claiming that Intents (not just the wrapping PendingIntents) are compared to check whether a particular alarm is already set.
Do not rely on AlarmManager matching Intents, instead rely on the matching of PendingIntents, which appears to be working as advertised.

How to cancel Android notifications?

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

Categories

Resources