react-native-bg-geolocation 0.2.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/BgGeolocation.podspec +39 -0
- package/LICENSE +20 -0
- package/README.md +366 -0
- package/android/build.gradle +69 -0
- package/android/src/main/AndroidManifest.xml +53 -0
- package/android/src/main/java/com/bggeolocation/BgGeolocationActivityRecognitionReceiver.kt +116 -0
- package/android/src/main/java/com/bggeolocation/BgGeolocationBootReceiver.kt +44 -0
- package/android/src/main/java/com/bggeolocation/BgGeolocationForegroundService.kt +373 -0
- package/android/src/main/java/com/bggeolocation/BgGeolocationGeofenceReceiver.kt +55 -0
- package/android/src/main/java/com/bggeolocation/BgGeolocationHeadlessTask.kt +138 -0
- package/android/src/main/java/com/bggeolocation/BgGeolocationModule.kt +1030 -0
- package/android/src/main/java/com/bggeolocation/BgGeolocationMotionStateMachine.kt +159 -0
- package/android/src/main/java/com/bggeolocation/BgGeolocationPackage.kt +31 -0
- package/android/src/main/res/drawable/bg_geo_notification.xml +9 -0
- package/ios/BgGeolocation.h +14 -0
- package/ios/BgGeolocation.mm +709 -0
- package/ios/engine/AtomicBoolean.swift +48 -0
- package/ios/engine/BGActivityChangeEvent.swift +20 -0
- package/ios/engine/BGActivityConfig.swift +71 -0
- package/ios/engine/BGAppConfig.swift +92 -0
- package/ios/engine/BGAppState.swift +147 -0
- package/ios/engine/BGAuthorization.swift +85 -0
- package/ios/engine/BGAuthorizationAlertPresenter.swift +39 -0
- package/ios/engine/BGAuthorizationConfig.swift +50 -0
- package/ios/engine/BGAuthorizationEvent.swift +40 -0
- package/ios/engine/BGBackgroundTaskManager.swift +143 -0
- package/ios/engine/BGCLRouter.swift +101 -0
- package/ios/engine/BGCallback.swift +19 -0
- package/ios/engine/BGConfig.swift +440 -0
- package/ios/engine/BGConfigModuleBase.swift +180 -0
- package/ios/engine/BGConfigOLD.swift +582 -0
- package/ios/engine/BGConnectivityChangeEvent.swift +15 -0
- package/ios/engine/BGCrashDetector.swift +122 -0
- package/ios/engine/BGCurrentPositionRequest.swift +87 -0
- package/ios/engine/BGDataStore.swift +75 -0
- package/ios/engine/BGDatabase.swift +677 -0
- package/ios/engine/BGDatabasePool.swift +220 -0
- package/ios/engine/BGDatabaseQueue.swift +215 -0
- package/ios/engine/BGDateUtils.swift +26 -0
- package/ios/engine/BGDeviceInfo.swift +54 -0
- package/ios/engine/BGDeviceManager.swift +65 -0
- package/ios/engine/BGEnabledChangeEvent.swift +11 -0
- package/ios/engine/BGEnv.swift +17 -0
- package/ios/engine/BGEventBus.swift +83 -0
- package/ios/engine/BGEventManager.swift +169 -0
- package/ios/engine/BGEventNames.swift +51 -0
- package/ios/engine/BGGeofence.swift +233 -0
- package/ios/engine/BGGeofenceDAO.swift +152 -0
- package/ios/engine/BGGeofenceEvent.swift +42 -0
- package/ios/engine/BGGeofenceLocationRequest.swift +94 -0
- package/ios/engine/BGGeofenceManager.swift +315 -0
- package/ios/engine/BGGeofenceTransition.swift +97 -0
- package/ios/engine/BGGeofencesChangeEvent.swift +26 -0
- package/ios/engine/BGGeolocationConfig.swift +136 -0
- package/ios/engine/BGHeartbeatEvent.swift +31 -0
- package/ios/engine/BGHeartbeatService.swift +51 -0
- package/ios/engine/BGHttpConfig.swift +105 -0
- package/ios/engine/BGHttpErrorCodes.swift +63 -0
- package/ios/engine/BGHttpEvent.swift +34 -0
- package/ios/engine/BGHttpRequest.swift +126 -0
- package/ios/engine/BGHttpResponse.swift +93 -0
- package/ios/engine/BGHttpService.swift +428 -0
- package/ios/engine/BGKalmanFilter.swift +105 -0
- package/ios/engine/BGLMActionNames.swift +55 -0
- package/ios/engine/BGLicenseManager.swift +26 -0
- package/ios/engine/BGLiveActivityManager.swift +327 -0
- package/ios/engine/BGLocation.swift +311 -0
- package/ios/engine/BGLocationAuthorization.swift +427 -0
- package/ios/engine/BGLocationDAO.swift +252 -0
- package/ios/engine/BGLocationErrors.swift +28 -0
- package/ios/engine/BGLocationEvent.swift +43 -0
- package/ios/engine/BGLocationFilter.swift +82 -0
- package/ios/engine/BGLocationFilterConfig.swift +57 -0
- package/ios/engine/BGLocationHelper.swift +54 -0
- package/ios/engine/BGLocationManager.swift +662 -0
- package/ios/engine/BGLocationMetricsEngine.swift +116 -0
- package/ios/engine/BGLocationRequestService.swift +459 -0
- package/ios/engine/BGLocationSatisfier.swift +14 -0
- package/ios/engine/BGLocationStreamEvent.swift +27 -0
- package/ios/engine/BGLog.swift +337 -0
- package/ios/engine/BGLogLevel.swift +26 -0
- package/ios/engine/BGLoggerConfig.swift +60 -0
- package/ios/engine/BGMotionActivity.swift +31 -0
- package/ios/engine/BGMotionActivityClassifier.swift +108 -0
- package/ios/engine/BGMotionActivityManagerAdapter.swift +40 -0
- package/ios/engine/BGMotionActivitySource.swift +46 -0
- package/ios/engine/BGMotionDetector.swift +377 -0
- package/ios/engine/BGMotionPermissionManager.swift +50 -0
- package/ios/engine/BGNativeLogger.swift +48 -0
- package/ios/engine/BGNotificaitons.swift +37 -0
- package/ios/engine/BGOdometer.swift +66 -0
- package/ios/engine/BGPersistenceConfig.swift +29 -0
- package/ios/engine/BGPolygonStreamRequest.swift +48 -0
- package/ios/engine/BGPowerSaveChangeEvent.swift +12 -0
- package/ios/engine/BGPropertySpec.swift +29 -0
- package/ios/engine/BGProviderChangeEvent.swift +31 -0
- package/ios/engine/BGQueue.swift +50 -0
- package/ios/engine/BGRPC.swift +194 -0
- package/ios/engine/BGReachability.swift +58 -0
- package/ios/engine/BGResultSet.swift +157 -0
- package/ios/engine/BGSchedule.swift +228 -0
- package/ios/engine/BGScheduleEvent.swift +13 -0
- package/ios/engine/BGScheduler.swift +116 -0
- package/ios/engine/BGSingleLocationRequest.swift +49 -0
- package/ios/engine/BGStreamLocationRequest.swift +42 -0
- package/ios/engine/BGTemplate.swift +54 -0
- package/ios/engine/BGTimerService.swift +46 -0
- package/ios/engine/BGTrackingAudioManager.swift +286 -0
- package/ios/engine/BGTrackingService.swift +879 -0
- package/ios/engine/BGWatchPositionRequest.swift +63 -0
- package/ios/engine/DatabaseQueue.swift +47 -0
- package/ios/engine/LogQuery.swift +10 -0
- package/ios/engine/SQLQuery.swift +65 -0
- package/ios/engine/TransistorAuthorizationToken.swift +182 -0
- package/ios/liveactivity/BGLiveTrackingAttributes.swift +52 -0
- package/ios/locationpush/BGLocationPushDeliverer.swift +260 -0
- package/ios/locationpush/BGLocationPushService.swift +161 -0
- package/ios/locationpush/BGLocationPushShared.swift +98 -0
- package/ios/locationpush/BGLocationPushSocketClient.swift +198 -0
- package/lib/module/NativeBgGeolocation.js +5 -0
- package/lib/module/NativeBgGeolocation.js.map +1 -0
- package/lib/module/events.js +20 -0
- package/lib/module/events.js.map +1 -0
- package/lib/module/index.js +706 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/types.js +2 -0
- package/lib/module/types.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/NativeBgGeolocation.d.ts +57 -0
- package/lib/typescript/src/NativeBgGeolocation.d.ts.map +1 -0
- package/lib/typescript/src/events.d.ts +18 -0
- package/lib/typescript/src/events.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +238 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +229 -0
- package/lib/typescript/src/types.d.ts.map +1 -0
- package/package.json +141 -0
- package/src/NativeBgGeolocation.ts +236 -0
- package/src/events.ts +17 -0
- package/src/index.tsx +935 -0
- package/src/types.ts +254 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import UIKit
|
|
3
|
+
import CoreLocation
|
|
4
|
+
|
|
5
|
+
@objc public class BGBackgroundTaskManager: NSObject, CLLocationManagerDelegate {
|
|
6
|
+
|
|
7
|
+
private static var _sharedInstance: BGBackgroundTaskManager?
|
|
8
|
+
private static let lock = NSLock()
|
|
9
|
+
|
|
10
|
+
public var backgroundTasks: [UIBackgroundTaskIdentifier] = []
|
|
11
|
+
public var preventSuspendTasks: [UIBackgroundTaskIdentifier] = []
|
|
12
|
+
@objc public var locationManager: CLLocationManager?
|
|
13
|
+
@objc public var preventSuspendTimer: Timer?
|
|
14
|
+
@objc public var acquisitionBufferTimer: Timer?
|
|
15
|
+
|
|
16
|
+
@objc public class func sharedInstance() -> BGBackgroundTaskManager {
|
|
17
|
+
lock.lock()
|
|
18
|
+
defer { lock.unlock() }
|
|
19
|
+
if _sharedInstance == nil {
|
|
20
|
+
_sharedInstance = BGBackgroundTaskManager()
|
|
21
|
+
}
|
|
22
|
+
return _sharedInstance!
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@objc public override init() {
|
|
26
|
+
super.init()
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
deinit {
|
|
30
|
+
NotificationCenter.default.removeObserver(self)
|
|
31
|
+
stopPreventSuspendTimer()
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@objc public func acquireBackgroundTime() {
|
|
35
|
+
let task = UIApplication.shared.beginBackgroundTask(expirationHandler: { [weak self] in
|
|
36
|
+
self?.stopKeepAlive()
|
|
37
|
+
})
|
|
38
|
+
if task != .invalid {
|
|
39
|
+
backgroundTasks.append(task)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@objc public func createBackgroundTask() -> UIBackgroundTaskIdentifier {
|
|
44
|
+
return UIApplication.shared.beginBackgroundTask(expirationHandler: nil)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@objc public func stopBackgroundTask(_ task: UIBackgroundTaskIdentifier) {
|
|
48
|
+
UIApplication.shared.endBackgroundTask(task)
|
|
49
|
+
backgroundTasks.removeAll { $0 == task }
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@objc public func startKeepAlive() {
|
|
53
|
+
acquireBackgroundTime()
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@objc public func stopKeepAlive() {
|
|
57
|
+
for task in backgroundTasks {
|
|
58
|
+
UIApplication.shared.endBackgroundTask(task)
|
|
59
|
+
}
|
|
60
|
+
backgroundTasks.removeAll()
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
@objc public func startPreventSuspend(_ task: UIBackgroundTaskIdentifier) {
|
|
64
|
+
// Begin a REAL UIBackgroundTask so a backgrounded app gets an extended
|
|
65
|
+
// (~30s) execution window. Previously this just appended the passed
|
|
66
|
+
// identifier (always .invalid) and did nothing. The passed argument is
|
|
67
|
+
// ignored. Note: true stationary/kill-state longevity comes from SLC +
|
|
68
|
+
// region wake, not from background tasks — this only bridges short gaps
|
|
69
|
+
// (e.g. so a heartbeat tick can complete an HTTP flush).
|
|
70
|
+
DispatchQueue.main.async {
|
|
71
|
+
let id = UIApplication.shared.beginBackgroundTask(withName: "BGPreventSuspend") { [weak self] in
|
|
72
|
+
self?.stopAllPreventSuspend()
|
|
73
|
+
}
|
|
74
|
+
if id != .invalid {
|
|
75
|
+
self.preventSuspendTasks.append(id)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
@objc public func renewPreventSuspend() {
|
|
81
|
+
// Relay pattern: take a fresh task BEFORE ending the previous ones so the
|
|
82
|
+
// execution window rolls forward without a gap. Driven from the heartbeat.
|
|
83
|
+
DispatchQueue.main.async {
|
|
84
|
+
let previous = self.preventSuspendTasks
|
|
85
|
+
let id = UIApplication.shared.beginBackgroundTask(withName: "BGPreventSuspend") { [weak self] in
|
|
86
|
+
self?.stopAllPreventSuspend()
|
|
87
|
+
}
|
|
88
|
+
if id != .invalid {
|
|
89
|
+
self.preventSuspendTasks.append(id)
|
|
90
|
+
}
|
|
91
|
+
for t in previous {
|
|
92
|
+
UIApplication.shared.endBackgroundTask(t)
|
|
93
|
+
self.preventSuspendTasks.removeAll { $0 == t }
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
@objc public func stopAllPreventSuspend() {
|
|
99
|
+
DispatchQueue.main.async {
|
|
100
|
+
for t in self.preventSuspendTasks {
|
|
101
|
+
UIApplication.shared.endBackgroundTask(t)
|
|
102
|
+
}
|
|
103
|
+
self.preventSuspendTasks.removeAll()
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
@objc public func stopPreventSuspend(_ task: UIBackgroundTaskIdentifier) {
|
|
108
|
+
// The argument is historically meaningless (.invalid); end every
|
|
109
|
+
// prevent-suspend task we actually hold.
|
|
110
|
+
stopAllPreventSuspend()
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
@objc public func startPreventSuspendTimer(_ interval: TimeInterval) {
|
|
114
|
+
stopPreventSuspendTimer()
|
|
115
|
+
preventSuspendTimer = Timer.scheduledTimer(timeInterval: interval,
|
|
116
|
+
target: self,
|
|
117
|
+
selector: #selector(onPreventSuspendTimer),
|
|
118
|
+
userInfo: nil,
|
|
119
|
+
repeats: true)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
@objc public func stopPreventSuspendTimer() {
|
|
123
|
+
preventSuspendTimer?.invalidate()
|
|
124
|
+
preventSuspendTimer = nil
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
@objc public func onPreventSuspendTimer() {
|
|
128
|
+
pleaseStayAwake()
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
@objc public func pleaseStayAwake() {
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
@objc public func onResume(_ notification: Notification) {
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
@objc public func onSuspend(_ notification: Notification) {
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
@objc public func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {}
|
|
141
|
+
@objc public func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {}
|
|
142
|
+
@objc public func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {}
|
|
143
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import CoreLocation
|
|
3
|
+
|
|
4
|
+
/// Single delegate for the one shared `CLLocationManager`.
|
|
5
|
+
///
|
|
6
|
+
/// A `CLLocationManager` has exactly ONE `delegate`. Previously
|
|
7
|
+
/// `BGTrackingService`, `BGGeofenceManager`, `BGLocationRequestService` and
|
|
8
|
+
/// `BGLocationAuthorization` each assigned `manager.delegate = self`, so whoever
|
|
9
|
+
/// ran last silently stole every callback from the others (e.g. starting
|
|
10
|
+
/// geofences right after tracking killed all `onLocation`/persist events, and
|
|
11
|
+
/// the auth callbacks never reached `BGLocationAuthorization`, so
|
|
12
|
+
/// `requestPermission` only resolved on its 20s timeout).
|
|
13
|
+
///
|
|
14
|
+
/// Routing every callback through this one object lets all four services
|
|
15
|
+
/// participate again. Each downstream handler already self-filters (region
|
|
16
|
+
/// handlers guard by identifier prefix), so fan-out is safe.
|
|
17
|
+
@objc public class BGCLRouter: NSObject, CLLocationManagerDelegate {
|
|
18
|
+
|
|
19
|
+
private static var _sharedInstance: BGCLRouter?
|
|
20
|
+
private static let instanceLock = NSLock()
|
|
21
|
+
|
|
22
|
+
@objc public class func sharedInstance() -> BGCLRouter {
|
|
23
|
+
instanceLock.lock()
|
|
24
|
+
defer { instanceLock.unlock() }
|
|
25
|
+
if _sharedInstance == nil { _sharedInstance = BGCLRouter() }
|
|
26
|
+
return _sharedInstance!
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
private var tracking: BGTrackingService { BGTrackingService.sharedInstance() }
|
|
30
|
+
private var geofences: BGGeofenceManager { BGGeofenceManager.sharedInstance() }
|
|
31
|
+
private var requests: BGLocationRequestService { BGLocationRequestService.sharedInstance() }
|
|
32
|
+
private var authorization: BGLocationAuthorization { BGLocationAuthorization.sharedInstance() }
|
|
33
|
+
|
|
34
|
+
// MARK: - Location updates
|
|
35
|
+
|
|
36
|
+
@objc public func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
|
|
37
|
+
// Always feed the request service: it owns getCurrentPosition sampling
|
|
38
|
+
// and watchPosition streams (no-ops when neither is active).
|
|
39
|
+
requests.locationManager(manager, didUpdateLocations: locations)
|
|
40
|
+
|
|
41
|
+
// Tracking owns persist + the `location` event; it also forwards each
|
|
42
|
+
// fix to BGGeofenceManager.setLocation internally. Only when tracking is
|
|
43
|
+
// disabled do we feed geofences directly (geofences-only mode).
|
|
44
|
+
if tracking.isEnabled {
|
|
45
|
+
tracking.locationManager(manager, didUpdateLocations: locations)
|
|
46
|
+
} else if geofences.enabled {
|
|
47
|
+
geofences.locationManager(manager, didUpdateLocations: locations)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@objc public func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
|
|
52
|
+
requests.locationManager(manager, didFailWithError: error)
|
|
53
|
+
if tracking.isEnabled {
|
|
54
|
+
tracking.locationManager(manager, didFailWithError: error)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// MARK: - Authorization
|
|
59
|
+
|
|
60
|
+
@available(iOS 14.0, *)
|
|
61
|
+
@objc public func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
|
|
62
|
+
// Primary path on iOS 14+. Resolves any in-flight requestPermission().
|
|
63
|
+
authorization.locationManagerDidChangeAuthorization(manager)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Legacy path (< iOS 14). On iOS 14+ this is not called.
|
|
67
|
+
@objc public func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
|
|
68
|
+
authorization.locationManager(manager, didChangeAuthorization: status)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// MARK: - Region monitoring (handlers self-filter by identifier)
|
|
72
|
+
|
|
73
|
+
@objc public func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
|
|
74
|
+
geofences.locationManager(manager, didEnterRegion: region)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@objc public func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
|
|
78
|
+
// "BGStationary" -> tracking (start-detection); "BGGeofence:" -> geofences.
|
|
79
|
+
tracking.locationManager(manager, didExitRegion: region)
|
|
80
|
+
geofences.locationManager(manager, didExitRegion: region)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
@objc public func locationManager(_ manager: CLLocationManager, didStartMonitoringFor region: CLRegion) {
|
|
84
|
+
geofences.locationManager(manager, didStartMonitoringFor: region)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
@objc public func locationManager(_ manager: CLLocationManager, monitoringDidFailFor region: CLRegion?, withError error: Error) {
|
|
88
|
+
tracking.locationManager(manager, monitoringDidFailFor: region, withError: error)
|
|
89
|
+
geofences.locationManager(manager, monitoringDidFailFor: region, withError: error)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// MARK: - Pause / resume
|
|
93
|
+
|
|
94
|
+
@objc public func locationManagerDidPauseLocationUpdates(_ manager: CLLocationManager) {
|
|
95
|
+
tracking.locationManagerDidPauseLocationUpdates(manager)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
@objc public func locationManagerDidResumeLocationUpdates(_ manager: CLLocationManager) {
|
|
99
|
+
tracking.locationManagerDidResumeLocationUpdates(manager)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
@objc public final class BGCallback: NSObject {
|
|
4
|
+
|
|
5
|
+
@objc public var success: Any?
|
|
6
|
+
@objc public var failure: Any?
|
|
7
|
+
@objc public private(set) var options: Any?
|
|
8
|
+
|
|
9
|
+
@objc public init(success: Any?, failure: Any?) {
|
|
10
|
+
self.success = success
|
|
11
|
+
self.failure = failure
|
|
12
|
+
super.init()
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
@objc public convenience init(success: Any?, failure: Any?, options: Any?) {
|
|
16
|
+
self.init(success: success, failure: failure)
|
|
17
|
+
self.options = options
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
@objc public class BGConfig: NSObject, NSCoding {
|
|
4
|
+
|
|
5
|
+
private static var _sharedInstance: BGConfig?
|
|
6
|
+
private static let instanceLock = NSLock()
|
|
7
|
+
private static let storageKey = "BGLocationManager_config"
|
|
8
|
+
|
|
9
|
+
// MARK: - Singleton
|
|
10
|
+
|
|
11
|
+
@objc public class func sharedInstance() -> BGConfig {
|
|
12
|
+
instanceLock.lock()
|
|
13
|
+
defer { instanceLock.unlock() }
|
|
14
|
+
if _sharedInstance == nil {
|
|
15
|
+
_sharedInstance = BGConfig.decode() ?? BGConfig()
|
|
16
|
+
}
|
|
17
|
+
return _sharedInstance!
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
@objc public class func decode(config dict: [String: Any]) -> BGConfig {
|
|
21
|
+
let config = BGConfig()
|
|
22
|
+
config.updateWithDictionary(dict)
|
|
23
|
+
return config
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@objc public class func uptime() -> TimeInterval {
|
|
27
|
+
return ProcessInfo.processInfo.systemUptime
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@objc public class func userDefaults() -> UserDefaults {
|
|
31
|
+
return UserDefaults.standard
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// MARK: - Sub-modules
|
|
35
|
+
|
|
36
|
+
@objc public lazy var geolocation: BGGeolocationConfig = BGGeolocationConfig()
|
|
37
|
+
@objc public lazy var http: BGHttpConfig = BGHttpConfig()
|
|
38
|
+
@objc public lazy var logger: BGLoggerConfig = BGLoggerConfig()
|
|
39
|
+
@objc public lazy var app: BGAppConfig = BGAppConfig()
|
|
40
|
+
@objc public lazy var authorization: BGAuthorizationConfig = BGAuthorizationConfig()
|
|
41
|
+
@objc public lazy var persistence: BGPersistenceConfig = BGPersistenceConfig()
|
|
42
|
+
@objc public lazy var activity: BGActivityConfig = {
|
|
43
|
+
let c = BGActivityConfig()
|
|
44
|
+
return c
|
|
45
|
+
}()
|
|
46
|
+
|
|
47
|
+
// MARK: - Top-level state
|
|
48
|
+
|
|
49
|
+
@objc public var enabled: Bool = false
|
|
50
|
+
@objc public var isMoving: Bool = false
|
|
51
|
+
@objc public var trackingMode: Int = 1
|
|
52
|
+
@objc public var didLaunchInBackground: Bool = false
|
|
53
|
+
@objc public var didRequestUpgradeLocationAuthorization: Bool = false
|
|
54
|
+
@objc public var lastLocationAuthorizationStatus: Int = 0
|
|
55
|
+
@objc public var iOSHasWarnedLocationServicesOff: Bool = false
|
|
56
|
+
@objc public var schedulerEnabled: Bool = false
|
|
57
|
+
@objc public var includeDeprecatedPropertiesInDictionary: Bool = false
|
|
58
|
+
|
|
59
|
+
private var listeners: [String: [[String: Any]]] = [:]
|
|
60
|
+
private var plugins: [String: AnyObject] = [:]
|
|
61
|
+
private let listenersLock = NSLock()
|
|
62
|
+
private let persistQueue = DispatchQueue(label: "BGConfig.persist")
|
|
63
|
+
private var pendingPersist: Bool = false
|
|
64
|
+
|
|
65
|
+
// MARK: - Init
|
|
66
|
+
|
|
67
|
+
@objc public override init() {
|
|
68
|
+
super.init()
|
|
69
|
+
commonInitSetup()
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
@objc public required init?(coder: NSCoder) {
|
|
73
|
+
super.init()
|
|
74
|
+
commonInitSetup()
|
|
75
|
+
if let data = coder.decodeObject(forKey: "geolocation") as? Data,
|
|
76
|
+
let dict = try? JSONSerialization.jsonObject(with: data) as? [String: Any] {
|
|
77
|
+
geolocation.updateWithDictionary(dict)
|
|
78
|
+
}
|
|
79
|
+
if let data = coder.decodeObject(forKey: "http") as? Data,
|
|
80
|
+
let dict = try? JSONSerialization.jsonObject(with: data) as? [String: Any] {
|
|
81
|
+
http.updateWithDictionary(dict)
|
|
82
|
+
}
|
|
83
|
+
if let data = coder.decodeObject(forKey: "logger") as? Data,
|
|
84
|
+
let dict = try? JSONSerialization.jsonObject(with: data) as? [String: Any] {
|
|
85
|
+
logger.updateWithDictionary(dict)
|
|
86
|
+
}
|
|
87
|
+
if let data = coder.decodeObject(forKey: "app") as? Data,
|
|
88
|
+
let dict = try? JSONSerialization.jsonObject(with: data) as? [String: Any] {
|
|
89
|
+
app.updateWithDictionary(dict)
|
|
90
|
+
}
|
|
91
|
+
enabled = coder.decodeBool(forKey: "enabled")
|
|
92
|
+
isMoving = coder.decodeBool(forKey: "isMoving")
|
|
93
|
+
trackingMode = coder.decodeInteger(forKey: "trackingMode")
|
|
94
|
+
schedulerEnabled = coder.decodeBool(forKey: "schedulerEnabled")
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
@objc public func encode(with coder: NSCoder) {
|
|
98
|
+
if let data = try? JSONSerialization.data(withJSONObject: geolocation.toDictionary()) { coder.encode(data, forKey: "geolocation") }
|
|
99
|
+
if let data = try? JSONSerialization.data(withJSONObject: http.toDictionary()) { coder.encode(data, forKey: "http") }
|
|
100
|
+
if let data = try? JSONSerialization.data(withJSONObject: logger.toDictionary()) { coder.encode(data, forKey: "logger") }
|
|
101
|
+
if let data = try? JSONSerialization.data(withJSONObject: app.toDictionary()) { coder.encode(data, forKey: "app") }
|
|
102
|
+
coder.encode(enabled, forKey: "enabled")
|
|
103
|
+
coder.encode(isMoving, forKey: "isMoving")
|
|
104
|
+
coder.encode(trackingMode, forKey: "trackingMode")
|
|
105
|
+
coder.encode(schedulerEnabled, forKey: "schedulerEnabled")
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
@objc public func commonInitSetup() {
|
|
109
|
+
applyDefaults()
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
@objc public func initPrivateForBoot() {
|
|
113
|
+
didLaunchInBackground = true
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// MARK: - Defaults
|
|
117
|
+
|
|
118
|
+
@objc public func applyDefaults() {
|
|
119
|
+
applyModuleDefaults()
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
@objc public func applyModuleDefaults() {
|
|
123
|
+
geolocation.applyDefaults()
|
|
124
|
+
http.applyDefaults()
|
|
125
|
+
logger.applyDefaults()
|
|
126
|
+
app.applyDefaults()
|
|
127
|
+
authorization.applyDefaults()
|
|
128
|
+
persistence.applyDefaults()
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// MARK: - Module access
|
|
132
|
+
|
|
133
|
+
@objc public func allModules() -> [BGConfigModuleBase] {
|
|
134
|
+
return [geolocation, http, logger, app, authorization, persistence]
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
@objc public func moduleMap() -> [String: BGConfigModuleBase] {
|
|
138
|
+
return [
|
|
139
|
+
"geolocation": geolocation,
|
|
140
|
+
"http": http,
|
|
141
|
+
"logger": logger,
|
|
142
|
+
"app": app,
|
|
143
|
+
"authorization": authorization,
|
|
144
|
+
"persistence": persistence
|
|
145
|
+
]
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
@objc public func module(forKey key: String) -> BGConfigModuleBase? {
|
|
149
|
+
return moduleMap()[key]
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
@objc public func key(forModule module: BGConfigModuleBase) -> String? {
|
|
153
|
+
return moduleMap().first(where: { $0.value === module })?.key
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// MARK: - Update
|
|
157
|
+
|
|
158
|
+
@objc public func updateWithDictionary(_ dict: [String: Any]) {
|
|
159
|
+
updateWithDictionaryUnsafe(dict)
|
|
160
|
+
persist()
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
@objc public func updateWithDictionaryUnsafe(_ dict: [String: Any]) {
|
|
164
|
+
for (key, value) in dict {
|
|
165
|
+
if let module = module(forKey: key), let subDict = value as? [String: Any] {
|
|
166
|
+
module.updateWithDictionary(subDict)
|
|
167
|
+
} else {
|
|
168
|
+
applyTopLevelValue(value, forKey: key)
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
flattenModulesFrom(dict)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
@objc public func flattenedModules(from dict: [String: Any]) -> [String: Any] {
|
|
175
|
+
var flattened: [String: Any] = [:]
|
|
176
|
+
for module in allModules() {
|
|
177
|
+
let specs = module.propertySpecs()
|
|
178
|
+
for spec in specs {
|
|
179
|
+
if let value = dict[spec.name] {
|
|
180
|
+
flattened[spec.name] = value
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return flattened
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
@objc public func flattenModulesFrom(_ dict: [String: Any]) {
|
|
188
|
+
for module in allModules() {
|
|
189
|
+
let specs = module.propertySpecs()
|
|
190
|
+
var moduleDict: [String: Any] = [:]
|
|
191
|
+
for spec in specs {
|
|
192
|
+
if let value = dict[spec.name] {
|
|
193
|
+
moduleDict[spec.name] = value
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if !moduleDict.isEmpty {
|
|
197
|
+
module.updateWithDictionary(moduleDict)
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
private func applyTopLevelValue(_ value: Any, forKey key: String) {
|
|
203
|
+
switch key {
|
|
204
|
+
case "enabled": if let v = value as? Bool { enabled = v }
|
|
205
|
+
case "isMoving", "is_moving": if let v = value as? Bool { isMoving = v }
|
|
206
|
+
case "trackingMode": if let v = value as? Int { trackingMode = v }
|
|
207
|
+
case "schedulerEnabled": if let v = value as? Bool { schedulerEnabled = v }
|
|
208
|
+
default: break
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// MARK: - Batch update
|
|
213
|
+
|
|
214
|
+
@objc public func batchUpdate(_ block: (BGConfig) -> Void) {
|
|
215
|
+
block(self)
|
|
216
|
+
persist()
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// MARK: - Serialization
|
|
220
|
+
|
|
221
|
+
@objc public func toDictionary() -> [String: Any] {
|
|
222
|
+
return toDictionary(false)
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
@objc public func toDictionary(_ includeDeprecated: Bool) -> [String: Any] {
|
|
226
|
+
return toDictionaryUnsafe(includeDeprecated)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
@objc public func toDictionaryUnsafe(_ includeDeprecated: Bool) -> [String: Any] {
|
|
230
|
+
var dict: [String: Any] = [:]
|
|
231
|
+
dict["enabled"] = enabled
|
|
232
|
+
dict["isMoving"] = isMoving
|
|
233
|
+
dict["trackingMode"] = trackingMode
|
|
234
|
+
dict["schedulerEnabled"] = schedulerEnabled
|
|
235
|
+
|
|
236
|
+
let moduleDict = moduleMap()
|
|
237
|
+
for (key, module) in moduleDict {
|
|
238
|
+
dict[key] = module.toDictionary()
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
for module in allModules() {
|
|
242
|
+
let moduleData = module.toDictionary()
|
|
243
|
+
dict.merge(moduleData) { $1 }
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return dict
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
@objc public func toJson() -> String? {
|
|
250
|
+
let dict = toDictionary()
|
|
251
|
+
guard let data = try? JSONSerialization.data(withJSONObject: dict) else { return nil }
|
|
252
|
+
return String(data: data, encoding: .utf8)
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
@objc public func currentStateDictionary() -> [String: Any] {
|
|
256
|
+
return toDictionary()
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// MARK: - Persistence
|
|
260
|
+
|
|
261
|
+
@objc public func persist() {
|
|
262
|
+
persistQueue.async { self.persistUnsafe() }
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
@objc public func forcePersistNow() {
|
|
266
|
+
persistUnsafe()
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
@objc public func persistUnsafe() {
|
|
270
|
+
let data = try? NSKeyedArchiver.archivedData(withRootObject: self, requiringSecureCoding: false)
|
|
271
|
+
UserDefaults.standard.set(data, forKey: BGConfig.storageKey)
|
|
272
|
+
UserDefaults.standard.synchronize()
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
@objc public func _flushDefaults() {
|
|
276
|
+
UserDefaults.standard.synchronize()
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
@objc public class func decode() -> BGConfig? {
|
|
280
|
+
guard let data = UserDefaults.standard.data(forKey: storageKey) else { return nil }
|
|
281
|
+
return try? NSKeyedUnarchiver.unarchivedObject(ofClass: BGConfig.self, from: data)
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// MARK: - Listeners
|
|
285
|
+
|
|
286
|
+
@objc public func addListener(_ property: String, callback: @escaping ([String: Any]) -> Void) -> Int {
|
|
287
|
+
listenersLock.lock()
|
|
288
|
+
defer { listenersLock.unlock() }
|
|
289
|
+
let token = Int.random(in: 1...Int.max)
|
|
290
|
+
var entry: [String: Any] = ["callback": callback, "token": token]
|
|
291
|
+
if listeners[property] == nil { listeners[property] = [] }
|
|
292
|
+
listeners[property]!.append(entry)
|
|
293
|
+
return token
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
@objc public func onChange(_ block: @escaping ([String: Any]) -> Void) {
|
|
297
|
+
_ = addListener("*", callback: block)
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
@objc public func removeListener(_ property: String, forProperty prop: String) {
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
@objc public func removeListener(_ property: String, token: Int) {
|
|
304
|
+
listenersLock.lock()
|
|
305
|
+
defer { listenersLock.unlock() }
|
|
306
|
+
listeners[property]?.removeAll { ($0["token"] as? Int) == token }
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
@objc public func removeAllListeners(forProperty property: String) {
|
|
310
|
+
listenersLock.lock()
|
|
311
|
+
defer { listenersLock.unlock() }
|
|
312
|
+
listeners.removeValue(forKey: property)
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
@objc public func removeAllListeners() {
|
|
316
|
+
listenersLock.lock()
|
|
317
|
+
defer { listenersLock.unlock() }
|
|
318
|
+
listeners.removeAll()
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
@objc public func emitChangeEvent(_ event: String, value: Any?) {
|
|
322
|
+
listenersLock.lock()
|
|
323
|
+
let list = (listeners[event] ?? []) + (listeners["*"] ?? [])
|
|
324
|
+
listenersLock.unlock()
|
|
325
|
+
let payload: [String: Any] = ["event": event, "value": value ?? NSNull()]
|
|
326
|
+
for entry in list {
|
|
327
|
+
if let cb = entry["callback"] as? ([String: Any]) -> Void {
|
|
328
|
+
cb(payload)
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
@objc public func emitModuleEvents(forPrefixes prefixes: [String]) {
|
|
334
|
+
for prefix in prefixes {
|
|
335
|
+
emitChangeEvent(prefix, value: nil)
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
@objc public func moduleEventPrefixes(forChangedKeypaths keypaths: [String]) -> [String] {
|
|
340
|
+
return keypaths
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// MARK: - KVO
|
|
344
|
+
|
|
345
|
+
@objc public func setupKVO() {
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
@objc public func setupModuleObservation() {
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
@objc public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) {
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// MARK: - Plugins
|
|
355
|
+
|
|
356
|
+
@objc public func registerPlugin(_ plugin: AnyObject) {
|
|
357
|
+
plugins[NSStringFromClass(type(of: plugin))] = plugin
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
@objc public func hasPlugin(forEvent event: String) -> Bool {
|
|
361
|
+
return false
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// MARK: - Convenience computed properties
|
|
365
|
+
|
|
366
|
+
@objc public func hasSchedule() -> Bool {
|
|
367
|
+
return app.hasSchedule
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
@objc public func hasTriggerActivities() -> Bool {
|
|
371
|
+
return false
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
@objc public func hasValidUrl() -> Bool {
|
|
375
|
+
return http.hasValidUrl
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
@objc public func isLocationTrackingMode() -> Bool {
|
|
379
|
+
return trackingMode == 1
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
@objc public func isFirstBoot() -> Bool {
|
|
383
|
+
return !UserDefaults.standard.bool(forKey: "BGLocationManager_booted")
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
@objc public func didDeviceReboot() -> Bool {
|
|
387
|
+
let stored = UserDefaults.standard.double(forKey: "BGLocationManager_uptime")
|
|
388
|
+
let current = ProcessInfo.processInfo.systemUptime
|
|
389
|
+
return stored > current
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
@objc public func shouldPersist(_ location: BGLocation) -> Bool {
|
|
393
|
+
return persistence.persistMode > 0
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
@objc public func isValue(_ value1: Any?, equalTo value2: Any?) -> Bool {
|
|
397
|
+
guard let v1 = value1, let v2 = value2 else { return value1 == nil && value2 == nil }
|
|
398
|
+
if let n1 = v1 as? NSObject, let n2 = v2 as? NSObject { return n1.isEqual(n2) }
|
|
399
|
+
return false
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// MARK: - State
|
|
403
|
+
|
|
404
|
+
@objc public func reset() {
|
|
405
|
+
resetUnsafe()
|
|
406
|
+
persist()
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
@objc public func resetUnsafe() {
|
|
410
|
+
enabled = false
|
|
411
|
+
isMoving = false
|
|
412
|
+
applyModuleDefaults()
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
@objc public func resetConfig(_ dict: [String: Any]) {
|
|
416
|
+
resetUnsafe()
|
|
417
|
+
updateWithDictionaryUnsafe(dict)
|
|
418
|
+
persist()
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
@objc public func lockForInvalidLicense() {
|
|
422
|
+
enabled = false
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
@objc public func validateLicense() {
|
|
426
|
+
BGLicenseManager.sharedManager().validateLicense()
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
@objc public func migrateFromLegacyObject(_ legacy: AnyObject) {
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
@objc public func getLocationAuthorizationAlertStrings() -> [String: Any] {
|
|
433
|
+
return [
|
|
434
|
+
"title": "Background Location Access",
|
|
435
|
+
"message": "This app requires location access to function properly.",
|
|
436
|
+
"cancel": "Cancel",
|
|
437
|
+
"settings": "Settings"
|
|
438
|
+
]
|
|
439
|
+
}
|
|
440
|
+
}
|