pendingIntent is fire again again in given radius. - android

i have set multiple approximate alerts in application. i set notifications in this way :
private void addProximityAlert(double latitude, double longitude) {
try{
LatLonPair latLon;
for(int i = 0; i < mPositions.size(); i++) {
latLon = mPositions.get(i);
Intent intent = new Intent(PROXIMTY_ALERT_INTENT);
intent.putExtra(ProximityIntentReceiver.EVENT_ID_INTENT_EXTRA, i);
intent.putExtra(ProximityIntentReceiver.ITEM_NAME,latLon.getItemName());
intent.putExtra(ProximityIntentReceiver.PLACE_NAME,latLon.getPlaceName());
PendingIntent proximityIntent = PendingIntent.getBroadcast(this, i, intent, 0);
locationManager.addProximityAlert(latLon.getLatitude(), latLon.getLongitude(), radius, expiration, proximityIntent);// alerts set here.
IntentFilter filter = new IntentFilter(PROXIMTY_ALERT_INTENT);
registerReceiver(new ProximityIntentReceiver(), filter);
}
}
catch(Exception ex){
ex.toString();
}
}
all stored location register for proximity Alert at once .now what happened there when one notification is fire and keep firing again again in given radius. i need to help that how i can remove Alert that has fired once do not give notification again and again when device is still in define radius.

in ProximityIntentReceiver unregister the receiver, so that it won't be called again.
See unregisterReceiver

The problem is not removing the alerts. The problem is that you are registering the receiver several times. Do it only once.

Related

Android - How to destroy a BroadcastReceiver?

I have implemented a BroadcastReceiver (called ProximityIntentReceiver) in my Android app, but i have some annoying problems. In the onCreate() method of my MainActivity I call this function:
addProximityAlert():
private ProximityIntentReceiver proximityIntentReceiver;
private void addProximityAlert() {
pendingIntentList = new HashMap<Integer, PendingIntent>();
double latitude = 37.567072;
double longitude = 14.273046;
Intent intent = new Intent(PROX_ALERT_INTENT);
PendingIntent proximityIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
locationManager.addProximityAlert(
latitude, // the latitude of the central point of the alert region
longitude, // the longitude of the central point of the alert region
POINT_RADIUS, // the radius of the central point of the alert region, in meters
PROX_ALERT_EXPIRATION, // time for this proximity alert, in milliseconds, or -1 to indicate no expiration
proximityIntent // will be used to generate an Intent to fire when entry to or exit from the alert region is detected
);
pendingIntentList.put(1, proximityIntent);
proximityIntentReceiver = new ProximityIntentReceiver();
registerReceiver(proximityIntentReceiver, filter);
Toast.makeText(getApplicationContext(),"Alert Added",Toast.LENGTH_SHORT).show();
}
This is, instead, the onDestroy of my MainActivity:
onDestroy():
#Override
public void onDestroy() {
super.onDestroy();
accessTokenTracker.stopTracking();
if(proximityIntentReceiver!=null) {
unregisterReceiver(proximityIntentReceiver);
removeProximityAlert();
}
}
removeProximityAlert():
private HashMap<Integer, PendingIntent> pendingIntentList;
private void removeProximityAlert () {
Iterator<Map.Entry<Integer, PendingIntent>> iterator = pendingIntentList.entrySet().iterator() ;
while(iterator.hasNext()){
Map.Entry<Integer, PendingIntent> pendingIntentMap = iterator.next();
locationManager.removeProximityAlert(pendingIntentMap.getValue());
}
}
Now if i change something on my BroadcastReceiver or in my addProximityAlert(), changes remain the same until I reboot the device. Why? I thought that by calling the unregisterReceiver() method was sufficient to remove the instance of the BroadcastReceiver...
What should I add to my code?
A BroadcastReceiver only exists during the execution of its onReceive() method. So, this evaluates to a throw-away instance every time the broadcast is fired/received. For dynamically registering/unregistering of BroadcastReceivers, you have to remember the instance of your receiver in onPause() to register it again during onResume().
#Override
protected void onPause() {
unregisterReceiver(mReceiver);
super.onPause();
}
For an Activity:
In order to register your BroadcastReceiver from within your app, first, remove the <receiver> tag from your AndroidManifest.xml file. Then, call registerReceiver(BroadcastReceiver receiver, IntentFilter filter) in your onResume(). Use unregisterReceiver(BroadcastReceiver receiver) in your onPause() to unregister the BroadcastReceiver.
For a Service:
Remove the receiver tag from the manifest file. You can then register your BroadcastReceiver with the same method in the onCreate() and unregister in the onDestroy().
Took Reference From: 1: Unregister broadcast receiver 2: unregister receiver
You can check here, which gives you more details about the working of BroadcastReceivers.
Still not your problem is solved, please let me know.
I suspect your constraints (POINT_RADIUS etc) are not being updated because you're retrieving your pending intent with flags '0':
PendingIntent.getBroadcast(this, 0, intent, 0);
LocationManager.addProximityAlert() takes the pending intent you give it and sets up a recurring alarm using the intent it contains. If you subsequently call addProximityAlert() with a PendingIntent that matches the previous one (according to Intent.filterEquals()), the alarm won't get updated, and your new constraints will not take effect.
To fix, you need to set the right flag so that the pending intent replaces the previous one.
PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
The flags are documented here (incidentally, I think '0' is undefined behaviour): http://developer.android.com/reference/android/app/PendingIntent.html
NB, if FLAG_UPDATE_CURRENT does not work, you may need FLAG_CANCEL_CURRENT instead.

