Android Geofencing with Google Maps

A geofence is a virtual perimeter of interest that can be set up to fire notifications when it is entered or exited, or both. For example, a geofencing app can alert us that our kid has left a previously specified area, or send us a coupon (e.g. the “Present this SMS an get 20% off” offer type) when we happen to walk or drive in the proximity of a movie theater.

Now, with the new Location APIs, Google’s location algorithm has been rewritten to be more accurate and use significantly less battery life. There is just enough documentation  plus sample code and a downloadable sample app (GeofenceDetection) to help us get started creating geofencing apps. Prerequisites are:

  1. Download  Google Play Services (via Android’s SDK Manager) and set it up as a library
  2. Get a Google Maps v2 API key, and maybe run the sample app. This short Quick Start guide might help
  3. Download (again via SDK Manager)  the Support Library to cater to older Android versions.

For practical purposes, let’s just start where the sample geofencing app (GeofenceDetection) stops, and introduce a few enhancements to make the app semi-decent and show a sample of possibilities with the new Location API.

1. Zoom and Camera Position

First, a little taste of Google Maps API v2. Let’s choose zoom level and camera angle:

import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.model.CameraPosition;
import com.google.android.gms.maps.model.LatLng;
//...
// inside class, for a given lat/lon
CameraPosition INIT =
new CameraPosition.Builder()
.target(new LatLng(lat, lon))
.zoom( 17.5F )
.bearing( 300F) // orientation
.tilt( 50F) // viewing angle
.build();
 // use GooggleMap mMap to move camera into position
 mMap.animateCamera( CameraUpdateFactory.newCameraPosition(INIT) );

The code above has a zoom level allowing the viewing of buildings in 3D. Google Maps v2 uses OpenGL for Embedded Systems (OpenGL ES v2) to render 2D and 3D computer graphics.

2. Options menu

Even if we are not big fans of an Options menu, it might be adequate in this case, since we would not want to clutter the map with too much “touch” functionality (we will  have plenty of that shortly).

GoogleMaps-v2 Options-Menu

We can toggle between “normal” and satellite view:

/**
 * Toggle View Satellite-Normal
 */
 public static void toggleView(){
 mMap.setMapType( mMap.getMapType() ==
              GoogleMap.MAP_TYPE_NORMAL ?
              GoogleMap.MAP_TYPE_SATELLITE :
              GoogleMap.MAP_TYPE_NORMAL);
 }

We can also provide a “Flight Mode”, where we let the camera scroll away. Not tremendously useful, but kind of cool nonetheless:

import com.google.android.gms.maps.GoogleMap.CancelableCallback;
//...
private static CancelableCallback callback = new CancelableCallback() {
@Override
 public void onFinish() {
   scroll();
 }
 @Override
 public void onCancel() {}
};

public static void scroll() {
   // we don't want to scroll too fast since
  // loading new areas in map takes time
   mMap.animateCamera( CameraUpdateFactory.scrollBy(10, -10),
                       callback ); // 10 pix
}

3. Geocoding/Reverse Geocoding
The sample is here to demonstrate features and makes heavy use of latitude/longitude coordinates. But we need to provide a more user-friendly way to interface with locations on the map, like a street address. We can use geocoding/reverse geocoding  to transform a  street address to coordinates and vice-versa using Android’s Geocoder.

4. Adding geofences
OK, now on to geofences. Thanks to geocoding, we can request an actual physical address from the user instead of coordinates. We will  just change that address to a latitude/longitude pair internally to process user input. Notice how we use transparent UIs as much as possible to enhance what some might call the user experience. Notice also that we provide a spinner so that the user can choose between predefined values. That saves the user some typing and it saves us from validating coordinates values each time.

Still, if we want to be even more user-friendly, we can give our users the possibility to pre-fill the address field by long-pressing a point on the map. We will then use reverse geocoding to translate the coordinates to a physical address for display (screen below on the right):

Add-Fence-Option Add-Fence-LongTouch

Processing long-presses is pretty straightforward:

import com.google.android.gms.maps.GoogleMap.OnMapLongClickListener;
//...
public class MainActivity extends FragmentActivity
                          implements OnMapLongClickListener  {
  //...
  mMap.setOnMapLongClickListener(this);
  //...
  @Override
  public void onMapLongClick(LatLng point) {
     // reverse geocode point
  }
}

Adding /Removing geofences is pretty much covered in the sample app (by the GeofenceRequester and GeofenceRemover classes). The thing to remember is that the process of adding/removing fences is as follows :

  1. A connection to Google’s Location Services is requested by our app.
  2. Once/if the connection is available, the request to add/remove a fence is done using  a PendingIntent.
  3. If a similar request made by our app is still underway, the operation fails.
  4. Although the method we call (e.g. addGeofences()) returns at once, we won’t know if the request was successful until Location Services calls back into our app (e.g. OnAddGeofencesResultListener‘s onAddGeofencesResult() ) with a success status code.
  5. Finally, the preceding method will use a Broadcast Intent to notify other components of our app of  success/failure.

