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.
- package/RNBackgroundGeolocation.podspec +1 -1
- package/android/build.gradle +1 -1
- package/android/src/main/java/com/transistorsoft/rnbackgroundgeolocation/HeadlessTask.java +10 -1
- package/android/src/main/java/com/transistorsoft/rnbackgroundgeolocation/HeadlessTaskManager.java +6 -15
- package/android/src/main/java/com/transistorsoft/rnbackgroundgeolocation/RNBackgroundGeolocationModule.java +69 -31
- package/expo/plugin/tsconfig.tsbuildinfo +1 -1
- package/ios/RNBackgroundGeolocation/RNBackgroundGeolocation.mm +24 -9
- package/package.json +7 -5
- package/src/NativeModule.js +2 -2
- package/src/TransistorAuthorizationService.js +4 -1
- package/src/index.js +21 -6
- package/src/specs/NativeRNBackgroundGeolocation.js +2 -2
- package/.github/ISSUE_TEMPLATE/1-bug.yml +0 -80
- package/.github/ISSUE_TEMPLATE/2-help-wanted.yml +0 -82
- package/.github/workflows/stale.yaml +0 -25
- package/babel.config.js +0 -5
- package/jest.config.js +0 -14
- package/mocks/react-native.js +0 -55
- package/tests/enums-bridge.test.js +0 -16
- package/tests/enums.test.js +0 -23
- package/tests/insert-location.test.js +0 -51
- package/tests/location-filter-event.test.js +0 -30
- package/tests/ready-compound.test.js +0 -28
- package/tests/ready.test.js +0 -6
- package/tests/request-permission.test.js +0 -78
- package/tests-types/api-surface.ts +0 -33
- package/tests-types/config-surface.ts +0 -38
- package/tests-types/enums-smoke-test.ts +0 -35
- package/tests-types/events-typing.ts +0 -23
- package/tests-types/flat-config.ts +0 -0
- package/tests-types/ok.ts +0 -20
- package/tests-types/tsconfig.json +0 -14
- 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.
|
|
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++'
|
package/android/build.gradle
CHANGED
|
@@ -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.
|
|
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
|
|
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)) {
|
package/android/src/main/java/com/transistorsoft/rnbackgroundgeolocation/HeadlessTaskManager.java
CHANGED
|
@@ -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 "
|
|
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
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
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
|
-
*
|
|
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
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
adapter.
|
|
163
|
-
adapter.
|
|
164
|
-
adapter.
|
|
165
|
-
|
|
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
|
-
|
|
395
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
741
|
-
|
|
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
|
-
|
|
835
|
-
|
|
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
|
-
|
|
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/
|
|
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
|
|
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
|
-
|
|
212
|
-
|
|
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
|
-
|
|
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
|
-
|
|
503
|
-
|
|
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
|
-
|
|
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.
|
|
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": "
|
|
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": "
|
|
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+
|
|
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.
|
|
45
|
+
"@transistorsoft/background-geolocation-types": "^5.3.8",
|
|
44
46
|
"tslib": "^2.6.3"
|
|
45
47
|
},
|
|
46
48
|
"author": "Chris Scott <chris@transistorsoft.com>",
|
package/src/NativeModule.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
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<
|
|
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
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
|
-
};
|
package/mocks/react-native.js
DELETED
|
@@ -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
|
-
});
|
package/tests/enums.test.js
DELETED
|
@@ -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
|
-
});
|
package/tests/ready.test.js
DELETED
|
@@ -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
|
-
}
|