1. Code
  2. Coding Fundamentals
  3. Tools

How to Work With Geofences on Android

Location aware resources allow your application to interact with the physical world and they are ideal for increasing user engagement. Although many mobile apps use them, the topic of this tutorial is a feature that is often overlooked, geofencing.
Scroll to top

Location aware resources allow your application to interact with the physical world and they are ideal for increasing user engagement. Although many mobile apps use them, the topic of this tutorial is a feature that is often overlooked, geofencing.

A geofence is a virtual perimeter set on a real geographic area. Combining a user position with a geofence perimeter, it is possible to know if the user is inside or outside the geofence or even if he is exiting or entering the area.

A geofence knows if a location is inside or outside its delimited areaA geofence knows if a location is inside or outside its delimited areaA geofence knows if a location is inside or outside its delimited area

Imagine a university app that can tell you which colleagues and professors are currently on campus. Or an app for a mall that rewards regular customers. There are many other interesting possibilities that you can explore.

In this tutorial, you learn how to use geofences on Android by creating an application that shows the user a notification when they enter or exit a geofence. It helps if you have previous knowledge of Google Play Services, the Google Maps Android API, or IntentService. If you don't, then you can still follow along, but you may want to do some research about these topics after reading this tutorial.

1. Geofences on Android

On Android, there are several ways to work with geofences. You could even create your own implementation to work with geofences, but it is easier to use Google's GeofencingApi.

This APIs is part of Google's Location APIs. It includes Geofence, GeofencingRequest, GeofenceApiGeofencingEvent, and GeofenceStatusCodes. In this tutorial, we use these classes to create and work with geofences.

Geofence Interface

Geofence is an interface that represents a geographical area that should be monitored. It is created by using the Geofence.Builder. During its creation, you set the monitored region, the geofence's expiration date, responsiveness, an identifier, and the kind of transitions that it should be looking for.

To keep power consumption to a minimum, it is recommended to use a geofence with a radius of at least 100 meters for most situations. If geofences are located in the countryside, you should increase the radius to 500 meters or higher to make sure the geofences are effective.

1
Geofence geofence = new Geofence.Builder()
2
    .setRequestId(GEOFENCE_REQ_ID) // Geofence ID

3
    .setCircularRegion( LATITUDE, LONGITUDE, RADIUS) // defining fence region

4
    .setExpirationDuration( DURANTION ) // expiring date

5
    // Transition types that it should look for

6
    .setTransitionTypes( Geofence.GEOFENCE_TRANSITION_ENTER | Geofence.GEOFENCE_TRANSITION_EXIT )
7
    .build();

Geofence Transitions

  • GEOFENCE_TRANSITION_DWELL indicates that the user entered the area and spent some time there. It is useful to avoid multiple alerts when the user is entering and exiting the area too fast. You can configure the dwelling time using the setLoiteringDelay parameter.
  • GEOFENCE_TRANSITION_ENTER indicates when the user enters the monitored region.
  • GEOFENCE_TRANSITION_EXIT  indicates when the user exits the region.

GeofenceRequest

The GeofencingRequest class receives the geofences that should be monitored. You can create an instance by using a Builder, passing a Geofence or a List<Geofence>, and the kind of notification to trigger when the geofence(s) is created.

1
GeofencingRequest request = new GeofencingRequest.Builder()
2
    // Notification to trigger when the Geofence is created

3
    .setInitialTrigger( GeofencingRequest.INITIAL_TRIGGER_ENTER )
4
    .addGeofence( geofence ) // add a Geofence

5
    .build();

GeofencingApi

The GeofencingApi class is the entry point for all interactions with Google's geofencing API. It is part of the Location APIs and it depends on a GoogleApiClient to work. You will use the GeofencingApi to add and remove geofences.

To add a geofence, you call the addGeofence() method. It monitors the given area using the settings passed to the GeofencingRequest and shoots a PendingIntent when a geofence transition, entering or exiting the area, takes place.

1
PendingResult<Status> addGeofences (GoogleApiClient client, 
2
    GeofencingRequest geofencingRequest, 
3
    PendingIntent pendingIntent)