Needless to say, we need to code defensively at almost every step of the way. Now, once a geofence is added, we can add a marker (the default or a customized one) and choose between different shapes (circle, polygon, etc.) to delimit the geofence. For instance we can write this code to add the default marker and circle the fence within a specified radius:

import com.google.android.gms.maps.model.Circle;
import com.google.android.gms.maps.model.CircleOptions;
import com.google.android.gms.maps.model.MarkerOptions;
//...
public static void addMarkerForFence(SimpleGeofence fence){
if(fence == null){
    // display en error message and return
   return;
}
mMap.addMarker( new MarkerOptions()
  .position( new LatLng(fence.getLatitude(), fence.getLongitude()) )
  .title("Fence " + fence.getId())
  .snippet("Radius: " + fence.getRadius()) ).showInfoWindow();

//Instantiates a new CircleOptions object +  center/radius
CircleOptions circleOptions = new CircleOptions()
  .center( new LatLng(fence.getLatitude(), fence.getLongitude()) )
  .radius( fence.getRadius() )
  .fillColor(0x40ff0000)
  .strokeColor(Color.TRANSPARENT)
  .strokeWidth(2);

// Get back the mutable Circle
Circle circle = mMap.addCircle(circleOptions);
// more operations on the circle...

}

Here are the resulting screens, including the one we get once we “Touch to Edit” the Info Window:

Add-Fence Edit-Fence

The sample app has all we need to fire notifications once the circled area above is entered or exited. Notice how we set up the marker’s Info window to allow editing the geofence radius, or removing the geofence altogether. To implement a clickable custom Info window, all we need is to create our own InfoWindowAdapter and OnInfoWindowClickListener.

As for the notifications themselves, this is how they look like in the sample app:

notif3 notif2

We can of course change a notification’s appearance and functionality, and… that would be the subject of another article.  Hopefully, this one gave a glimpse of what is possible with the new Location API.  Have fun with Android geofences.

This article is also available at Java DZone.

About these ads

, , ,

  1. #1 by Nitish on June 27, 2013 - 9:33 am

    Great article..will you please share your source code with us?

  2. #2 by ameikle on July 7, 2013 - 1:15 pm

    Hi Tony, Can you please share the code on how you created the sample app

  3. #3 by Sohaib on November 9, 2013 - 2:53 pm

    i have try to run the code above but it is giving error when I import the following:

    import com.google.android.gms.maps.model.Circle;
    import com.google.android.gms.maps.model.CircleOptions;
    import com.google.android.gms.maps.model.MarkerOptions;

    is their any solution i have also tried the android.developers code but got the same problem

    • #4 by Tony S. on November 9, 2013 - 6:48 pm

      You need to import the google-play-services as a library project..This should help:

      https://blog-emildesign.rhcloud.com/?p=435

      • #5 by Sohaib on November 16, 2013 - 7:24 am

        thanks for your response… got the solution and imported the google-play services as a library project but the application stops unexpectedly and got the error in logcat could not execute method of the activity. i just tried to run the android.developers code. can you explain why i am getting this error???

      • #6 by Tony S. on November 16, 2013 - 8:28 am

        What’s the stack trace?

        BTW, if you’re just trying to run the sample app, you should post the issue on http://stackoverflow.com/.

  4. #7 by athira on December 23, 2013 - 9:58 am

    hello sir… i need your help in completing my app. but i cannot reveal its details here. can u please mail me.. my mail id: athi09mitk@gmail.com ….waiting to hear from u

  5. #8 by soeyannaingblog1 on February 28, 2014 - 7:50 am

    Reblogged this on Configurer Corporation ©.

  6. #9 by Gonzalo on April 23, 2014 - 4:55 pm

    Hi Tony, Can you please share the code of the sample app?

    Thanks!!!!

  7. #10 by Babar Shahzaad on June 2, 2014 - 11:58 am

    Can you provide me complete source code i need this for my final year project module waiting for your kind reply
    email id: babarshahzaad@gmail.com

  8. #11 by fernando on June 10, 2014 - 4:21 am

    hii! can you share the complete code ? i need it for my final project pleaasee! zerpa.fernando@yahoo.com

  9. #12 by kasun on June 25, 2014 - 4:36 am

    can u send the source code ?
    kwickramanayake007@gmail.com

  10. #13 by Naveed Ahmad on July 3, 2014 - 6:17 am

    Sir can you please send me the source code, I am facing problem while implementing your CODE.
    Email: naveed_ahmad412@yahoo.com

  11. #14 by Lakshman on July 24, 2014 - 11:04 am

    Great job…..Thank you very much.

  12. #15 by laxmikant on September 8, 2014 - 8:25 am

    hi can i get the source..
    please share it on lucky.nuchi@gmail.com

  13. #16 by Mohammed Mansoor on September 17, 2014 - 6:55 am

    Hi,

    It is seems to be working. Can u pls send me the code to koori.m@gmail.com

  14. #17 by Rajesh.K on October 16, 2014 - 9:13 am

    Hi Please share source code rajeshkit7@gmail.com

  15. #18 by xcharva5 on October 30, 2014 - 9:14 am

    Hi, could u share source code with me please, thanks a lot.
    andycharvat@gmail.com

  1. Android Location with Google Maps – Part 1 | Blog T

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: