react-native-background-geolocation 5.6.0 → 5.7.0

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.
Files changed (33) hide show
  1. package/RNBackgroundGeolocation.podspec +1 -1
  2. package/android/build.gradle +1 -1
  3. package/android/src/main/java/com/transistorsoft/rnbackgroundgeolocation/HeadlessTask.java +10 -1
  4. package/android/src/main/java/com/transistorsoft/rnbackgroundgeolocation/HeadlessTaskManager.java +6 -15
  5. package/android/src/main/java/com/transistorsoft/rnbackgroundgeolocation/RNBackgroundGeolocationModule.java +69 -31
  6. package/expo/plugin/tsconfig.tsbuildinfo +1 -1
  7. package/ios/RNBackgroundGeolocation/RNBackgroundGeolocation.mm +24 -9
  8. package/package.json +7 -5
  9. package/src/NativeModule.js +2 -2
  10. package/src/TransistorAuthorizationService.js +4 -1
  11. package/src/index.js +21 -6
  12. package/src/specs/NativeRNBackgroundGeolocation.js +2 -2
  13. package/.github/ISSUE_TEMPLATE/1-bug.yml +0 -80
  14. package/.github/ISSUE_TEMPLATE/2-help-wanted.yml +0 -82
  15. package/.github/workflows/stale.yaml +0 -25
  16. package/babel.config.js +0 -5
  17. package/jest.config.js +0 -14
  18. package/mocks/react-native.js +0 -55
  19. package/tests/enums-bridge.test.js +0 -16
  20. package/tests/enums.test.js +0 -23
  21. package/tests/insert-location.test.js +0 -51
  22. package/tests/location-filter-event.test.js +0 -30
  23. package/tests/ready-compound.test.js +0 -28
  24. package/tests/ready.test.js +0 -6
  25. package/tests/request-permission.test.js +0 -78
  26. package/tests-types/api-surface.ts +0 -33
  27. package/tests-types/config-surface.ts +0 -38
  28. package/tests-types/enums-smoke-test.ts +0 -35
  29. package/tests-types/events-typing.ts +0 -23
  30. package/tests-types/flat-config.ts +0 -0
  31. package/tests-types/ok.ts +0 -20
  32. package/tests-types/tsconfig.json +0 -14
  33. package/typedoc.json +0 -49
@@ -26,7 +26,7 @@ Pod::Spec.new do |s|
26
26
  s.source_files = 'ios/RNBackgroundGeolocation/*.{h,m,mm}'
27
27
  s.preserve_paths = 'docs', 'CHANGELOG.md', 'LICENSE', 'package.json', 'RNBackgroundGeolocation.ios.js'
28
28
 
29
- tslm_version = ENV['TSLOCATIONMANAGER_VERSION'] || '~> 4.6.0'
29
+ tslm_version = ENV['TSLOCATIONMANAGER_VERSION'] || '~> 4.7.1'
30
30
  s.dependency 'TSLocationManager', tslm_version
31
31
 
32
32
  s.libraries = 'sqlite3', 'z', 'stdc++'
@@ -9,7 +9,7 @@ def DEFAULT_TARGET_SDK_VERSION = 35
9
9
 
10
10
  // Plugin dependencies
11
11
  def DEFAULT_PLAY_SERVICES_LOCATION_VERSION = "21.3.0"
12
- def DEFAULT_TSLOCATIONMANAGER_VERSION = "4.5.+"
12
+ def DEFAULT_TSLOCATIONMANAGER_VERSION = "4.6.+"
13
13
  def DEFAULT_OK_HTTP_VERSION = "4.12.0"
14
14
  def DEFAULT_ANDROID_PERMISSIONS_VERSION = "2.1.6"
15
15
  def DEFAULT_EVENTBUS_VERSION = "3.3.1"
@@ -27,7 +27,7 @@ import java.util.Map;
27
27
 
