addProximityAlert doesn't work as expected - android

ios sdk has great region monitoring functions. I need something like that in android and i think we have two alternatives. Geofencing and LocationManager.
Geofencing has really tidy examples and bugs , so i prefered LocationManager. Everyting works fine in LocationManager except one. If you add your current location as ProximityAlert , it immediatly fires "ENTERING" , but it is my current location , it doesnt mean that i entered this region. Because of that , it fires "ENTERING" each time i start my application if i am in region.(Even if i am not moving)
How can i solve this problem and fire event only if user is really ENTERING the region ?
Here is how i am adding PeddingIntents for my locations.
LocationManager locationManager = (LocationManager)mContext.getSystemService(Context.LOCATION_SERVICE);
for(Place p : places)
{
Log.e("location", p.location);
Bundle extras = new Bundle();
extras.putString("name", p.displayName);
extras.putString("id", p.id);
Intent intent = new Intent(CommandTypes.PROX_ALERT_INTENT);
intent.putExtra(CommandTypes.PROX_ALERT_INTENT, extras);
PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext,Integer.parseInt(p.id), intent,PendingIntent.FLAG_CANCEL_CURRENT);
float radius = 50f;
locationManager.addProximityAlert(p.lat,
p.lon, radius, 1000000, pendingIntent);
}
Receiver
public class ProximityReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
final String key = LocationManager.KEY_PROXIMITY_ENTERING;
final Boolean entering = intent.getBooleanExtra(key, false);
Bundle b = intent.getBundleExtra(CommandTypes.PROX_ALERT_INTENT);
String id = b.getString("id");
Log.e("here" + id, "here");
if (entering) {
Log.e(TAG,"entering");
} else {
Log.e(TAG,"leaving");
}
}
Manifest
<receiver android:name=".ProximityReceiver">
<intent-filter>
<action android:name="ACTION_PROXIMITY_ALERT" />
</intent-filter>
</receiver>
Thank you very much
PS: iOS does not have this problem and their documentation explains it so
Monitoring of a geographical region begins immediately after registration for authorized apps. However, do not expect to receive an event right away. Only boundary crossings generate an event. Thus, if at registration time the user’s location is already inside the region, the location manager does not automatically generate an event. Instead, your app must wait for the user to cross the region boundary before an event is generated and sent to the delegate. That said, you can use the requestStateForRegion: method of the CLLocationManager class to check whether the user is already inside the boundary of a region.

EDIT: since i wrote this, there has been a new thing added to the geofence API, 'setInitialTrigger' that allevates this:
https://developers.google.com/android/reference/com/google/android/gms/location/GeofencingRequest.Builder#setInitialTrigger%28int%29
Yeah, this is a nuisance, and is one of the major points where Android and IOS geofencing differs, unfortunately.
Android alerts when you're inside the Geofence if it knows you were outside before OR you added the geofence while inside it.
The way i solve this is with a 'grace period' in my broadcast reciever. Basically, when i create the Geofence, i store away its creation time in sharedpreferences, and i check against that value in onReceive.
By doing this, any 'immediate' hit will be filtered away. Perhaps 3 minutes is too long for someone else, but it works for me based on how i work with the geofences in my app.
private static final Long MIN_PROXALERT_INTERVAL = 18000l; // 3 mins in milliseconds
...
long geofenceCreationTime = session.getPrefs().getCurrentGeofenceCreation();
long elapsedSinceCreation = now - geofenceCreationTime;
if(elapsedSinceCreation < CREATIONTIME_GRACE_PERIOD){
if (ApplicationSession.DEBUG) {
Log.d(TAG, "elapsedSinceCreation;"+elapsedSinceCreation+";less than;"+CREATIONTIME_GRACE_PERIOD+";exiting");
}
return;
}
Hope you see what i'm getting at.
Hope it helps.

Related

Android share location data between activities

