diff --git a/app/build.gradle b/app/build.gradle
index 8bcfb89..34bae3b 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -40,6 +40,7 @@ dependencies {
implementation 'com.google.android.material:material:1.2.1'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
+ implementation 'com.google.android.gms:play-services-location:17.1.0'
implementation 'com.google.android.gms:play-services-maps:17.0.0'
testImplementation 'junit:junit:4.13.1'
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 1d0c446..fb7b89b 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -25,6 +25,7 @@
+
\ No newline at end of file
diff --git a/app/src/main/java/com/a1/nextlocation/data/Data.java b/app/src/main/java/com/a1/nextlocation/data/Data.java
index 73e37b7..182090e 100644
--- a/app/src/main/java/com/a1/nextlocation/data/Data.java
+++ b/app/src/main/java/com/a1/nextlocation/data/Data.java
@@ -11,6 +11,15 @@ public enum Data {
private int locationsVisited = 0;
private long totalTime = 0;
private double zoom = 0;
+ private LocationProximityListener locationProximityListener;
+
+ public LocationProximityListener getLocationProximityListener() {
+ return locationProximityListener;
+ }
+
+ public void setLocationProximityListener(LocationProximityListener locationProximityListener) {
+ this.locationProximityListener = locationProximityListener;
+ }
public double getZoom() {
return zoom;
@@ -50,5 +59,10 @@ public enum Data {
return locationsVisited;
}
+ @FunctionalInterface
+ public interface LocationProximityListener {
+ void onLocationVisited(Location location);
+ }
+
}
diff --git a/app/src/main/java/com/a1/nextlocation/data/Location.java b/app/src/main/java/com/a1/nextlocation/data/Location.java
index 86785cc..ae99308 100644
--- a/app/src/main/java/com/a1/nextlocation/data/Location.java
+++ b/app/src/main/java/com/a1/nextlocation/data/Location.java
@@ -25,6 +25,8 @@ public class Location implements Parcelable {
private String imageUrl;
private String iconUrl;
+ private boolean visited;
+
public Location(@NotNull String name, String coordinates, String description, @Nullable String imageUrl) {
this.name = name;
this.coordinates = coordinates;
@@ -171,4 +173,12 @@ public class Location implements Parcelable {
parcel.writeString(description);
parcel.writeString(imageUrl);
}
+
+ public boolean isVisited() {
+ return visited;
+ }
+
+ public void setVisited(boolean visited) {
+ this.visited = visited;
+ }
}
diff --git a/app/src/main/java/com/a1/nextlocation/fragments/HomeFragment.java b/app/src/main/java/com/a1/nextlocation/fragments/HomeFragment.java
index cf442a1..27970a4 100644
--- a/app/src/main/java/com/a1/nextlocation/fragments/HomeFragment.java
+++ b/app/src/main/java/com/a1/nextlocation/fragments/HomeFragment.java
@@ -26,6 +26,7 @@ import androidx.fragment.app.FragmentActivity;
import com.a1.nextlocation.R;
import com.a1.nextlocation.data.Data;
import com.a1.nextlocation.data.RouteHandler;
+import com.a1.nextlocation.geofencing.GeofenceInitalizer;
import com.a1.nextlocation.json.DirectionsResult;
import com.a1.nextlocation.network.ApiHandler;
import com.a1.nextlocation.recyclerview.LocationListManager;
@@ -71,6 +72,7 @@ public class HomeFragment extends Fragment implements LocationListener {
Manifest.permission.WRITE_EXTERNAL_STORAGE);
color = requireContext().getColor(R.color.red);
+ Data.INSTANCE.setLocationProximityListener(this::onLocationVisited);
}
@Override
@@ -217,6 +219,8 @@ public class HomeFragment extends Fragment implements LocationListener {
}
+
+
/**
* displays the route that is currently being followed as a red line
*/
@@ -299,6 +303,17 @@ public class HomeFragment extends Fragment implements LocationListener {
mapView.getOverlays().add(allLocationsOverlay);
Log.d(TAG, "addLocations: successfully added locations");
+ addGeofences(locations);
+
+ }
+
+ /**
+ * adds the geofences for the currently active locations
+ * @param locations the locations to add geofences for
+ */
+ private void addGeofences(List locations) {
+ GeofenceInitalizer initializer = new GeofenceInitalizer(requireContext());
+ initializer.init(locations);
}
/**
@@ -365,6 +380,11 @@ public class HomeFragment extends Fragment implements LocationListener {
}
+ public void onLocationVisited(com.a1.nextlocation.data.Location location) {
+ Data.INSTANCE.visitLocation(location);
+
+ }
+
// empty override methods for the LocationListener
@Override
diff --git a/app/src/main/java/com/a1/nextlocation/geofencing/GeoFenceBroadcastReceiver.java b/app/src/main/java/com/a1/nextlocation/geofencing/GeoFenceBroadcastReceiver.java
new file mode 100644
index 0000000..37c49a4
--- /dev/null
+++ b/app/src/main/java/com/a1/nextlocation/geofencing/GeoFenceBroadcastReceiver.java
@@ -0,0 +1,55 @@
+package com.a1.nextlocation.geofencing;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+import com.a1.nextlocation.data.Location;
+import com.a1.nextlocation.recyclerview.LocationListManager;
+import com.google.android.gms.location.Geofence;
+import com.google.android.gms.location.GeofenceStatusCodes;
+import com.google.android.gms.location.GeofencingEvent;
+
+import java.util.List;
+
+public class GeoFenceBroadcastReceiver extends BroadcastReceiver {
+ private final String TAG = GeoFenceBroadcastReceiver.class.getCanonicalName();
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+
+ GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
+
+ if (geofencingEvent.hasError()) {
+ String errorMessage = GeofenceStatusCodes
+ .getStatusCodeString(geofencingEvent.getErrorCode());
+ Log.e(TAG, errorMessage);
+ return;
+ }
+
+ // Get the transition type.
+ int geofenceTransition = geofencingEvent.getGeofenceTransition();
+
+ switch (geofenceTransition) {
+ case Geofence.GEOFENCE_TRANSITION_ENTER:
+ List geofenceList = geofencingEvent.getTriggeringGeofences();
+ // loop through list of geofences
+ for (Geofence geofence : geofenceList) {
+ for (Location l : LocationListManager.INSTANCE.getLocationList()) {
+ if (geofence.getRequestId().equals(l.getName())) {
+ l.setVisited(true);
+ // let the homefragment know that we are close to a location
+
+ break;
+ }
+ }
+ }
+
+ break;
+ case Geofence.GEOFENCE_TRANSITION_EXIT:
+ Log.d(TAG, "onReceive: exiting geofence...");
+ break;
+ }
+ }
+}
diff --git a/app/src/main/java/com/a1/nextlocation/geofencing/GeoFencingHelper.java b/app/src/main/java/com/a1/nextlocation/geofencing/GeoFencingHelper.java
new file mode 100644
index 0000000..dd9e8d4
--- /dev/null
+++ b/app/src/main/java/com/a1/nextlocation/geofencing/GeoFencingHelper.java
@@ -0,0 +1,51 @@
+package com.a1.nextlocation.geofencing;
+
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.provider.SyncStateContract;
+
+import com.google.android.gms.location.Geofence;
+import com.google.android.gms.location.GeofencingRequest;
+
+import org.osmdroid.util.GeoPoint;
+
+public class GeoFencingHelper extends ContextWrapper {
+ private PendingIntent pendingIntent;
+
+ public GeoFencingHelper(Context base) {
+ super(base);
+ }
+
+ public GeofencingRequest getGeoFencingRequest(Geofence geofence) {
+ GeofencingRequest.Builder builder = new GeofencingRequest.Builder();
+ builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER);
+ builder.addGeofence(geofence);
+ return builder.build();
+
+ }
+
+ public Geofence getGeofence(String ID, GeoPoint point, float radius) {
+
+ return new Geofence.Builder()
+ .setCircularRegion(point.getLatitude(), point.getLongitude(), radius)
+ .setRequestId(ID)
+ .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER |
+ Geofence.GEOFENCE_TRANSITION_EXIT)
+ .setLoiteringDelay(5000)
+ .setExpirationDuration(Geofence.NEVER_EXPIRE)
+ .build();
+ }
+
+ public PendingIntent getPendingIntent() {
+ if (pendingIntent != null) {
+ return pendingIntent;
+ }
+
+ Intent intent = new Intent(this, GeoFenceBroadcastReceiver.class);
+ pendingIntent = PendingIntent.getBroadcast(this, 2607, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+
+ return pendingIntent;
+ }
+}
diff --git a/app/src/main/java/com/a1/nextlocation/geofencing/GeofenceInitalizer.java b/app/src/main/java/com/a1/nextlocation/geofencing/GeofenceInitalizer.java
new file mode 100644
index 0000000..874c6b8
--- /dev/null
+++ b/app/src/main/java/com/a1/nextlocation/geofencing/GeofenceInitalizer.java
@@ -0,0 +1,62 @@
+package com.a1.nextlocation.geofencing;
+
+import android.Manifest;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.util.Log;
+
+import androidx.core.app.ActivityCompat;
+
+import com.a1.nextlocation.data.Data;
+import com.a1.nextlocation.data.Location;
+import com.a1.nextlocation.recyclerview.LocationListManager;
+import com.google.android.gms.location.Geofence;
+import com.google.android.gms.location.GeofencingClient;
+import com.google.android.gms.location.GeofencingRequest;
+import com.google.android.gms.location.LocationServices;
+
+import org.osmdroid.util.GeoPoint;
+
+import java.util.List;
+
+public class GeofenceInitalizer {
+ private GeofencingClient geofencingClient;
+ private GeoFencingHelper geoFencingHelper;
+ private final Context context;
+ private final String TAG = GeofenceInitalizer.class.getCanonicalName();
+ private List locations;
+
+ public GeofenceInitalizer(Context context) {
+ this.context = context;
+ }
+
+ public void init(List locations) {
+ geofencingClient = LocationServices.getGeofencingClient(context);
+ geoFencingHelper = new GeoFencingHelper(context);
+ this.locations = locations;
+ addFences();
+ }
+
+ private void addFences() {
+ for (Location location : locations) {
+ GeoPoint t = new GeoPoint(location.getLat(), location.getLong());
+ addGeofence(t, location.getName());
+ }
+ }
+
+ private void addGeofence(GeoPoint p, String name) {
+ Geofence geofence = geoFencingHelper.getGeofence(name, p, 30);
+ GeofencingRequest geofencingRequest = geoFencingHelper.getGeoFencingRequest(geofence);
+ PendingIntent pendingIntent = geoFencingHelper.getPendingIntent();
+
+ if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+ geofencingClient.addGeofences(geofencingRequest, pendingIntent).addOnSuccessListener(v -> {
+ Log.d(TAG, "addGeofence: added geofence");
+ }).addOnFailureListener(v -> {
+
+ });
+ }
+}