Geofencing is a service that triggers an action when a device enters or exits from the specified predefined location or geographical area for a given radius. This is a location-based service in which a creator of an app or other platform/software uses GPS, RFID, Wi-Fi or cellular data to send/trigger messages like SMS, Email or In-App/App-based notifications (pre-programmed action) when mobile devices that enter, exit center or remain parked in the geographical location.
Android allows you to create a geo-fence by specifying the radius and lat-long (short for latitude and longitude) of the center of the real geographical region that you wish to monitor.
There are smart companies that send product offers or specific promotions to consumers’ smartphones when they trigger a search in a particular geographic location, enter a mall, neighborhood, or store.
So far, we’ve covered a lot about Geo fencing and what it can do for your business. Do you know how it works?
It’s simple though. Geo fencing helps you to keep in control of your business by notifying you when a potential consumer is passing by your store, by a competitor’s, or entering into a predefined area.
Because this app uses Google Maps, you’ll need to obtain an API key.
classpath 'com.google.gms:google-services:4.3.3'
Using geofences requires the play-services-location library added to your project. To do that, open the build.gradle file for the app module and add the following dependency:
apply plugin: 'com.google.gms.google-services' dependencies { implementation 'com.google.android.gms:play-services-location:17.0.0' implementation 'com.google.android.gms:play-services-maps:17.0.0' }
You have to implements OnMapReadyCallback, GoogleApiClient.ConnectionCallbacks and GoogleApiClient.OnConnectionFailedListener in activity class
private static final int REQUEST_LOCATION_PERMISSION_CODE = 101; public static final String GEOFENCE_ID = "RLOGICAL"; public static final float GEOFENCE_RADIUS_IN_METERS = 100; public HashMap<String, LatLng> LOCATION_LIST = new HashMap<String, LatLng>(); private GoogleMap googleMap; private GeofencingRequest geofencingRequest; private GoogleApiClient googleApiClient; private boolean isMonitoring = false; private MarkerOptions markerOptions; private Marker currentLocationMarker; private PendingIntent pendingIntent;
LOCATION_LIST.put(GEOFENCE_ID, new LatLng(23.029840, 72.557776));
GoogleApiClient is used with a variety of static methods. Some of these methods require that GoogleApiClient be connected, some will queue up calls before GoogleApiClient is connected; check the specific API documentation to determine whether you need to be connected.
googleApiClient = new GoogleApiClient.Builder(this) .addApi(LocationServices.API).addConnectionCallbacks(this).addOnConnectionFailedListener(this).build();
Secrets You Didn’t Know: How to Hire Android App Developers
The first step in requesting geofence monitoring is to request the necessary permissions.
If your app needs to access the user’s location, you must request permission by adding the relevant Android location permission to your app.
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION}, REQUEST_LOCATION_PERMISSION_CODE); }
Used for receiving notifications from the LocationManager when the location has changed. These methods are called if the LocationListener has been registered with the location manager service using the LocationManager#requestLocationUpdates(String, long, float, LocationListener) method.
private void startLocationMonitor() { Log.d(TAG, "Start Location monitor"); LocationRequest locationRequest = LocationRequest.create() .setInterval(2000) .setFastestInterval(1000) .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); try { LocationServices.FusedLocationApi.requestLocationUpdates(googleApiClient, locationRequest, new LocationListener() { @Override public void onLocationChanged(Location location) { if (currentLocationMarker != null) { currentLocationMarker.remove(); } markerOptions = new MarkerOptions(); markerOptions.position(new LatLng(location.getLatitude(), location.getLongitude())); markerOptions.title("Current Location"); currentLocationMarker = googleMap.addMarker(markerOptions); Log.d(TAG, "Location Change Lat Lng " + location.getLatitude() + " " + location.getLongitude()); } }); } catch (SecurityException e) { Log.d(TAG, e.getMessage()); } }
Geofencing combines awareness of the user’s current location with awareness of the user’s proximity to locations that may be of interest. To mark a location of interest, you specify its latitude and longitude. To adjust the proximity for the location, you add a radius. The latitude, longitude, and radius define a geofence, creating a circular area, or fence, around the location of interest.
To add geofences, use the GeofencingClient.addGeofences() method. Provide the GeofencingRequest object, and the PendingIntent
private void startGeofencing() { Log.d(TAG, "Start geofencing monitoring call"); pendingIntent = getGeofencePendingIntent(); geofencingRequest = new GeofencingRequest.Builder() .setInitialTrigger(Geofence.GEOFENCE_TRANSITION_ENTER) .addGeofence(getGeofence()) .build(); if (!googleApiClient.isConnected()) { Log.d(TAG, "Google API client not connected"); } else { try { LocationServices.GeofencingApi.addGeofences(googleApiClient, geofencingRequest, pendingIntent).setResultCallback(new ResultCallback<Status>() { @Override public void onResult(@NonNull Status status) { if (status.isSuccess()) { Log.d(TAG, "Successfully Geofencing Connected"); } else { Log.d(TAG, "Failed to add Geofencing " + status.getStatus()); } } }); } catch (SecurityException e) { Log.d(TAG, e.getMessage()); } } isMonitoring = true; } @NonNull private Geofence getGeofence() { LatLng latLng = LOCATION_LIST.get(GEOFENCE_ID); return new Geofence.Builder().setRequestId(GEOFENCE_ID).setExpirationDuration(Geofence.NEVER_EXPIRE) .setCircularRegion(latLng.latitude, latLng.longitude, GEOFENCE_RADIUS_IN_METERS).setNotificationResponsiveness(1000) .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER | Geofence.GEOFENCE_TRANSITION_EXIT).build(); } Start Intent service in background private PendingIntent getGeofencePendingIntent() { if (pendingIntent != null) { return pendingIntent; } Intent intent = new Intent(this, GeofenceService.class); return PendingIntent.getService(this, 0, intent, PendingIntent. FLAG_UPDATE_CURRENT); } @Override protected void onResume() { super.onResume(); int response = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(MainActivity.this); if (response != ConnectionResult.SUCCESS) { Log.d(TAG, "Google Play Service Not Available"); GoogleApiAvailability.getInstance().getErrorDialog(MainActivity.this, response, 1).show(); } else { Log.d(TAG, "Google play service available"); } }
If Google Play services is not installed on the device, the user will be prompted to install it, and the onMapReady(GoogleMap) method will only be triggered when the user has installed it and returned to the app.
@Override public void onMapReady(GoogleMap googleMap) { if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { return; } this.googleMap = googleMap; LatLng latLng = LOCATION_LIST.get(GEOFENCE_ID); googleMap.addMarker(new MarkerOptions().position(latLng).title("RLogical")); googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, 17f)); googleMap.setMyLocationEnabled(true); // Draw Geofence on Map Circle circle = googleMap.addCircle(new CircleOptions().center(new LatLng(latLng.latitude, latLng.longitude)) .radius(GEOFENCE_RADIUS_IN_METERS).strokeColor(Color.RED).strokeWidth(4f)); } Connect with Google and Start Monitoring @Override public void onConnected(@Nullable Bundle bundle) { Log.d(TAG, "Google Api Client Connected"); isMonitoring = true; startGeofencing(); startLocationMonitor(); } @Override public void onConnectionSuspended(int i) { Log.d(TAG, "Google Connection Suspended"); } @Override public void onConnectionFailed(@NonNull ConnectionResult connectionResult) { isMonitoring = false; Log.e(TAG, "Connection Failed:" + connectionResult.getErrorMessage()); }
Define an Intent Service for geofence transitions
An Intent sent from Location Services can trigger various actions in your app, but you should not have it start an activity or fragment, because components should only become visible in response to a user action. In many cases, an Intent Service is a good way to handle a geofence transition. An Intent Service gets updates when an event occurs, such as a transition into or out of a geofence, and can start long-running background work.
When Location Services detects that the user has entered or exited a geofence, it sends out the Intent contained in the PendingIntent you included in the request to add geofences. A intent service like Geofence Service notices that the Intent was invoked and can then obtain the geofencing event from the intent, determine the type of Geofence transition(s), and determine which of the defined geofences was triggered. The Intent service can direct an app to start performing background work or, if desired, send a notification as output.
public class GeofenceService extends IntentService { private static final String TAG = "GeoIntentService"; public GeofenceService() { super(TAG); } @Override protected void onHandleIntent(@Nullable Intent intent) { GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent); if (!geofencingEvent.hasError()) { int transaction = geofencingEvent.getGeofenceTransition(); List<Geofence> geofences = geofencingEvent.getTriggeringGeofences(); Geofence geofence = geofences.get(0); if (transaction == Geofence.GEOFENCE_TRANSITION_ENTER && geofence.getRequestId().equals(Constants.GEOFENCE_ID)) { Log.d(TAG, "You are inside Rlogical (Geofence Location)"); } else { Log.d(TAG, "You are outside Rlogical (Geofence Location)"); } // Fetch Entering / Exiting Detail String geofenceTransitionDetails = getGeofenceTrasitionDetails(transaction, geofences); sendNotification(geofenceTransitionDetails); } }}
private String getGeofenceTrasitionDetails(int geoFenceTransition, List<Geofence> triggeringGeofences) { // get the ID of each geofence triggered ArrayList<String> triggeringGeofencesList = new ArrayList<>(); for (Geofence geofence : triggeringGeofences) { triggeringGeofencesList.add(geofence.getRequestId()); } String status = null; if (geoFenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER) status = "Entering "; else if (geoFenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT) status = "Exiting "; return status + TextUtils.join(", ", triggeringGeofencesList); }
private void sendNotification(String msg) { // Intent to start the main Activity Intent notificationIntent = new Intent(this, MainActivity.class); TaskStackBuilder stackBuilder = TaskStackBuilder.create(this); stackBuilder.addParentStack(MainActivity.class); stackBuilder.addNextIntent(notificationIntent); PendingIntent notificationPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT); // Creating and sending Notification long when = Calendar.getInstance().getTimeInMillis(); NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); if (notificationManager == null) return; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // Configure the notification channel. NotificationChannel notifChannel = new NotificationChannel(msg, msg, NotificationManager.IMPORTANCE_DEFAULT); notifChannel.enableLights(true); notifChannel.setLightColor(Color.GREEN); notifChannel.setVibrationPattern(new long[]{0, 1000, 500, 1000}); notifChannel.enableVibration(true); notificationManager.createNotificationChannel(notifChannel); } NotificationCompat.Builder noBuilder = new NotificationCompat.Builder(this, "CH_ID") .setTicker(msg).setContentTitle(msg).setOngoing(false).setAutoCancel(true).setWhen(when) .setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)) .setVibrate(new long[]{0, 100, 100, 100, 100, 100}).setSmallIcon(R.mipmap.ic_launcher_round); notificationManager.notify((int) when, noBuilder.build()); }
Uses Permission
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.INTERNET" />
Inside Application tag:
<service android:name=".GeofenceService" android:enabled="true" android:exported="true" /> <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" /> <!-- The API key for Google Maps-based APIs. --> <meta-data android:name="com.google.android.geo.API_KEY" android:value="@string/YOUR_KEY_HERE" />
Rlogical Techsoft is a leading IT company known for web & mobile app development in India. We have expertise in providing app solutions based on Geo-location leveraging Geo-location based mobile app development and solutions.
Nikunj Sorathiya is Sr. Android Developer at Rlogical Techsoft Pvt. Ltd. He is passionate about Android and expert in building an innovative Android Application Development.