@capgo/background-geolocation 7.0.6 → 7.0.7

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 CHANGED
@@ -6,7 +6,8 @@
6
6
  <h2><a href="https://capgo.app/consulting/?ref=plugin"> Missing a feature? We’ll build the plugin for you 💪</a></h2>
7
7
  </div>
8
8
 
9
- A Capacitor plugin that lets you receive geolocation updates even while the app is backgrounded. Only iOS and Android platforms are supported.
9
+ A Capacitor plugin that lets you receive geolocation updates even while the app is backgrounded.
10
+ It has a web API to facilitate for a similar usage, but background geolocation is not supported in a regular browser, only in an app environment.
10
11
 
11
12
  ## Usage
12
13
 
@@ -121,12 +122,6 @@ function guess_location(callback, timeout) {
121
122
  }
122
123
  ```
123
124
 
124
- ### Typescript support
125
-
126
- ```typescript
127
- import { BackgroundGeolocation } from "@capgo/background-geolocation";
128
- ```
129
-
130
125
  ## Installation
131
126
 
132
127
  This plugin supports Capacitor v7:
@@ -162,7 +157,7 @@ Add the following keys to `Info.plist.`:
162
157
 
163
158
  Set the the `android.useLegacyBridge` option to `true` in your Capacitor configuration. This prevents location updates halting after 5 minutes in the background. See https://capacitorjs.com/docs/config and https://github.com/capacitor-community/background-geolocation/issues/89.
164
159
 
165
- On Android 13+, the app needs the `POST_NOTIFICATIONS` runtime permission to show the persistent notification informing the user that their location is being used in the background. You may need to [request this permission](https://developer.android.com/develop/ui/views/notifications/notification-permission) from the user, this can be accomplished [using the `@capacitor/local-notifications` plugin](https://capacitorjs.com/docs/apis/local-notifications#checkpermissions).
160
+ On Android 13+, the app needs the `POST_NOTIFICATIONS` runtime permission to show the persistent notification informing the user that their location is being used in the background. This runtime permission is requested after the location permission is granted.
166
161
 
167
162
  If your app forwards location updates to a server in real time, be aware that after 5 minutes in the background Android will throttle HTTP requests initiated from the WebView. The solution is to use a native HTTP plugin such as [CapacitorHttp](https://capacitorjs.com/docs/apis/http). See https://github.com/capacitor-community/background-geolocation/issues/14.
168
163
 
@@ -11,7 +11,6 @@ import android.content.Context;
11
11
  import android.content.Intent;
12
12
  import android.content.IntentFilter;
13
13
  import android.content.ServiceConnection;
14
- import android.content.pm.PackageManager;
15
14
  import android.graphics.Color;
16
15
  import android.location.Location;
17
16
  import android.location.LocationManager;
@@ -30,7 +29,7 @@ import com.getcapacitor.annotation.CapacitorPlugin;
30
29
  import com.getcapacitor.annotation.Permission;
31
30
  import com.getcapacitor.annotation.PermissionCallback;
32
31
  import com.google.android.gms.location.LocationServices;
33
- import com.google.android.gms.tasks.OnSuccessListener;
32
+ import java.util.concurrent.CompletableFuture;
34
33
  import org.json.JSONObject;
35
34
 
36
35
  @CapacitorPlugin(
@@ -43,140 +42,178 @@ import org.json.JSONObject;
43
42
  },
44
43
  alias = "location"
45
44
  ),
45
+ @Permission(
46
+ strings = { Manifest.permission.POST_NOTIFICATIONS },
47
+ alias = "notification"
48
+ ),
46
49
  }
47
50
  )
48
51
  public class BackgroundGeolocation extends Plugin {
49
52
 
50
- private BackgroundGeolocationService.LocalBinder service = null;
51
- private Boolean stoppedWithoutPermissions = false;
53
+ private CompletableFuture<
54
+ BackgroundGeolocationService.LocalBinder
55
+ > serviceConnectionFuture;
56
+ private CompletableFuture<Void> locationPermissionFuture;
52
57
 
53
58
  private void fetchLastLocation(PluginCall call) {
54
59
  try {
55
60
  LocationServices.getFusedLocationProviderClient(getContext())
56
61
  .getLastLocation()
57
- .addOnSuccessListener(
58
- getActivity(),
59
- new OnSuccessListener<Location>() {
60
- @Override
61
- public void onSuccess(Location location) {
62
- if (location != null) {
63
- call.resolve(formatLocation(location));
64
- }
65
- }
62
+ .addOnSuccessListener(getActivity(), location -> {
63
+ if (location != null) {
64
+ call.resolve(formatLocation(location));
66
65
  }
67
- );
66
+ });
68
67
  } catch (SecurityException ignore) {}
69
68
  }
70
69
 
71
70
  @PluginMethod(returnType = PluginMethod.RETURN_CALLBACK)
72
71
  public void addWatcher(final PluginCall call) {
73
- if (service == null) {
74
- call.reject("Service not running.");
72
+ if (
73
+ getPermissionState("location") != PermissionState.GRANTED &&
74
+ !call.getBoolean("requestPermissions", true)
75
+ ) {
76
+ call.reject("User denied location permission", "NOT_AUTHORIZED");
75
77
  return;
76
78
  }
77
- call.setKeepAlive(true);
78
79
 
79
- if (getPermissionState("location") != PermissionState.GRANTED) {
80
- if (call.getBoolean("requestPermissions", true)) {
81
- requestPermissionForAlias(
82
- "location",
83
- call,
84
- "locationPermissionsCallback"
85
- );
86
- } else {
87
- call.reject("Permission denied.", "NOT_AUTHORIZED");
88
- }
89
- } else if (!isLocationEnabled(getContext())) {
80
+ if (
81
+ getPermissionState("location") != PermissionState.GRANTED &&
82
+ call.getBoolean("requestPermissions", true)
83
+ ) {
84
+ call.setKeepAlive(true);
85
+ requestLocationPermissions(call)
86
+ .thenRun(() -> {
87
+ proceedWithWatcher(call);
88
+ })
89
+ .exceptionally(throwable -> {
90
+ call.reject("User denied location permission", "NOT_AUTHORIZED");
91
+ return null;
92
+ });
93
+ return;
94
+ }
95
+
96
+ // location permission granted.
97
+ if (!isLocationEnabled(getContext())) {
90
98
  call.reject("Location services disabled.", "NOT_AUTHORIZED");
99
+ return;
91
100
  }
101
+
102
+ // Everything is OK, continuing to adding a watcher
103
+ call.setKeepAlive(true);
104
+ proceedWithWatcher(call);
105
+ }
106
+
107
+ private void proceedWithWatcher(PluginCall call) {
92
108
  if (call.getBoolean("stale", false)) {
93
109
  fetchLastLocation(call);
94
110
  }
95
- Notification backgroundNotification = null;
96
- String backgroundMessage = call.getString("backgroundMessage");
97
-
98
- if (backgroundMessage != null) {
99
- Notification.Builder builder = new Notification.Builder(getContext())
100
- .setContentTitle(
101
- call.getString("backgroundTitle", "Using your location")
102
- )
103
- .setContentText(backgroundMessage)
104
- .setOngoing(true)
105
- .setPriority(Notification.PRIORITY_HIGH)
106
- .setWhen(System.currentTimeMillis());
107
-
108
- try {
109
- String name = getAppString(
110
- "capacitor_background_geolocation_notification_icon",
111
- "mipmap/ic_launcher"
111
+ getServiceConnection().thenAccept(serviceBinder -> {
112
+ serviceBinder.addWatcher(
113
+ call.getCallbackId(),
114
+ createBackgroundNotification(call),
115
+ call.getFloat("distanceFilter", 0f)
112
116
  );
113
- String[] parts = name.split("/");
114
- // It is actually necessary to set a valid icon for the notification to behave
115
- // correctly when tapped. If there is no icon specified, tapping it will open the
116
- // app's settings, rather than bringing the application to the foreground.
117
- builder.setSmallIcon(getAppResourceIdentifier(parts[1], parts[0]));
118
- } catch (Exception e) {
119
- Logger.error("Could not set notification icon", e);
120
- }
117
+ });
118
+ }
121
119
 
122
- try {
123
- String color = getAppString(
124
- "capacitor_background_geolocation_notification_color",
125
- null
126
- );
127
- if (color != null) {
128
- builder.setColor(Color.parseColor(color));
129
- }
130
- } catch (Exception e) {
131
- Logger.error("Could not set notification color", e);
132
- }
120
+ private CompletableFuture<Void> requestLocationPermissions(PluginCall call) {
121
+ if (locationPermissionFuture != null) {
122
+ return locationPermissionFuture;
123
+ }
124
+ locationPermissionFuture = new CompletableFuture<>();
125
+ requestPermissionForAlias("location", call, "locationPermissionsCallback");
126
+ return locationPermissionFuture;
127
+ }
133
128
 
134
- Intent launchIntent = getContext()
135
- .getPackageManager()
136
- .getLaunchIntentForPackage(getContext().getPackageName());
137
- if (launchIntent != null) {
138
- launchIntent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
139
- builder.setContentIntent(
140
- PendingIntent.getActivity(
141
- getContext(),
142
- 0,
143
- launchIntent,
144
- PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE
145
- )
146
- );
147
- }
129
+ private Notification createBackgroundNotification(PluginCall call) {
130
+ String backgroundMessage = call.getString("backgroundMessage", "");
148
131
 
149
- // Set the Channel ID for Android O.
150
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
151
- builder.setChannelId(
152
- BackgroundGeolocationService.class.getPackage().getName()
153
- );
132
+ Notification.Builder builder = new Notification.Builder(getContext())
133
+ .setContentTitle(call.getString("backgroundTitle", "Using your location"))
134
+ .setContentText(backgroundMessage)
135
+ .setOngoing(true)
136
+ .setPriority(Notification.PRIORITY_HIGH)
137
+ .setWhen(System.currentTimeMillis());
138
+
139
+ try {
140
+ String name = getAppString(
141
+ "capacitor_background_geolocation_notification_icon",
142
+ "mipmap/ic_launcher"
143
+ );
144
+ String[] parts = name.split("/");
145
+ // It is actually necessary to set a valid icon for the notification to behave
146
+ // correctly when tapped. If there is no icon specified, tapping it will open the
147
+ // app's settings, rather than bringing the application to the foreground.
148
+ builder.setSmallIcon(getAppResourceIdentifier(parts[1], parts[0]));
149
+ } catch (Exception e) {
150
+ Logger.error("Could not set notification icon", e);
151
+ }
152
+
153
+ try {
154
+ String color = getAppString(
155
+ "capacitor_background_geolocation_notification_color",
156
+ null
157
+ );
158
+ if (color != null) {
159
+ builder.setColor(Color.parseColor(color));
154
160
  }
161
+ } catch (Exception e) {
162
+ Logger.error("Could not set notification color", e);
163
+ }
155
164
 
156
- backgroundNotification = builder.build();
165
+ Intent launchIntent = getContext()
166
+ .getPackageManager()
167
+ .getLaunchIntentForPackage(getContext().getPackageName());
168
+ if (launchIntent != null) {
169
+ launchIntent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
170
+ builder.setContentIntent(
171
+ PendingIntent.getActivity(
172
+ getContext(),
173
+ 0,
174
+ launchIntent,
175
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE
176
+ )
177
+ );
157
178
  }
158
- service.addWatcher(
159
- call.getCallbackId(),
160
- backgroundNotification,
161
- call.getFloat("distanceFilter", 0f)
162
- );
179
+
180
+ // Set the Channel ID for Android O.
181
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
182
+ builder.setChannelId(
183
+ BackgroundGeolocationService.class.getPackage().getName()
184
+ );
185
+ }
186
+
187
+ return builder.build();
163
188
  }
164
189
 
165
190
  @PermissionCallback
166
191
  private void locationPermissionsCallback(PluginCall call) {
167
- if (getPermissionState("location") != PermissionState.GRANTED) {
168
- call.reject("User denied location permission", "NOT_AUTHORIZED");
192
+ if (locationPermissionFuture == null) {
169
193
  return;
170
194
  }
171
- if (call.getBoolean("stale", false)) {
172
- fetchLastLocation(call);
173
- }
174
- if (service != null) {
175
- service.onPermissionsGranted();
176
- // The handleOnResume method will now be called, and we don't need it to call
177
- // service.onPermissionsGranted again so we reset this flag.
178
- stoppedWithoutPermissions = false;
195
+
196
+ requestPermissionForAlias(
197
+ "notification",
198
+ call,
199
+ "notificationPermissionsCallback"
200
+ );
201
+
202
+ if (getPermissionState("location") != PermissionState.GRANTED) {
203
+ locationPermissionFuture.completeExceptionally(
204
+ new SecurityException("User denied location permission")
205
+ );
206
+ locationPermissionFuture = null;
207
+ return;
179
208
  }
209
+
210
+ locationPermissionFuture.complete(null);
211
+ locationPermissionFuture = null;
212
+ }
213
+
214
+ @PermissionCallback
215
+ private void notificationPermissionsCallback(PluginCall call) {
216
+ Logger.debug("notification permission callback");
180
217
  }
181
218
 
182
219
  @PluginMethod
@@ -186,12 +223,20 @@ public class BackgroundGeolocation extends Plugin {
186
223
  call.reject("Missing id.");
187
224
  return;
188
225
  }
189
- service.removeWatcher(callbackId);
190
- PluginCall savedCall = getBridge().getSavedCall(callbackId);
191
- if (savedCall != null) {
192
- savedCall.release(getBridge());
193
- }
194
- call.resolve();
226
+
227
+ getServiceConnection()
228
+ .thenAccept(service -> {
229
+ service.removeWatcher(callbackId);
230
+ PluginCall savedCall = getBridge().getSavedCall(callbackId);
231
+ if (savedCall != null) {
232
+ savedCall.release(getBridge());
233
+ }
234
+ call.resolve();
235
+ })
236
+ .exceptionally(throwable -> {
237
+ call.reject("Service connection failed: " + throwable.getMessage());
238
+ return null;
239
+ });
195
240
  }
196
241
 
197
242
  @PluginMethod
@@ -313,51 +358,77 @@ public class BackgroundGeolocation extends Plugin {
313
358
  manager.createNotificationChannel(channel);
314
359
  }
315
360
 
361
+ LocalBroadcastManager.getInstance(this.getContext()).registerReceiver(
362
+ new ServiceReceiver(),
363
+ new IntentFilter(BackgroundGeolocationService.ACTION_BROADCAST)
364
+ );
365
+ }
366
+
367
+ private CompletableFuture<
368
+ BackgroundGeolocationService.LocalBinder
369
+ > getServiceConnection() {
370
+ if (
371
+ serviceConnectionFuture != null &&
372
+ !serviceConnectionFuture.isCompletedExceptionally()
373
+ ) {
374
+ return serviceConnectionFuture;
375
+ }
376
+
377
+ serviceConnectionFuture = new CompletableFuture<>();
378
+
379
+ Intent serviceIntent = new Intent(
380
+ this.getContext(),
381
+ BackgroundGeolocationService.class
382
+ );
383
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
384
+ this.getContext().startForegroundService(serviceIntent);
385
+ } else {
386
+ this.getContext().startService(serviceIntent);
387
+ }
388
+
316
389
  this.getContext().bindService(
317
- new Intent(this.getContext(), BackgroundGeolocationService.class),
390
+ serviceIntent,
318
391
  new ServiceConnection() {
319
392
  @Override
320
393
  public void onServiceConnected(ComponentName name, IBinder binder) {
321
- BackgroundGeolocation.this.service =
322
- (BackgroundGeolocationService.LocalBinder) binder;
394
+ serviceConnectionFuture.complete(
395
+ (BackgroundGeolocationService.LocalBinder) binder
396
+ );
323
397
  }
324
398
 
325
399
  @Override
326
- public void onServiceDisconnected(ComponentName name) {}
400
+ public void onServiceDisconnected(ComponentName name) {
401
+ serviceConnectionFuture = null;
402
+ }
327
403
  },
328
404
  Context.BIND_AUTO_CREATE
329
405
  );
330
406
 
331
- LocalBroadcastManager.getInstance(this.getContext()).registerReceiver(
332
- new ServiceReceiver(),
333
- new IntentFilter(BackgroundGeolocationService.ACTION_BROADCAST)
334
- );
407
+ return serviceConnectionFuture;
335
408
  }
336
409
 
337
410
  @Override
338
411
  protected void handleOnResume() {
339
- if (service != null) {
340
- if (
341
- stoppedWithoutPermissions &&
342
- getPermissionState("location") == PermissionState.GRANTED
343
- ) {
344
- service.onPermissionsGranted();
345
- }
346
- }
347
412
  super.handleOnResume();
348
413
  }
349
414
 
350
415
  @Override
351
416
  protected void handleOnPause() {
352
- stoppedWithoutPermissions =
353
- getPermissionState("location") != PermissionState.GRANTED;
354
417
  super.handleOnPause();
355
418
  }
356
419
 
357
420
  @Override
358
421
  protected void handleOnDestroy() {
359
- if (service != null) {
360
- service.stopService();
422
+ if (serviceConnectionFuture != null) {
423
+ serviceConnectionFuture.thenAccept(
424
+ BackgroundGeolocationService.LocalBinder::stopService
425
+ );
426
+ }
427
+
428
+ if (
429
+ locationPermissionFuture != null && !locationPermissionFuture.isDone()
430
+ ) {
431
+ locationPermissionFuture.cancel(true);
361
432
  }
362
433
  super.handleOnDestroy();
363
434
  }
@@ -2,20 +2,14 @@ package com.capgo.capacitor_background_geolocation;
2
2
 
3
3
  import android.app.Notification;
4
4
  import android.app.Service;
5
+ import android.content.Context;
5
6
  import android.content.Intent;
6
- import android.content.pm.ServiceInfo;
7
- import android.location.Location;
7
+ import android.location.LocationListener;
8
+ import android.location.LocationManager;
8
9
  import android.os.Binder;
9
- import android.os.Build;
10
10
  import android.os.IBinder;
11
11
  import androidx.localbroadcastmanager.content.LocalBroadcastManager;
12
12
  import com.getcapacitor.Logger;
13
- import com.google.android.gms.location.FusedLocationProviderClient;
14
- import com.google.android.gms.location.LocationAvailability;
15
- import com.google.android.gms.location.LocationCallback;
16
- import com.google.android.gms.location.LocationRequest;
17
- import com.google.android.gms.location.LocationResult;
18
- import com.google.android.gms.location.LocationServices;
19
13
  import java.util.HashSet;
20
14
 
21
15
  // A bound and started service that is promoted to a foreground service
@@ -30,16 +24,16 @@ public class BackgroundGeolocationService extends Service {
30
24
  // Must be unique for this application.
31
25
  private static final int NOTIFICATION_ID = 28351;
32
26
 
33
- private class Watcher {
27
+ private static class Watcher {
34
28
 
35
29
  public String id;
36
- public FusedLocationProviderClient client;
37
- public LocationRequest locationRequest;
38
- public LocationCallback locationCallback;
30
+ public LocationManager client;
31
+ public float distanceFilter;
32
+ public LocationListener locationCallback;
39
33
  public Notification backgroundNotification;
40
34
  }
41
35
 
42
- private HashSet<Watcher> watchers = new HashSet<Watcher>();
36
+ private HashSet<Watcher> watchers = new HashSet<>();
43
37
 
44
38
  @Override
45
39
  public IBinder onBind(Intent intent) {
@@ -53,20 +47,26 @@ public class BackgroundGeolocationService extends Service {
53
47
  @Override
54
48
  public boolean onUnbind(Intent intent) {
55
49
  for (Watcher watcher : watchers) {
56
- watcher.client.removeLocationUpdates(watcher.locationCallback);
50
+ watcher.client.removeUpdates(watcher.locationCallback);
57
51
  }
58
- watchers = new HashSet<Watcher>();
52
+ watchers = new HashSet<>();
59
53
  stopSelf();
60
54
  return false;
61
55
  }
62
56
 
63
- Notification getNotification() {
64
- for (Watcher watcher : watchers) {
65
- if (watcher.backgroundNotification != null) {
66
- return watcher.backgroundNotification;
67
- }
57
+ private void requestLocationUpdates(Watcher watcher) {
58
+ try {
59
+ watcher.client.requestLocationUpdates(
60
+ LocationManager.GPS_PROVIDER,
61
+ 1000,
62
+ watcher.distanceFilter,
63
+ watcher.locationCallback
64
+ );
65
+ } catch (SecurityException ignore) {
66
+ // According to Android Studio, this method can throw a Security Exception if
67
+ // permissions are not yet granted. Rather than check the permissions, which is fiddly,
68
+ // we simply ignore the exception.
68
69
  }
69
- return null;
70
70
  }
71
71
 
72
72
  // Handles requests from the activity.
@@ -77,54 +77,28 @@ public class BackgroundGeolocationService extends Service {
77
77
  Notification backgroundNotification,
78
78
  float distanceFilter
79
79
  ) {
80
- FusedLocationProviderClient client =
81
- LocationServices.getFusedLocationProviderClient(
82
- BackgroundGeolocationService.this
83
- );
84
- LocationRequest locationRequest = new LocationRequest();
85
- locationRequest.setMaxWaitTime(1000);
86
- locationRequest.setInterval(1000);
87
- locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
88
- locationRequest.setSmallestDisplacement(distanceFilter);
89
-
90
- LocationCallback callback = new LocationCallback() {
91
- @Override
92
- public void onLocationResult(LocationResult locationResult) {
93
- Location location = locationResult.getLastLocation();
94
- Intent intent = new Intent(ACTION_BROADCAST);
95
- intent.putExtra("location", location);
96
- intent.putExtra("id", id);
97
- LocalBroadcastManager.getInstance(
98
- getApplicationContext()
99
- ).sendBroadcast(intent);
100
- }
101
-
102
- @Override
103
- public void onLocationAvailability(LocationAvailability availability) {
104
- if (!availability.isLocationAvailable()) {
105
- Logger.debug("Location not available");
106
- }
107
- }
80
+ LocationManager locationManager = (LocationManager) getSystemService(
81
+ Context.LOCATION_SERVICE
82
+ );
83
+
84
+ LocationListener listener = location -> {
85
+ Intent intent = new Intent(ACTION_BROADCAST);
86
+ intent.putExtra("location", location);
87
+ intent.putExtra("id", id);
88
+ LocalBroadcastManager.getInstance(
89
+ getApplicationContext()
90
+ ).sendBroadcast(intent);
108
91
  };
109
92
 
110
93
  Watcher watcher = new Watcher();
111
94
  watcher.id = id;
112
- watcher.client = client;
113
- watcher.locationRequest = locationRequest;
114
- watcher.locationCallback = callback;
95
+ watcher.client = locationManager;
96
+ watcher.distanceFilter = distanceFilter;
97
+ watcher.locationCallback = listener;
115
98
  watcher.backgroundNotification = backgroundNotification;
116
99
  watchers.add(watcher);
117
100
 
118
- // According to Android Studio, this method can throw a Security Exception if
119
- // permissions are not yet granted. Rather than check the permissions, which is fiddly,
120
- // we simply ignore the exception.
121
- try {
122
- watcher.client.requestLocationUpdates(
123
- watcher.locationRequest,
124
- watcher.locationCallback,
125
- null
126
- );
127
- } catch (SecurityException ignore) {}
101
+ requestLocationUpdates(watcher);
128
102
 
129
103
  // Promote the service to the foreground if necessary.
130
104
  // Ideally we would only call 'startForeground' if the service is not already
@@ -132,46 +106,32 @@ public class BackgroundGeolocationService extends Service {
132
106
  // in API level 29 and seems to behave weirdly, as reported in #120. However,
133
107
  // it appears that 'startForeground' is idempotent, so we just call it repeatedly
134
108
  // each time a background watcher is added.
135
- if (backgroundNotification != null) {
136
- try {
137
- // This method has been known to fail due to weird
138
- // permission bugs, so we prevent any exceptions from
139
- // crashing the app. See issue #86.
140
- startForeground(NOTIFICATION_ID, backgroundNotification);
141
- } catch (Exception exception) {
142
- Logger.error("Failed to foreground service", exception);
143
- }
109
+ try {
110
+ // This method has been known to fail due to weird
111
+ // permission bugs, so we prevent any exceptions from
112
+ // crashing the app. See issue #86.
113
+ startForeground(NOTIFICATION_ID, backgroundNotification);
114
+ } catch (Exception exception) {
115
+ Logger.error("Failed to foreground service", exception);
144
116
  }
145
117
  }
146
118
 
147
119
  void removeWatcher(String id) {
148
120
  for (Watcher watcher : watchers) {
149
121
  if (watcher.id.equals(id)) {
150
- watcher.client.removeLocationUpdates(watcher.locationCallback);
122
+ watcher.client.removeUpdates(watcher.locationCallback);
151
123
  watchers.remove(watcher);
152
- if (getNotification() == null) {
153
- stopForeground(true);
154
- }
155
- return;
124
+ break;
156
125
  }
157
126
  }
158
- }
159
-
160
- void onPermissionsGranted() {
161
- // If permissions were granted while the app was in the background, for example in
162
- // the Settings app, the watchers need restarting.
163
- for (Watcher watcher : watchers) {
164
- watcher.client.removeLocationUpdates(watcher.locationCallback);
165
- watcher.client.requestLocationUpdates(
166
- watcher.locationRequest,
167
- watcher.locationCallback,
168
- null
169
- );
127
+ if (watchers.isEmpty()) {
128
+ stopService();
170
129
  }
171
130
  }
172
131
 
173
132
  void stopService() {
174
- BackgroundGeolocationService.this.stopSelf();
133
+ stopForeground(true);
134
+ stopSelf();
175
135
  }
176
136
  }
177
137
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/background-geolocation",
3
- "version": "7.0.6",
3
+ "version": "7.0.7",
4
4
  "description": "Receive geolocation updates even while the app is in the background.",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",