I have an app with two activity A and B (a google map), both need to periodically receive GPS location. I need that going from activity A to B and back app continues to retrieve the GPS position without interruption. To solve this problem I thought to two ways:
The first is to use a service that starts when app start and continuously retrieve GPS position and somehow notify both activities about new data, then, when the app finish it stop retrieving position.
The second way is to use the fragments, I have only one activity that continuously retrieve GPS position, then I have two fragments that show A and B content and somehow use the position data received from the activity.
When I say "somehow" I want to say that I don't know how :-)
Do you have suggestions on how to implement these two approaches or can you suggest better approaches?
#1 Whenever location changed, send intent and receive it where you want
LocalBroadcastManager mLocalBroadcastManager;
#Override
public void onLocationChanged(Location location) {
Intent intent = new Intent();
intent.putExtra("com.exmaple.sample", location);
mLocalBroadcastManager.sendBroadcast(intent);
}
#2 Whenever location changed, send intent and receive it where you want (same with 1)
#Override
public void onLocationChanged(Location location) {
Intent intent = new Intent();
intent.putExtra("com.exmaple.sample", location);
sendBroadcast(intent);
}
#3 Or, pass a pending intent only one time, then receive locations
Intent intent = new Intent(mContext, YourBroadcastReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 1, intent, 0);
mLocManager = (LocationManager)getSystemService(LOCATION_SERVICE);
mLocManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, pendingIntent);
#4 Make a static variable, then poll it from an activity periodically. Note that, you should optimize polling frequency because it's not guaranteed getting fresh location. This is maybe be bad approach but it could be worth it for your scenario
public static Location sLastLocation;
#Override
public void onLocationChanged(Location location) {
sLastLocation = location;
}
#5 Use event bus, link

Location Manager ProxmityAlert always says entering

I want to alert the user when he is near a particular location. For this I have included a ProxmityAlert and a corresponding service in my app. But no matter what coordinates I give, it always shows me "Thank you for visiting my Area!! entering" Am I doing this the wrong way?
This is how I am doing it :
public class ProxTest extends Activity {
LocationManager lm;
double lat = 30.085514, long1 = 77.082603;
float radius = 50;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lm = (LocationManager) getSystemService(LOCATION_SERVICE);
Intent i = new Intent();
i.setAction("com.example.test.proximityalert");
PendingIntent pi = PendingIntent.getBroadcast(getApplicationContext(),
-1, i, 0);
lm.addProximityAlert(lat, long1, radius, -1, pi);
sendBroadcast(i);
System.out.println("Prox alert added ");
}
}
And this is the receiver :
public class ProximityReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context arg0, Intent arg1) {
// TODO Auto-generated method stub
String k = LocationManager.KEY_PROXIMITY_ENTERING;
boolean state = arg1.getBooleanExtra(k, false);
if (state) {
Toast.makeText(arg0, "Welcome to my Area", Toast.LENGTH_LONG)
.show();
} else {
Toast.makeText(arg0, "Thank you for visiting my Area!!"+k,
Toast.LENGTH_LONG).show();
}
}
}
In the Manifest, I have added :
<receiver android:name="ProximityReceiver">
<intent-filter>
<action android:name="com.example.test.proximityalert">
</action>
</intent-filter>
</receiver>
In my view you should use multiple geofencing, as the simplest way to achieve what you want. You can do something like a target area. Thus the target eye is the center where you want the user to be heading off or achieving it.
(source: cision.com)
Like in this target image, for example you will have counting from outside to inside, a total of 6 geofences. The closest to the target, the higher the geofence ID 6, the most far away area from the target will be the geofence ID 1. Basically you will need to coordinate multiple geofencing to achieve what you wish. So, instead of having separated geofences with overlapping border areas, you will have centric geofences.
Each time your user gets a geofence higher value, an intent gives a signal or tone, such as: "you are getting closer". The other way around is also true, for the case the user is moving far away from the target area: "your geofence ID changed from 3 for 2, you are moving in the opposite direction from your target destination".
Your current code doesn't make this kind of distinction of different directions related to geofencing, neither shows handling of multiple geofencing. That's why the code is not behaving the way you would like it.

addProximityAlert not working

I'm trying to use addProximityAlert. I've followed the tutorial from this site http://myandroidtuts.blogspot.dk/2012/10/proximity-alerts.html.
I've also tried other tutorials but I couldn't get them to work either.
This is my code for creating the proximity alert:
double lat = 55.659890;
double longi = 12.591188;
float radius = 3000;
lm = (LocationManager)
getSystemService(Context.LOCATION_SERVICE);
Intent intent = new Intent("dk.itu.percom.tourguide.android.ProximityAlert");
long expire = -1;
proximityIntent = PendingIntent.getBroadcast(getApplicationContext(), -1, intent, 0);
lm.addProximityAlert(lat, longi, radius, expire, proximityIntent);
This is my receiver:
public class ProximityIntentReceiver extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
String k=LocationManager.KEY_PROXIMITY_ENTERING;
boolean state=intent.getBooleanExtra(k, false);
if(state){
Toast.makeText(context, "Welcome to my Area", Toast.LENGTH_LONG).show();
}else{
Toast.makeText(context, "Thank you for visiting my Area,come back again!!",Toast.LENGTH_LONG).show();
}
}
}
And my manifest:
<receiver android:name="ProximityIntentReceiver"
android:exported="false" >
<intent-filter>
<action android:name="dk.itu.percom.tourguide.android.ProximityAlert" />
</intent-filter>
</receiver>
And I've added the permissions
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
I have also tried to add the receiver with
IntentFilter filter = new IntentFilter("dk.itu.percom.tourguide.android.ProximityAlert");
registerReceiver(new ProximityIntentReceiver(), filter);
But that doesn't work either. Somewhere I also read that using -1 for expiration doesn't work with 4.3 and you instead should use a very high number, but no luck with that either.
Does anybody know what the problem could be?
If you read the Android officiel doc (here) you'll find this sentence "Due to the approximate nature of position estimation, if the device passes through the given area briefly, it is possible that no Intent will be fired"
what is that mean? it means when you are testing your app on the AVD and u send a gps coordinates(latitude, longitude) from the DDMS to AVD its really hard to simulate the real aspect of a gps, (because in the first place u pick some point to be your proximPoint and just after that you choose anthor point very far from the proximPoint to see if its work) and thats not what it's happing with a real device.
so the solution is to test your app on a real device or with the DDMS try to change the coordiantes very slowly until you are in the zone wanted.

