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,428 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import UIKit
|
|
3
|
+
|
|
4
|
+
@objc public class BGHttpSyncMetrics: NSObject {
|
|
5
|
+
|
|
6
|
+
@objc public var flushId: String = UUID().uuidString
|
|
7
|
+
@objc public var lockedRecords: [[String: Any]] = []
|
|
8
|
+
@objc public var lockedRecordsIsBatch: Bool = false
|
|
9
|
+
@objc public var currentBatchUuids: [String] = []
|
|
10
|
+
@objc public var queuedBefore: Int = 0
|
|
11
|
+
@objc public var pages: Int = 0
|
|
12
|
+
@objc public var synced: Int = 0
|
|
13
|
+
@objc public var retryCount: Int = 0
|
|
14
|
+
@objc public var authRefreshAttempted: Bool = false
|
|
15
|
+
@objc public var watchdogArmed: Bool = false
|
|
16
|
+
@objc public var t0: Date = Date()
|
|
17
|
+
|
|
18
|
+
@objc public override init() {
|
|
19
|
+
super.init()
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@objc public class BGHttpService: NSObject {
|
|
24
|
+
|
|
25
|
+
private static var _sharedInstance: BGHttpService?
|
|
26
|
+
private static let instanceLock = NSLock()
|
|
27
|
+
|
|
28
|
+
@objc public class func sharedInstance() -> BGHttpService {
|
|
29
|
+
instanceLock.lock()
|
|
30
|
+
defer { instanceLock.unlock() }
|
|
31
|
+
if _sharedInstance == nil { _sharedInstance = BGHttpService() }
|
|
32
|
+
return _sharedInstance!
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// MARK: - State
|
|
36
|
+
|
|
37
|
+
@objc public var isBusy: Bool = false
|
|
38
|
+
@objc public var hasNetworkConnection: Bool = true
|
|
39
|
+
@objc public var overrideSyncThreshold: Bool = false
|
|
40
|
+
@objc public var autoSyncThreshold: Int = 0
|
|
41
|
+
@objc public var auth: BGAuthorization?
|
|
42
|
+
@objc public var reachability: BGReachability?
|
|
43
|
+
@objc public var metrics: BGHttpSyncMetrics?
|
|
44
|
+
@objc public var bgTask: UIBackgroundTaskIdentifier = .invalid
|
|
45
|
+
@objc public var callback: (() -> Void)?
|
|
46
|
+
@objc public var syncedRecords: [String] = []
|
|
47
|
+
|
|
48
|
+
private let flushQueue = DispatchQueue(label: "BGHttpService.flush")
|
|
49
|
+
private var watchdogTimer: Timer?
|
|
50
|
+
private var isMonitoring: Bool = false
|
|
51
|
+
|
|
52
|
+
@objc public override init() {
|
|
53
|
+
super.init()
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@objc public func startMonitoring() {
|
|
57
|
+
guard !isMonitoring else {
|
|
58
|
+
resumePendingAutoSync()
|
|
59
|
+
return
|
|
60
|
+
}
|
|
61
|
+
isMonitoring = true
|
|
62
|
+
reachability = BGReachability.reachability(forHostName: "google.com")
|
|
63
|
+
reachability?.startMonitoring { [weak self] isReachable in
|
|
64
|
+
self?.onConnectivityChange(isReachable)
|
|
65
|
+
}
|
|
66
|
+
registerConfigChangeHandlers()
|
|
67
|
+
resumePendingAutoSync()
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@objc public func stopMonitoring() {
|
|
71
|
+
reachability?.stopMonitoring()
|
|
72
|
+
reachability = nil
|
|
73
|
+
isMonitoring = false
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
@objc public func registerConfigChangeHandlers() {
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
@objc public func onChangeAutoSync() {
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// MARK: - Flush
|
|
83
|
+
|
|
84
|
+
@objc public func flush() {
|
|
85
|
+
flush(nil, failure: nil)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
@objc public func flush(_ success: (([String: Any]) -> Void)?) {
|
|
89
|
+
flush(success, failure: nil)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
@objc public func flush(_ success: (([String: Any]) -> Void)?, failure: ((Error) -> Void)?) {
|
|
93
|
+
beginFlush(withCallback: success, overrideSyncThreshold: false, error: nil)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/// Flush persisted locations during a brief Core Location background wake.
|
|
97
|
+
///
|
|
98
|
+
/// The record is already durable in SQLite before this is called. Reserving
|
|
99
|
+
/// background execution here, before hopping onto flushQueue, gives the
|
|
100
|
+
/// request the best chance to finish when iOS has relaunched or resumed the
|
|
101
|
+
/// app for a location event. If the request cannot finish, the record stays
|
|
102
|
+
/// queued and resumePendingAutoSync retries it on the next native startup.
|
|
103
|
+
@objc public func flushForBackgroundWake() {
|
|
104
|
+
beginBackgroundFlushTask()
|
|
105
|
+
beginFlush(withCallback: nil, overrideSyncThreshold: true, error: nil)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
@objc public func resumePendingAutoSync() {
|
|
109
|
+
let http = BGConfig.sharedInstance().http
|
|
110
|
+
guard http.autoSync, http.hasValidUrl else { return }
|
|
111
|
+
if BGAppState.sharedInstance().isInBackground ||
|
|
112
|
+
BGAppState.sharedInstance().didLaunchInBackground {
|
|
113
|
+
flushForBackgroundWake()
|
|
114
|
+
} else {
|
|
115
|
+
flush()
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
@objc public func beginFlush(withCallback callback: (([String: Any]) -> Void)?, overrideSyncThreshold override: Bool, error: UnsafeMutablePointer<NSError?>?) {
|
|
120
|
+
flushQueue.async { [weak self] in
|
|
121
|
+
guard let self = self else { return }
|
|
122
|
+
guard !self.isBusy else { return }
|
|
123
|
+
guard self.hasNetworkConnection else {
|
|
124
|
+
self.endBackgroundFlushTask()
|
|
125
|
+
return
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
let dao = BGLocationDAO.sharedInstance()
|
|
129
|
+
let records = dao.allWithLocking(true)
|
|
130
|
+
guard !records.isEmpty else {
|
|
131
|
+
callback?([:])
|
|
132
|
+
self.endBackgroundFlushTask()
|
|
133
|
+
return
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
self.autoSyncThreshold = BGConfig.sharedInstance().http.autoSyncThreshold
|
|
137
|
+
let threshold = self.autoSyncThreshold
|
|
138
|
+
guard override || records.count >= threshold || threshold == 0 else {
|
|
139
|
+
self.endBackgroundFlushTask()
|
|
140
|
+
return
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
self.isBusy = true
|
|
144
|
+
let m = BGHttpSyncMetrics()
|
|
145
|
+
m.lockedRecords = records
|
|
146
|
+
m.queuedBefore = records.count
|
|
147
|
+
self.metrics = m
|
|
148
|
+
|
|
149
|
+
self.armFlushWatchdog()
|
|
150
|
+
self.continueFlush()
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
@objc public func continueFlush() {
|
|
155
|
+
guard let m = metrics else { resetFlushStateLocked(); return }
|
|
156
|
+
|
|
157
|
+
let config = BGConfig.sharedInstance()
|
|
158
|
+
let httpConfig = config.http
|
|
159
|
+
|
|
160
|
+
if httpConfig.batchSync {
|
|
161
|
+
scheduleBatchPost()
|
|
162
|
+
} else {
|
|
163
|
+
schedulePost()
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
@objc public func schedulePost() {
|
|
168
|
+
guard let m = metrics, !m.lockedRecords.isEmpty else {
|
|
169
|
+
finish([:], error: nil)
|
|
170
|
+
return
|
|
171
|
+
}
|
|
172
|
+
let record = m.lockedRecords[m.pages]
|
|
173
|
+
post(record)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
@objc public func scheduleBatchPost() {
|
|
177
|
+
guard let m = metrics else { return }
|
|
178
|
+
let config = BGConfig.sharedInstance().http
|
|
179
|
+
let maxBatch = config.effectiveBatchSize
|
|
180
|
+
let slice = maxBatch > 0 ? Array(m.lockedRecords.prefix(maxBatch)) : m.lockedRecords
|
|
181
|
+
m.lockedRecordsIsBatch = true
|
|
182
|
+
m.currentBatchUuids = slice.compactMap { $0["uuid"] as? String }
|
|
183
|
+
postBatch(slice)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
@objc public func post(_ record: [String: Any]) {
|
|
187
|
+
let config = BGConfig.sharedInstance()
|
|
188
|
+
let httpConfig = config.http
|
|
189
|
+
guard httpConfig.hasValidUrl else {
|
|
190
|
+
finish([:], error: nil)
|
|
191
|
+
return
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
let request = buildRequest(for: record, config: httpConfig)
|
|
195
|
+
doPost(request) { [weak self] response in
|
|
196
|
+
self?.parseResponse(response)
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
@objc public func postBatch(_ records: [[String: Any]]) {
|
|
201
|
+
let config = BGConfig.sharedInstance()
|
|
202
|
+
let httpConfig = config.http
|
|
203
|
+
guard httpConfig.hasValidUrl else {
|
|
204
|
+
finish([:], error: nil)
|
|
205
|
+
return
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
var body: [String: Any] = [:]
|
|
209
|
+
let root = httpConfig.rootProperty
|
|
210
|
+
if !root.isEmpty {
|
|
211
|
+
body[root] = records
|
|
212
|
+
} else {
|
|
213
|
+
body["locations"] = records
|
|
214
|
+
}
|
|
215
|
+
body.merge(httpConfig.params) { $1 }
|
|
216
|
+
|
|
217
|
+
let request = buildHTTPRequest(url: httpConfig.fullUrlWithParams(), method: httpConfig.method, headers: httpConfig.headersWithAuth(auth), body: body)
|
|
218
|
+
doPost(request) { [weak self] response in
|
|
219
|
+
self?.parseResponse(response)
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
@objc public func doPost(_ request: URLRequest, callback: @escaping ([String: Any]) -> Void) {
|
|
224
|
+
beginBackgroundFlushTask()
|
|
225
|
+
NSLog("[BGGEO] HTTP POST -> \(request.url?.absoluteString ?? "?")")
|
|
226
|
+
URLSession.shared.dataTask(with: request) { [weak self] data, response, error in
|
|
227
|
+
guard let self = self else { return }
|
|
228
|
+
var result: [String: Any] = [:]
|
|
229
|
+
if let httpResponse = response as? HTTPURLResponse {
|
|
230
|
+
result["status"] = httpResponse.statusCode
|
|
231
|
+
NSLog("[BGGEO] HTTP response status=\(httpResponse.statusCode)")
|
|
232
|
+
if let data = data {
|
|
233
|
+
if let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] {
|
|
234
|
+
result["data"] = json
|
|
235
|
+
BGRPC.sharedInstance().ingestHTTPResponse(withData: data, contentType: httpResponse.value(forHTTPHeaderField: "Content-Type") ?? "")
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
if let error = error {
|
|
240
|
+
NSLog("[BGGEO] HTTP error: \(error.localizedDescription)")
|
|
241
|
+
result["error"] = error.localizedDescription
|
|
242
|
+
}
|
|
243
|
+
callback(result)
|
|
244
|
+
}.resume()
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
@objc public func parseResponse(_ response: [String: Any]) {
|
|
248
|
+
let status = response["status"] as? Int ?? 0
|
|
249
|
+
guard let m = metrics else { return }
|
|
250
|
+
|
|
251
|
+
if (200...299).contains(status) {
|
|
252
|
+
if m.lockedRecordsIsBatch {
|
|
253
|
+
// Destroy ONLY the records actually sent in this batch (the
|
|
254
|
+
// prefix slice), not the entire queue — otherwise records beyond
|
|
255
|
+
// maxBatchSize were deleted without ever being POSTed. Then loop
|
|
256
|
+
// to send the next batch until the queue is drained.
|
|
257
|
+
let sent = m.currentBatchUuids
|
|
258
|
+
_ = BGLocationDAO.sharedInstance().destroyAll(sent)
|
|
259
|
+
m.synced += sent.count
|
|
260
|
+
let sentSet = Set(sent)
|
|
261
|
+
m.lockedRecords.removeAll { rec in
|
|
262
|
+
if let u = rec["uuid"] as? String { return sentSet.contains(u) }
|
|
263
|
+
return false
|
|
264
|
+
}
|
|
265
|
+
m.currentBatchUuids = []
|
|
266
|
+
if m.lockedRecords.isEmpty {
|
|
267
|
+
finish(response, error: nil)
|
|
268
|
+
} else {
|
|
269
|
+
scheduleBatchPost()
|
|
270
|
+
}
|
|
271
|
+
} else {
|
|
272
|
+
if let uuid = m.lockedRecords[m.pages]["uuid"] as? String {
|
|
273
|
+
_ = BGLocationDAO.sharedInstance().destroy(uuid)
|
|
274
|
+
m.synced += 1
|
|
275
|
+
}
|
|
276
|
+
m.pages += 1
|
|
277
|
+
if m.pages < m.lockedRecords.count {
|
|
278
|
+
schedulePost()
|
|
279
|
+
} else {
|
|
280
|
+
finish(response, error: nil)
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
fireConnectivityChangeEvent(true)
|
|
284
|
+
} else if status == 401 && !m.authRefreshAttempted {
|
|
285
|
+
m.authRefreshAttempted = true
|
|
286
|
+
onAuthorization(response)
|
|
287
|
+
} else {
|
|
288
|
+
let error = NSError(domain: "BGHttpService", code: status, userInfo: [NSLocalizedDescriptionKey: "HTTP \(status)"])
|
|
289
|
+
finish(response, error: error)
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
@objc public func onAuthorization(_ response: [String: Any]) {
|
|
294
|
+
metrics?.authRefreshAttempted = true
|
|
295
|
+
continueFlush()
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
@objc public func finish(_ response: [String: Any], error: Error?) {
|
|
299
|
+
let m = metrics
|
|
300
|
+
resetFlushStateLocked()
|
|
301
|
+
|
|
302
|
+
let event: [String: Any] = [
|
|
303
|
+
"status": response["status"] ?? 0,
|
|
304
|
+
"data": response["data"] ?? NSNull()
|
|
305
|
+
]
|
|
306
|
+
BGEventBus.sharedInstance().emit(BGEventNames.http, payload: event)
|
|
307
|
+
callback?()
|
|
308
|
+
callback = nil
|
|
309
|
+
|
|
310
|
+
if m != nil && bgTask != .invalid {
|
|
311
|
+
stopWatchdog()
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
@objc public func clearSyncedRecordsAndResetBusy() {
|
|
316
|
+
syncedRecords.removeAll()
|
|
317
|
+
isBusy = false
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
@objc public func resetFlushStateLocked() {
|
|
321
|
+
isBusy = false
|
|
322
|
+
metrics = nil
|
|
323
|
+
syncedRecords.removeAll()
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
@objc public func isCurrentFlushId(_ flushId: String) -> Bool {
|
|
327
|
+
return metrics?.flushId == flushId
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// MARK: - Watchdog
|
|
331
|
+
|
|
332
|
+
@objc public func armFlushWatchdog() {
|
|
333
|
+
metrics?.watchdogArmed = true
|
|
334
|
+
DispatchQueue.main.async {
|
|
335
|
+
self.watchdogTimer?.invalidate()
|
|
336
|
+
self.watchdogTimer = Timer.scheduledTimer(withTimeInterval: 30.0, repeats: false) { [weak self] _ in
|
|
337
|
+
self?.resetFlushStateLocked()
|
|
338
|
+
self?.endBackgroundFlushTask()
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
func stopWatchdog() {
|
|
344
|
+
watchdogTimer?.invalidate()
|
|
345
|
+
watchdogTimer = nil
|
|
346
|
+
endBackgroundFlushTask()
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
private func beginBackgroundFlushTask() {
|
|
350
|
+
let begin = {
|
|
351
|
+
guard self.bgTask == .invalid else { return }
|
|
352
|
+
self.bgTask = UIApplication.shared.beginBackgroundTask(withName: "BGHttpService.flush") { [weak self] in
|
|
353
|
+
self?.resetFlushStateLocked()
|
|
354
|
+
self?.endBackgroundFlushTask()
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
if Thread.isMainThread {
|
|
358
|
+
begin()
|
|
359
|
+
} else {
|
|
360
|
+
DispatchQueue.main.sync(execute: begin)
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
private func endBackgroundFlushTask() {
|
|
365
|
+
DispatchQueue.main.async {
|
|
366
|
+
guard self.bgTask != .invalid else { return }
|
|
367
|
+
UIApplication.shared.endBackgroundTask(self.bgTask)
|
|
368
|
+
self.bgTask = .invalid
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// MARK: - Connectivity
|
|
373
|
+
|
|
374
|
+
@objc public func onConnectivityChange(_ isReachable: Bool) {
|
|
375
|
+
hasNetworkConnection = isReachable
|
|
376
|
+
fireConnectivityChangeEvent(isReachable)
|
|
377
|
+
if isReachable && !isBusy {
|
|
378
|
+
flush()
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
@objc public func fireConnectivityChangeEvent(_ connected: Bool) {
|
|
383
|
+
BGEventBus.sharedInstance().emit(BGEventNames.connectivityChange, payload: ["connected": connected])
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
@objc public func fireAuthorizationEvent(_ response: [String: Any]) {
|
|
387
|
+
BGEventBus.sharedInstance().emit(BGEventNames.authorization, payload: response)
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// MARK: - Helpers
|
|
391
|
+
|
|
392
|
+
private func buildRequest(for record: [String: Any], config: BGHttpConfig) -> URLRequest {
|
|
393
|
+
var body: [String: Any] = [:]
|
|
394
|
+
let root = config.rootProperty
|
|
395
|
+
if !root.isEmpty {
|
|
396
|
+
body[root] = record
|
|
397
|
+
} else {
|
|
398
|
+
body = record
|
|
399
|
+
// A common location endpoint accepts flat coordinate aliases. Keep
|
|
400
|
+
// the complete BGLocation payload while making the native request
|
|
401
|
+
// compatible with the same REST contract used by the JS fallback.
|
|
402
|
+
if let coords = record["coords"] as? [String: Any] {
|
|
403
|
+
let latitude = coords["latitude"]
|
|
404
|
+
let longitude = coords["longitude"]
|
|
405
|
+
body["lat"] = latitude
|
|
406
|
+
body["long"] = longitude
|
|
407
|
+
body["latitude"] = latitude
|
|
408
|
+
body["longitude"] = longitude
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
body.merge(config.params) { $1 }
|
|
412
|
+
return buildHTTPRequest(url: config.fullUrlWithParams(), method: config.method, headers: config.headersWithAuth(auth), body: body)
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
private func buildHTTPRequest(url: String, method: String, headers: [String: String], body: [String: Any]) -> URLRequest {
|
|
416
|
+
var request = URLRequest(url: URL(string: url)!)
|
|
417
|
+
request.httpMethod = method
|
|
418
|
+
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
419
|
+
for (key, value) in headers {
|
|
420
|
+
request.setValue(value, forHTTPHeaderField: key)
|
|
421
|
+
}
|
|
422
|
+
request.httpBody = try? JSONSerialization.data(withJSONObject: body)
|
|
423
|
+
request.timeoutInterval = BGConfig.sharedInstance().http.timeout
|
|
424
|
+
return request
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
import UIKit
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import CoreLocation
|
|
3
|
+
|
|
4
|
+
@objc public class BGKalmanFilter: NSObject {
|
|
5
|
+
|
|
6
|
+
@objc public var Q: Double = 3.0
|
|
7
|
+
@objc public var R: Double = 15.0
|
|
8
|
+
@objc public var profile: String = "default"
|
|
9
|
+
@objc public var debug: Bool = false
|
|
10
|
+
|
|
11
|
+
private var P: Double = 1.0
|
|
12
|
+
private var K: Double = 0.0
|
|
13
|
+
private var estimate: CLLocation?
|
|
14
|
+
private var lastProcessed: CLLocation?
|
|
15
|
+
private var diagnostics: [[String: Any]] = []
|
|
16
|
+
|
|
17
|
+
@objc public init(withInitialEstimate location: CLLocation) {
|
|
18
|
+
self.estimate = location
|
|
19
|
+
super.init()
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@objc public override init() {
|
|
23
|
+
super.init()
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@objc public func configureForSpeed(_ speed: CLLocationSpeed,
|
|
27
|
+
accuracy: CLLocationAccuracy,
|
|
28
|
+
distanceFilter: CLLocationDistance) {
|
|
29
|
+
Q = max(1.0, min(speed * 0.1, 10.0))
|
|
30
|
+
R = max(accuracy * 0.5, 5.0)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@objc public func process(_ location: CLLocation, accuracy: CLLocationAccuracy) -> CLLocation {
|
|
34
|
+
guard let current = estimate else {
|
|
35
|
+
estimate = location
|
|
36
|
+
return location
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let dt = location.timestamp.timeIntervalSince(current.timestamp)
|
|
40
|
+
guard dt > 0 else { return current }
|
|
41
|
+
|
|
42
|
+
P = P + Q * dt
|
|
43
|
+
K = P / (P + R)
|
|
44
|
+
P = (1.0 - K) * P
|
|
45
|
+
|
|
46
|
+
let lat = current.coordinate.latitude + K * (location.coordinate.latitude - current.coordinate.latitude)
|
|
47
|
+
let lon = current.coordinate.longitude + K * (location.coordinate.longitude - current.coordinate.longitude)
|
|
48
|
+
let alt = current.altitude + K * (location.altitude - current.altitude)
|
|
49
|
+
|
|
50
|
+
let smoothed = CLLocation(
|
|
51
|
+
coordinate: CLLocationCoordinate2D(latitude: lat, longitude: lon),
|
|
52
|
+
altitude: alt,
|
|
53
|
+
horizontalAccuracy: location.horizontalAccuracy,
|
|
54
|
+
verticalAccuracy: location.verticalAccuracy,
|
|
55
|
+
timestamp: location.timestamp
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
estimate = smoothed
|
|
59
|
+
lastProcessed = location
|
|
60
|
+
|
|
61
|
+
if debug {
|
|
62
|
+
diagnostics.append([
|
|
63
|
+
"timestamp": location.timestamp,
|
|
64
|
+
"raw": ["lat": location.coordinate.latitude, "lon": location.coordinate.longitude],
|
|
65
|
+
"smoothed": ["lat": lat, "lon": lon],
|
|
66
|
+
"K": K, "P": P
|
|
67
|
+
])
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return smoothed
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
@objc public func reset(withValue location: CLLocation, coldStart: Bool) {
|
|
74
|
+
estimate = location
|
|
75
|
+
P = coldStart ? 1.0 : P
|
|
76
|
+
K = 0.0
|
|
77
|
+
diagnostics = []
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
@objc public func tuning() -> [String: Double] {
|
|
81
|
+
return ["Q": Q, "R": R]
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
@objc public func setTuning(_ tuning: [String: Double]) {
|
|
85
|
+
if let q = tuning["Q"] { Q = q }
|
|
86
|
+
if let r = tuning["R"] { R = r }
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
@objc public func getDiagnostics() -> [[String: Any]] {
|
|
90
|
+
return diagnostics
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
@objc public func exportDiagnosticsToCSV(_ path: String) {
|
|
94
|
+
var csv = "timestamp,raw_lat,raw_lon,smoothed_lat,smoothed_lon,K,P\n"
|
|
95
|
+
for entry in diagnostics {
|
|
96
|
+
let ts = (entry["timestamp"] as? Date)?.timeIntervalSince1970 ?? 0
|
|
97
|
+
let raw = entry["raw"] as? [String: Double] ?? [:]
|
|
98
|
+
let smoothed = entry["smoothed"] as? [String: Double] ?? [:]
|
|
99
|
+
let k = entry["K"] as? Double ?? 0
|
|
100
|
+
let p = entry["P"] as? Double ?? 0
|
|
101
|
+
csv += "\(ts),\(raw["lat"] ?? 0),\(raw["lon"] ?? 0),\(smoothed["lat"] ?? 0),\(smoothed["lon"] ?? 0),\(k),\(p)\n"
|
|
102
|
+
}
|
|
103
|
+
try? csv.write(toFile: path, atomically: true, encoding: .utf8)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
// Global NSString action-name constants (extern NSString * const BGLMAction…).
|
|
4
|
+
|
|
5
|
+
public let BGLMActionReady = "ready"
|
|
6
|
+
public let BGLMActionStart = "start"
|
|
7
|
+
public let BGLMActionStop = "stop"
|
|
8
|
+
public let BGLMActionGetState = "getState"
|
|
9
|
+
public let BGLMActionStartGeofences = "startGeofences"
|
|
10
|
+
public let BGLMActionStartSchedule = "startSchedule"
|
|
11
|
+
public let BGLMActionStopSchedule = "stopSchedule"
|
|
12
|
+
public let BGLMActionStartBackgroundTask = "startBackgroundTask"
|
|
13
|
+
public let BGLMActionFinish = "finish"
|
|
14
|
+
public let BGLMActionReset = "reset"
|
|
15
|
+
public let BGLMActionSetConfig = "setConfig"
|
|
16
|
+
public let BGLMActionChangePace = "changePace"
|
|
17
|
+
public let BGLMActionGetCurrentPosition = "getCurrentPosition"
|
|
18
|
+
public let BGLMActionWatchPosition = "watchPosition"
|
|
19
|
+
public let BGLMActionStopWatchPosition = "stopWatchPosition"
|
|
20
|
+
public let BGLMActionGetLocations = "getLocations"
|
|
21
|
+
public let BGLMActionInsertLocation = "insertLocation"
|
|
22
|
+
public let BGLMActionGetCount = "getCount"
|
|
23
|
+
public let BGLMActionDestroyLocations = "destroyLocations"
|
|
24
|
+
public let BGLMActionDestroyLocation = "destroyLocation"
|
|
25
|
+
public let BGLMActionSync = "sync"
|
|
26
|
+
public let BGLMActionGetOdometer = "getOdometer"
|
|
27
|
+
public let BGLMActionSetOdometer = "setOdometer"
|
|
28
|
+
public let BGLMActionResetOdometer = "resetOdometer"
|
|
29
|
+
public let BGLMActionAddGeofence = "addGeofence"
|
|
30
|
+
public let BGLMActionAddGeofences = "addGeofences"
|
|
31
|
+
public let BGLMActionRemoveGeofence = "removeGeofence"
|
|
32
|
+
public let BGLMActionRemoveGeofences = "removeGeofences"
|
|
33
|
+
public let BGLMActionGetGeofences = "getGeofences"
|
|
34
|
+
public let BGLMActionGetGeofence = "getGeofence"
|
|
35
|
+
public let BGLMActionGeofenceExists = "geofenceExists"
|
|
36
|
+
public let BGLMActionGetLog = "getLog"
|
|
37
|
+
public let BGLMActionEmailLog = "emailLog"
|
|
38
|
+
public let BGLMActionUploadLog = "uploadLog"
|
|
39
|
+
public let BGLMActionDestroyLog = "destroyLog"
|
|
40
|
+
public let BGLMActionLog = "log"
|
|
41
|
+
public let BGLMActionGetSensors = "getSensors"
|
|
42
|
+
public let BGLMActionIsPowerSaveMode = "isPowerSaveMode"
|
|
43
|
+
public let BGLMActionPlaySound = "playSound"
|
|
44
|
+
public let BGLMActionRegisterHeadlessTask = "registerHeadlessTask"
|
|
45
|
+
public let BGLMActionInitialized = "initialized"
|
|
46
|
+
public let BGLMActionRequestPermission = "requestPermission"
|
|
47
|
+
public let BGLMActionRequestTemporaryFullAccuracy = "requestTemporaryFullAccuracy"
|
|
48
|
+
public let BGLMActionGetProviderState = "getProviderState"
|
|
49
|
+
public let BGLMActionIsIgnoringBatteryOptimizations = "isIgnoringBatteryOptimizations"
|
|
50
|
+
public let BGLMActionRequestSettings = "requestSettings"
|
|
51
|
+
public let BGLMActionShowSettings = "showSettings"
|
|
52
|
+
public let BGLMActionRegisterPlugin = "registerPlugin"
|
|
53
|
+
public let BGLMActionGetDeviceInfo = "getDeviceInfo"
|
|
54
|
+
public let BGLMActionGetTransistorToken = "getTransistorToken"
|
|
55
|
+
public let BGLMActionDestroyTransistorToken = "destroyTransistorToken"
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// BGLicenseManager — stub for our open source build.
|
|
2
|
+
// The real BGLicenseManager is replaced with a no-op so BGConfig compiles.
|
|
3
|
+
// This package has no billing, no license check, no banner — it is a clean
|
|
4
|
+
// open implementation built from the provided source reference.
|
|
5
|
+
|
|
6
|
+
import Foundation
|
|
7
|
+
import UIKit
|
|
8
|
+
|
|
9
|
+
@objc public class BGLicenseManager: NSObject {
|
|
10
|
+
|
|
11
|
+
private static let lock = NSLock()
|
|
12
|
+
private static var _shared: BGLicenseManager?
|
|
13
|
+
|
|
14
|
+
@objc public class func sharedManager() -> BGLicenseManager {
|
|
15
|
+
lock.lock()
|
|
16
|
+
defer { lock.unlock() }
|
|
17
|
+
if _shared == nil { _shared = BGLicenseManager() }
|
|
18
|
+
return _shared!
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/// No-op — this build has no license requirement.
|
|
22
|
+
@objc public func validateLicense() { }
|
|
23
|
+
|
|
24
|
+
@objc public class func hasEntitlement(_ name: String) -> Bool { return true }
|
|
25
|
+
@objc public class func tokenHasEntitlement(_ token: String) -> Bool { return true }
|
|
26
|
+
}
|