9

Hello I am trying to add feature of Geo Fence in Android. I am using the http://developer.android.com/training/location/geofencing.html for creating and monitoring Geo Fences. I am using the IntentService for the alert (Entered/Exited) but for me it is not working.

But When I went away and come back into region to test it then it didn't work for me. I have turned ON the GPS of the device but device is not connected with internet.

Anyone can you please help me to make it more accurate and perfectly without any issue.

public class MainActivity extends Activity implements GooglePlayServicesClient.ConnectionCallbacks,
        GooglePlayServicesClient.OnConnectionFailedListener,
        LocationClient.OnAddGeofencesResultListener {

    private LocationClient locationClient;

    private String TAG = "MainActivity";

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        int resp = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
        if (resp == ConnectionResult.SUCCESS) {
            locationClient = new LocationClient(this, this, this);
            locationClient.connect();
        }
    }

    @Override
    public void onConnected(Bundle bundle) {
        ArrayList<Store> storeList = getStoreList();
        if (null != storeList && storeList.size() > 0) {
            ArrayList<Geofence> geofenceList = new ArrayList<Geofence>();
            for (Store store : storeList) {
                float radius = (float) store.radius;
                Geofence geofence = new Geofence.Builder()
                        .setRequestId(store.id)
                        .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER | Geofence.GEOFENCE_TRANSITION_EXIT)
                        .setCircularRegion(store.latitude, store.longitude, radius)
                        .setExpirationDuration(Geofence.NEVER_EXPIRE)
                        .build();

                geofenceList.add(geofence);
            }

            PendingIntent geoFencePendingIntent = PendingIntent.getService(this, 0,
                    new Intent(this, GeofenceIntentService.class), PendingIntent.FLAG_UPDATE_CURRENT);
            locationClient.addGeofences(geofenceList, geoFencePendingIntent, this);
        }
    }

    @Override
    public void onDisconnected() {
        Log.e(TAG, "Disconnected !");
    }

    @Override
    public void onAddGeofencesResult(int i, String[] strings) {
        if (LocationStatusCodes.SUCCESS == i) {
            //todo check geofence status
        } else {

        }
    }

    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {
        Log.e(TAG, connectionResult.getErrorCode() + "");
    }

    private ArrayList<Store> getStoreList() {
        ArrayList<Store> storeList = new ArrayList<Store>();
        for (int i = 0; i < 1; i++) {
            Store store = new Store();
            store.id = String.valueOf(i);
            store.address = "India";
            store.latitude = 26.7802187;
            store.longitude = 75.860322;
            store.radius = 100.0;

            storeList.add(store);
        }

        return storeList;
    }

    public class Store {
        String id;
        String address;
        double latitude;
        double longitude;
        double radius;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (null != locationClient) {
            locationClient.disconnect();
        }
    }
}

GeofenceIntentService.java

public static final String TRANSITION_INTENT_SERVICE = "ReceiveTransitionsIntentService";

public GeofenceIntentService() {
    super(TRANSITION_INTENT_SERVICE);
}

@Override
protected void onHandleIntent(Intent intent) {
    if (LocationClient.hasError(intent)) {
        //todo error process
    } else {
        int transitionType = LocationClient.getGeofenceTransition(intent);
        if (transitionType == Geofence.GEOFENCE_TRANSITION_ENTER ||
                transitionType == Geofence.GEOFENCE_TRANSITION_EXIT) {
            List<Geofence> triggerList = LocationClient.getTriggeringGeofences(intent);

            for (Geofence geofence : triggerList) {
                generateNotification(geofence.getRequestId(), "address you defined");
            }
        }
    }
}

private void generateNotification(String locationId, String address) {
    long when = System.currentTimeMillis();
    Intent notifyIntent = new Intent(this, MainActivity.class);
    notifyIntent.putExtra("id", locationId);
    notifyIntent.putExtra("address", address);
    notifyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

    PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notifyIntent, PendingIntent.FLAG_UPDATE_CURRENT);

    NotificationCompat.Builder builder =
            new NotificationCompat.Builder(this)
                    .setSmallIcon(R.drawable.dac_logo)
                    .setContentTitle(locationId)
                    .setContentText(address)
                    .setContentIntent(pendingIntent)
                    .setAutoCancel(true)
                    .setDefaults(Notification.DEFAULT_SOUND)
                    .setWhen(when);    

    NotificationManager notificationManager =   
            (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    notificationManager.notify((int) when, builder.build());   
 }
 }