28
28
  public class HeadlessTask {
29
29
  private static final String HEADLESS_TASK_NAME = "BackgroundGeolocation";
30
- // Hard-coded time-limit for headless-tasks is 60000 @todo configurable?
30
+ // Hard-coded time-limit for headless-tasks is 120s @todo configurable?
31
31
  private static final int TASK_TIMEOUT = 60000 * 2;
32
32
 
33
33
  /**
@@ -49,6 +49,15 @@ public class HeadlessTask {
49
49
  params = config.toMap(false);
50
50
  } else if (name.equals(EventName.LOCATION)) {
51
51
  params = event.getLocationEvent().toMap();
52
+ } else if (name.equals(EventName.LOCATION_ERROR)) {
53
+ // EventManager normalizes LocationErrorEvent to its Integer error-code — the same value
54
+ // the foreground onLocation failure-callback receives.
55
+ Object error = event.getEvent();
56
+ if (error instanceof Integer) {
57
+ clientEvent.putInt("params", (Integer) error);
58
+ } else {
59
+ clientEvent.putNull("params");
60
+ }
52
61
  } else if (name.equals(EventName.MOTIONCHANGE)) {
53
62
  params = event.getMotionChangeEvent().toMap();
54
63
  } else if (name.equals(EventName.HTTP)) {
@@ -43,23 +43,14 @@ import java.util.concurrent.atomic.AtomicInteger;
43
43
  * might build up in the queue before the Host is finally launched, when we drain the queue (see #drainTaskQueue).
44
44
  *
45
45
  * For finally sending events to the client, we wrap the RN HeadlessJsTaskConfig with our own TaskConfig class. This class
46
- * adds our own auto-incremented "taskId" field to maintain a mapping between our taskId and RN's. See #invokeStartTask.
47
- * This class appends our custom taskId into the the event params sent to Javascript, for the following purpose:
46
+ * adds our own auto-incremented "_transistorHeadlessTaskId" field to maintain a mapping between our taskId and RN's. See #invokeStartTask.
48
47
  *
49
- * ```javascript
50
- * const BackgroundGeolocationHeadlessTask = async (event) => {
51
- * console.log('[HeadlessTask] taskId: ', event.taskId); // <-- here's our custom taskId.
48
+ * The client never sees it: the wrapper installed by BackgroundGeolocation.registerHeadlessTask (src/index.js) removes
49
+ * _transistorHeadlessTaskId from the event before calling the developer's task, then, once the task's Promise settles,
50
+ * signals back to this native HeadlessTask instance that the JS task is complete: BackgroundGeolocation.finishHeadlessTask(taskId).
51
+ * This is a pretty easy task to do via EventBus -- Just create a new instance of FinishHeadlessTaskEvent(taskId);
52
52
  *
53
- * await doWork(); // <-- perform some arbitrarily long process (eg: http request).
54
- *
55
- * BackgroundGeolocation.finishHeadlessTask(event.taskId); // <-- $$$ Here's the money $$$
56
- * }
57
- * ```
58
- *
59
- * See "Here's the $money" above: We want to signal back to this native HeadlessTask instance , that our JS task is now complete.
60
- * This is a pretty easy task to do via EventBus -- Just create a new instance of FinishHeadlessTaskEvent(params.taskId);
61
- *
62
- * The code then looks up a TaskConfig from mEventQueue using the given event.taskId.
53
+ * The code then looks up a TaskConfig from mEventQueue using the given taskId.
63
54
  *
64
55
  * All this extra fussing with taking care to finish our RN HeadlessTasks seems to be more important with RN's "new architecture", where before,
65
56
  * RN seemed to automatically complete its tasks seemingly when the Javascript function stopped executing. This is why it was always so
@@ -102,6 +102,10 @@ public class RNBackgroundGeolocationModule
102
102
  private boolean mInitialized = false;
103
103
  private boolean mReady = false;
104
104
 
105
+ // Every SDK callback this module registered, so a re-registration can close the previous one.
106
+ private final List<AutoCloseable> mSubscriptions = new ArrayList<>();
107
+ private AutoCloseable mPlayServicesSubscription;
108
+
105
109
  // Map of event listener-counts
106
110
  private final HashMap<String, Integer> mListeners = new HashMap<>();
107
111
  private List<String> mEvents = new ArrayList<>();
@@ -148,21 +152,40 @@ public class RNBackgroundGeolocationModule
148
152
  private void registerEvents() {
149
153
  BackgroundGeolocation adapter = getAdapter();
150
154
 
151
- adapter.onLocation(new LocationCallback());
152
- adapter.onMotionChange(new MotionChangeCallback());
153
- adapter.onActivityChange(new ActivityChangeCallback());
154
- adapter.onLocationFilter(new LocationFilterCallback());
155
- adapter.onLocationProviderChange(new LocationProviderChangeCallback());
156
- adapter.onGeofencesChange(new GeofencesChangeCallback());
157
- adapter.onGeofence(new GeofenceCallback());
158
- adapter.onHeartbeat(new HeartbeatCallback());
159
- adapter.onHttp(new HttpResponseCallback());
160
- adapter.onSchedule(new ScheduleCallback());
161
- adapter.onPowerSaveChange(new PowerSaveChangeCallack());
162
- adapter.onConnectivityChange(new ConnectivityChangeCallback());
163
- adapter.onEnabledChange(new EnabledChangeCallback());
164
- adapter.onNotificationAction(new NotificationActionCallback());
165
- HttpService.getInstance(getReactApplicationContext()).onAuthorization(new AuthorizationCallback());
155
+ // onHostDestroy() clears mReady, so an Activity recreated for a configuration change calls
156
+ // ready() again — and the SDK's callbacks now survive that recreation, having stopped
157
+ // taking the termination path that used to clear them. addListener does not dedupe, so
158
+ // close the previous registrations or every event is delivered once more per recreation.
159
+ // Close only what this method registered: clearing them all would take the play-services
160
+ // listener with it, which only initializeLocationManager() re-registers.
161
+ for (AutoCloseable subscription : mSubscriptions) {
162
+ closeSubscription(subscription);
163
+ }
164
+ mSubscriptions.clear();
165
+
166
+ mSubscriptions.add(adapter.onLocation(new LocationCallback()));
167
+ mSubscriptions.add(adapter.onMotionChange(new MotionChangeCallback()));
168
+ mSubscriptions.add(adapter.onActivityChange(new ActivityChangeCallback()));
169
+ mSubscriptions.add(adapter.onLocationFilter(new LocationFilterCallback()));
170
+ mSubscriptions.add(adapter.onLocationProviderChange(new LocationProviderChangeCallback()));
171
+ mSubscriptions.add(adapter.onGeofencesChange(new GeofencesChangeCallback()));
172
+ mSubscriptions.add(adapter.onGeofence(new GeofenceCallback()));
173
+ mSubscriptions.add(adapter.onHeartbeat(new HeartbeatCallback()));
174
+ mSubscriptions.add(adapter.onHttp(new HttpResponseCallback()));
175
+ mSubscriptions.add(adapter.onSchedule(new ScheduleCallback()));
176
+ mSubscriptions.add(adapter.onPowerSaveChange(new PowerSaveChangeCallack()));
177
+ mSubscriptions.add(adapter.onConnectivityChange(new ConnectivityChangeCallback()));
178
+ mSubscriptions.add(adapter.onEnabledChange(new EnabledChangeCallback()));
179
+ mSubscriptions.add(adapter.onNotificationAction(new NotificationActionCallback()));
180
+ mSubscriptions.add(HttpService.getInstance(getReactApplicationContext()).onAuthorization(new AuthorizationCallback()));
181
+ }
182
+
183
+ private void closeSubscription(AutoCloseable subscription) {
184
+ try {
185
+ subscription.close();
186
+ } catch (Exception e) {
187
+ TSLog.logger.warn(TSLog.warn(e.getMessage()));
188
+ }
166
189
  }
167
190
 
168
191
  /**
@@ -391,8 +414,10 @@ public class RNBackgroundGeolocationModule
391
414
  config.updateWithJSONObject(mapToJson(setHeadlessJobService(params)));
392
415
  } else {
393
416
  if (reset) {
394
- config.reset();
395
- config.updateWithJSONObject(mapToJson(setHeadlessJobService(params)));
417
+ // One commit: a separate reset() exposed the SDK's config listeners to the
418
+ // defaults in between while it was still configured — onHostDestroy() clears
419
+ // mReady, so an Activity recreation runs this path again.
420
+ config.reset(mapToJson(setHeadlessJobService(params)));
396
421
  } else if (params.hasKey(TSAuthorization.NAME)) {
397
422
  ReadableMap readableMap = params.getMap(TSAuthorization.NAME);
398
423
  if (readableMap != null) {
@@ -415,8 +440,7 @@ public class RNBackgroundGeolocationModule
415
440
  @ReactMethod
416
441
  public void configure(ReadableMap params, final Promise response){
417
442
  final TSConfig config = TSConfig.getInstance(getReactApplicationContext());
418
- config.reset();
419
- config.updateWithJSONObject(mapToJson(setHeadlessJobService(params)));
443
+ config.reset(mapToJson(setHeadlessJobService(params)));
420
444
 
421
445
  getAdapter().ready(new TSCallback() {
422
446
  @Override public void onSuccess() { response.resolve(getState()); }
@@ -434,8 +458,7 @@ public class RNBackgroundGeolocationModule
434
458
  @ReactMethod
435
459
  public void reset(ReadableMap defaultConfig, final Promise response) {
436
460
  TSConfig config = TSConfig.getInstance(getReactApplicationContext());
437
- config.reset();
438
- config.updateWithJSONObject(mapToJson(setHeadlessJobService(defaultConfig)));
461
+ config.reset(mapToJson(setHeadlessJobService(defaultConfig)));
439
462
  response.resolve(getState());
440
463
  }
441
464
 
@@ -481,7 +504,9 @@ public class RNBackgroundGeolocationModule
481
504
  @ReactMethod
482
505
  public void changePace(final boolean moving, final Promise response) {
483
506
  getAdapter().changePace(moving, new TSCallback() {
484
- @Override public void onSuccess() { response.resolve(moving); }
507
+ // (WO-033) The State the types declare, read once the adapter reports success — the
508
+ // same helper start() resolves with. This echoed the caller's own argument until now.
509
+ @Override public void onSuccess() { response.resolve(getState()); }
485
510
  @Override public void onFailure(String error) { response.reject(error); }
486
511
  });
487
512
  }
@@ -736,10 +761,20 @@ public class RNBackgroundGeolocationModule
736
761
  }
737
762
 
738
763
  @ReactMethod
739
- public void removeGeofences(final Promise response) {
740
- // TODO allow JS api to delete a list-of-geofences.
741
- // TODO accept WritableArray geofences from Client js API, allowing to remove a set of geofences
764
+ public void removeGeofences(ReadableArray data, final Promise response) {
765
+ // (WO-047) An empty list means "remove all" to the core: a bad argument rejects, never becomes [].
766
+ if (data == null) {
767
+ response.reject("removeGeofences: identifiers must be an Array");
768
+ return;
769
+ }
742
770
  List<String> identifiers = new ArrayList<>();
771
+ for (int n=0;n<data.size();n++) {
772
+ if (data.getType(n) != ReadableType.String) {
773
+ response.reject("removeGeofences: identifier at index " + n + " is not a String");
774
+ return;
775
+ }
776
+ identifiers.add(data.getString(n));
777
+ }
743
778
  getAdapter().removeGeofences(identifiers, new TSCallback() {
744
779
  @Override public void onSuccess() {
745
780
  response.resolve(true);
@@ -831,10 +866,8 @@ public class RNBackgroundGeolocationModule
831
866
  response.resolve(mapToWritableMap(token.toMap()));
832
867
  }
833
868
  @Override public void onFailure(String error) {
834
- WritableMap params = new WritableNativeMap();
835
- params.putString("status", error);
836
- params.putString("message", error);
837
- response.reject(error);
869
+ // (WO-073) The core's status ("403") as the code: a rejection cannot carry a `status` key.
870
+ response.reject(error, error);
838
871
  }
839
872
  });
840
873
  }
@@ -1273,8 +1306,13 @@ public class RNBackgroundGeolocationModule
1273
1306
  }
1274
1307
  BackgroundGeolocation adapter = getAdapter();
1275
1308
  adapter.setActivity(activity);
1276
- // Handle play-services connect errors.
1277
- adapter.onPlayServicesConnectError((new TSPlayServicesConnectErrorCallback() {
1309
+ // Handle play-services connect errors. onHostDestroy() clears mInitialized, so a
1310
+ // recreated Activity runs this again: close the previous registration, or the
1311
+ // resolution dialog is raised once per recreation.
1312
+ if (mPlayServicesSubscription != null) {
1313
+ closeSubscription(mPlayServicesSubscription);
1314
+ }
1315
+ mPlayServicesSubscription = adapter.onPlayServicesConnectError((new TSPlayServicesConnectErrorCallback() {
1278
1316
  @Override
1279
1317
  public void onPlayServicesConnectError(int errorCode) {
1280
1318
  handlePlayServicesConnectError(errorCode);
@@ -1 +1 @@
1
- {"root":["./src/androidplugin.ts","./src/iosplugin.ts","./src/index.ts"],"version":"5.9.3"}
1
+ {"root":["./src/androidPlugin.ts","./src/iOSPlugin.ts","./src/index.ts"],"version":"5.9.3"}
@@ -163,8 +163,7 @@ RCT_EXPORT_METHOD(reset:(NSDictionary*)params
163
163
  TSConfig *config = [TSConfig sharedInstance];
164
164
  @try {
165
165
  if ([[params allKeys] count] > 0) {
166
- [config resetConfig:YES];
167
- [config updateWithDictionary:params];
166
+ [config resetWithDictionary:params]; // (WO-039) one commit, as in ready()
168
167
  } else {
169
168
  [config reset];
170
169
  }
@@ -208,8 +207,9 @@ RCT_EXPORT_METHOD(ready:(NSDictionary*)params
208
207
  [config updateWithDictionary:params];
209
208
  } else {
210
209
  if (resetFlag) {
211
- [config resetConfig:YES];
212
- [config updateWithDictionary:params];
210
+ // (WO-039) ONE commit: -resetConfig:YES then -updateWithDictionary: diffed against the
211
+ // defaults — unchanged keys re-fired every launch, omitted keys reverted with no event.
212
+ [config resetWithDictionary:params];
213
213
  } else if ([params objectForKey:@"authorization"]) {
214
214
  [config batchUpdate:^(TSConfig * _Nonnull cfg) {
215
215
  [cfg.authorization updateWithDictionary:[params objectForKey:@"authorization"]];
@@ -312,7 +312,10 @@ RCT_EXPORT_METHOD(startGeofences:(RCTPromiseResolveBlock)resolve reject:(RCTProm
312
312
  RCT_EXPORT_METHOD(changePace:(BOOL)moving resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
313
313
  {
314
314
  [locationManager changePace:moving];
315
- resolve(@(YES));
315
+ // (WO-033) The State the types declare, read after the void core call — the shape start()
316
+ // uses at :262. This resolved @(YES) until now: `true` even for changePace(false). No
317
+ // main-queue hop is added: -changePace: is not -start:, and the read is the statement after.
318
+ resolve([locationManager getState]);
316
319
  }
317
320
 
318
321
  RCT_EXPORT_METHOD(beginBackgroundTask:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
@@ -497,10 +500,21 @@ RCT_EXPORT_METHOD(removeGeofence:(NSString*)identifier resolve:(RCTPromiseResolv
497
500
  }];
498
501
  }
499
502
 
500
- RCT_EXPORT_METHOD(removeGeofences:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
503
+ RCT_EXPORT_METHOD(removeGeofences:(NSArray*)identifiers resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
501
504
  {
502
- NSArray *geofences = @[];
503
- [locationManager removeGeofences:geofences success:^{
505
+ // (WO-047) An empty list means "remove all" to the core: a bad argument rejects, never reaches it.
506
+ // In DEBUG, RCTConvert turns a non-array into nil, so nil must reject too (index.js always sends an array).
507
+ if (![identifiers isKindOfClass:[NSArray class]]) {
508
+ reject(@"remove_geofences_error", @"removeGeofences: identifiers must be an Array", nil);
509
+ return;
510
+ }
511
+ for (NSUInteger n = 0; n < identifiers.count; n++) {
512
+ if (![identifiers[n] isKindOfClass:[NSString class]]) {
513
+ reject(@"remove_geofences_error", [NSString stringWithFormat:@"removeGeofences: identifier at index %lu is not a String", (unsigned long)n], nil);
514
+ return;
515
+ }
516
+ }
517
+ [locationManager removeGeofences:identifiers success:^{
504
518
  resolve(@(YES));
505
519
  } failure:^(NSString* error) {
506
520
  reject(@"remove_geofences_error", error, nil);
@@ -692,7 +706,8 @@ RCT_EXPORT_METHOD(getTransistorToken:(NSString*)orgname
692
706
  success:^(TransistorAuthorizationToken *token) {
693
707
  resolve([token toDictionary]);
694
708
  } failure:^(NSError *error) {
695
- reject(@"get_transistor_token_error", error.localizedDescription, error);
709
+ // (WO-073) The HTTP status as the code, so the JavaScript can tell a 403 refusal from any other failure.
710
+ reject([NSString stringWithFormat:@"%ld", (long)error.code], error.localizedDescription, error);
696
711
  }];
697
712
  }
698
713
 
package/package.json CHANGED
@@ -1,14 +1,16 @@
1
1
  {
2
2
  "name": "react-native-background-geolocation",
3
- "version": "5.6.0",
3
+ "version": "5.7.0",
4
4
  "description": "The most sophisticated cross-platform background location-tracking & geofencing module with battery-conscious motion-detection intelligence",
5
5
  "scripts": {
6
- "build": "yarn run build:expo",
6
+ "build": "pnpm run build:expo",
7
7
  "test": "jest && tsc -p tests-types --noEmit",
8
8
  "build:expo": "rimraf expo/plugin/build && tsc --build ./expo/plugin",
9
9
  "docs": "node ./scripts/generate-docs.mjs",
10
10
  "docs:publish": "./scripts/publish-docs.sh",
11
- "prepublishOnly": "npm run build"
11
+ "prepublishOnly": "pnpm run build",
12
+ "preflight": "scripts/preflight.sh",
13
+ "release": "scripts/release.sh"
12
14
  },
13
15
  "codegenConfig": {
14
16
  "name": "RNBackgroundGeolocation",
@@ -25,7 +27,7 @@
25
27
  "main": "src/index.js",
26
28
  "repository": {
27
29
  "type": "git",
28
- "url": "git+ssh://git@github.com/transistorsoft/react-native-background-geolocation.git"
30
+ "url": "git+https://github.com/transistorsoft/react-native-background-geolocation.git"
29
31
  },
30
32
  "keywords": [
31
33
  "react-native",
@@ -40,7 +42,7 @@
40
42
  ],
41
43
  "dependencies": {
42
44
  "@babel/runtime": "^7.26.0",
43
- "@transistorsoft/background-geolocation-types": "^5.3.2",
45
+ "@transistorsoft/background-geolocation-types": "^5.3.8",
44
46
  "tslib": "^2.6.3"
45
47
  },
46
48
  "author": "Chris Scott <chris@transistorsoft.com>",
@@ -373,8 +373,8 @@ export default class NativeModule {
373
373
  return RNBackgroundGeolocation.addGeofences(geofences);
374
374
  }
375
375
 
376
- static removeGeofences() {
377
- return RNBackgroundGeolocation.removeGeofences();
376
+ static removeGeofences(identifiers) {
377
+ return RNBackgroundGeolocation.removeGeofences(identifiers);
378
378
  }
379
379
 
380
380
  static getGeofences() {
@@ -24,7 +24,10 @@ export default class TransistorAuthorizationService {
24
24
  console.warn('[TransistorAuthorizationService findOrCreate] ERROR: ', error);
25
25
  // Return a dummy token on error. this is a weird thing to do but it probably failed due to no network connection to demo server.
26
26
  // Once app will request the token once again after restarting one's app.
27
- if (error.status == '403') {
27
+ // (WO-073) A React Native rejection carries only code and message: the natives put the HTTP status in
28
+ // `code`, and `status` is set here so a refusal reads as it does on Capacitor, Cordova and React Native 4.x.
29
+ if (error && error.code != null && String(error.code) == '403') {
30
+ error.status = String(error.code);
28
31
  reject(error);
29
32
  return;
30
33
  }
package/src/index.js CHANGED
@@ -578,12 +578,27 @@ export default class BackgroundGeolocation {
578
578
  * Remove geofences. You may either supply an array of identifiers or nothing to destroy all geofences.
579
579
  * 1. removeGeofences() <-- Promise
580
580
  * 2. removeGeofences(['foo']) <-- Promise
581
- *
582
- * 3. removeGeofences(success, [failure])
583
- * 4. removeGeofences(['foo'], success, [failure])
584
- */
585
- static removeGeofences() {
586
- return NativeModule.removeGeofences();
581
+ * 3. removeGeofences(success, [failure]) <-- v4 callback form: remove all
582
+ */
583
+ static removeGeofences(identifiers, failure) {
584
+ if (typeof(identifiers) == 'function') {
585
+ // (WO-047) The v4 callback form removes all, exactly as v4 did.
586
+ const success = identifiers;
587
+ NativeModule.removeGeofences([]).then(success).catch(failure);
588
+ return;
589
+ }
590
+ if (identifiers == null) identifiers = [];
591
+ // (WO-047) An empty list means "remove all" in both cores: anything malformed must reject,
592
+ // never fall through to []. An index loop, not .some(), which skips the holes of a sparse list.
593
+ // Covered by tests/remove-geofences.test.js.
594
+ let valid = Array.isArray(identifiers);
595
+ for (let n = 0; valid && n < identifiers.length; n++) {
596
+ valid = typeof(identifiers[n]) == 'string';
597
+ }
598
+ if (!valid) {
599
+ return Promise.reject(new Error("BackgroundGeolocation#removeGeofences must be provided an {Array} of {String} identifiers, or nothing to remove all geofences."));
600
+ }
601
+ return NativeModule.removeGeofences(identifiers);
587
602
  }
588
603
 
589
604
  /**
@@ -24,7 +24,7 @@ export interface Spec extends TurboModule {
24
24
  +startGeofences: () => Promise<Object>;
25
25
  +stop: () => Promise<Object>;
26
26
 
27
- +changePace: (moving: boolean) => Promise<boolean>;
27
+ +changePace: (moving: boolean) => Promise<Object>; // (WO-033) State, like +start
28
28
  +getState: () => Promise<Object>;
29
29
 
30
30
  // Locations / persistence
@@ -53,7 +53,7 @@ export interface Spec extends TurboModule {
53
53
  +addGeofence: (config: Object) => Promise<boolean>;
54
54
  +addGeofences: (geofences: Array<Object>) => Promise<boolean>;
55
55
  +removeGeofence: (identifier: string) => Promise<boolean>;
56
- +removeGeofences: () => Promise<boolean>;
56
+ +removeGeofences: (identifiers: Array<string>) => Promise<boolean>;
57
57
  +getGeofences: () => Promise<Array<Object>>;
58
58
  +getGeofence: (identifier: string) => Promise<Object>;
59
59
  +geofenceExists: (identifier: string) => Promise<boolean>;
@@ -1,80 +0,0 @@
1
- name: Bug Report
2
- description: File a bug report.
3
- title: "[Bug]: "
4
- labels: ["bug", "triage"]
5
- body:
6
- - type: markdown
7
- attributes:
8
- value: |
9
- Thanks for taking the time to fill out this support request.
10
- - type: checkboxes
11
- id: terms
12
- attributes:
13
- label: Required Reading
14
- description: "- I have read the Wikis [Philosophy of Operation](./wiki/Philosophy-of-Operation) and [Debugging](./wiki/Debugging)\n- I am aware of the [API Documentation](https://transistorsoft.github.io/react-native-background-geolocation/)\n- I am aware of the [CHANGELOG](./blob/master/CHANGELOG.md)"
15
- options:
16
- - label: Confirmed
17
- required: true
18
- - type: input
19
- id: version
20
- attributes:
21
- label: Plugin Version
22
- description: What version of our software are you running?
23
- placeholder: "Consult your package.json (do not enter 'latest')"
24
- validations:
25
- required: true
26
- - type: checkboxes
27
- id: os
28
- attributes:
29
- label: Mobile operating-system(s)
30
- description: Which mobile operating-system(s) is this issue reported upon?
31
- options:
32
- - label: iOS
33
- - label: Android
34
- - type: input
35
- id: device-info
36
- attributes:
37
- label: Device Manufacturer(s) and Model(s)
38
- description: What is the device model(s) and manufacturer(s)
39
- placeholder: eg Google Pixel 6, iPhone 13 Pro
40
- validations:
41
- required: true
42
- - type: input
43
- id: device-os
44
- attributes:
45
- label: Device operating-systems(s)
46
- description: Affected device operating system
47
- placeholder: eg iOS 18.1, Android 13
48
- validations:
49
- required: true
50
- - type: input
51
- id: framework-version
52
- attributes:
53
- label: React Native / Expo version
54
- description: What version React Native / Expo are you using
55
- placeholder: "eg: 0.76.6, Expo 52 (consult your package.json)"
56
- - type: textarea
57
- id: what-happened
58
- attributes:
59
- label: What happened?
60
- description: Also tell us, what did you expect to happen?
61
- placeholder: Tell us what happened and what did you expect to happen.
62
- validations:
63
- required: true
64
- - type: textarea
65
- id: config
66
- attributes:
67
- label: Plugin Code and/or Config
68
- description: Provide the Config you're using with the `BackgroundGeolocation.ready(config)` method along with any other relevant code.
69
- placeholder: "Paste your Config or code here, eg: BackgroundGeolocation.ready(config).\n\nYour code will be automatically syntax-highlighted"
70
- render: typescript
71
- validations:
72
- required: true
73
- - type: textarea
74
- id: logs
75
- attributes:
76
- label: Relevant log output
77
- description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
78
- placeholder: "include iOS / Android logs\n- $ adb logcat -s TSLocationManager:V\n- ios XCode logs\n- Logs from BackgroundGeolocation.emailLog(address)"
79
- render: shell
80
-
@@ -1,82 +0,0 @@
1
- name: Help Wanted
2
- description: I need assistance
3
- title: "[Help Wanted]: "
4
- labels: ["triage"]
5
- body:
6
- - type: markdown
7
- attributes:
8
- value: |
9
- Thanks for taking the time to fill out this support request.
10
- - type: checkboxes
11
- id: terms
12
- attributes:
13
- label: Required Reading
14
- description: "- I have read the Wikis [Philosophy of Operation](./wiki/Philosophy-of-Operation) and [Debugging](./wiki/Debugging)\n- I am aware of the [API Documentation](https://transistorsoft.github.io/react-native-background-geolocation/)\n- I am aware of the [CHANGELOG](./blob/master/CHANGELOG.md)"
15
- options:
16
- - label: Confirmed
17
- required: true
18
- - type: input
19
- id: version
20
- attributes:
21
- label: Plugin Version
22
- description: What version of our software are you running?
23
- placeholder: "Consult your package.json (do not enter 'latest')"
24
- validations:
25
- required: true
26
- - type: checkboxes
27
- id: os
28
- attributes:
29
- label: Mobile operating-system(s)
30
- description: Which mobile operating-system(s) is this about?
31
- options:
32
- - label: iOS
33
- - label: Android
34
- validations:
35
- required: true
36
- - type: input
37
- id: device-info
38
- attributes:
39
- label: Device Manufacturer(s) and Model(s)
40
- description: What is the device model(s) and manufacturer(s)
41
- placeholder: eg Google Pixel 6, iPhone 13 Pro
42
- validations:
43
- required: true
44
- - type: input
45
- id: device-os
46
- attributes:
47
- label: Device operating-systems(s)
48
- description: Affected device operating system
49
- placeholder: eg iOS 18.1, Android 13
50
- validations:
51
- required: true
52
- - type: input
53
- id: framework-version
54
- attributes:
55
- label: React Native / Expo version
56
- description: What version React Native / Expo are you using
57
- placeholder: "eg: 0.76.6, Expo 52 (consult your package.json)"
58
- - type: textarea
59
- id: what-happened
60
- attributes:
61
- label: What do you require assistance about?
62
- validations:
63
- required: true
64
- - type: textarea
65
- id: config
66
- attributes:
67
- label: "[Optional] Plugin Code and/or Config"
68
- description: Provide the Config you're using with the `BackgroundGeolocation.ready(config)` method along with any other relevant code.
69
- placeholder: "Paste your Config or code here, eg: BackgroundGeolocation.ready(config).\n\nYour code will be automatically syntax-highlighted"
70
- render: typescript
71
- validations:
72
- required: false
73
- - type: textarea
74
- id: logs
75
- attributes:
76
- label: "[Optional] Relevant log output"
77
- description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
78
- placeholder: "include iOS / Android logs\n- $ adb logcat -s TSLocationManager:V\n- ios XCode logs\n- Logs from BackgroundGeolocation.emailLog(address)"
79
- render: shell
80
- validations:
81
- required: false
82
-
@@ -1,25 +0,0 @@
1
- name: Close inactive issues
2
- on:
3
- schedule:
4
- - cron: "30 1 * * *"
5
-
6
- jobs:
7
- close-issues:
8
- runs-on: ubuntu-latest
9
- permissions:
10
- issues: write
11
- pull-requests: write
12
- steps:
13
- - uses: actions/stale@v5
14
- with:
15
- operations-per-run: 100
16
- days-before-issue-stale: 30
17
- days-before-issue-close: 14
18
- stale-issue-label: "stale"
19
- stale-issue-message: "This issue is stale because it has been open for 30 days with no activity."
20
- close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale."
21
- days-before-pr-stale: -1
22
- days-before-pr-close: -1
23
- repo-token: ${{ secrets.GITHUB_TOKEN }}
24
-
25
-
package/babel.config.js DELETED
@@ -1,5 +0,0 @@
1
- module.exports = {
2
- presets: [
3
- ['@babel/preset-env', { targets: { node: 'current' } }]
4
- ],
5
- };
package/jest.config.js DELETED
@@ -1,14 +0,0 @@
1
- /** @type {import('jest').Config} */
2
-
3
- module.exports = {
4
- testEnvironment: 'node',
5
- roots: ['<rootDir>/src', '<rootDir>/tests'],
6
- transform: {
7
- '^.+\\.[jt]sx?$': 'babel-jest'
8
- },
9
- moduleNameMapper: {
10
- '^react-native$': '<rootDir>/mocks/react-native'
11
- },
12
- testPathIgnorePatterns: ['/node_modules/', '/dist/'],
13
- transformIgnorePatterns: ['/node_modules/']
14
- };
@@ -1,55 +0,0 @@
1
- const listeners = new Set();
2
-
3
- class NativeEventEmitter {
4
- addListener(event, cb) {
5
- const sub = { remove: () => listeners.delete(sub) };
6
- listeners.add(sub);
7
- return sub;
8
- }
9
- removeAllListeners() { listeners.clear(); }
10
- }
11
-
12
- // The native module is a TurboModule: every data method returns a Promise (not the pre-migration
13
- // callback convention). NativeModule.js returns these calls directly, so the mocks must resolve a
14
- // Promise. `log`/`playSound` are fire-and-forget (void).
15
- const RNBackgroundGeolocation = {
16
- // Minimal surface your JS calls in tests:
17
- ready: () => Promise.resolve({ enabled: false }),
18
- configure: () => Promise.resolve({}),
19
- setConfig: () => Promise.resolve({}),
20
- reset: () => Promise.resolve({ enabled: false }),
21
- getState: () => Promise.resolve({ enabled: false }),
22
- beginBackgroundTask: () => Promise.resolve(1),
23
- finish: (id) => Promise.resolve(id),
24
- addGeofence: () => Promise.resolve(),
25
- addGeofences: () => Promise.resolve(),
26
- removeGeofence: () => Promise.resolve(),
27
- removeGeofences: () => Promise.resolve(),
28
- getGeofences: () => Promise.resolve([]),
29
- getGeofence: () => Promise.resolve(null),
30
- geofenceExists: () => Promise.resolve(false),
31
- changePace: () => Promise.resolve(),
32
- getLog: () => Promise.resolve(''),
33
- destroyLog: () => Promise.resolve(),
34
- emailLog: () => Promise.resolve(),
35
- getOdometer: () => Promise.resolve(0),
36
- setOdometer: () => Promise.resolve({}),
37
- getLocations: () => Promise.resolve([]),
38
- getCount: () => Promise.resolve(0),
39
- destroyLocations: () => Promise.resolve(),
40
- insertLocation: () => Promise.resolve('00000000-0000-0000-0000-000000000000'),
41
- sync: () => Promise.resolve({ success: true }),
42
- getProviderState: () => Promise.resolve({ enabled: true }),
43
- requestPermission: () => Promise.resolve(1),
44
- requestTemporaryFullAccuracy: () => Promise.resolve(1),
45
- log: () => {},
46
- getDeviceInfo: () => Promise.resolve({ model: 'mock' }),
47
- playSound: () => {},
48
- };
49
-
50
- module.exports = {
51
- NativeModules: { RNBackgroundGeolocation },
52
- NativeEventEmitter,
53
- Platform: { OS: 'ios' },
54
- AppRegistry: { registerHeadlessTask: () => {} },
55
- };
@@ -1,16 +0,0 @@
1
- const BG = require('../src/index.js').default;
2
- const Shared = require('@transistorsoft/background-geolocation-types');
3
-
4
- test('RN enum namespaces mirror shared enum values', () => {
5
- // Spot-check full object equality for safety
6
- expect(BG.LogLevel).toEqual(Shared.LogLevel);
7
- expect(BG.DesiredAccuracy).toEqual(Shared.DesiredAccuracy);
8
- expect(BG.PersistMode).toEqual(Shared.PersistMode);
9
- expect(BG.AuthorizationStrategy).toEqual(Shared.AuthorizationStrategy);
10
- expect(BG.LocationFilterPolicy).toEqual(Shared.LocationFilterPolicy);
11
- expect(BG.KalmanProfile).toEqual(Shared.KalmanProfile);
12
- expect(BG.NotificationPriority).toEqual(Shared.NotificationPriority);
13
- expect(BG.HttpMethod).toEqual(Shared.HttpMethod);
14
- expect(BG.TriggerActivity).toEqual(Shared.TriggerActivity);
15
- expect(BG.ActivityType).toEqual(Shared.ActivityType);
16
- });
@@ -1,23 +0,0 @@
1
- const BG = require('../src/index.js').default;
2
-
3
- test('exposes attached enum namespaces', () => {
4
- const BG = require('../src/index.js').default;
5
-
6
- // Core enums
7
- expect(BG.LogLevel.Verbose).toBeDefined();
8
- expect(BG.DesiredAccuracy.High).toBeDefined();
9
- expect(BG.PersistMode.All).toBeDefined();
10
- expect(BG.AuthorizationStrategy.Jwt).toBe('jwt');
11
- expect(BG.LocationFilterPolicy.Adjust).toBeDefined();
12
- expect(BG.KalmanProfile.Default).toBeDefined();
13
-
14
- // Notification priority & activity
15
- expect(BG.NotificationPriority.Default).toBeDefined();
16
- expect(BG.NotificationPriority.Min).toBeDefined();
17
- expect(BG.ActivityType.Other).toBeDefined();
18
- expect(BG.ActivityType.AutomotiveNavigation).toBeDefined();
19
-
20
- // HTTP + trigger activity
21
- expect(BG.HttpMethod.Post).toBeDefined();
22
- expect(BG.TriggerActivity.Walking).toBeDefined();
23
- });
@@ -1,51 +0,0 @@
1
- // JS-side marshaling test for insertLocation: does the JavaScript API layer forward the caller's
2
- // location to the native bridge boundary *unchanged*, and pass the returned uuid back — with no
3
- // real SDK and no device? The native module is a jest spy standing in for the bridge; we assert on
4
- // what it receives (the args that crossed JS -> native) and what the caller ultimately gets back.
5
- //
6
- // This is the JS half of the two-halves bridge-marshaling strategy. The native half (ReadableMap ->
7
- // JSONObject) is covered by the Android Robolectric test `RNBackgroundGeolocationModuleTest`.
8
-
9
- const { NativeModules } = require('react-native'); // mapped to mocks/react-native.js via jest config
10
- const BG = require('../src/index.js').default;
11
-
12
- const native = NativeModules.RNBackgroundGeolocation;
13
-
14
- describe('insertLocation — JS -> native bridge marshaling', () => {
15
- afterEach(() => {
16
- jest.restoreAllMocks();
17
- });
18
-
19
- test('forwards the location to the native module unchanged and resolves its uuid', async () => {
20
- const insert = jest.spyOn(native, 'insertLocation').mockResolvedValue('uuid-abc');
21
-
22
- const location = {
23
- coords: { latitude: 45.5152, longitude: -73.6104 },
24
- timestamp: 1705314600000, // numeric epoch
25
- extras: { source: 'import' },
26
- };
27
-
28
- const uuid = await BG.insertLocation(location);
29
-
30
- // The exact object reaches the native boundary — no mangling by index.js / NativeModule.
31
- expect(insert).toHaveBeenCalledTimes(1);
32
- expect(insert).toHaveBeenCalledWith(location);
33
-
34
- // Type/shape preserved across the JS layers: numeric timestamp stays a number, coords/extras nested.
35
- const [passed] = insert.mock.calls[0];
36
- expect(typeof passed.timestamp).toBe('number');
37
- expect(passed.coords).toEqual({ latitude: 45.5152, longitude: -73.6104 });
38
- expect(passed.extras).toEqual({ source: 'import' });
39
-
40
- // The uuid string flows back out through NativeModule + index.js.
41
- expect(uuid).toBe('uuid-abc');
42
- });
43
-
44
- test('propagates a native rejection to the caller', async () => {
45
- jest.spyOn(native, 'insertLocation').mockRejectedValue(new Error('insert_location_error'));
46
-
47
- await expect(
48
- BG.insertLocation({ coords: { latitude: 0, longitude: 0 }, timestamp: 0 })
49
- ).rejects.toThrow('insert_location_error');
50
- });
51
- });
@@ -1,30 +0,0 @@
1
- jest.mock('../src/NativeModule', () => ({
2
- addListener: jest.fn(() => ({ remove: jest.fn() })),
3
- }));
4
-
5
- const NativeModule = require('../src/NativeModule');
6
- const BG = require('../src/index.js').default;
7
- const Shared = require('@transistorsoft/background-geolocation-types');
8
-
9
- // onLocationFilter event wiring (issue #2585).
10
- test('exposes the locationfilter event name + subscription method', () => {
11
- // Shared enum carries the new event name.
12
- expect(Shared.Event.LocationFilter).toBe('locationfilter');
13
-
14
- // Backward-compat static getter mirrors the shared enum.
15
- expect(BG.EVENT_LOCATIONFILTER).toBe('locationfilter');
16
-
17
- // Subscription method is exposed.
18
- expect(typeof BG.onLocationFilter).toBe('function');
19
- });
20
-
21
- test('onLocationFilter routes through the native bridge with the locationfilter event', () => {
22
- NativeModule.addListener.mockClear();
23
- const cb = jest.fn();
24
-
25
- const subscription = BG.onLocationFilter(cb);
26
-
27
- expect(NativeModule.addListener).toHaveBeenCalledWith('locationfilter', cb);
28
- expect(subscription).toBeDefined();
29
- expect(typeof subscription.remove).toBe('function');
30
- });
@@ -1,28 +0,0 @@
1
- jest.mock('../src/NativeModule', () => ({
2
- ready: jest.fn().mockResolvedValue({ enabled: false })
3
- }));
4
-
5
- const NativeModule = require('../src/NativeModule');
6
- const BG = require('../src/index.js').default;
7
-
8
- test('ready forwards compound config to native', async () => {
9
- const cfg = {
10
- logger: { logLevel: BG.LogLevel.Verbose },
11
- geolocation: {
12
- desiredAccuracy: BG.DesiredAccuracy.High,
13
- distanceFilter: 10,
14
- },
15
- http: {
16
- url: 'https://example.com',
17
- autoSync: true,
18
- },
19
- };
20
-
21
- await BG.ready(cfg);
22
-
23
- expect(NativeModule.ready).toHaveBeenCalledTimes(1);
24
- const [arg] = NativeModule.ready.mock.calls[0];
25
-
26
- // Same object shape (or deep-equal)
27
- expect(arg).toMatchObject(cfg);
28
- });
@@ -1,6 +0,0 @@
1
- const BG = require('../src/index.js').default;
2
-
3
- test('ready resolves and returns state', async () => {
4
- const state = await BG.ready({ logger: { logLevel: BG.LogLevel.Verbose } });
5
- expect(state.enabled).toBe(false);
6
- });
@@ -1,78 +0,0 @@
1
- // (WO-007) JS-side marshaling for the per-permission requestPermission API: does the
2
- // JavaScript layer forward the Permission selector to the native bridge boundary, and
3
- // normalize native rejections to the bare AuthorizationStatus value the cross-platform
4
- // contract documents? The native module is a jest spy standing in for the bridge.
5
-
6
- const { NativeModules } = require('react-native'); // mapped to mocks/react-native.js via jest config
7
- const BG = require('../src/index.js').default;
8
- const { Permission, AuthorizationStatus } = require('../src/index.js');
9
-
10
- const native = NativeModules.RNBackgroundGeolocation;
11
-
12
- describe('named runtime exports (WO-007 regression)', () => {
13
- // index.d.ts has always promised `export *` of the shared types package, but the
14
- // runtime module exported only the default class — named VALUE imports were
15
- // silently undefined (field: "Cannot read property 'Location' of undefined").
16
- test('Permission and AuthorizationStatus are real values, importable by name', () => {
17
- expect(Permission).toBeDefined();
18
- expect(Permission.Location).toBe('location');
19
- expect(Permission.Motion).toBe('motion');
20
- expect(AuthorizationStatus).toBeDefined();
21
- expect(AuthorizationStatus.DeniedAlways).toBe(5);
22
- // The class statics remain, unchanged.
23
- expect(BG.Permission).toBe(Permission);
24
- expect(BG.AuthorizationStatus).toBe(AuthorizationStatus);
25
- });
26
-
27
- // Drift-proof: EVERY runtime value the types package exports must be re-exported
28
- // by name from this module — a hardcoded list rots (the first fix missed 7 of 22).
29
- test('every types-package runtime export is re-exported by name', () => {
30
- const typesPkg = require('@transistorsoft/background-geolocation-types');
31
- const sdk = require('../src/index.js');
32
- const runtimeKeys = Object.keys(typesPkg).filter((k) => k !== 'default' && k !== '__esModule');
33
- expect(runtimeKeys.length).toBeGreaterThan(0);
34
- for (const key of runtimeKeys) {
35
- expect(sdk[key]).toBe(typesPkg[key]);
36
- }
37
- });
38
- });
39
-
40
- describe('requestPermission — JS -> native bridge marshaling (WO-007)', () => {
41
- afterEach(() => {
42
- jest.restoreAllMocks();
43
- });
44
-
45
- test('forwards the permission selector to the native module', async () => {
46
- const request = jest.spyOn(native, 'requestPermission').mockResolvedValue(3);
47
-
48
- await expect(BG.requestPermission('location')).resolves.toBe(3);
49
- expect(request).toHaveBeenLastCalledWith('location');
50
-
51
- await expect(BG.requestPermission('motion')).resolves.toBe(3);
52
- expect(request).toHaveBeenLastCalledWith('motion');
53
- });
54
-
55
- test('the no-argument form passes an explicit null (both bridge architectures expect the slot)', async () => {
56
- const request = jest.spyOn(native, 'requestPermission').mockResolvedValue(4);
57
-
58
- await expect(BG.requestPermission()).resolves.toBe(4);
59
- expect(request).toHaveBeenLastCalledWith(null);
60
- });
61
-
62
- test('normalizes a native rejection to the bare AuthorizationStatus carried in the error code', async () => {
63
- // Both natives reject with code = String(status).
64
- const error = new Error('5');
65
- error.code = '5';
66
- jest.spyOn(native, 'requestPermission').mockRejectedValue(error);
67
-
68
- await expect(BG.requestPermission('motion')).rejects.toBe(5); // DeniedAlways
69
- });
70
-
71
- test('passes an unparseable native error through unchanged', async () => {
72
- const error = new Error('bridge exploded');
73
- error.code = 'E_UNEXPECTED';
74
- jest.spyOn(native, 'requestPermission').mockRejectedValue(error);
75
-
76
- await expect(BG.requestPermission()).rejects.toBe(error);
77
- });
78
- });
@@ -1,33 +0,0 @@
1
- // Type-surface assertions — compile-only, no runtime. (WO-007)
2
- //
3
- // Verifies the per-permission requestPermission API against the published
4
- // @transistorsoft/background-geolocation-types surface this package re-exports.
5
- import BackgroundGeolocation, {
6
- Permission,
7
- AuthorizationStatus,
8
- } from "react-native-background-geolocation";
9
-
10
- async function requestPermissionSurface(): Promise<void> {
11
- // A-form: no argument — everything the configuration requires.
12
- const all: AuthorizationStatus = await BackgroundGeolocation.requestPermission();
13
-
14
- // B-form: location only.
15
- const location: AuthorizationStatus =
16
- await BackgroundGeolocation.requestPermission(Permission.Location);
17
-
18
- // C-form: motion only; DeniedAlways is the Android permanent-denial status.
19
- try {
20
- const motion: AuthorizationStatus =
21
- await BackgroundGeolocation.requestPermission(Permission.Motion);
22
- void motion;
23
- } catch (status) {
24
- if (status === AuthorizationStatus.DeniedAlways) {
25
- // Only the app-settings screen can restore the motion permission now.
26
- }
27
- }
28
-
29
- void all;
30
- void location;
31
- }
32
-
33
- void requestPermissionSurface;
@@ -1,38 +0,0 @@
1
- import BackgroundGeolocation, {
2
- type Config as RNConfig,
3
- type State
4
- } from 'react-native-background-geolocation';
5
-
6
- const config: RNConfig = {
7
- logger: {
8
- logLevel: BackgroundGeolocation.LogLevel.Verbose,
9
- },
10
- geolocation: {
11
- desiredAccuracy: BackgroundGeolocation.DesiredAccuracy.High,
12
- distanceFilter: 10,
13
- },
14
- http: {
15
- url: 'https://example.com',
16
- autoSync: true,
17
- },
18
- persistence: {
19
- persistMode: BackgroundGeolocation.PersistMode.All,
20
- maxDaysToPersist: 7,
21
- },
22
- app: {
23
- stopOnTerminate: false,
24
- startOnBoot: true,
25
- },
26
- activity: {
27
- disableStopDetection: false,
28
- },
29
- };
30
-
31
- // Ensure `ready` accepts RNConfig and returns a Promise-like thing
32
- BackgroundGeolocation.ready(config).then((state: State) => {
33
- const enabled: boolean = state.enabled;
34
- void enabled;
35
- });
36
-
37
- // no runtime; compile-only
38
- export {};
@@ -1,35 +0,0 @@
1
- // Type-only smoke test to ensure static enum members on BackgroundGeolocation
2
- // align with the shared @transistorsoft/background-geolocation-types definitions.
3
- // This test runs via `pnpm run test:types` using tsc --noEmit.
4
-
5
- import BackgroundGeolocation from 'react-native-background-geolocation';
6
-
7
- // Accessors — ensure all enum namespaces exist and have valid members.
8
- const logLevelVerbose = BackgroundGeolocation.LogLevel.Verbose;
9
- const desiredAccuracyHigh = BackgroundGeolocation.DesiredAccuracy.High;
10
- const persistModeAll = BackgroundGeolocation.PersistMode.All;
11
- const authJwt = BackgroundGeolocation.AuthorizationStrategy.Jwt;
12
- const policyAdjust = BackgroundGeolocation.LocationFilterPolicy.Adjust;
13
- const kalmanDefault = BackgroundGeolocation.KalmanProfile.Default;
14
- const httpPost = BackgroundGeolocation.HttpMethod.Post;
15
- const triggerWalking = BackgroundGeolocation.TriggerActivity.Walking;
16
- const notificationDefault = BackgroundGeolocation.NotificationPriority.Default;
17
- const activityOther = BackgroundGeolocation.ActivityType.Other;
18
-
19
- // Assignments to typed constants to ensure they match the exported type shapes.
20
- let testLogLevel: typeof BackgroundGeolocation.LogLevel = BackgroundGeolocation.LogLevel;
21
- let testDesiredAccuracy: typeof BackgroundGeolocation.DesiredAccuracy = BackgroundGeolocation.DesiredAccuracy;
22
- let testPersistMode: typeof BackgroundGeolocation.PersistMode = BackgroundGeolocation.PersistMode;
23
- let testAuthorizationStrategy: typeof BackgroundGeolocation.AuthorizationStrategy = BackgroundGeolocation.AuthorizationStrategy;
24
- let testLocationFilterPolicy: typeof BackgroundGeolocation.LocationFilterPolicy = BackgroundGeolocation.LocationFilterPolicy;
25
- let testKalmanProfile: typeof BackgroundGeolocation.KalmanProfile = BackgroundGeolocation.KalmanProfile;
26
- let testHttpMethod: typeof BackgroundGeolocation.HttpMethod = BackgroundGeolocation.HttpMethod;
27
- let testTriggerActivity: typeof BackgroundGeolocation.TriggerActivity = BackgroundGeolocation.TriggerActivity;
28
-
29
- let testNotificationPriority: typeof BackgroundGeolocation.NotificationPriority =
30
- BackgroundGeolocation.NotificationPriority;
31
- let testActivityType: typeof BackgroundGeolocation.ActivityType =
32
- BackgroundGeolocation.ActivityType;
33
-
34
- // No runtime code — this is a static type check only.
35
- export {};
@@ -1,23 +0,0 @@
1
- import BackgroundGeolocation, {
2
- type Location,
3
- type GeofenceEvent
4
- } from 'react-native-background-geolocation';
5
-
6
-
7
- // callbacks should see the bridged types
8
- BackgroundGeolocation.onLocation((location: Location) => {
9
- const loc: Location = location;
10
- console.log(loc.coords.latitude, loc.is_moving);
11
- });
12
-
13
- BackgroundGeolocation.onGeofence((event: GeofenceEvent) => {
14
- const gfEvent: GeofenceEvent = event;
15
- console.log(gfEvent.identifier, gfEvent.location.coords.longitude);
16
- });
17
-
18
- // Ensure that invalid usage fails if uncommented:
19
- // BackgroundGeolocation.onLocation((location) => {
20
- // const bad: string = location; // should be type error
21
- // });
22
-
23
- export {};
File without changes
package/tests-types/ok.ts DELETED
@@ -1,20 +0,0 @@
1
- import BackgroundGeolocation, { type Geofence } from 'react-native-background-geolocation';
2
- import type { Config as SharedConfig } from '@transistorsoft/background-geolocation-types';
3
-
4
- const gf: Geofence = {
5
- identifier: 'home',
6
- latitude: 1,
7
- longitude: 2,
8
- radius: 150,
9
- notifyOnEntry: true,
10
- };
11
-
12
- const compoundConfig: SharedConfig = {
13
- logger: { logLevel: BackgroundGeolocation.LogLevel.Verbose },
14
- geolocation: {
15
- desiredAccuracy: BackgroundGeolocation.DesiredAccuracy.High,
16
- distanceFilter: 10,
17
- },
18
- };
19
-
20
- BackgroundGeolocation.ready(compoundConfig);
@@ -1,14 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "strict": true,
4
- "target": "ES2020",
5
- "module": "ESNext",
6
- "moduleResolution": "Node",
7
- "skipLibCheck": true,
8
- "baseUrl": "..",
9
- "paths": {
10
- "react-native-background-geolocation": ["src/index.d.ts"]
11
- }
12
- },
13
- "include": ["./**/*.ts"]
14
- }
package/typedoc.json DELETED
@@ -1,49 +0,0 @@
1
- {
2
- "$schema": "https://typedoc.org/schema.json",
3
- "entryPoints": [
4
- "../../background-geolocation-types/src/docs-entry.ts"
5
- ],
6
- "tsconfig": "../../background-geolocation-types/tsconfig.json",
7
-
8
- "name": "react-native-background-geolocation",
9
- "favicon": "../../background-geolocation-types/assets/images/favicon.ico",
10
- "includeVersion": false,
11
- "out": "docs",
12
- "readme": "none",
13
- "disableSources": true,
14
-
15
- "sort": ["source-order"],
16
- "highlightLanguages": [
17
- "ts",
18
- "js",
19
- "json",
20
- "xml",
21
- "bash",
22
- "console",
23
- "erb",
24
- "java"
25
- ],
26
-
27
- "categoryOrder": [
28
- "Primary API",
29
- "Config",
30
- "Events",
31
- "Data",
32
- "Logger",
33
- "Device",
34
- "*"
35
- ],
36
- "categorizeByGroup": false,
37
- "useFirstParagraphOfCommentAsSummary": true,
38
- "excludeInternal": true,
39
- "excludePrivate": true,
40
- "excludeProtected": true,
41
-
42
- "gitRevision": "main",
43
-
44
- "plugin": [
45
- "../../background-geolocation-types/tools/typedoc-plugin-gitlink/index.cjs",
46
- "../../background-geolocation-types/tools/typedoc-plugin-mediaplayer/index.cjs",
47
- "../../background-geolocation-types/tools/typedoc-plugin-site/index.cjs"
48
- ]
49
- }