Android Proximity Alerts on Galaxy Nexus with Android 4.1.1 - does it really work?

I would like to use Android's LocationManager and the addProximityAlert method to setup proximity alerts. For this, I've created a small application that shows a crosshair on top of a map, plus a text field for the proximity alert name and a button to trigger the addition of the alert.
Unfortunately, the BroadcastReceiver that should receive the proximity alerts is not triggered. I've tested the intent alone (not wrapped via a PendingIntent) and that works. Also, I see that once a proximity alert is set, the GPS / location icon appears in the notification bar.
I've found information about proximity alerts a bit confusing - some are telling the alerts cannot be used if the activity is no longer in the foreground. I think it should work, so I assume something else is wrong.
1 Adding a Proximity alert
GeoPoint geo = mapView.getMapCenter();
Toast.makeText(this, geo.toString(), Toast.LENGTH_LONG).show();
Log.d("demo", "Current center location is: " + geo);
PendingIntent pIntent = PendingIntent.getBroadcast(this, 0, getLocationAlertIntent(), 0);
locationManager.addProximityAlert(geo.getLatitudeE6()/1E6, geo.getLongitudeE6()/1E6, 1000f, 8*60*60*1000, pIntent);
The intent itself is here:
private Intent getLocationAlertIntent()
{
Intent intent = new Intent("com.hybris.proxi.LOCATION_ALERT");
intent.putExtra("date", new Date().toString());
intent.putExtra("name", locationName.getEditableText().toString());
return intent;
}
I created a receiver which is supposed to receive the location alerts, registered in AndroidManifest.xml:
<receiver android:name=".LocationAlertReceiver">
<intent-filter>
<action android:name="com.hybris.proxi.LOCATION_ALERT" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
The implementation itself is hopefully straightforward. It is supposed to show a notification (and I checked that via directly sending an intent with a test button).
public class LocationAlertReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context ctx, Intent intent) {
Log.d("demo", "Received Intent!");
String dateString = intent.getStringExtra("date");
String locationName = intent.getStringExtra("name");
boolean isEntering = intent.getBooleanExtra(LocationManager.KEY_PROXIMITY_ENTERING, false);
NotificationManager notificationManager = (NotificationManager) ctx.getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = new Notification.Builder(ctx)
.setContentTitle("LocAlert: " + locationName)
.setContentText(dateString + "|enter: " + isEntering)
.setSmallIcon(R.drawable.ic_stat_loc_notification)
.build();
notificationManager.notify(randomInteger(), notification);
}
private int randomInteger()
{
Random rand = new Random(System.currentTimeMillis());
return rand.nextInt(1000);
}
A few things that I am not 100% sure of, maybe this triggers something in you:
I assume it is OK to register a proximity alert using a pending intent like I do and the Activity that created the proximity alert can later be closed.
Converting from the Map via getCenter returns a GeoPoint with lat/lon as int values. I thnk I am correctly converting them the the double values expected by addProximityAlert by dividing by 1E6
The distance from the center is relatively large - 1000m - I assume that is a nice value.
Examples that I've found online used broadcast receivers registered programmatically. this is not what I want to do. but the book Android 4 Profession Dev by Reto Meier mentions that registering the broadcast receiver in xml is also fine.
Any help greatly appreciated!!
I ran into the same issue. Setting target class of the intent explicitly helped in my case. In your case it should look the following:
private Intent getLocationAlertIntent()
{
Intent intent = new Intent(context, LocationAlertReceiver.class);
intent.setAction("com.hybris.proxi.LOCATION_ALERT"); //not sure if this is needed
intent.putExtra("date", new Date().toString());
intent.putExtra("name", locationName.getEditableText().toString());
return intent;
}

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