To remove the geofence, you call removeGeofences(). You can either remove the geofence using its request identifier or its pending intent.

1
PendingResult<Status> removeGeofences(GoogleApiClient client, 
2
    List<String> geofenceRequestIds)
1
 PendingResult<Status> removeGeofences (GoogleApiClient client, 
2
    PendingIntent pendingIntent)

2. Creating a Geofencing App

In this tutorial, we create a simple application that monitors the user location and posts a notification when the user enters or exits a geofenced area. The app consists of only one Activity and an IntentService. We also take a quick look at GoogleMap, GoogleApiClient, and FusedLocationProviderApi, and we explore some caveats of the geofence API.

App ScreenshotApp ScreenshotApp Screenshot

Step 1: Project Setup

GeofencingApi is part of Google Play Services. To access it, you need to correctly set up your development environment and create an instance of the GoogleApiClient. Create a new  project with a blank Activity, edit the project's build.gradle file as shown below, and synchronize your project.

Step 2: Permissions

We need to set the correct permissions to create and use geofences. Add the following permission to the project's manifest:

1
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

Starting with Android 6.0, the app asks for permission at run time and not during the installation. We address this later in the tutorial.

Step 3: Creating the Layout

The project consists of one layout, the MainActity layout. It contains the device's current latitude and longitude, and a GoogleMap fragment that displays the geofences and the user's position.

Since activity_main.xml is pretty straightforward, I want to concentrate only on the MapFragment element. You can take a look at the completed layout in the source files of this tutorial.

1
<!--GoogleMap fragment-->
2
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
3
    android:name="com.google.android.gms.maps.MapFragment"
4
    android:id="@+id/map"
5
    android:layout_width="match_parent"
6
    android:layout_height="match_parent"/>

Step 4: Google Maps API Key

Since we are using a MapFragment, we need to set up and initialize a GoogleMap instance. First, you need to obtain an API key. Once you have an API key, add it to the project's manifest.

1
<meta-data
2
   android:name="com.google.android.geo.API_KEY"
3
   android:value="YOUR_API_KEY"/>

Let's begin with the GoogleMap instance. Implement GoogleMap.OnMapReadyCallbackGoogleMap.OnMapClickListener, and GoogleMap.OnMarkerClickListener in the Activity class and initialize the map.

1
public class MainActivity extends AppCompatActivity
2
    implements 
3
			OnMapReadyCallback, 
4
			GoogleMap.OnMapClickListener,
5
            GoogleMap.OnMarkerClickListener
6
    {
7
    
8
    private static final String TAG = MainActivity.class.getSimpleName();
9
        
10
    private TextView textLat, textLong;
11
    private MapFragment mapFragment;
12
    private GoogleMap map;
13
    
14
    @Override
15
    protected void onCreate(Bundle savedInstanceState) {
16
        super.onCreate(savedInstanceState);
17
        setContentView(R.layout.activity_main);
18
        textLat = (TextView) findViewById(R.id.lat);
19
        textLong = (TextView) findViewById(R.id.lon);
20
        
21
        // initialize GoogleMaps

22
        initGMaps();
23
    }
24
    
25
    // Initialize GoogleMaps

26
    private void initGMaps(){
27
        mapFragment = (MapFragment) getFragmentManager().findFragmentById(R.id.map);
28
        mapFragment.getMapAsync(this);
29
    } 
30
    
31
    // Callback called when Map is ready

32
    @Override
33
    public void onMapReady(GoogleMap googleMap) {
34
        Log.d(TAG, "onMapReady()");
35
        map = googleMap;
36
        map.setOnMapClickListener(this);
37
        map.setOnMarkerClickListener(this);
38
    }
39
    
40
	// Callback called when Map is touched

41
    @Override
42
    public void onMapClick(LatLng latLng) {
43
        Log.d(TAG, "onMapClick("+latLng +")");
44
    }
45
46
	// Callback called when Marker is touched

47
    @Override
48
    public boolean onMarkerClick(Marker marker) {
49
        Log.d(TAG, "onMarkerClickListener: " + marker.getPosition() );
50
        return false;
51
    } 
52
}

Step 5: GoogleApiClient

To use the GeofencingApi interface, we need a GoogleApiClient entry point. Let's implement a GoogleApiClient.ConnectionCallbacks and a GoogleApiClient.OnConnectionFailedListener in the Activity as shown below.

1
public class MainActivity extends AppCompatActivity
2
        implements
3
            GoogleApiClient.ConnectionCallbacks,
4
            GoogleApiClient.OnConnectionFailedListener,
5
            OnMapReadyCallback,
6
            GoogleMap.OnMapClickListener,
7
            GoogleMap.OnMarkerClickListener {
8
            
9
    // ...

10
    private GoogleApiClient googleApiClient;
11
            
12
    @Override
13
    protected void onCreate(Bundle savedInstanceState) {
14
        
15
        // ...

16
17
        // create GoogleApiClient

18
        createGoogleApi();
19
    }
20
    
21
    // Create GoogleApiClient instance

22
    private void createGoogleApi() {
23
        Log.d(TAG, "createGoogleApi()");
24
        if ( googleApiClient == null ) {
25
            googleApiClient = new GoogleApiClient.Builder( this )
26
                    .addConnectionCallbacks( this )
27
                    .addOnConnectionFailedListener( this )
28
                    .addApi( LocationServices.API )
29
                    .build();
30
        }
31
    }
32
    
33
    @Override
34
    protected void onStart() {
35
        super.onStart();
36
37
        // Call GoogleApiClient connection when starting the Activity

38
        googleApiClient.connect();
39
    }
40
41
    @Override
42
    protected void onStop() {
43
        super.onStop();
44
45
        // Disconnect GoogleApiClient when stopping Activity

46
        googleApiClient.disconnect();
47
    }
48
    
49
    // GoogleApiClient.ConnectionCallbacks connected

50
    @Override
51
    public void onConnected(@Nullable Bundle bundle) {
52
        Log.i(TAG, "onConnected()");
53
    }
54
55
    // GoogleApiClient.ConnectionCallbacks suspended

56
    @Override
57
    public void onConnectionSuspended(int i) {
58
        Log.w(TAG, "onConnectionSuspended()");
59
    }
60
61
    // GoogleApiClient.OnConnectionFailedListener fail

62
    @Override
63
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
64
        Log.w(TAG, "onConnectionFailed()");
65
    }
66
} 

Step 6: FusedLocationProviderApi

We also need to access the user's current location. The FusedLocationProviderApi interface gives us this information and allows a great level of control of the location request. This is very important, considering that location requests have a direct effect over the device's battery consumption.

Now, let's implement a LocationListener. Check if the user gave the application the appropriate permissions by creating the Location request and display their the current location on the screen.

1
public class MainActivity extends AppCompatActivity
2
        implements
3
            // ....

4
            LocationListener