AndroidManifest.xml

  <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.demo"
android:versionCode="1"    
android:versionName="1.0" >

<uses-sdk
    android:minSdkVersion="8"
    android:targetSdkVersion="17" />

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION" />

<application
    android:allowBackup="true"
    android:icon="@drawable/dac_logo"
    android:label="@string/app_name" >
    <activity
        android:name=".MainActivity"
        android:excludeFromRecents="true"
        android:label="@string/app_name"
        android:launchMode="singleTask"
        android:taskAffinity="" >
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

    <service
        android:name=".GeofenceIntentService"
        android:exported="false" />

    <receiver android:name="com.location.demo.receivers.BootCompleteReceiver" >    
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />    
        </intent-filter>
    </receiver>

    <meta-data
        android:name="com.google.android.gms.version"    
        android:value="@integer/google_play_services_version" />    
</application>

N Sharma
  • 28,073
  • 81
  • 228
  • 405
  • are you able to receive GPS Coordinates in your app? – Pankaj May 28 '14 at 04:21
  • 2
    There's a bug in android Geofence Sample,so using IntentService Class is discouraged,you should use BroadcastReceiver to trigger events for enter and exit – Pankaj May 28 '14 at 04:30
  • How did you import LocationClient?? It isn't available in the SDK. – IgorGanapolsky Jan 28 '15 at 04:35
  • 2
    @Pankaj *"using IntentService Class is discouraged"* - nonsense. The geofence API doesn't even whether you're using a broadcastreceiver or an intentservice. Perhaps it has changed since you commented that but Im using intentservice in my app and it works fine – Tim Mar 23 '16 at 15:47

5 Answers5

20

I've found that the GeoFencing never intelligently retrieves locations from the GPS hardware. The GeoFence API will observe the most accurate location available from the OS or if no recent location reading is available, it will cause a location to be calculated from Wifi / Cellular. (which sucks because cellular is wildly inaccurate and wifi is often unavailable)

So to get at all responsive or accurate results out of the Geofencing API you have to set up your Geofences and then poll the GPS hardware on an interval, not even doing anything with the result received, so that under the surface you are providing worthwhile data to the OS.

This is probably at the core of why your results are inaccurate. The geofence exit won't trigger until the OS is sure you're 100% outside the fence - so if a location reading has an accuracy of 500 meters (not improbable when using cell geolocation) and your fence has a radius of 50m you'd have to be at least 550m from your fence point to produce an exit event.

TLDR; Poll the GPS hardware on an interval without doing anything with the result and you'll start getting more accurate geofences.

