add geofencing

This commit is contained in:
Sem van der Hoeven
2021-01-12 13:22:00 +01:00
parent e286929b77
commit 5544da5d2e
8 changed files with 102 additions and 12 deletions

View File

@@ -43,6 +43,7 @@ dependencies {
implementation 'com.google.android.gms:play-services-location:17.1.0' implementation 'com.google.android.gms:play-services-location:17.1.0'
implementation 'com.google.android.gms:play-services-maps:17.0.0' implementation 'com.google.android.gms:play-services-maps:17.0.0'
testImplementation 'junit:junit:4.13.1' testImplementation 'junit:junit:4.13.1'
// implementation "com.android.support:support-compat:28.0.0"
// okhttp // okhttp
implementation 'com.squareup.okhttp3:okhttp:4.9.0' implementation 'com.squareup.okhttp3:okhttp:4.9.0'

View File

@@ -25,7 +25,11 @@
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<receiver android:name=".geofencing.GeoFenceBroadcastReceiver"/> <receiver android:name=".geofencing.GeoFenceBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.USER_PRESENT" />
</intent-filter>
</receiver>
</application> </application>
</manifest> </manifest>

View File

@@ -2,6 +2,8 @@ package com.a1.nextlocation.fragments;
import android.Manifest; import android.Manifest;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context; import android.content.Context;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
@@ -19,6 +21,7 @@ import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat; import androidx.core.app.ActivityCompat;
import androidx.core.app.NotificationCompat;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentActivity;
@@ -61,6 +64,8 @@ public class HomeFragment extends Fragment implements LocationListener {
private int color; private int color;
private Location currentLocation; private Location currentLocation;
private Overlay allLocationsOverlay; private Overlay allLocationsOverlay;
private GeofenceInitalizer initializer;
private final static String CHANNEL_ID = "next_location01";
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
@@ -143,6 +148,7 @@ public class HomeFragment extends Fragment implements LocationListener {
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
initializer = new GeofenceInitalizer(requireContext(),requireActivity());
initMap(view); initMap(view);
} }
@@ -249,6 +255,7 @@ public class HomeFragment extends Fragment implements LocationListener {
private void addLocations() { private void addLocations() {
// get the locations of the current route or all locations // get the locations of the current route or all locations
List<com.a1.nextlocation.data.Location> locations = RouteHandler.INSTANCE.isFollowingRoute() ? RouteHandler.INSTANCE.getCurrentRoute().getLocations() : LocationListManager.INSTANCE.getLocationList(); List<com.a1.nextlocation.data.Location> locations = RouteHandler.INSTANCE.isFollowingRoute() ? RouteHandler.INSTANCE.getCurrentRoute().getLocations() : LocationListManager.INSTANCE.getLocationList();
initializer.removeGeoFences();
final ArrayList<OverlayItem> items = new ArrayList<>(locations.size()); final ArrayList<OverlayItem> items = new ArrayList<>(locations.size());
// marker icon // marker icon
Drawable marker = ContextCompat.getDrawable(requireContext(), R.drawable.ic_baseline_location_on_24); Drawable marker = ContextCompat.getDrawable(requireContext(), R.drawable.ic_baseline_location_on_24);
@@ -312,7 +319,9 @@ public class HomeFragment extends Fragment implements LocationListener {
* @param locations the locations to add geofences for * @param locations the locations to add geofences for
*/ */
private void addGeofences(List<com.a1.nextlocation.data.Location> locations) { private void addGeofences(List<com.a1.nextlocation.data.Location> locations) {
GeofenceInitalizer initializer = new GeofenceInitalizer(requireContext());
Log.d(TAG, "addGeofences: adding geofences!");
initializer.init(locations); initializer.init(locations);
} }
@@ -382,9 +391,31 @@ public class HomeFragment extends Fragment implements LocationListener {
public void onLocationVisited(com.a1.nextlocation.data.Location location) { public void onLocationVisited(com.a1.nextlocation.data.Location location) {
Data.INSTANCE.visitLocation(location); Data.INSTANCE.visitLocation(location);
showNotification(location);
} }
private void showNotification(com.a1.nextlocation.data.Location location) {
NotificationManager mNotificationManager = (NotificationManager) requireActivity().getSystemService(Context.NOTIFICATION_SERVICE);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
int importance = NotificationManager.IMPORTANCE_LOW;
NotificationChannel notificationChannel = new NotificationChannel(CHANNEL_ID, "next_location", importance);
notificationChannel.enableLights(true);
notificationChannel.enableVibration(true);
notificationChannel.setVibrationPattern(new long[]{100, 200, 300, 400, 500, 400, 300, 200, 400});
mNotificationManager.createNotificationChannel(notificationChannel);
}
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(requireContext(),CHANNEL_ID)
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentTitle(getString(R.string.notification_title))
.setContentText(getString(R.string.notification_text,location.getName()))
.setAutoCancel(true);
mNotificationManager.notify(0,mBuilder.build());
}
// empty override methods for the LocationListener // empty override methods for the LocationListener
@Override @Override

View File

@@ -5,6 +5,7 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.util.Log; import android.util.Log;
import com.a1.nextlocation.data.Data;
import com.a1.nextlocation.data.Location; import com.a1.nextlocation.data.Location;
import com.a1.nextlocation.recyclerview.LocationListManager; import com.a1.nextlocation.recyclerview.LocationListManager;
import com.google.android.gms.location.Geofence; import com.google.android.gms.location.Geofence;
@@ -20,6 +21,7 @@ public class GeoFenceBroadcastReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent); GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
Log.i(TAG, "onReceive: RECEIVED GEOFENCE STUFF");
if (geofencingEvent.hasError()) { if (geofencingEvent.hasError()) {
String errorMessage = GeofenceStatusCodes String errorMessage = GeofenceStatusCodes
@@ -40,7 +42,8 @@ public class GeoFenceBroadcastReceiver extends BroadcastReceiver {
if (geofence.getRequestId().equals(l.getName())) { if (geofence.getRequestId().equals(l.getName())) {
l.setVisited(true); l.setVisited(true);
// let the homefragment know that we are close to a location // let the homefragment know that we are close to a location
Data.INSTANCE.getLocationProximityListener().onLocationVisited(l);
Log.d(TAG, "onReceive: VISITED LOCATION " + l.getName());
break; break;
} }
} }

View File

@@ -44,7 +44,7 @@ public class GeoFencingHelper extends ContextWrapper {
} }
Intent intent = new Intent(this, GeoFenceBroadcastReceiver.class); Intent intent = new Intent(this, GeoFenceBroadcastReceiver.class);
pendingIntent = PendingIntent.getBroadcast(this, 2607, intent, PendingIntent.FLAG_UPDATE_CURRENT); pendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
return pendingIntent; return pendingIntent;
} }

View File

@@ -1,12 +1,16 @@
package com.a1.nextlocation.geofencing; package com.a1.nextlocation.geofencing;
import android.Manifest; import android.Manifest;
import android.app.Activity;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.content.Context; import android.content.Context;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.os.Build;
import android.util.Log; import android.util.Log;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat; import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import com.a1.nextlocation.data.Data; import com.a1.nextlocation.data.Data;
import com.a1.nextlocation.data.Location; import com.a1.nextlocation.data.Location;
@@ -15,27 +19,53 @@ import com.google.android.gms.location.Geofence;
import com.google.android.gms.location.GeofencingClient; import com.google.android.gms.location.GeofencingClient;
import com.google.android.gms.location.GeofencingRequest; import com.google.android.gms.location.GeofencingRequest;
import com.google.android.gms.location.LocationServices; import com.google.android.gms.location.LocationServices;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import org.osmdroid.util.GeoPoint; import org.osmdroid.util.GeoPoint;
import java.util.List; import java.util.List;
import static android.content.ContentValues.TAG;
public class GeofenceInitalizer { public class GeofenceInitalizer {
private final Activity activity;
private GeofencingClient geofencingClient; private GeofencingClient geofencingClient;
private GeoFencingHelper geoFencingHelper; private GeoFencingHelper geoFencingHelper;
private final Context context; private final Context context;
private final String TAG = GeofenceInitalizer.class.getCanonicalName(); private final String TAG = GeofenceInitalizer.class.getCanonicalName();
private List<Location> locations; private List<Location> locations;
private int BACKGROUND_LOCATION_ACCESS_REQUEST_CODE = 10002;
public GeofenceInitalizer(Context context) { public GeofenceInitalizer(Context context, Activity activity) {
this.context = context; this.context = context;
this.activity = activity;
} }
public void init(List<Location> locations) { public void init(List<Location> locations) {
if (!checkFineLocationPermission()) return;
geofencingClient = LocationServices.getGeofencingClient(context); geofencingClient = LocationServices.getGeofencingClient(context);
geoFencingHelper = new GeoFencingHelper(context); geoFencingHelper = new GeoFencingHelper(context);
this.locations = locations; this.locations = locations;
addFences(); if (Build.VERSION.SDK_INT >= 29) {
//If API is higher then 29 we need background permission
if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_BACKGROUND_LOCATION) == PackageManager.PERMISSION_GRANTED) {
addFences();
} else {
//Permission is not granted!! Need to request it..
if (ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.ACCESS_BACKGROUND_LOCATION)) {
//We show a dialog and ask for permission
ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.ACCESS_BACKGROUND_LOCATION}, BACKGROUND_LOCATION_ACCESS_REQUEST_CODE);
} else {
ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.ACCESS_BACKGROUND_LOCATION}, BACKGROUND_LOCATION_ACCESS_REQUEST_CODE);
}
}
} else {
addFences();
}
} }
private void addFences() { private void addFences() {
@@ -45,18 +75,35 @@ public class GeofenceInitalizer {
} }
} }
public void removeGeoFences() {
geofencingClient = LocationServices.getGeofencingClient(context);
geoFencingHelper = new GeoFencingHelper(context);
PendingIntent pendingIntent = geoFencingHelper.getPendingIntent();
geofencingClient.removeGeofences(pendingIntent)
.addOnSuccessListener(aVoid -> Log.d(TAG, "Geofence is removed... "))
.addOnFailureListener(e -> Log.d(TAG, e.getLocalizedMessage()));
if (this.locations != null) this.locations.clear();
}
private void addGeofence(GeoPoint p, String name) { private void addGeofence(GeoPoint p, String name) {
Geofence geofence = geoFencingHelper.getGeofence(name, p, 30); if (!checkFineLocationPermission()) return;
Geofence geofence = geoFencingHelper.getGeofence(name, p, 45);
GeofencingRequest geofencingRequest = geoFencingHelper.getGeoFencingRequest(geofence); GeofencingRequest geofencingRequest = geoFencingHelper.getGeoFencingRequest(geofence);
PendingIntent pendingIntent = geoFencingHelper.getPendingIntent(); PendingIntent pendingIntent = geoFencingHelper.getPendingIntent();
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
return;
}
geofencingClient.addGeofences(geofencingRequest, pendingIntent).addOnSuccessListener(v -> { geofencingClient.addGeofences(geofencingRequest, pendingIntent).addOnSuccessListener(v -> {
Log.d(TAG, "addGeofence: added geofence"); Log.i(TAG, "addGeofence: added geofence");
}).addOnFailureListener(v -> { }).addOnFailureListener(v -> {
Log.e(TAG, "addGeofence: failure adding geofence " + v.getMessage());
}); });
Log.i(TAG, "addGeofence: added geofence to client");
}
private boolean checkFineLocationPermission() {
return ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED;
} }
} }