5
            {
6
            
7
    private Location lastLocation;
8
    //...

9
    
10
    // GoogleApiClient.ConnectionCallbacks connected

11
    @Override
12
    public void onConnected(@Nullable Bundle bundle) {
13
        Log.i(TAG, "onConnected()");
14
        getLastKnownLocation();
15
    }
16
    
17
    // Get last known location

18
    private void getLastKnownLocation() {
19
        Log.d(TAG, "getLastKnownLocation()");
20
        if ( checkPermission() ) {
21
            lastLocation = LocationServices.FusedLocationApi.getLastLocation(googleApiClient);
22
            if ( lastLocation != null ) {
23
                Log.i(TAG, "LasKnown location. " +
24
                        "Long: " + lastLocation.getLongitude() +
25
                        " | Lat: " + lastLocation.getLatitude());
26
                writeLastLocation();
27
                startLocationUpdates();
28
            } else {
29
                Log.w(TAG, "No location retrieved yet");
30
                startLocationUpdates();
31
            }
32
        }
33
        else askPermission();
34
    }
35
    
36
    private LocationRequest locationRequest;
37
    // Defined in mili seconds. 

38
    // This number in extremely low, and should be used only for debug

39
    private final int UPDATE_INTERVAL =  1000;
40
    private final int FASTEST_INTERVAL = 900;
41
42
    // Start location Updates

43
    private void startLocationUpdates(){
44
        Log.i(TAG, "startLocationUpdates()");
45
        locationRequest = LocationRequest.create()
46
                .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
47
                .setInterval(UPDATE_INTERVAL)
48
                .setFastestInterval(FASTEST_INTERVAL);
49
50
        if ( checkPermission() )
51
            LocationServices.FusedLocationApi.requestLocationUpdates(googleApiClient, locationRequest, this);
52
    }
53
    
54
    @Override
55
    public void onLocationChanged(Location location) {
56
        Log.d(TAG, "onLocationChanged ["+location+"]");
57
        lastLocation = location;
58
        writeActualLocation(location);
59
    }
60
    
61
    // Write location coordinates on UI

62
    private void writeActualLocation(Location location) {
63
        textLat.setText( "Lat: " + location.getLatitude() );
64
        textLong.setText( "Long: " + location.getLongitude() );
65
    }
66
67
    private void writeLastLocation() {
68
        writeActualLocation(lastLocation);
69
    }
70
    
71
    // Check for permission to access Location

72
    private boolean checkPermission() {
73
        Log.d(TAG, "checkPermission()");
74
        // Ask for permission if it wasn't granted yet

75
        return (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
76
                == PackageManager.PERMISSION_GRANTED );
77
    }
78
    
79
    // Asks for permission

80
    private void askPermission() {
81
        Log.d(TAG, "askPermission()");
82
        ActivityCompat.requestPermissions(
83
                this,
84
                new String[] { Manifest.permission.ACCESS_FINE_LOCATION },
85
                REQ_PERMISSION
86
        );
87
    }
88
89
    // Verify user's response of the permission requested

90
    @Override
91
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
92
        Log.d(TAG, "onRequestPermissionsResult()");
93
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
94
        switch ( requestCode ) {
95
            case REQ_PERMISSION: {
96
                if ( grantResults.length > 0
97
                        && grantResults[0] == PackageManager.PERMISSION_GRANTED ){
98
                    // Permission granted

99
                    getLastKnownLocation();
100
101
                } else {
102
                    // Permission denied

103
                    permissionsDenied();
104
                }
105
                break;
106
            }
107
        }
108
    }
109
110
    // App cannot work without the permissions

111
    private void permissionsDenied() {
112
        Log.w(TAG, "permissionsDenied()");
113
    }
114
}

It is important to address that the LocationRequest created above isn't optimized for a production environment. The UPDATE_INTERVAL is too short and would consume too much battery power. A more realistic configuration for production could be:

1
private final int UPDATE_INTERVAL =  3 * 60 * 1000; // 3 minutes

2
private final int FASTEST_INTERVAL = 30 * 1000;  // 30 secs

3
private void startLocationUpdates(){
4
        Log.i(TAG, "startLocationUpdates()");
5
        locationRequest = LocationRequest.create()
6
                .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
7
                .setInterval(UPDATE_INTERVAL)
8
                .setFastestInterval(FASTEST_INTERVAL);
9
10
        if ( checkPermission() )
11
            LocationServices.FusedLocationApi.requestLocationUpdates(googleApiClient, locationRequest, this);
12
}

Step 7: GoogleMap Markers

Our Activity needs two different markers. A locationMarker uses the latitude and longitude given by the FusedLocationProviderApi to inform the device's current location. A geoFenceMarker is the target for the geofence creation as it uses the last touch given on the map to retrieve its position.

1
@Override
2
public void onMapClick(LatLng latLng) {
3
    Log.d(TAG, "onMapClick("+latLng +")");
4
    markerForGeofence(latLng);
5
}
6
7
private void writeActualLocation(Location location) {
8
    // ...

9
    markerLocation(new LatLng(location.getLatitude(), location.getLongitude()));
10
}
11
12
private Marker locationMarker;
13
// Create a Location Marker 