Google location API: request location updates with pending intent?

I have started to implement the Google Location API using this tutorial.
I've managed to get it to work in my application quite fine, it updates my location at the right intervals etc. Now I am working on how to update my location when the device is in sleep mode. According to the documentation, this method is the way to go:
public void requestLocationUpdates (LocationRequest request, PendingIntent callbackIntent);
My question is, how do I set up this PendingIntent, and how do I handle it? I've seen tutorials of how to handle other types of intent, but I am not sure how to apply them to this.
You can either register Broardcast Reciever or Activity through pending intent.Sample Example for registering boardcast reciever:
String proximitys = "ACTION";
IntentFilter filter = new IntentFilter(proximitys);
registerReceiver(mybroadcast, filter);
Intent intent = new Intent(proximitys);
PendingIntent proximityIntent = PendingIntent.getBroadcast(this, 0,
intent, PendingIntent.FLAG_CANCEL_CURRENT);
locationManager.requestLocationUpdates(provider, mintime, mindistance,
proximityIntent);
Your Broardcast Reciever:
public class ProximityIntentReceiver extends BroadcastReceiver {
#SuppressWarnings("deprecation")
#Override
public void onReceive(Context arg0, Intent intent) {
//action to be performed
}

requestLocationUpdates with PendingIntent and Broadcast - what broadcasts do I get

I have setup an alarm which is received by a BroadcastReceiver which launches a WakefulIntentService (class LocationMonitor). In the LocationMonitor I have :
private static final int MIN_TIME_BETWEEN_SCANS = 1 * 30 * 1000;
private static final int MIN_DISTANCE = 0;
#Override
protected void doWakefulWork(Intent intent) {
final CharSequence action = intent.getAction();
if (action == null) { // monitor command from the alarm manager
// the call below enables the LocationReceiver
BaseReceiver.enable(this, ENABLE, LocationReceiver.class);
if (lm == null) lm = (LocationManager) this
.getSystemService(Context.LOCATION_SERVICE);
Intent i = new Intent(this, LocationReceiver.class);
PendingIntent pi = PendingIntent.getBroadcast(this, NOT_USED, i,
PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_CANCEL_CURRENT);
lm.requestLocationUpdates(LocationManager.GPS_PROVIDER,
MIN_TIME_BETWEEN_SCANS, MIN_DISTANCE, pi);
} else if (ac_location_data.equals(action)) {
final Bundle extras = intent.getExtras();
if (extras != null) {
final Location loc = (Location) extras
.get(LocationManager.KEY_LOCATION_CHANGED);
if (loc == null) {
w("NULL LOCATION - EXTRAS : " + extras); //Log.w
// while gps is disabled I keep getting this :
// NULL LOCATION - EXTRAS : Bundle[{providerEnabled=false}]
} else {
final double lon = loc.getLongitude();
final double lat = loc.getLatitude();
w("latitude :" + lat + " -- longitude : " + lon);
}
}
}
}
I have several issues with the code above.
If GPS is initially disabled and then I enable it I get a bunch of W/GpsLocationProvider(...): Unneeded remove listener for uid 1000. The warning comes from here. I can't find in the code where is this removing of listeners triggered, nor can I see where they are assigned the uid 1000 (apparently the system server).
When I enable the gps I get the location as expected and then a "RemoteException"
LocationManagerService(...): RemoteException calling onLocationChanged on Receiver{4083ee68 Intent PendingIntent{4084e6b8: PendingIntentRecord{4083ef78 gr.uoa.di.monitoring.android broadcastIntent}}}mUpdateRecords: {gps=UpdateRecord{40838180 mProvider: gps mUid: 10064}}
which is not really a RemoteException, just a PendingIntent.CancelledException - the message is quite misleading. Or so I think : it comes from here which calls this. My question is : why is it reusing the Intent - shouldn't the FLAG_ONE_SHOT dispose of it ?
But the most important question is : when I register a PendingIntent like this what intents do I expect to receive ? And what flags should I use ?
Keep in mind I am using this pattern cause I want to have the phone update its position even when asleep and this achieves it (I do get the location updates). I try to simulate requestSingleUpdate (unavailable in 2.3) using FLAG_ONE_SHOT.
Receiver :
public final class LocationReceiver extends BaseReceiver {
private static final Class<? extends Monitor> MONITOR_CLASS =
LocationMonitor.class;
#Override
public void onReceive(Context context, Intent intent) {
d(intent.toString());
final String action = intent.getAction();
d(action + "");
final Intent i = new Intent(context, MONITOR_CLASS);
i.fillIn(intent, 0); // TODO do I need flags ?
i.setAction(ac_location_data.toString());
WakefulIntentService.sendWakefulWork(context, i);
}
}
To this question:
when I register a PendingIntent like this what intents do I expect to
receive ? And what flags should I use ?
When you register for location updates and pass a PendingIntent, this PendingIntent will be triggered when the LocationManager decides to inform you about a location update. You can provide pretty much whatever you want, depending on what you want to happen when the PendingIntent is triggered. The LocationManager will add an extra to the Intent that is sent. This extra has the bundle key LocationManager.KEY_LOCATION_CHANGED and the object associated with that key is a Location object.
LocationManager will use this PendingIntent again and again to inform your app of location updates, so I think using PendingIntent.FLAG_ONE_SHOT is probably not such a good idea. If you only want a single update, why don't you just unregister after you get one update?
EDIT: Add code to cancel any previously requested updates before registering for updates
Before you call registerLocationUpdates(), do this to cancel any previously registered updates:
Intent i = new Intent(this, LocationReceiver.class);
// Get any existing matching PendingIntent
PendingIntent pi = PendingIntent.getBroadcast(this, NOT_USED, i,
PendingIntent.FLAG_NO_CREATE);
if (pi != null) {
// Cancel any updates for this PendingIntent, because we are about to
// invalidate it
lm.removeUpdates(pi);
}
// Create a new PendingIntent and cancel any previous one
pi = PendingIntent.getBroadcast(this, NOT_USED, i,
PendingIntent.FLAG_CANCEL_CURRENT);
// Now register for location updates...
lm.requestLocationUpdates(LocationManager.GPS_PROVIDER,
MIN_TIME_BETWEEN_SCANS, MIN_DISTANCE, pi);
NOTE: Actually, I don't know why you need to cancel any previous PendingIntent and create a new one in this case. You can just get a PendingIntent and if you have already registered for location updates with that PendingIntent, I don't think that registering again will cause the PendingIntent to be used multiple times. If you want to try that, all you need to do is to remove PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_CANCEL_CURRENT from your existing code. I think that is a better/cleaner/clearer solution.

Android Proximity alert doesn't get fired

this is my very first question:
I'm trying to configure proximity alerts that will be feed from a Database and webservice provider; but I have a problem configuring a simple proximity alert for testing. I manage to create the alert but it never gets fired, I'm only trying in the emulator for now and donĀ“t know if I need some extra code to trigger the alerts.
I've read somewhere that the GPS provider to disabled so the network provider can be used in order to trigger the alerts on the emulator.
My code looks like this:
Proximity intent declaration
private String proximityIntentAction = new String("gpsdelivery.gpship.getLocation.GPS_PROXIMITY_ALERT");
Inside onStart() where the parameters of the alerts are set
IntentFilter intentFilter = new IntentFilter(proximityIntentAction);
registerReceiver(new ProximityAlert(), intentFilter);
setProximityAlert(45.150344, 9.999815, -1);
Proximity alert function where the alerts get created
private void setProximityAlert(double lat, double lon, int requestCode)
{
// 100 meter radius
float radius = 100f;
// Expiration is 10 Minutes
long expiration = 600000;
LocationManager locManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
Intent intent = new Intent(proximityIntentAction);
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), requestCode, intent, PendingIntent.FLAG_CANCEL_CURRENT);
locManager.addProximityAlert(lat, lon, radius, expiration, pendingIntent);
}
And finally my proximity alert class with the Broadcast receiver
public class ProximityAlert extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
Log.v("SomeTag","Proximity alert received");
}
}
Please let me know what I'm missing or what am I doing wrong. Thanks in advance, any source code would be appreciated.
Well, I believe your setProximityAlert(45.150344, 9.999815, -1); is incorrect. Specifically, your -1. It doesn't contain the information needed in the locManager.addProximityAlert(lat, lon, radius, expiration, pendingIntent);.

