0

I have some days dealing with Geofence in android and haven't found a way to make it work.

As for now i have:

GeofenceBroadcastReceiver

public class GeofenceBroadcastReceiver extends BroadcastReceiver {
    private static final String TAG = "GeofenceBroadcastReceiver";
    private static final String CHANNEL_ID = "GeofenceChannel";

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d(TAG, "Received intent: " + intent);
        Log.d(TAG, "Received intent action: " + intent.getAction());
        Log.d(TAG, "Received intent extras: " + intent.getExtras());


        GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);

        if (geofencingEvent == null) {
            Log.e(TAG, "Invalid geofencing event");
            return;
        }

        if (geofencingEvent.hasError()) {
            Log.e(TAG, "Geofencing error: " + geofencingEvent.getErrorCode());
            return;
        }

        int transition = geofencingEvent.getGeofenceTransition();
        List<Geofence> triggeringGeofences = geofencingEvent.getTriggeringGeofences();

        switch (transition) {
            case Geofence.GEOFENCE_TRANSITION_ENTER:
                for (Geofence geofence : triggeringGeofences) {
                    showNotification(context, "Entered zone: " + geofence.getRequestId());
                }
                break;
            case Geofence.GEOFENCE_TRANSITION_EXIT:
                // Handle the parent geofence exit event
                if (triggeringGeofences.get(0).getRequestId().equals("PARENT_GEOFENCE")) {
                    GeofenceManager geofenceManager = new GeofenceManager(context);
                    geofenceManager.removeAllGeofences();

                    // Send a local broadcast to notify MainActivity
                    Intent parentExitIntent = new Intent("com.tmc.safetynet.PARENT_GEOFENCE_EXIT");
                    LocalBroadcastManager.getInstance(context).sendBroadcast(parentExitIntent);
                }
                break;
        }
    }

    private void showNotification(Context context, String message) {
        createNotificationChannel(context);

        NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID)
                .setSmallIcon(android.R.drawable.ic_dialog_info)
                .setContentTitle("Geofence Alert")
                .setContentText(message)
                .setPriority(NotificationCompat.PRIORITY_HIGH);

        NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
        notificationManager.notify((int) System.currentTimeMillis(), builder.build());
    }

    private void createNotificationChannel(Context context) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            CharSequence name = "Geofence Channel";
            String description = "Channel for geofence notifications";
            int importance = NotificationManager.IMPORTANCE_HIGH;
            NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);
            channel.setDescription(description);

            NotificationManager notificationManager = context.getSystemService(NotificationManager.class);
            notificationManager.createNotificationChannel(channel);
        }
    }
}

GeofenceManager

public class GeofenceManager implements OnCompleteListener<Void> {
    private static final String TAG = "GeofenceManager";
    private static final float PARENT_RADIUS = 50000; // 60 KM
    private static final float CHILD_RADIUS = 10000; // 5 KM
    private static final int LOCATION_PERMISSION_REQUEST_CODE = 1000;

    private Context context;
    private GeofencingClient geofencingClient;
    private PendingIntent geofencePendingIntent;
    private List<Geofence> childGeofences;
    private String tag;

    public GeofenceManager(Context context) {
        this.context = context;
        this.geofencingClient = LocationServices.getGeofencingClient(context);
        this.childGeofences = new ArrayList<>();
    }

    public void setupParentGeofence(Location currentLocation, String tag) {
        this.tag = tag;

        ActivityCompat.requestPermissions((Activity) context, new String[]{Manifest.permission.ACCESS_FINE_LOCATION,
                Manifest.permission.ACCESS_BACKGROUND_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION}, LOCATION_PERMISSION_REQUEST_CODE);
        if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED ||
                ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_BACKGROUND_LOCATION) != PackageManager.PERMISSION_GRANTED ||
                ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            Log.e(TAG, "Location permission not granted.");
            return;
        }

        Geofence parentGeofence = new Geofence.Builder()
                .setRequestId("PARENT_GEOFENCE")
                .setCircularRegion(currentLocation.getLatitude(), currentLocation.getLongitude(), PARENT_RADIUS)
                .setExpirationDuration(Geofence.NEVER_EXPIRE)
                .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_EXIT)
                .build();

        GeofencingRequest geofencingRequest = new GeofencingRequest.Builder()
                .setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER)
                .addGeofence(parentGeofence)
                .build();

        geofencingClient.addGeofences(geofencingRequest, getGeofencePendingIntent()).addOnCompleteListener(this);
        //intent.putExtra("com.google.android.gms.location.GEOFENCE_EXTRA", geofencingRequest);


    }

    public void setupChildGeofences(List<Location> filteredLocations, String tag) {
        this.tag = tag;
        Log.d("GeofenceManager", "Filtered locations count: " + filteredLocations.size());

        for (Location location : filteredLocations) {

            Geofence childGeofence = new Geofence.Builder()
                    .setRequestId(location.getLatitude() + "," + location.getLongitude())
                    .setCircularRegion(location.getLatitude(), location.getLongitude(), CHILD_RADIUS)
                    .setExpirationDuration(Geofence.NEVER_EXPIRE)
                    .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER)
                    .build();
            childGeofences.add(childGeofence);
        }

        GeofencingRequest geofencingRequest = new GeofencingRequest.Builder()
                .setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER)
                .addGeofences(childGeofences)
                .build();

        if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            return;
        }
        geofencingClient.addGeofences(geofencingRequest, getGeofencePendingIntent()).addOnCompleteListener(this);
    }

    private PendingIntent getGeofencePendingIntent() {

        if (geofencePendingIntent != null) {
            return geofencePendingIntent;
        }

        Intent intent = new Intent(context, GeofenceBroadcastReceiver.class);
        intent.setAction("com.tmc.safetynet.ACTION_GEOFENCE_EVENT");

        // Add FLAG_IMMUTABLE for Android S (API level 31) and above
        int flags = PendingIntent.FLAG_UPDATE_CURRENT;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            flags |= PendingIntent.FLAG_IMMUTABLE;
        }

        geofencePendingIntent = PendingIntent.getBroadcast(context, 0, intent, flags);

        return geofencePendingIntent;
    }

    public void removeAllGeofences() {
        geofencingClient.removeGeofences(getGeofencePendingIntent()).addOnCompleteListener(this);
    }

    @Override
    public void onComplete(@NonNull Task<Void> task) {
        if (task.isSuccessful()) {
            Log.d("GeofenceManager", "Geofences " + tag + " added/removed successfully.");
        } else {
            Log.e("GeofenceManager", "Failed to add/remove " + tag + " geofences.", task.getException());
        }
    }

}