14
private void markerLocation(LatLng latLng) {
15
    Log.i(TAG, "markerLocation("+latLng+")");
16
    String title = latLng.latitude + ", " + latLng.longitude;
17
    MarkerOptions markerOptions = new MarkerOptions()
18
            .position(latLng)
19
            .title(title);
20
    if ( map!=null ) {
21
        // Remove the anterior marker

22
        if ( locationMarker != null )
23
            locationMarker.remove();
24
        locationMarker = map.addMarker(markerOptions);
25
        float zoom = 14f;
26
        CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngZoom(latLng, zoom);
27
        map.animateCamera(cameraUpdate);
28
    }
29
}
30
31
private Marker geoFenceMarker;
32
// Create a marker for the geofence creation

33
private void markerForGeofence(LatLng latLng) {
34
    Log.i(TAG, "markerForGeofence("+latLng+")");
35
    String title = latLng.latitude + ", " + latLng.longitude;
36
    // Define marker options

37
    MarkerOptions markerOptions = new MarkerOptions()
38
            .position(latLng)
39
            .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_ORANGE))
40
            .title(title);
41
    if ( map!=null ) {
42
        // Remove last geoFenceMarker

43
        if (geoFenceMarker != null)
44
            geoFenceMarker.remove();
45
46
        geoFenceMarker = map.addMarker(markerOptions);
47
    }
48
}

Step 8: Creating a Geofence

At last, it is time to create a geofence. We use the geoFenceMarker as the center point for the geofence.

1
private static final long GEO_DURATION = 60 * 60 * 1000;
2
private static final String GEOFENCE_REQ_ID = "My Geofence";
3
private static final float GEOFENCE_RADIUS = 500.0f; // in meters

4
5
// Create a Geofence

6
private Geofence createGeofence( LatLng latLng, float radius ) {
7
    Log.d(TAG, "createGeofence");
8
    return new Geofence.Builder()
9
            .setRequestId(GEOFENCE_REQ_ID)
10
            .setCircularRegion( latLng.latitude, latLng.longitude, radius)
11
            .setExpirationDuration( GEO_DURATION )
12
            .setTransitionTypes( Geofence.GEOFENCE_TRANSITION_ENTER
13
                    | Geofence.GEOFENCE_TRANSITION_EXIT )
14
            .build();
15
}

Next, we create the GeofencingRequest object.

1
// Create a Geofence Request

2
private GeofencingRequest createGeofenceRequest( Geofence geofence ) {
3
    Log.d(TAG, "createGeofenceRequest");
4
    return new GeofencingRequest.Builder()
5
            .setInitialTrigger( GeofencingRequest.INITIAL_TRIGGER_ENTER )
6
            .addGeofence( geofence )
7
            .build();
8
}

We use a PendingIntent object to call a IntentService that will handle the GeofenceEvent. We create the GeofenceTrasitionService.class later.

1
private PendingIntent geoFencePendingIntent;
2
private final int GEOFENCE_REQ_CODE = 0;
3
private PendingIntent createGeofencePendingIntent() {
4
    Log.d(TAG, "createGeofencePendingIntent");
5
    if ( geoFencePendingIntent != null )
6
        return geoFencePendingIntent;
7
8
    Intent intent = new Intent( this, GeofenceTrasitionService.class);
9
    return PendingIntent.getService(
10
            this, GEOFENCE_REQ_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT );
11
}
12
13
// Add the created GeofenceRequest to the device's monitoring list

14
private void addGeofence(GeofencingRequest request) {
15
    Log.d(TAG, "addGeofence");
16
    if (checkPermission())
17
        LocationServices.GeofencingApi.addGeofences(
18
                googleApiClient,
19
                request,
20
                createGeofencePendingIntent()
21
        ).setResultCallback(this);
22
}

We also draw the geofence on the map as a visual reference.

1
@Override
2
public void onResult(@NonNull Status status) {
3
    Log.i(TAG, "onResult: " + status);
4
    if ( status.isSuccess() ) {
5
        drawGeofence();
6
    } else {
7
        // inform about fail

8
    }
9
}
10
11
// Draw Geofence circle on GoogleMap

