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.
Related
I have a Broadcast Receiver which displays a toast if GPS is put on or off. I just want to know why doesn't toast get displayed continuously as long as the GPS keep on.
please find below the code i used.
public class gpsbroad extends BroadcastReceiver implements LocationListener{
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
LocationManager mlocManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
boolean enabled = mlocManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
if (enabled==true) {
Toast.makeText(context, "gps on",
Toast.LENGTH_SHORT).show();
//Intent pushIntent = new Intent(context, LocalService.class);
//context.startService(pushIntent);
}
else
{
Toast.makeText(context, "gps off",
Toast.LENGTH_SHORT).show();
}
if (intent.getAction().matches("android.location.PROVIDERS_CHANGED")) {
Toast.makeText(context, "in android.location.PROVIDERS_CHANGED",
Toast.LENGTH_SHORT).show();
//Intent pushIntent = new Intent(context, LocalService.class);
//context.startService(pushIntent);
}
}
A BroadcastReceiver "wakes up" when there is an Intent it is interested in receiving. It will execute the code in onReceive() and that's it. In fact, beyond that point it may as well be destroyed as far as the system is concerned.
A BroadcastReceiver object is only valid for the duration of the call
to onReceive(Context, Intent). Once your code returns from this
function, the system considers the object to be finished and no longer
active.
A Toast can only show for a limited duration, either Toast.LENGTH_SHORT or Toast.LENGTH_LONG. In fact, those are the only durations you can specify.
Put the two together and the result is you'll see a brief message pop up on screen when you get the intent your want. In short there is nothing about your code that would make a Toast (or anything for that matter) remain on screen for as long as GPS is on. I suggest you look into NotificationManager, but I really don't see the point -- the phone will display a GPS icon in the system status bar anyway.
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.
I'm trying to implement a Geofencing mechanism where a geofence is monitored and once the user exits the current geofence, the current co-ordinates are used to create a new geofence and db query is initiated for fetching some data.
My problem is that the pending intent is never fired.
From the logs i can see that the geofences are being added into the location client. However no pending intents are fired upon location change.(i've set the fence radius at 2m and i've walked over 100mts). Is there something wrong in the way i've declared the intent service ?
Here is the intent service class.
public class GeoFenceIntentService extends IntentService{
private static final String mIntentName = "GeoFenceIntentService";
public GeoFenceIntentService() {
super(mIntentName);
}
#Override
protected void onHandleIntent(Intent intent) {
int transitionType = LocationClient.getGeofenceTransition(intent);
Log.e(TAG,"Inside fence handler");
if(transitionType == Geofence.GEOFENCE_TRANSITION_EXIT){
//Query DB here with current co-ords
//create new GeoFence
Location location = LocationHelper.getInstance(mContext).getLastLocation();
mLat = String.valueOf(location.getLatitude());
mLong = String.valueOf(location.getLongitude());
addGeofenceToMonitor(location);
queryDb();
}
}
}
Also here is where i add the pending intents and the geofence to the location client
addGeofenceToMonitor(Location location){
List<Geofence> list = new ArrayList<Geofence>();
list.add(getNewGeofence(location.getLatitude(), location.getLongitude()));
PendingIntent pendingIntent = PendingIntent.getService(mContext, 0,
new Intent(mContext,GeoFenceIntentService.class), PendingIntent.FLAG_UPDATE_CURRENT);
OnRemoveGeofencesResultListener removeListener = new OnRemoveGeofencesResultListener() {
#Override
public void onRemoveGeofencesByRequestIdsResult(int statusCode, String[] requestIDs) {
//To be used
}
#Override
public void onRemoveGeofencesByPendingIntentResult(int statusCode,PendingIntent pendingIntent) {
//Not used
}
};
LocationHelper.getInstance(mContext).removeGeoFence(mGeofenceRequestIDs, removeListener);
OnAddGeofencesResultListener addListener = new OnAddGeofencesResultListener() {
#Override
public void onAddGeofencesResult(int statusCode, String[] geofenceRequestIds) {
if(statusCode != LocationStatusCodes.SUCCESS){
//handle error cases
}
else
Log.i(TAG, "Successfully added Geofence "+geofenceRequestIds[0]+" for monitoring");
}
};
LocationHelper.getInstance(mContext).addGeoFence(list, pendingIntent, addListener);
}
Here is the snippet from the manifest file
<service
android:name="com.myexample.sample.GeoFenceIntentService"
android:label="#string/app_name"
android:exported="true">
</service>
Read this.
Have you checked the position estimation circle you are getting? You can use mock locations app to set the position as well as the accuracy circle. Your geofence may be too small to accommodate your position circle and that is why the events are not triggered.
Android GeoFences never enable the GPS (because their API is awful and their device power consumption is already so out of hand). You have to set up your geofences and then constantly poll the GPS separately if you want geofencing over GPS.
The handler of the GPS polling can be null, the poll only exists to force accurate information into their awful location API and in turn trigger the fences.
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.
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;
}