@capgo/background-geolocation 8.0.31 → 8.0.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +297 -13
- package/android/build.gradle +2 -0
- package/android/src/main/AndroidManifest.xml +17 -0
- package/android/src/main/java/com/capgo/capacitor_background_geolocation/BackgroundGeolocation.java +281 -2
- package/android/src/main/java/com/capgo/capacitor_background_geolocation/GeofenceBootReceiver.java +58 -0
- package/android/src/main/java/com/capgo/capacitor_background_geolocation/GeofenceBroadcastReceiver.java +83 -0
- package/android/src/main/java/com/capgo/capacitor_background_geolocation/GeofenceStore.java +255 -0
- package/android/src/main/java/com/capgo/capacitor_background_geolocation/GeofenceTransitionWorker.java +35 -0
- package/dist/docs.json +740 -4
- package/dist/esm/definitions.d.ts +317 -0
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.d.ts +17 -1
- package/dist/esm/web.js +108 -0
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +108 -0
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +108 -0
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/CapgoBackgroundGeolocationPlugin/CapgoCapacitorBackgroundGeolocationPlugin.swift +425 -4
- package/ios/Tests/CapgoBackgroundGeolocationPluginTests/CapgoBackgroundGeolocationPluginTests.swift +5 -0
- package/package.json +17 -10
package/android/src/main/java/com/capgo/capacitor_background_geolocation/BackgroundGeolocation.java
CHANGED
|
@@ -9,13 +9,14 @@ import android.content.Context;
|
|
|
9
9
|
import android.content.Intent;
|
|
10
10
|
import android.content.IntentFilter;
|
|
11
11
|
import android.content.ServiceConnection;
|
|
12
|
+
import android.content.pm.PackageManager;
|
|
12
13
|
import android.location.Location;
|
|
13
14
|
import android.location.LocationManager;
|
|
14
15
|
import android.net.Uri;
|
|
15
16
|
import android.os.Build;
|
|
16
17
|
import android.os.IBinder;
|
|
17
18
|
import android.provider.Settings;
|
|
18
|
-
import androidx.
|
|
19
|
+
import androidx.core.content.ContextCompat;
|
|
19
20
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
|
20
21
|
import com.getcapacitor.JSArray;
|
|
21
22
|
import com.getcapacitor.JSObject;
|
|
@@ -27,7 +28,13 @@ import com.getcapacitor.PluginMethod;
|
|
|
27
28
|
import com.getcapacitor.annotation.CapacitorPlugin;
|
|
28
29
|
import com.getcapacitor.annotation.Permission;
|
|
29
30
|
import com.getcapacitor.annotation.PermissionCallback;
|
|
31
|
+
import com.google.android.gms.location.Geofence;
|
|
32
|
+
import com.google.android.gms.location.GeofencingClient;
|
|
33
|
+
import com.google.android.gms.location.GeofencingRequest;
|
|
30
34
|
import com.google.android.gms.location.LocationServices;
|
|
35
|
+
import java.net.URL;
|
|
36
|
+
import java.util.Collections;
|
|
37
|
+
import java.util.Set;
|
|
31
38
|
import java.util.concurrent.CompletableFuture;
|
|
32
39
|
import org.json.JSONArray;
|
|
33
40
|
import org.json.JSONException;
|
|
@@ -37,6 +44,7 @@ import org.json.JSONObject;
|
|
|
37
44
|
name = "BackgroundGeolocation",
|
|
38
45
|
permissions = {
|
|
39
46
|
@Permission(strings = { Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION }, alias = "location"),
|
|
47
|
+
@Permission(strings = { Manifest.permission.ACCESS_BACKGROUND_LOCATION }, alias = "backgroundLocation"),
|
|
40
48
|
@Permission(strings = { Manifest.permission.POST_NOTIFICATIONS }, alias = "notification")
|
|
41
49
|
}
|
|
42
50
|
)
|
|
@@ -46,6 +54,9 @@ public class BackgroundGeolocation extends Plugin {
|
|
|
46
54
|
|
|
47
55
|
private CompletableFuture<BackgroundGeolocationService.LocalBinder> serviceConnectionFuture;
|
|
48
56
|
private CompletableFuture<Void> locationPermissionFuture;
|
|
57
|
+
private CompletableFuture<Void> geofencePermissionFuture;
|
|
58
|
+
private BroadcastReceiver serviceReceiver;
|
|
59
|
+
private BroadcastReceiver geofenceEventReceiver;
|
|
49
60
|
|
|
50
61
|
private void fetchLastLocation(PluginCall call) {
|
|
51
62
|
try {
|
|
@@ -199,6 +210,236 @@ public class BackgroundGeolocation extends Plugin {
|
|
|
199
210
|
}
|
|
200
211
|
}
|
|
201
212
|
|
|
213
|
+
@PluginMethod
|
|
214
|
+
public void setupGeofencing(PluginCall call) {
|
|
215
|
+
String url = call.getString("url");
|
|
216
|
+
if (url != null && !url.isEmpty()) {
|
|
217
|
+
try {
|
|
218
|
+
URL urlObject = new URL(url);
|
|
219
|
+
String protocol = urlObject.getProtocol();
|
|
220
|
+
if (!"http".equalsIgnoreCase(protocol) && !"https".equalsIgnoreCase(protocol)) {
|
|
221
|
+
call.reject("Given url is not valid");
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
} catch (Exception exception) {
|
|
225
|
+
call.reject("Given url is not valid");
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
JSObject payload = call.getObject("payload", new JSObject());
|
|
231
|
+
GeofenceStore.saveSetup(getContext(), url, call.getBoolean("notifyOnEntry", true), call.getBoolean("notifyOnExit", true), payload);
|
|
232
|
+
|
|
233
|
+
if (!call.getBoolean("requestPermissions", true)) {
|
|
234
|
+
call.resolve();
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
requestGeofencePermissions(call)
|
|
239
|
+
.thenRun(call::resolve)
|
|
240
|
+
.exceptionally((throwable) -> {
|
|
241
|
+
call.reject("Background location permission is required for geofencing", "NOT_AUTHORIZED");
|
|
242
|
+
return null;
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
@PluginMethod
|
|
247
|
+
public void addGeofence(PluginCall call) {
|
|
248
|
+
if (!hasGeofencePermissions()) {
|
|
249
|
+
call.reject("Background location permission is required for geofencing", "NOT_AUTHORIZED");
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
if (!isLocationEnabled(getContext())) {
|
|
253
|
+
call.reject("Location services disabled.", "NOT_AUTHORIZED");
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
Double latitude = call.getDouble("latitude");
|
|
258
|
+
Double longitude = call.getDouble("longitude");
|
|
259
|
+
String identifier = call.getString("identifier");
|
|
260
|
+
double radius = call.getDouble("radius", 50.0);
|
|
261
|
+
if (identifier == null || identifier.isEmpty()) {
|
|
262
|
+
call.reject("Identifier is required");
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
if (latitude == null || latitude < -90 || latitude > 90) {
|
|
266
|
+
call.reject("Latitude must be between -90 and 90");
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
if (longitude == null || longitude < -180 || longitude > 180) {
|
|
270
|
+
call.reject("Longitude must be between -180 and 180");
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
if (radius <= 0) {
|
|
274
|
+
call.reject("Radius must be greater than 0");
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
boolean notifyOnEntry = call.getBoolean("notifyOnEntry", GeofenceStore.getNotifyOnEntry(getContext()));
|
|
279
|
+
boolean notifyOnExit = call.getBoolean("notifyOnExit", GeofenceStore.getNotifyOnExit(getContext()));
|
|
280
|
+
int transitionTypes = 0;
|
|
281
|
+
int initialTrigger = 0;
|
|
282
|
+
if (notifyOnEntry) {
|
|
283
|
+
transitionTypes |= Geofence.GEOFENCE_TRANSITION_ENTER;
|
|
284
|
+
initialTrigger |= GeofencingRequest.INITIAL_TRIGGER_ENTER;
|
|
285
|
+
}
|
|
286
|
+
if (notifyOnExit) {
|
|
287
|
+
transitionTypes |= Geofence.GEOFENCE_TRANSITION_EXIT;
|
|
288
|
+
}
|
|
289
|
+
if (transitionTypes == 0) {
|
|
290
|
+
call.reject("At least one transition must be enabled");
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
JSObject payload = call.getObject("payload", new JSObject());
|
|
295
|
+
Geofence geofence = new Geofence.Builder()
|
|
296
|
+
.setRequestId(identifier)
|
|
297
|
+
.setCircularRegion(latitude, longitude, (float) radius)
|
|
298
|
+
.setTransitionTypes(transitionTypes)
|
|
299
|
+
.setExpirationDuration(Geofence.NEVER_EXPIRE)
|
|
300
|
+
.build();
|
|
301
|
+
GeofencingRequest request = new GeofencingRequest.Builder().setInitialTrigger(initialTrigger).addGeofence(geofence).build();
|
|
302
|
+
|
|
303
|
+
try {
|
|
304
|
+
getGeofencingClient()
|
|
305
|
+
.addGeofences(request, getGeofencePendingIntent())
|
|
306
|
+
.addOnSuccessListener((unused) -> {
|
|
307
|
+
try {
|
|
308
|
+
GeofenceStore.saveRegion(
|
|
309
|
+
getContext(),
|
|
310
|
+
identifier,
|
|
311
|
+
latitude,
|
|
312
|
+
longitude,
|
|
313
|
+
(float) radius,
|
|
314
|
+
notifyOnEntry,
|
|
315
|
+
notifyOnExit,
|
|
316
|
+
payload
|
|
317
|
+
);
|
|
318
|
+
call.resolve();
|
|
319
|
+
} catch (JSONException exception) {
|
|
320
|
+
call.reject("Could not persist geofence", exception);
|
|
321
|
+
}
|
|
322
|
+
})
|
|
323
|
+
.addOnFailureListener((exception) -> call.reject("Could not start monitoring the geofence", exception));
|
|
324
|
+
} catch (SecurityException exception) {
|
|
325
|
+
call.reject("Background location permission is required for geofencing", "NOT_AUTHORIZED", exception);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
@PluginMethod
|
|
330
|
+
public void removeGeofence(PluginCall call) {
|
|
331
|
+
String identifier = call.getString("identifier");
|
|
332
|
+
if (identifier == null || identifier.isEmpty()) {
|
|
333
|
+
call.reject("Identifier is required");
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
getGeofencingClient()
|
|
337
|
+
.removeGeofences(Collections.singletonList(identifier))
|
|
338
|
+
.addOnSuccessListener((unused) -> {
|
|
339
|
+
GeofenceStore.removeRegion(getContext(), identifier);
|
|
340
|
+
call.resolve();
|
|
341
|
+
})
|
|
342
|
+
.addOnFailureListener((exception) -> call.reject("Could not stop monitoring the geofence", exception));
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
@PluginMethod
|
|
346
|
+
public void removeAllGeofences(PluginCall call) {
|
|
347
|
+
getGeofencingClient()
|
|
348
|
+
.removeGeofences(getGeofencePendingIntent())
|
|
349
|
+
.addOnSuccessListener((unused) -> {
|
|
350
|
+
GeofenceStore.clearRegions(getContext());
|
|
351
|
+
call.resolve();
|
|
352
|
+
})
|
|
353
|
+
.addOnFailureListener((exception) -> call.reject("Could not stop monitoring geofences", exception));
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
@PluginMethod
|
|
357
|
+
public void getMonitoredGeofences(PluginCall call) {
|
|
358
|
+
JSObject result = new JSObject();
|
|
359
|
+
Set<String> regionIds = GeofenceStore.getRegionIds(getContext());
|
|
360
|
+
JSArray regions = new JSArray();
|
|
361
|
+
for (String regionId : regionIds) {
|
|
362
|
+
regions.put(regionId);
|
|
363
|
+
}
|
|
364
|
+
result.put("regions", regions);
|
|
365
|
+
call.resolve(result);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
private CompletableFuture<Void> requestGeofencePermissions(PluginCall call) {
|
|
369
|
+
if (hasGeofencePermissions()) {
|
|
370
|
+
return CompletableFuture.completedFuture(null);
|
|
371
|
+
}
|
|
372
|
+
if (geofencePermissionFuture != null) {
|
|
373
|
+
return geofencePermissionFuture;
|
|
374
|
+
}
|
|
375
|
+
CompletableFuture<Void> future = new CompletableFuture<>();
|
|
376
|
+
geofencePermissionFuture = future;
|
|
377
|
+
if (getPermissionState("location") != PermissionState.GRANTED) {
|
|
378
|
+
requestPermissionForAlias("location", call, "geofenceLocationPermissionsCallback");
|
|
379
|
+
return future;
|
|
380
|
+
}
|
|
381
|
+
requestBackgroundLocationPermissionIfNeeded(call);
|
|
382
|
+
return future;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
@PermissionCallback
|
|
386
|
+
private void geofenceLocationPermissionsCallback(PluginCall call) {
|
|
387
|
+
if (geofencePermissionFuture == null) {
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
if (getPermissionState("location") != PermissionState.GRANTED) {
|
|
391
|
+
geofencePermissionFuture.completeExceptionally(new SecurityException("User denied location permission"));
|
|
392
|
+
geofencePermissionFuture = null;
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
requestBackgroundLocationPermissionIfNeeded(call);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
@PermissionCallback
|
|
399
|
+
private void geofenceBackgroundPermissionsCallback(PluginCall call) {
|
|
400
|
+
if (geofencePermissionFuture == null) {
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
if (!hasBackgroundLocationPermission()) {
|
|
404
|
+
geofencePermissionFuture.completeExceptionally(new SecurityException("User denied background location permission"));
|
|
405
|
+
geofencePermissionFuture = null;
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
geofencePermissionFuture.complete(null);
|
|
409
|
+
geofencePermissionFuture = null;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
private void requestBackgroundLocationPermissionIfNeeded(PluginCall call) {
|
|
413
|
+
if (hasBackgroundLocationPermission()) {
|
|
414
|
+
geofencePermissionFuture.complete(null);
|
|
415
|
+
geofencePermissionFuture = null;
|
|
416
|
+
return;
|
|
417
|
+
}
|
|
418
|
+
requestPermissionForAlias("backgroundLocation", call, "geofenceBackgroundPermissionsCallback");
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
private boolean hasGeofencePermissions() {
|
|
422
|
+
return getPermissionState("location") == PermissionState.GRANTED && hasBackgroundLocationPermission();
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
private boolean hasBackgroundLocationPermission() {
|
|
426
|
+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
|
427
|
+
return true;
|
|
428
|
+
}
|
|
429
|
+
return (
|
|
430
|
+
ContextCompat.checkSelfPermission(getContext(), Manifest.permission.ACCESS_BACKGROUND_LOCATION) ==
|
|
431
|
+
PackageManager.PERMISSION_GRANTED
|
|
432
|
+
);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
private GeofencingClient getGeofencingClient() {
|
|
436
|
+
return LocationServices.getGeofencingClient(getContext());
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
private android.app.PendingIntent getGeofencePendingIntent() {
|
|
440
|
+
return GeofenceBroadcastReceiver.createPendingIntent(getContext());
|
|
441
|
+
}
|
|
442
|
+
|
|
202
443
|
private static double[][] getJavaDoubleArray(JSArray jsArray) throws JSONException {
|
|
203
444
|
int rows = jsArray.length();
|
|
204
445
|
if (rows == 0) {
|
|
@@ -277,6 +518,27 @@ public class BackgroundGeolocation extends Plugin {
|
|
|
277
518
|
}
|
|
278
519
|
}
|
|
279
520
|
|
|
521
|
+
private class GeofenceEventReceiver extends BroadcastReceiver {
|
|
522
|
+
|
|
523
|
+
@Override
|
|
524
|
+
public void onReceive(Context context, Intent intent) {
|
|
525
|
+
boolean errorEvent = GeofenceStore.ACTION_GEOFENCE_ERROR.equals(intent.getAction());
|
|
526
|
+
String payload = intent.getStringExtra(errorEvent ? GeofenceStore.EXTRA_GEOFENCE_ERROR : GeofenceStore.EXTRA_GEOFENCE_PAYLOAD);
|
|
527
|
+
if (payload == null || payload.isEmpty()) {
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
try {
|
|
531
|
+
notifyListeners(
|
|
532
|
+
errorEvent ? "geofenceError" : "geofenceTransition",
|
|
533
|
+
GeofenceStore.toJSObject(new JSONObject(payload)),
|
|
534
|
+
true
|
|
535
|
+
);
|
|
536
|
+
} catch (JSONException exception) {
|
|
537
|
+
Logger.error("Could not parse geofence payload", exception);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
280
542
|
@Override
|
|
281
543
|
public void load() {
|
|
282
544
|
super.load();
|
|
@@ -299,10 +561,16 @@ public class BackgroundGeolocation extends Plugin {
|
|
|
299
561
|
manager.createNotificationChannel(channel);
|
|
300
562
|
}
|
|
301
563
|
|
|
564
|
+
serviceReceiver = new ServiceReceiver();
|
|
302
565
|
LocalBroadcastManager.getInstance(this.getContext()).registerReceiver(
|
|
303
|
-
|
|
566
|
+
serviceReceiver,
|
|
304
567
|
new IntentFilter(BackgroundGeolocationService.ACTION_BROADCAST)
|
|
305
568
|
);
|
|
569
|
+
|
|
570
|
+
geofenceEventReceiver = new GeofenceEventReceiver();
|
|
571
|
+
IntentFilter geofenceFilter = new IntentFilter(GeofenceStore.ACTION_GEOFENCE_EVENT);
|
|
572
|
+
geofenceFilter.addAction(GeofenceStore.ACTION_GEOFENCE_ERROR);
|
|
573
|
+
LocalBroadcastManager.getInstance(this.getContext()).registerReceiver(geofenceEventReceiver, geofenceFilter);
|
|
306
574
|
}
|
|
307
575
|
|
|
308
576
|
private CompletableFuture<BackgroundGeolocationService.LocalBinder> getServiceConnection() {
|
|
@@ -347,6 +615,17 @@ public class BackgroundGeolocation extends Plugin {
|
|
|
347
615
|
if (locationPermissionFuture != null && !locationPermissionFuture.isDone()) {
|
|
348
616
|
locationPermissionFuture.cancel(true);
|
|
349
617
|
}
|
|
618
|
+
if (geofencePermissionFuture != null && !geofencePermissionFuture.isDone()) {
|
|
619
|
+
geofencePermissionFuture.cancel(true);
|
|
620
|
+
}
|
|
621
|
+
if (serviceReceiver != null) {
|
|
622
|
+
LocalBroadcastManager.getInstance(this.getContext()).unregisterReceiver(serviceReceiver);
|
|
623
|
+
serviceReceiver = null;
|
|
624
|
+
}
|
|
625
|
+
if (geofenceEventReceiver != null) {
|
|
626
|
+
LocalBroadcastManager.getInstance(this.getContext()).unregisterReceiver(geofenceEventReceiver);
|
|
627
|
+
geofenceEventReceiver = null;
|
|
628
|
+
}
|
|
350
629
|
super.handleOnDestroy();
|
|
351
630
|
}
|
|
352
631
|
|
package/android/src/main/java/com/capgo/capacitor_background_geolocation/GeofenceBootReceiver.java
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
package com.capgo.capacitor_background_geolocation;
|
|
2
|
+
|
|
3
|
+
import android.content.BroadcastReceiver;
|
|
4
|
+
import android.content.Context;
|
|
5
|
+
import android.content.Intent;
|
|
6
|
+
import com.getcapacitor.Logger;
|
|
7
|
+
import com.google.android.gms.location.LocationServices;
|
|
8
|
+
import com.google.android.gms.tasks.Task;
|
|
9
|
+
import com.google.android.gms.tasks.Tasks;
|
|
10
|
+
import java.util.ArrayList;
|
|
11
|
+
import java.util.List;
|
|
12
|
+
import org.json.JSONObject;
|
|
13
|
+
|
|
14
|
+
public class GeofenceBootReceiver extends BroadcastReceiver {
|
|
15
|
+
|
|
16
|
+
@Override
|
|
17
|
+
public void onReceive(Context context, Intent intent) {
|
|
18
|
+
if (intent == null || !shouldRestoreAction(intent.getAction())) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
PendingResult pendingResult = goAsync();
|
|
23
|
+
List<Task<Void>> tasks = restorePersistedGeofences(context);
|
|
24
|
+
if (tasks.isEmpty()) {
|
|
25
|
+
pendingResult.finish();
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
Tasks.whenAllComplete(tasks).addOnCompleteListener((unused) -> pendingResult.finish());
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
static boolean shouldRestoreAction(String action) {
|
|
32
|
+
return Intent.ACTION_BOOT_COMPLETED.equals(action) || Intent.ACTION_MY_PACKAGE_REPLACED.equals(action);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
private static List<Task<Void>> restorePersistedGeofences(Context context) {
|
|
36
|
+
List<Task<Void>> tasks = new ArrayList<>();
|
|
37
|
+
var client = LocationServices.getGeofencingClient(context);
|
|
38
|
+
for (String identifier : GeofenceStore.getRegionIds(context)) {
|
|
39
|
+
JSONObject region = GeofenceStore.getRegion(context, identifier);
|
|
40
|
+
try {
|
|
41
|
+
tasks.add(
|
|
42
|
+
client
|
|
43
|
+
.addGeofences(GeofenceStore.buildGeofencingRequest(region), GeofenceBroadcastReceiver.createPendingIntent(context))
|
|
44
|
+
.addOnSuccessListener((unused) -> Logger.debug("Restored geofence after boot: " + identifier))
|
|
45
|
+
.addOnFailureListener((exception) ->
|
|
46
|
+
Logger.error("Could not restore geofence after boot: " + identifier, exception)
|
|
47
|
+
)
|
|
48
|
+
);
|
|
49
|
+
} catch (SecurityException exception) {
|
|
50
|
+
Logger.error("Missing permission to restore geofence after boot: " + identifier, exception);
|
|
51
|
+
} catch (Exception exception) {
|
|
52
|
+
GeofenceStore.removeRegion(context, identifier);
|
|
53
|
+
Logger.error("Invalid persisted geofence removed after boot: " + identifier, exception);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return tasks;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
package com.capgo.capacitor_background_geolocation;
|
|
2
|
+
|
|
3
|
+
import android.app.PendingIntent;
|
|
4
|
+
import android.content.BroadcastReceiver;
|
|
5
|
+
import android.content.Context;
|
|
6
|
+
import android.content.Intent;
|
|
7
|
+
import android.os.Build;
|
|
8
|
+
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
|
9
|
+
import com.getcapacitor.Logger;
|
|
10
|
+
import com.google.android.gms.location.Geofence;
|
|
11
|
+
import com.google.android.gms.location.GeofenceStatusCodes;
|
|
12
|
+
import com.google.android.gms.location.GeofencingEvent;
|
|
13
|
+
import java.util.List;
|
|
14
|
+
import org.json.JSONObject;
|
|
15
|
+
|
|
16
|
+
public class GeofenceBroadcastReceiver extends BroadcastReceiver {
|
|
17
|
+
|
|
18
|
+
private static final int GEOFENCE_PENDING_INTENT_REQUEST_CODE = 83620;
|
|
19
|
+
|
|
20
|
+
@Override
|
|
21
|
+
public void onReceive(Context context, Intent intent) {
|
|
22
|
+
GeofencingEvent event = GeofencingEvent.fromIntent(intent);
|
|
23
|
+
if (event == null) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (event.hasError()) {
|
|
27
|
+
int errorCode = event.getErrorCode();
|
|
28
|
+
String message = GeofenceStatusCodes.getStatusCodeString(errorCode);
|
|
29
|
+
Logger.error("Geofence event failed with code: " + errorCode);
|
|
30
|
+
if (shouldClearStoredRegions(errorCode)) {
|
|
31
|
+
GeofenceStore.clearRegions(context);
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
JSONObject data = new JSONObject();
|
|
35
|
+
data.put("code", errorCode);
|
|
36
|
+
data.put("message", message);
|
|
37
|
+
Intent localIntent = new Intent(GeofenceStore.ACTION_GEOFENCE_ERROR);
|
|
38
|
+
localIntent.putExtra(GeofenceStore.EXTRA_GEOFENCE_ERROR, data.toString());
|
|
39
|
+
LocalBroadcastManager.getInstance(context).sendBroadcast(localIntent);
|
|
40
|
+
} catch (Exception exception) {
|
|
41
|
+
Logger.error("Failed to emit geofence error", exception);
|
|
42
|
+
}
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
int transition = event.getGeofenceTransition();
|
|
47
|
+
if (transition != Geofence.GEOFENCE_TRANSITION_ENTER && transition != Geofence.GEOFENCE_TRANSITION_EXIT) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
List<Geofence> triggeringGeofences = event.getTriggeringGeofences();
|
|
52
|
+
if (triggeringGeofences == null || triggeringGeofences.isEmpty()) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
boolean enter = transition == Geofence.GEOFENCE_TRANSITION_ENTER;
|
|
57
|
+
try {
|
|
58
|
+
for (Geofence geofence : triggeringGeofences) {
|
|
59
|
+
JSONObject data = GeofenceStore.buildTransitionData(context, geofence.getRequestId(), enter);
|
|
60
|
+
Intent localIntent = new Intent(GeofenceStore.ACTION_GEOFENCE_EVENT);
|
|
61
|
+
localIntent.putExtra(GeofenceStore.EXTRA_GEOFENCE_PAYLOAD, data.toString());
|
|
62
|
+
LocalBroadcastManager.getInstance(context).sendBroadcast(localIntent);
|
|
63
|
+
GeofenceStore.enqueueTransition(context, data);
|
|
64
|
+
}
|
|
65
|
+
} catch (Exception exception) {
|
|
66
|
+
Logger.error("Failed to handle geofence transition", exception);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
static boolean shouldClearStoredRegions(int errorCode) {
|
|
71
|
+
return errorCode == GeofenceStatusCodes.GEOFENCE_NOT_AVAILABLE;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
static PendingIntent createPendingIntent(Context context) {
|
|
75
|
+
Intent intent = new Intent(context, GeofenceBroadcastReceiver.class);
|
|
76
|
+
intent.setPackage(context.getPackageName());
|
|
77
|
+
int flags = PendingIntent.FLAG_UPDATE_CURRENT;
|
|
78
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
|
79
|
+
flags |= PendingIntent.FLAG_MUTABLE;
|
|
80
|
+
}
|
|
81
|
+
return PendingIntent.getBroadcast(context, GEOFENCE_PENDING_INTENT_REQUEST_CODE, intent, flags);
|
|
82
|
+
}
|
|
83
|
+
}
|