View File

@@ -26,4 +26,6 @@
<string name="Chinese">Chinees</string> <string name="Chinese">Chinees</string>
<string name="help">HELP</string> <string name="help">HELP</string>
<string name="help_discription">Onderaan het scherm zijn verschillende knoppen te zien. Deze knoppen hebben de volgende functies: \n\nLocaties: toont een lijst met alle locaties die bezocht kunnen worden. Elke locatie wordt kort beschreven. \n\nRoutes: Toont een lijst met alle routes die gelopen kunnen worden. Van elke route wordt een omschrijving gegeven. \n\nStatistieken: Toont persoonlijke statistieken. \n\nInstellingen: Hier kunnen app-instellingen worden aangepast naar eigen voorkeur. \n\nEen locatie ingedrukt houden laat extra informatie zien over de gekozen locatie</string> <string name="help_discription">Onderaan het scherm zijn verschillende knoppen te zien. Deze knoppen hebben de volgende functies: \n\nLocaties: toont een lijst met alle locaties die bezocht kunnen worden. Elke locatie wordt kort beschreven. \n\nRoutes: Toont een lijst met alle routes die gelopen kunnen worden. Van elke route wordt een omschrijving gegeven. \n\nStatistieken: Toont persoonlijke statistieken. \n\nInstellingen: Hier kunnen app-instellingen worden aangepast naar eigen voorkeur. \n\nEen locatie ingedrukt houden laat extra informatie zien over de gekozen locatie</string>
<string name="notification_title">Je bent dicht bij een locatie!</string>
<string name="notification_text">Je bent bijna bij %1$s</string>
</resources> </resources>

View File

@@ -25,4 +25,6 @@
<string name="Chinese">Chinese</string> <string name="Chinese">Chinese</string>
<string name="help">HELP</string> <string name="help">HELP</string>
<string name="help_discription">Hasn\'t been translated yet</string> <string name="help_discription">Hasn\'t been translated yet</string>
<string name="notification_title">You\'re close to a location!</string>
<string name="notification_text">You\'re almost at %1$s</string>
</resources> </resources>