Search code examples
androidgpsandroid-mapsgeofencingandroid-geofence

Handling multiple geofences transition with common area


If I have these two geofences, after registering these geofences I should get notified when I'm entering or exiting the circumference of these circles. However, I don't want my App to send a notification if I'm moving through the common area, i.e. from once circle to another.

Is it possible? If so, then how?

Image of map with two circles intersecting


Solution

  • You will have to use a class for monitoring your fencing:

    public class GeofenceReceiver extends BroadcastReceiver {
        Context context;
    
        Intent broadcastIntent = new Intent();
    
        @Override
        public void onReceive(Context context, Intent intent) {
            this.context = context;
    
            broadcastIntent.addCategory(GeofenceUtils.CATEGORY_LOCATION_SERVICES);
    
            if (LocationClient.hasError(intent)) {
                handleError(intent);
            } else {
                handleEnterExit(intent);
            }
        }
    
        private void handleError(Intent intent){
            // Get the error code
            int errorCode = LocationClient.getErrorCode(intent);
    
            // Get the error message
            String errorMessage = LocationServiceErrorMessages.getErrorString(
                    context, errorCode);
    
            // Log the error
            Log.e(GeofenceUtils.APPTAG,
                    context.getString(R.string.geofence_transition_error_detail,
                            errorMessage));
    
            // Set the action and error message for the broadcast intent
            broadcastIntent
                    .setAction(GeofenceUtils.ACTION_GEOFENCE_ERROR)
                    .putExtra(GeofenceUtils.EXTRA_GEOFENCE_STATUS, errorMessage);
    
            // Broadcast the error *locally* to other components in this app
            LocalBroadcastManager.getInstance(context).sendBroadcast(
                    broadcastIntent);
        }
    
    
        private void handleEnterExit(Intent intent) {
            // Get the type of transition (entry or exit)
            int transition = LocationClient.getGeofenceTransition(intent);
    
            // Test that a valid transition was reported
            if ((transition == Geofence.GEOFENCE_TRANSITION_ENTER)
                    || (transition == Geofence.GEOFENCE_TRANSITION_EXIT)) {
    
                // Post a notification
                List<Geofence> geofences = LocationClient
                        .getTriggeringGeofences(intent);
                String[] geofenceIds = new String[geofences.size()];
                String ids = TextUtils.join(GeofenceUtils.GEOFENCE_ID_DELIMITER,
                        geofenceIds);
                String transitionType = GeofenceUtils
                        .getTransitionString(transition);
    
                for (int index = 0; index < geofences.size(); index++) {
                    Geofence geofence = geofences.get(index);
                    ...do something with the geofence entry or exit. I'm saving them to a local sqlite db
    
                }
                // Create an Intent to broadcast to the app
                broadcastIntent
                        .setAction(GeofenceUtils.ACTION_GEOFENCE_TRANSITION)
                        .addCategory(GeofenceUtils.CATEGORY_LOCATION_SERVICES)
                        .putExtra(GeofenceUtils.EXTRA_GEOFENCE_ID, geofenceIds)
                        .putExtra(GeofenceUtils.EXTRA_GEOFENCE_TRANSITION_TYPE,
                                transitionType);
    
                LocalBroadcastManager.getInstance(MyApplication.getContext())
                        .sendBroadcast(broadcastIntent);
    
                // Log the transition type and a message
                Log.d(GeofenceUtils.APPTAG, transitionType + ": " + ids);
                Log.d(GeofenceUtils.APPTAG,
                        context.getString(R.string.geofence_transition_notification_text));
    
                // In debug mode, log the result
                Log.d(GeofenceUtils.APPTAG, "transition");
    
                // An invalid transition was reported
            } else {
                // Always log as an error
                Log.e(GeofenceUtils.APPTAG,
                        context.getString(R.string.geofence_transition_invalid_type,
                                transition));
            }
        }
    
        //Posts a notification in the notification bar when a transition 
        private void sendNotification(String transitionType, String locationName) {
    
            // Create an explicit content Intent that starts the main Activity
            Intent notificationIntent = new Intent(context, MainActivity.class);
    
            // Construct a task stack
            TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);
    
            // Adds the main Activity to the task stack as the parent
            stackBuilder.addParentStack(MainActivity.class);
    
            // Push the content Intent onto the stack
            stackBuilder.addNextIntent(notificationIntent);
    
            // Get a PendingIntent containing the entire back stack
            PendingIntent notificationPendingIntent = stackBuilder
                    .getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
    
            // Get a notification builder that's compatible with platform versions
            // >= 4
            NotificationCompat.Builder builder = new NotificationCompat.Builder(
                    context);
    
            // Set the notification contents
            builder.setSmallIcon(R.drawable.ic_notification)
                    .setContentTitle(transitionType + ": " + locationName)
                    .setContentText(
                            context.getString(R.string.geofence_transition_notification_text))
                    .setContentIntent(notificationPendingIntent);
    
            // Get an instance of the Notification manager
            NotificationManager mNotificationManager = (NotificationManager) context
                    .getSystemService(Context.NOTIFICATION_SERVICE);
    
            // Issue the notification
            mNotificationManager.notify(0, builder.build());
        }
    

    You should create listeners for each one of the areas you wish to monitor, lets say listener1 and listener2. To optimize both areas and integrate it, the best approach is to make a grid using MongoDB, which in such case even allows you to integrate more than two points, as you build a grid.

    enter image description here

    Assuming that you're going to get out a polygon in the form of some Lat-Lon points, then you can generate a grid like the following:

    # Method to get the min and max values for the polygon  
    def get_bounding_box(coords)
    # get max and min coords
    max = coords.inject({lat:0, lon:0}) do |max, c|
    max[:lon] = c[0] if c[0] > max[:lon]
    max[:lat] = c[1] if c[1] > max[:lat]
    max
    end
    min = coords.inject({lat:MAX_LAT, lon:MAX_LON}) do |min, c|
    min[:lon] = c[0] if c[0] < min[:lon]
    min[:lat] = c[1] if c[1] < min[:lat]
    min
    end
    # add a little padding to the max and min
    max.each {|k, v| max[k] += 1 }
    min.each {|k, v| min[k] -= 1 }
    
    {min: min, max: max}
    end
    
    def generate_grid(bounds)
    lon_range = bounds[:min][:lon]...bounds[:max][:lon]
    lat_range = bounds[:min][:lat]...bounds[:max][:lat]
    
    grid = []
    lon_range.each do |lon|
    lat_range.each do |lat|
    grid << [lon + 0.25, lat + 0.25]
    grid << [lon + 0.25, lat + 0.75]
    grid << [lon + 0.75, lat + 0.25]
    grid << [lon + 0.75, lat + 0.75]
    end
    end
    
    grid
    end
    

    Such approach allows you to achieve very efficient geofencing with smart grids for monitoring target areas:

    enter image description here

    Most recently MongoDB also added support for Android, thus providing an easy way for your Android App back-end integration. In fact geofencing development with smart distributed data is expected to have a growing number of applications.