stealthwang
  • 877
  • 5
  • 7
  • 1
    Yes I know the approach which you are telling to " poll the GPS hardware on an interval" that is already done by Google Play Service Location Service and but here I have turned ON the GPS which I have already written in the question but still it is not triggering entering/existing event. Case which you are telling why results are inaccurate is not for my case because I have already turned ON the GPS – N Sharma May 22 '14 at 08:59
  • and How I can request to look for more accurate locations here ? – N Sharma May 22 '14 at 19:32
  • 2
    How did you turn on the GPS? I believe @stealthwang is trying to say that Google Play Services will always try to conserve your battery and wont go for GPS unless you explicitly require it to. You should have your own location listener that sets up LocationManager instance with LocationManager.GPS_PROVIDER, and requests location updates on 1 sec intervals or so. Check the LocationManager docs. – rootkit May 27 '14 at 20:45
  • @rootkit I turned the GPS by going through the location settings of the device and made it ON. The thing which you are telling to me to go through the `LocationManager` that is require when you need a location points but here I need only to trigger exit/enter event of Geo Fence that Google Play Service automatically do (fetch location points stuff etc) – N Sharma May 29 '14 at 09:27
  • and When I need a location points then Google provide a method to set location accuracy `// Use high accuracy mLocationRequest.setPriority( LocationRequest.PRIORITY_HIGH_ACCURACY)` http://developer.android.com/training/location/receive-location-updates.html#UpdateParameters but same Geo Fence concept do not have a way to set it manually a high accuracy afaik – N Sharma May 29 '14 at 09:31
  • The point here is to manually poll GPS as geofencing wouldn't. Use LocationManager or LocationRequest as you wish, but make sure you app actively turns on GPS – rootkit May 29 '14 at 17:57
  • @rootkit If I poll the GPS and do own Geo Fence coding on that basis that does not make much sense because Geo Fence concept of Google Play Service already do also fetch location according to its own but I don't what it is the accuracy of Geo Fence concept it sets – N Sharma May 30 '14 at 04:34
  • 4
    I was not suggesting to do your own geofencing. Just poll GPS on regular basis, no need to do anything with retrieved location data. The idea is that Google Play geofencing will then use GPS data instead of relying on low-power, low-accuracy location services – rootkit May 30 '14 at 19:26
  • @rootkit but Afaik Android Geo Fencing API already poll the GPS on regular basis then any mean to do same again ? – N Sharma Jun 08 '14 at 07:45
  • @Williams the purpose of my answer is to explain that no, the Geofencing API, as far as I've experienced, never uses the GPS to calculate location on it's own. In my own application modeled after the API example code I couldn't produce a fence entrance event for any radius smaller than ~500m, despite attempts at modifying the LocationClient config. As soon as I began polling the gps in addition to my geofence code, fences as small as ~30m would work. – stealthwang Jun 08 '14 at 21:41
  • @stealthwang GeoFencing uses fusion location provider that that do work in perfect manner like it fetch the location from GPS first then if it fails to fetch then it goes to find location using cell tower that is how it works and it takes location using Google play location services that run in your android phone. – N Sharma Jun 09 '14 at 03:22
  • 2
    haha no it doesn't "work in perfect manner". it never took a reading from GPS. small geofences never worked until i started polling the gps in the background. believe what you want. – stealthwang Jun 10 '14 at 17:46
  • See http://stackoverflow.com/questions/21886204/android-geo-fencing-receiver-not-calling – rootkit Jun 20 '14 at 14:48
  • Suppose I managed to do it with help of LocationServices.FusedLocationApi, and I am attaching this code to a Service, so that I can get a constant location updates from a user whether my app is running or closed, then will google block my app in future for violating any of its policy ? – Name is Nilay Jul 08 '15 at 13:40
  • Does it make a difference to poll the GPS location with either FusedLocatoinApi or LocationManager? And FusedLocationApi.getLastLocation() probably does not poll a new GPS position, but just returns the existing one, right? – bsautermeister Nov 12 '17 at 14:25
  • Haha, I am sure geofence api simply doesn't give transition event. I hope polling will resolve the problem. – Killer Apr 19 '19 at 09:52
1

I summarized the best practices from several StackOverflow threads regarding Geofencing in Android in a sample project on Github called Geofencer. This can be used to as a reference to implement GeoFencing into your application. It also allows to switch to a 3rd party GeoFencing implementation on the fly. I hope this is helpful for some of you!

bsautermeister
  • 1,051
  • 1
  • 11
  • 17
0

I also had problems with the geofence system jumping in and out of my geofences and then i read this (Quote from official docs):

There is no reliable network connectivity inside your geofence. If there is no reliable data connection, alerts might not be generated. This is because the geofence service depends on the network location provider which in turn requires a data connection.

From the official geofence documentation

Kaizie
  • 4,308
  • 3
  • 18
  • 18
0

Android's native geofencing is shockingly inaccurate and battery intensive. I suggest you use a third-party tool like Bluedot's Point SDK (accurate to 10m/30 feet) or Gimbal's SDK (accurate to 50m/165 feet), both of which are much more reliable with less battery drain.

Full disclosure: I work for Bluedot.

Andrew Stirling
  • 255
  • 1
  • 3
  • 11
  • 4
    Those SDKs may facilitate using the Android/Google APIs, but they provide no additional update frequency/accuracy that isn't already available using native/Google APIs - which these SDKs will be making use of internally. In fact, stating accuracy numbers is disingenuous, as the accuracy depends entirely on the device's hardware and environmental conditions. – TheIT Jul 07 '19 at 23:11
-5

If che device is not connected to internet google play services (like geofence or activityrecognition) don't work.

Simone
  • 785
  • 2
  • 7
  • 16