From e286929b77ad317f17e82f17b3435b054b88fddf Mon Sep 17 00:00:00 2001 From: Sem van der Hoeven Date: Tue, 12 Jan 2021 11:12:28 +0100 Subject: [PATCH] added geofencing stuff --- app/build.gradle | 1 + app/src/main/AndroidManifest.xml | 1 + .../java/com/a1/nextlocation/data/Data.java | 14 +++++ .../com/a1/nextlocation/data/Location.java | 10 +++ .../nextlocation/fragments/HomeFragment.java | 20 ++++++ .../geofencing/GeoFenceBroadcastReceiver.java | 55 ++++++++++++++++ .../geofencing/GeoFencingHelper.java | 51 +++++++++++++++ .../geofencing/GeofenceInitalizer.java | 62 +++++++++++++++++++ 8 files changed, 214 insertions(+) create mode 100644 app/src/main/java/com/a1/nextlocation/geofencing/GeoFenceBroadcastReceiver.java create mode 100644 app/src/main/java/com/a1/nextlocation/geofencing/GeoFencingHelper.java create mode 100644 app/src/main/java/com/a1/nextlocation/geofencing/GeofenceInitalizer.java 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 -> { + + }); + } +}