12
private Circle geoFenceLimits;
13
private void drawGeofence() {
14
    Log.d(TAG, "drawGeofence()");
15
16
    if ( geoFenceLimits != null )
17
        geoFenceLimits.remove();
18
19
    CircleOptions circleOptions = new CircleOptions()
20
            .center( geoFenceMarker.getPosition())
21
            .strokeColor(Color.argb(50, 70,70,70))
22
            .fillColor( Color.argb(100, 150,150,150) )
23
            .radius( GEOFENCE_RADIUS );
24
    geoFenceLimits = map.addCircle( circleOptions );
25
}

The startGeofence() method is responsible for starting the geofencing process in the MainActivity class.

1
    @Override
2
    public boolean onOptionsItemSelected(MenuItem item) {
3
        switch ( item.getItemId() ) {
4
            case R.id.geofence: {
5
                startGeofence();
6
                return true;
7
            }
8
        }
9
        return super.onOptionsItemSelected(item);
10
    }
11
    
12
	// Start Geofence creation process

13
    private void startGeofence() {
14
        Log.i(TAG, "startGeofence()");
15
        if( geoFenceMarker != null ) {
16
            Geofence geofence = createGeofence( geoFenceMarker.getPosition(), GEOFENCE_RADIUS );
17
            GeofencingRequest geofenceRequest = createGeofenceRequest( geofence );
18
            addGeofence( geofenceRequest );
19
        } else {
20
            Log.e(TAG, "Geofence marker is null");
21
        }
22
    }

Step 9: Geofence Transition Service

We can now finally create the GeofenceTrasitionService.class mentioned earlier. This class extends IntentService and is responsible for handling the GeofencingEvent. First, we get this event from the received intent.

1
GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);

We then check if the kind of geofencing transition that took place is of interest to us. If it is, we retrieve a list of the triggered geofences and create a notification with the appropriate actions.

1
// Retrieve GeofenceTrasition

2
int geoFenceTransition = geofencingEvent.getGeofenceTransition();
3
// Check if the transition type

4
if ( geoFenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER ||
5
        geoFenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT ) {
6
    // Get the geofence that were triggered

7
    List<Geofence> triggeringGeofences = geofencingEvent.getTriggeringGeofences();
8
    // Create a detail message with Geofences received

9
    String geofenceTransitionDetails = getGeofenceTrasitionDetails(geoFenceTransition, triggeringGeofences );
10
    // Send notification details as a String

11
    sendNotification( geofenceTransitionDetails );
12
}

I have also implemented a few helper methods to make the implementation of the class easier to understand.