set 2 proximity alerts with the same broadcast

I create a proximity alert in this way
private void setProximityAlert(float radius, double lat, double lng, String place)
{
long expiration = -1;
LocationManager locManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
Intent intent = new Intent(TREASURE_PROXIMITY_ALERT);
intent.putExtra("lat", lat);
intent.putExtra("lng", lng);
intent.putExtra("place", place);
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), -1, intent, 0);
locManager.addProximityAlert(lat, lng, radius, expiration, pendingIntent);
}
and on my activity I registered the receiver in this way
IntentFilter intentFilter = new IntentFilter(TREASURE_PROXIMITY_ALERT);
registerReceiver(new ProximityIntentReceiver(), intentFilter);
setProximityAlert(10, 45.150344, 9.999815, "POINT1");
and my broadcast receiver is correctly called.
So now, I want to add another proximity alert, is it possible? I want that the same boadcast receiver is called by 2 proximity alert.
I made this:
IntentFilter intentFilter1 = new IntentFilter(TREASURE_PROXIMITY_ALERT1);
registerReceiver(new ProximityIntentReceiver(), intentFilter1);
setProximityAlert(200f, 45.143848, 10.039741, "POINT2");
but it does not work, nothing happen. I'm really now on it and I was wondering if it is the right way. My intent is trigger 2 alerts, one when GPS get the position POINT1 and another one at the position POINT2.
Any helps are welcome.
You need to use whatever unique setAction so the system consider the two intents different, as otherwise will tend to reuse the first one.
I have this code:
Intent intent = new Intent(this,PlacesProximityHandlerService.class);
intent.setAction("foo"+objPlace.getId());
intent.putExtra(Poi._ID, objPlace.getId());
intent.putExtra(Poi.LAT, objPlace.getLat());
intent.putExtra(Poi.LON, objPlace.getLon());
PendingIntent sender = PendingIntent.getService(this,0, intent, 0);
LocationUtils.addProximity(this, objPlace.getLat(),objPlace.getLon(), objPlace.getError(), -1,sender);
Also note that the proximity alert works kinda tricky.
User enters the hot ZONE1 based on the signal precision and radius you set. Broadcast is fired for entering=true ZONE1. If you enter another zone ZONE2 that overlap with the current zone you don't get the alert as you are still in ZONE1.
You must leave the ZONE1, so the broadcast will fire again with entering=false. So once now you left ZONE1, if you arrive ZONE2 it will fire the broadcast entering=true ZONE2.
I've tested and it works just fine. Grab Location Spoofer free application from market and mock the location of the phone. You also need to enable mock locations in the phones Setting. And add additional permission to your application:
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" />
What I would do, set my location far away from me, probably Greenland, then set the position in a zone that triggers ZONE1, broadcast should fire. Then set again my location to Greeland, and set position that triggers ZONE2, broadcast should fire.
The entering flag can be get from the intent extras
Bundle b = intent.getExtras();
Boolean entering = (Boolean) b.get(android.location.LocationManager.KEY_PROXIMITY_ENTERING);
I used the above codes to setup proximity alerts for 100 POIs and all work well.

Categories

Resources