@capgo/background-geolocation 7.0.12 → 7.0.18

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.
@@ -34,382 +34,316 @@ import org.json.JSONException;
34
34
  import org.json.JSONObject;
35
35
 
36
36
  @CapacitorPlugin(
37
- name = "BackgroundGeolocation",
38
- permissions = {
39
- @Permission(
40
- strings = {
41
- Manifest.permission.ACCESS_COARSE_LOCATION,
42
- Manifest.permission.ACCESS_FINE_LOCATION,
43
- },
44
- alias = "location"
45
- ),
46
- @Permission(
47
- strings = { Manifest.permission.POST_NOTIFICATIONS },
48
- alias = "notification"
49
- ),
50
- }
37
+ name = "BackgroundGeolocation",
38
+ permissions = {
39
+ @Permission(strings = { Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION }, alias = "location"),
40
+ @Permission(strings = { Manifest.permission.POST_NOTIFICATIONS }, alias = "notification")
41
+ }
51
42
  )
52
43
  public class BackgroundGeolocation extends Plugin {
53
44
 
54
- private CompletableFuture<
55
- BackgroundGeolocationService.LocalBinder
56
- > serviceConnectionFuture;
57
- private CompletableFuture<Void> locationPermissionFuture;
58
-
59
- private void fetchLastLocation(PluginCall call) {
60
- try {
61
- LocationServices.getFusedLocationProviderClient(getContext())
62
- .getLastLocation()
63
- .addOnSuccessListener(getActivity(), location -> {
64
- if (location != null) {
65
- call.resolve(formatLocation(location));
66
- }
67
- });
68
- } catch (SecurityException ignore) {}
69
- }
70
-
71
- @PluginMethod(returnType = PluginMethod.RETURN_CALLBACK)
72
- public void start(final PluginCall call) {
73
- if (
74
- getPermissionState("location") != PermissionState.GRANTED &&
75
- !call.getBoolean("requestPermissions", true)
76
- ) {
77
- call.reject("User denied location permission", "NOT_AUTHORIZED");
78
- return;
45
+ private CompletableFuture<BackgroundGeolocationService.LocalBinder> serviceConnectionFuture;
46
+ private CompletableFuture<Void> locationPermissionFuture;
47
+
48
+ private void fetchLastLocation(PluginCall call) {
49
+ try {
50
+ LocationServices.getFusedLocationProviderClient(getContext())
51
+ .getLastLocation()
52
+ .addOnSuccessListener(getActivity(), (location) -> {
53
+ if (location != null) {
54
+ call.resolve(formatLocation(location));
55
+ }
56
+ });
57
+ } catch (SecurityException ignore) {}
79
58
  }
80
59
 
81
- if (serviceConnectionFuture != null) {
82
- call.reject("Service already started", "ALREADY_STARTED");
83
- return;
84
- }
60
+ @PluginMethod(returnType = PluginMethod.RETURN_CALLBACK)
61
+ public void start(final PluginCall call) {
62
+ if (getPermissionState("location") != PermissionState.GRANTED && !call.getBoolean("requestPermissions", true)) {
63
+ call.reject("User denied location permission", "NOT_AUTHORIZED");
64
+ return;
65
+ }
85
66
 
86
- if (
87
- getPermissionState("location") != PermissionState.GRANTED &&
88
- call.getBoolean("requestPermissions", true)
89
- ) {
90
- call.setKeepAlive(true);
91
- requestLocationPermissions(call)
92
- .thenRun(() -> {
93
- proceedWithStart(call);
94
- })
95
- .exceptionally(throwable -> {
96
- call.reject("User denied location permission", "NOT_AUTHORIZED");
97
- return null;
98
- });
99
- return;
100
- }
67
+ if (serviceConnectionFuture != null) {
68
+ call.reject("Service already started", "ALREADY_STARTED");
69
+ return;
70
+ }
101
71
 
102
- // location permission granted.
103
- if (!isLocationEnabled(getContext())) {
104
- call.reject("Location services disabled.", "NOT_AUTHORIZED");
105
- return;
106
- }
72
+ if (getPermissionState("location") != PermissionState.GRANTED && call.getBoolean("requestPermissions", true)) {
73
+ call.setKeepAlive(true);
74
+ requestLocationPermissions(call)
75
+ .thenRun(() -> {
76
+ proceedWithStart(call);
77
+ })
78
+ .exceptionally((throwable) -> {
79
+ call.reject("User denied location permission", "NOT_AUTHORIZED");
80
+ return null;
81
+ });
82
+ return;
83
+ }
107
84
 
108
- // Everything is OK, continuing to adding a watcher
109
- call.setKeepAlive(true);
110
- proceedWithStart(call);
111
- }
85
+ // location permission granted.
86
+ if (!isLocationEnabled(getContext())) {
87
+ call.reject("Location services disabled.", "NOT_AUTHORIZED");
88
+ return;
89
+ }
112
90
 
113
- private void proceedWithStart(PluginCall call) {
114
- if (call.getBoolean("stale", false)) {
115
- fetchLastLocation(call);
91
+ // Everything is OK, continuing to adding a watcher
92
+ call.setKeepAlive(true);
93
+ proceedWithStart(call);
116
94
  }
117
- getServiceConnection().thenAccept(serviceBinder -> {
118
- serviceBinder.start(
119
- call.getCallbackId(),
120
- call.getString("backgroundTitle", "Using your location"),
121
- call.getString("backgroundMessage", ""),
122
- call.getFloat("distanceFilter", 0f)
123
- );
124
- });
125
- }
126
95
 
127
- private CompletableFuture<Void> requestLocationPermissions(PluginCall call) {
128
- if (locationPermissionFuture != null) {
129
- return locationPermissionFuture;
130
- }
131
- locationPermissionFuture = new CompletableFuture<>();
132
- requestPermissionForAlias("location", call, "locationPermissionsCallback");
133
- return locationPermissionFuture;
134
- }
135
-
136
- @PermissionCallback
137
- private void locationPermissionsCallback(PluginCall call) {
138
- if (locationPermissionFuture == null) {
139
- return;
96
+ private void proceedWithStart(PluginCall call) {
97
+ if (call.getBoolean("stale", false)) {
98
+ fetchLastLocation(call);
99
+ }
100
+ getServiceConnection().thenAccept((serviceBinder) -> {
101
+ serviceBinder.start(
102
+ call.getCallbackId(),
103
+ call.getString("backgroundTitle", "Using your location"),
104
+ call.getString("backgroundMessage", ""),
105
+ call.getFloat("distanceFilter", 0f)
106
+ );
107
+ });
140
108
  }
141
109
 
142
- requestPermissionForAlias(
143
- "notification",
144
- call,
145
- "notificationPermissionsCallback"
146
- );
147
-
148
- if (getPermissionState("location") != PermissionState.GRANTED) {
149
- locationPermissionFuture.completeExceptionally(
150
- new SecurityException("User denied location permission")
151
- );
152
- locationPermissionFuture = null;
153
- return;
110
+ private CompletableFuture<Void> requestLocationPermissions(PluginCall call) {
111
+ if (locationPermissionFuture != null) {
112
+ return locationPermissionFuture;
113
+ }
114
+ locationPermissionFuture = new CompletableFuture<>();
115
+ requestPermissionForAlias("location", call, "locationPermissionsCallback");
116
+ return locationPermissionFuture;
154
117
  }
155
118
 
156
- locationPermissionFuture.complete(null);
157
- locationPermissionFuture = null;
158
- }
119
+ @PermissionCallback
120
+ private void locationPermissionsCallback(PluginCall call) {
121
+ if (locationPermissionFuture == null) {
122
+ return;
123
+ }
159
124
 
160
- @PermissionCallback
161
- private void notificationPermissionsCallback(PluginCall call) {
162
- Logger.debug("notification permission callback");
163
- }
125
+ requestPermissionForAlias("notification", call, "notificationPermissionsCallback");
164
126
 
165
- @PluginMethod
166
- public void stop(PluginCall call) {
167
- if (serviceConnectionFuture == null) {
168
- call.resolve();
169
- }
170
- getServiceConnection()
171
- .thenAccept(service -> {
172
- var callbackId = service.stop();
173
- PluginCall savedCall = getBridge().getSavedCall(callbackId);
174
- if (savedCall != null) {
175
- savedCall.release(getBridge());
127
+ if (getPermissionState("location") != PermissionState.GRANTED) {
128
+ locationPermissionFuture.completeExceptionally(new SecurityException("User denied location permission"));
129
+ locationPermissionFuture = null;
130
+ return;
176
131
  }
177
- call.resolve();
178
- serviceConnectionFuture = null;
179
- })
180
- .exceptionally(throwable -> {
181
- call.reject("Service connection failed: " + throwable.getMessage());
182
- return null;
183
- });
184
- }
185
-
186
- @PluginMethod
187
- public void openSettings(PluginCall call) {
188
- Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
189
- Uri uri = Uri.fromParts("package", getContext().getPackageName(), null);
190
- intent.setData(uri);
191
- getContext().startActivity(intent);
192
- call.resolve();
193
- }
194
-
195
- @PluginMethod
196
- public void setPlannedRoute(PluginCall call) {
197
- String soundFile = call.getString("soundFile");
198
- if (soundFile == null || soundFile.isEmpty()) {
199
- call.reject("Sound file is required");
200
- return;
132
+
133
+ locationPermissionFuture.complete(null);
134
+ locationPermissionFuture = null;
201
135
  }
202
- if (serviceConnectionFuture == null) {
203
- call.reject(
204
- "Service not started, make sure to call start() first",
205
- "NOT_STARTED"
206
- );
207
- return;
136
+
137
+ @PermissionCallback
138
+ private void notificationPermissionsCallback(PluginCall call) {
139
+ Logger.debug("notification permission callback");
208
140
  }
209
- try {
210
- double[][] javaDoubleArray = getJavaDoubleArray(call.getArray("route"));
211
- serviceConnectionFuture
212
- .thenAccept(service -> {
213
- service.setPlannedRoute(
214
- soundFile,
215
- javaDoubleArray,
216
- call.getFloat("distance", 50f)
217
- );
218
- call.resolve();
219
- })
220
- .exceptionally(throwable -> {
221
- call.reject("Failed to set route: " + throwable.getMessage());
222
- return null;
223
- });
224
- } catch (Exception ex) {
225
- call.reject("Unable to parse route parameters");
141
+
142
+ @PluginMethod
143
+ public void stop(PluginCall call) {
144
+ if (serviceConnectionFuture == null) {
145
+ call.resolve();
146
+ }
147
+ getServiceConnection()
148
+ .thenAccept((service) -> {
149
+ var callbackId = service.stop();
150
+ PluginCall savedCall = getBridge().getSavedCall(callbackId);
151
+ if (savedCall != null) {
152
+ savedCall.release(getBridge());
153
+ }
154
+ call.resolve();
155
+ serviceConnectionFuture = null;
156
+ })
157
+ .exceptionally((throwable) -> {
158
+ call.reject("Service connection failed: " + throwable.getMessage());
159
+ return null;
160
+ });
161
+ }
162
+
163
+ @PluginMethod
164
+ public void openSettings(PluginCall call) {
165
+ Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
166
+ Uri uri = Uri.fromParts("package", getContext().getPackageName(), null);
167
+ intent.setData(uri);
168
+ getContext().startActivity(intent);
169
+ call.resolve();
226
170
  }
227
- }
228
171
 
229
- private static double[][] getJavaDoubleArray(JSArray jsArray)
230
- throws JSONException {
231
- int rows = jsArray.length();
232
- if (rows == 0) {
233
- return new double[0][2];
172
+ @PluginMethod
173
+ public void setPlannedRoute(PluginCall call) {
174
+ String soundFile = call.getString("soundFile");
175
+ if (soundFile == null || soundFile.isEmpty()) {
176
+ call.reject("Sound file is required");
177
+ return;
178
+ }
179
+ if (serviceConnectionFuture == null) {
180
+ call.reject("Service not started, make sure to call start() first", "NOT_STARTED");
181
+ return;
182
+ }
183
+ try {
184
+ double[][] javaDoubleArray = getJavaDoubleArray(call.getArray("route"));
185
+ serviceConnectionFuture
186
+ .thenAccept((service) -> {
187
+ service.setPlannedRoute(soundFile, javaDoubleArray, call.getFloat("distance", 50f));
188
+ call.resolve();
189
+ })
190
+ .exceptionally((throwable) -> {
191
+ call.reject("Failed to set route: " + throwable.getMessage());
192
+ return null;
193
+ });
194
+ } catch (Exception ex) {
195
+ call.reject("Unable to parse route parameters");
196
+ }
234
197
  }
235
198
 
236
- JSONArray firstRow = jsArray.getJSONArray(0);
237
- int cols = firstRow.length();
199
+ private static double[][] getJavaDoubleArray(JSArray jsArray) throws JSONException {
200
+ int rows = jsArray.length();
201
+ if (rows == 0) {
202
+ return new double[0][2];
203
+ }
238
204
 
239
- var javaDoubleArray = new double[rows][cols];
205
+ JSONArray firstRow = jsArray.getJSONArray(0);
206
+ int cols = firstRow.length();
240
207
 
241
- for (int i = 0; i < rows; i++) {
242
- JSONArray rowArray = jsArray.getJSONArray(i);
243
- if (rowArray.length() != cols) {
244
- throw new JSONException("Input array is not a consistent 2D array.");
245
- }
246
- for (int j = 0; j < cols; j++) {
247
- javaDoubleArray[i][j] = rowArray.getDouble(j);
248
- }
208
+ var javaDoubleArray = new double[rows][cols];
209
+
210
+ for (int i = 0; i < rows; i++) {
211
+ JSONArray rowArray = jsArray.getJSONArray(i);
212
+ if (rowArray.length() != cols) {
213
+ throw new JSONException("Input array is not a consistent 2D array.");
214
+ }
215
+ for (int j = 0; j < cols; j++) {
216
+ javaDoubleArray[i][j] = rowArray.getDouble(j);
217
+ }
218
+ }
219
+ return javaDoubleArray;
249
220
  }
250
- return javaDoubleArray;
251
- }
252
-
253
- // Checks if device-wide location services are disabled
254
- private static Boolean isLocationEnabled(Context context) {
255
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
256
- LocationManager lm = (LocationManager) context.getSystemService(
257
- Context.LOCATION_SERVICE
258
- );
259
- return lm != null && lm.isLocationEnabled();
260
- } else {
261
- return (
262
- Settings.Secure.getInt(
263
- context.getContentResolver(),
264
- Settings.Secure.LOCATION_MODE,
265
- Settings.Secure.LOCATION_MODE_OFF
266
- ) !=
267
- Settings.Secure.LOCATION_MODE_OFF
268
- );
221
+
222
+ // Checks if device-wide location services are disabled
223
+ private static Boolean isLocationEnabled(Context context) {
224
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
225
+ LocationManager lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
226
+ return lm != null && lm.isLocationEnabled();
227
+ } else {
228
+ return (
229
+ Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF) !=
230
+ Settings.Secure.LOCATION_MODE_OFF
231
+ );
232
+ }
269
233
  }
270
- }
271
-
272
- private static JSObject formatLocation(Location location) {
273
- JSObject obj = new JSObject();
274
- obj.put("latitude", location.getLatitude());
275
- obj.put("longitude", location.getLongitude());
276
- // The docs state that all Location objects have an accuracy, but then why is there a
277
- // hasAccuracy method? Better safe than sorry.
278
- obj.put(
279
- "accuracy",
280
- location.hasAccuracy() ? location.getAccuracy() : JSONObject.NULL
281
- );
282
- obj.put(
283
- "altitude",
284
- location.hasAltitude() ? location.getAltitude() : JSONObject.NULL
285
- );
286
- if (Build.VERSION.SDK_INT >= 26 && location.hasVerticalAccuracy()) {
287
- obj.put("altitudeAccuracy", location.getVerticalAccuracyMeters());
288
- } else {
289
- obj.put("altitudeAccuracy", JSONObject.NULL);
234
+
235
+ private static JSObject formatLocation(Location location) {
236
+ JSObject obj = new JSObject();
237
+ obj.put("latitude", location.getLatitude());
238
+ obj.put("longitude", location.getLongitude());
239
+ // The docs state that all Location objects have an accuracy, but then why is there a
240
+ // hasAccuracy method? Better safe than sorry.
241
+ obj.put("accuracy", location.hasAccuracy() ? location.getAccuracy() : JSONObject.NULL);
242
+ obj.put("altitude", location.hasAltitude() ? location.getAltitude() : JSONObject.NULL);
243
+ if (Build.VERSION.SDK_INT >= 26 && location.hasVerticalAccuracy()) {
244
+ obj.put("altitudeAccuracy", location.getVerticalAccuracyMeters());
245
+ } else {
246
+ obj.put("altitudeAccuracy", JSONObject.NULL);
247
+ }
248
+ // In addition to mocking locations in development, Android allows the
249
+ // installation of apps which have the power to simulate location
250
+ // readings in other apps.
251
+ obj.put("simulated", location.isFromMockProvider());
252
+ obj.put("speed", location.hasSpeed() ? location.getSpeed() : JSONObject.NULL);
253
+ obj.put("bearing", location.hasBearing() ? location.getBearing() : JSONObject.NULL);
254
+ obj.put("time", location.getTime());
255
+ return obj;
290
256
  }
291
- // In addition to mocking locations in development, Android allows the
292
- // installation of apps which have the power to simulate location
293
- // readings in other apps.
294
- obj.put("simulated", location.isFromMockProvider());
295
- obj.put(
296
- "speed",
297
- location.hasSpeed() ? location.getSpeed() : JSONObject.NULL
298
- );
299
- obj.put(
300
- "bearing",
301
- location.hasBearing() ? location.getBearing() : JSONObject.NULL
302
- );
303
- obj.put("time", location.getTime());
304
- return obj;
305
- }
306
-
307
- // Receives messages from the service.
308
- private class ServiceReceiver extends BroadcastReceiver {
309
257
 
310
- @Override
311
- public void onReceive(Context context, Intent intent) {
312
- String id = intent.getStringExtra("id");
313
- PluginCall call = getBridge().getSavedCall(id);
314
- if (call == null) {
315
- return;
316
- }
317
- Location location = intent.getParcelableExtra("location");
318
- if (location != null) {
319
- call.resolve(formatLocation(location));
320
- } else {
321
- Logger.debug("No locations received");
322
- }
258
+ // Receives messages from the service.
259
+ private class ServiceReceiver extends BroadcastReceiver {
260
+
261
+ @Override
262
+ public void onReceive(Context context, Intent intent) {
263
+ String id = intent.getStringExtra("id");
264
+ PluginCall call = getBridge().getSavedCall(id);
265
+ if (call == null) {
266
+ return;
267
+ }
268
+ Location location = intent.getParcelableExtra("location");
269
+ if (location != null) {
270
+ call.resolve(formatLocation(location));
271
+ } else {
272
+ Logger.debug("No locations received");
273
+ }
274
+ }
323
275
  }
324
- }
325
276
 
326
- @Override
327
- public void load() {
328
- super.load();
277
+ @Override
278
+ public void load() {
279
+ super.load();
280
+
281
+ // Android O requires a Notification Channel.
282
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
283
+ NotificationManager manager = (NotificationManager) getContext().getSystemService(Context.NOTIFICATION_SERVICE);
284
+ NotificationChannel channel = new NotificationChannel(
285
+ BackgroundGeolocationService.class.getPackage().getName(),
286
+ BackgroundGeolocationService.getAppString(
287
+ "capacitor_background_geolocation_notification_channel_name",
288
+ "Background Tracking",
289
+ getContext()
290
+ ),
291
+ NotificationManager.IMPORTANCE_DEFAULT
292
+ );
293
+ channel.enableLights(false);
294
+ channel.enableVibration(false);
295
+ channel.setSound(null, null);
296
+ manager.createNotificationChannel(channel);
297
+ }
329
298
 
330
- // Android O requires a Notification Channel.
331
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
332
- NotificationManager manager =
333
- (NotificationManager) getContext().getSystemService(
334
- Context.NOTIFICATION_SERVICE
299
+ LocalBroadcastManager.getInstance(this.getContext()).registerReceiver(
300
+ new ServiceReceiver(),
301
+ new IntentFilter(BackgroundGeolocationService.ACTION_BROADCAST)
335
302
  );
336
- NotificationChannel channel = new NotificationChannel(
337
- BackgroundGeolocationService.class.getPackage().getName(),
338
- BackgroundGeolocationService.getAppString(
339
- "capacitor_background_geolocation_notification_channel_name",
340
- "Background Tracking",
341
- getContext()
342
- ),
343
- NotificationManager.IMPORTANCE_DEFAULT
344
- );
345
- channel.enableLights(false);
346
- channel.enableVibration(false);
347
- channel.setSound(null, null);
348
- manager.createNotificationChannel(channel);
349
303
  }
350
304
 
351
- LocalBroadcastManager.getInstance(this.getContext()).registerReceiver(
352
- new ServiceReceiver(),
353
- new IntentFilter(BackgroundGeolocationService.ACTION_BROADCAST)
354
- );
355
- }
356
-
357
- private CompletableFuture<
358
- BackgroundGeolocationService.LocalBinder
359
- > getServiceConnection() {
360
- if (
361
- serviceConnectionFuture != null &&
362
- !serviceConnectionFuture.isCompletedExceptionally()
363
- ) {
364
- return serviceConnectionFuture;
365
- }
305
+ private CompletableFuture<BackgroundGeolocationService.LocalBinder> getServiceConnection() {
306
+ if (serviceConnectionFuture != null && !serviceConnectionFuture.isCompletedExceptionally()) {
307
+ return serviceConnectionFuture;
308
+ }
366
309
 
367
- serviceConnectionFuture = new CompletableFuture<>();
310
+ serviceConnectionFuture = new CompletableFuture<>();
368
311
 
369
- Intent serviceIntent = new Intent(
370
- this.getContext(),
371
- BackgroundGeolocationService.class
372
- );
373
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
374
- this.getContext().startForegroundService(serviceIntent);
375
- } else {
376
- this.getContext().startService(serviceIntent);
377
- }
312
+ Intent serviceIntent = new Intent(this.getContext(), BackgroundGeolocationService.class);
313
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
314
+ this.getContext().startForegroundService(serviceIntent);
315
+ } else {
316
+ this.getContext().startService(serviceIntent);
317
+ }
378
318
 
379
- this.getContext().bindService(
380
- serviceIntent,
381
- new ServiceConnection() {
382
- @Override
383
- public void onServiceConnected(ComponentName name, IBinder binder) {
384
- serviceConnectionFuture.complete(
385
- (BackgroundGeolocationService.LocalBinder) binder
386
- );
387
- }
388
-
389
- @Override
390
- public void onServiceDisconnected(ComponentName name) {
391
- serviceConnectionFuture = null;
392
- }
393
- },
394
- Context.BIND_AUTO_CREATE
395
- );
396
-
397
- return serviceConnectionFuture;
398
- }
399
-
400
- @Override
401
- protected void handleOnDestroy() {
402
- if (serviceConnectionFuture != null) {
403
- serviceConnectionFuture.thenAccept(
404
- BackgroundGeolocationService.LocalBinder::stop
405
- );
319
+ this.getContext().bindService(
320
+ serviceIntent,
321
+ new ServiceConnection() {
322
+ @Override
323
+ public void onServiceConnected(ComponentName name, IBinder binder) {
324
+ serviceConnectionFuture.complete((BackgroundGeolocationService.LocalBinder) binder);
325
+ }
326
+
327
+ @Override
328
+ public void onServiceDisconnected(ComponentName name) {
329
+ serviceConnectionFuture = null;
330
+ }
331
+ },
332
+ Context.BIND_AUTO_CREATE
333
+ );
334
+
335
+ return serviceConnectionFuture;
406
336
  }
407
337
 
408
- if (
409
- locationPermissionFuture != null && !locationPermissionFuture.isDone()
410
- ) {
411
- locationPermissionFuture.cancel(true);
338
+ @Override
339
+ protected void handleOnDestroy() {
340
+ if (serviceConnectionFuture != null) {
341
+ serviceConnectionFuture.thenAccept(BackgroundGeolocationService.LocalBinder::stop);
342
+ }
343
+
344
+ if (locationPermissionFuture != null && !locationPermissionFuture.isDone()) {
345
+ locationPermissionFuture.cancel(true);
346
+ }
347
+ super.handleOnDestroy();
412
348
  }
413
- super.handleOnDestroy();
414
- }
415
349
  }