1
public class GeofenceTrasitionService extends IntentService {
2
3
    private static final String TAG = GeofenceTrasitionService.class.getSimpleName();
4
    public static final int GEOFENCE_NOTIFICATION_ID = 0;
5
6
    public GeofenceTrasitionService() {
7
        super(TAG);
8
    }
9
10
    @Override
11
    protected void onHandleIntent(Intent intent) {
12
        // Retrieve the Geofencing intent

13
        GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
14
15
        // Handling errors

16
        if ( geofencingEvent.hasError() ) {
17
            String errorMsg = getErrorString(geofencingEvent.getErrorCode() );
18
            Log.e( TAG, errorMsg );
19
            return;
20
        }
21
22
		// Retrieve GeofenceTrasition

23
        int geoFenceTransition = geofencingEvent.getGeofenceTransition();
24
        // Check if the transition type

25
        if ( geoFenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER ||
26
                geoFenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT ) {
27
            // Get the geofence that were triggered

28
            List<Geofence> triggeringGeofences = geofencingEvent.getTriggeringGeofences();
29
			// Create a detail message with Geofences received

30
            String geofenceTransitionDetails = getGeofenceTrasitionDetails(geoFenceTransition, triggeringGeofences );
31
            // Send notification details as a String

32
            sendNotification( geofenceTransitionDetails );
33
        }
34
    }
35
	
36
	// Create a detail message with Geofences received

37
    private String getGeofenceTrasitionDetails(int geoFenceTransition, List<Geofence> triggeringGeofences) {
38
        // get the ID of each geofence triggered

39
        ArrayList<String> triggeringGeofencesList = new ArrayList<>();
40
        for ( Geofence geofence : triggeringGeofences ) {
41
            triggeringGeofencesList.add( geofence.getRequestId() );
42
        }
43
44
        String status = null;
45
        if ( geoFenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER )
46
            status = "Entering ";
47
        else if ( geoFenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT )
48
            status = "Exiting ";
49
        return status + TextUtils.join( ", ", triggeringGeofencesList);
50
    }
51
52
	// Send a notification

53
    private void sendNotification( String msg ) {
54
        Log.i(TAG, "sendNotification: " + msg );
55
56
        // Intent to start the main Activity

57
        Intent notificationIntent = MainActivity.makeNotificationIntent(
58
                getApplicationContext(), msg
59
        );
60
61
        TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
62
        stackBuilder.addParentStack(MainActivity.class);
63
        stackBuilder.addNextIntent(notificationIntent);
64
        PendingIntent notificationPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
65
66
        // Creating and sending Notification

67
        NotificationManager notificatioMng =
68
                (NotificationManager) getSystemService( Context.NOTIFICATION_SERVICE );
69
        notificatioMng.notify(
70
                GEOFENCE_NOTIFICATION_ID,
71
                createNotification(msg, notificationPendingIntent));
72
    }
73
74
    // Create a notification

75
    private Notification createNotification(String msg, PendingIntent notificationPendingIntent) {
76
        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this);
77
        notificationBuilder
78
                .setSmallIcon(R.drawable.ic_action_location)
79
                .setColor(Color.RED)
80
                .setContentTitle(msg)
81
                .setContentText("Geofence Notification!")
82
                .setContentIntent(notificationPendingIntent)
83
                .setDefaults(Notification.DEFAULT_LIGHTS | Notification.DEFAULT_VIBRATE | Notification.DEFAULT_SOUND)
84
                .setAutoCancel(true);
85
        return notificationBuilder.build();
86
    }
87
88
	// Handle errors

89
    private static String getErrorString(int errorCode) {
90
        switch (errorCode) {
91
            case GeofenceStatusCodes.GEOFENCE_NOT_AVAILABLE:
92
                return "GeoFence not available";
93
            case GeofenceStatusCodes.GEOFENCE_TOO_MANY_GEOFENCES:
94
                return "Too many GeoFences";
95
            case GeofenceStatusCodes.GEOFENCE_TOO_MANY_PENDING_INTENTS:
96
                return "Too many pending intents";
97
            default:
98
                return "Unknown error.";
99
        }
100
    }
101
}

3. Testing

Testing on a Virtual Device

It is much simpler to test geofencing on a virtual device. There are several ways to do this. In Android Studio, open a virtual device and click the more options button in the bottom right.

Click on More OptionsClick on More OptionsClick on More Options

In the Location tab on the left, enter the coordinates for the location.

Send Latitude and LongitudeSend Latitude and LongitudeSend Latitude and Longitude

I prefer to use telnet commands to control the virtual device. To use this, you need to connect to the device from the command line using the following command:

1
telnet localhost [DEVICE_PORT]

The device port is shown in the virtual device window. The device port is usually equal to 5554.

It is possible that you need to authorize this connection using your auth_token, but the command line shows you where it is located. Navigate to that location and copy the token and type, auth [YOUR_AUTH_TOKEN].

You can now set the location of the device by running the following command:

1
geo fix [LATITUDE] [LONGITUDE]

Conclusion

Geofencing may be a great addition to your app as it can considerably increase user engagement. There are lots of possibilities to explore and you could even create a sophisticated experience using indoor beacons, such as the Estimote. With indoor beacons, you know exactly where the user has passed in, for example, a shopping mall.

Adding Geofencing to a project is simple, but we need to keep power consumption in mind at all times. This means that we need to carefully choose the size of the geofence and the update rate because both directly impact power consumption of your application.

Testing is therefore very important to get a realistic idea of the power consumption of your application. Also consider giving users the option to disable geofencing altogether if they don't want or need this feature.