MyActivity

@Override
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_geo_fence)

geofenceManager = new GeofenceManager(this);
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
setupParentGeofence();
}

    private void setupParentGeofence() {
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
                ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_BACKGROUND_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_BACKGROUND_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION}, LOCATION_PERMISSION_REQUEST_CODE);
        }

        fusedLocationClient.getLastLocation().addOnCompleteListener(new OnCompleteListener<Location>() {
            @Override
            public void onComplete(@NonNull Task<Location> task) {
                if (task.isSuccessful() && task.getResult() != null) {
                    Location currentLocation = task.getResult();
                    geofenceManager.setupParentGeofence(currentLocation, "Parent");
                    fetchAndFilterLocations(currentLocation);

                } else {
                    Toast.makeText(GeoFenceActivity.this, "Failed to get current location", Toast.LENGTH_SHORT).show();
                }
            }
        });
    }

  private BroadcastReceiver parentGeofenceExitReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            setupParentGeofence();
        }
    };

private void fetchAndFilterLocations(Location currentLocation) {
        DatabaseReference polygonsRef = FirebaseDatabase.getInstance().getReference("xxxx");

        polygonsRef.addListenerForSingleValueEvent(new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
                List<Location> filteredLocations = new ArrayList<>();

                for (DataSnapshot countrySnapshot : dataSnapshot.getChildren()) {
                    for (DataSnapshot provinceSnapshot : countrySnapshot.getChildren()) {
                        for (DataSnapshot districtSnapshot : provinceSnapshot.getChildren()) {

                            List<Double> latitudes = new ArrayList<>();
                            List<Double> longitudes = new ArrayList<>();

                            for (DataSnapshot zoneSnapshot : districtSnapshot.getChildren()) {
                                double lat = Double.parseDouble(Objects.requireNonNull(zoneSnapshot.child("lat").getValue(String.class)));
                                double lng = Double.parseDouble(Objects.requireNonNull(zoneSnapshot.child("long").getValue(String.class)));

                                latitudes.add(lat);
                                longitudes.add(lng);
                            }
                            // Calculate the center point of the polygon
                            double centerLat = average(latitudes);
                            double centerLng = average(longitudes);
                            Location centerLocation = new Location("");
                            centerLocation.setLatitude(centerLat);
                            centerLocation.setLongitude(centerLng);
                            float distance = currentLocation.distanceTo(centerLocation);
                            if (distance <= PARENT_RADIUS) {
                                filteredLocations.add(centerLocation);
                            }
                        }
                    }
                }
                geofenceManager.setupChildGeofences(filteredLocations, "child");
            }

            @Override
            public void onCancelled(@NonNull DatabaseError databaseError) {
                Log.e(TAG, "Failed to read data from polygons table", databaseError.toException());
            }
        });
    }

Manifest

<receiver android:name=".GeofenceBroadcastReceiver"/>

But each time i run the code i get in console:

D/GeofenceManager: Filtered locations count: 56 D/GeofenceManager: Geofences child added/removed successfully.

D/GeofenceBroadcastReceiver: Received intent: Intent { act=com.tmc.safetynet.ACTION_GEOFENCE_EVENT flg=0x10 cmp=com.tmc.safetynet/.GeofenceBroadcastReceiver } D/GeofenceBroadcastReceiver: Received intent action: com.tmc.safetynet.ACTION_GEOFENCE_EVENT D/GeofenceBroadcastReceiver: Received intent extras: null E/GeofenceBroadcastReceiver: Invalid geofencing event

And my geofences doesn't appear in the map. The code that throws Invalid geofencing event is located inside GeofenceBroadcastReceiver

 @Override
    public void onReceive(Context context, Intent intent) {
        Log.d(TAG, "Received intent: " + intent);
        Log.d(TAG, "Received intent action: " + intent.getAction());
        Log.d(TAG, "Received intent extras: " + intent.getExtras());


        GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);

        if (geofencingEvent == null) {
            Log.e(TAG, "Invalid geofencing event");
            return;
        }

Any idea?

Best regards

Jason
  • 85
  • 1
  • 1
  • 7

0 Answers0