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.
Files changed (142) hide show
  1. package/BgGeolocation.podspec +39 -0
  2. package/LICENSE +20 -0
  3. package/README.md +366 -0
  4. package/android/build.gradle +69 -0
  5. package/android/src/main/AndroidManifest.xml +53 -0
  6. package/android/src/main/java/com/bggeolocation/BgGeolocationActivityRecognitionReceiver.kt +116 -0
  7. package/android/src/main/java/com/bggeolocation/BgGeolocationBootReceiver.kt +44 -0
  8. package/android/src/main/java/com/bggeolocation/BgGeolocationForegroundService.kt +373 -0
  9. package/android/src/main/java/com/bggeolocation/BgGeolocationGeofenceReceiver.kt +55 -0
  10. package/android/src/main/java/com/bggeolocation/BgGeolocationHeadlessTask.kt +138 -0
  11. package/android/src/main/java/com/bggeolocation/BgGeolocationModule.kt +1030 -0
  12. package/android/src/main/java/com/bggeolocation/BgGeolocationMotionStateMachine.kt +159 -0
  13. package/android/src/main/java/com/bggeolocation/BgGeolocationPackage.kt +31 -0
  14. package/android/src/main/res/drawable/bg_geo_notification.xml +9 -0
  15. package/ios/BgGeolocation.h +14 -0
  16. package/ios/BgGeolocation.mm +709 -0
  17. package/ios/engine/AtomicBoolean.swift +48 -0
  18. package/ios/engine/BGActivityChangeEvent.swift +20 -0
  19. package/ios/engine/BGActivityConfig.swift +71 -0
  20. package/ios/engine/BGAppConfig.swift +92 -0
  21. package/ios/engine/BGAppState.swift +147 -0
  22. package/ios/engine/BGAuthorization.swift +85 -0
  23. package/ios/engine/BGAuthorizationAlertPresenter.swift +39 -0
  24. package/ios/engine/BGAuthorizationConfig.swift +50 -0
  25. package/ios/engine/BGAuthorizationEvent.swift +40 -0
  26. package/ios/engine/BGBackgroundTaskManager.swift +143 -0
  27. package/ios/engine/BGCLRouter.swift +101 -0
  28. package/ios/engine/BGCallback.swift +19 -0
  29. package/ios/engine/BGConfig.swift +440 -0
  30. package/ios/engine/BGConfigModuleBase.swift +180 -0
  31. package/ios/engine/BGConfigOLD.swift +582 -0
  32. package/ios/engine/BGConnectivityChangeEvent.swift +15 -0
  33. package/ios/engine/BGCrashDetector.swift +122 -0
  34. package/ios/engine/BGCurrentPositionRequest.swift +87 -0
  35. package/ios/engine/BGDataStore.swift +75 -0
  36. package/ios/engine/BGDatabase.swift +677 -0
  37. package/ios/engine/BGDatabasePool.swift +220 -0
  38. package/ios/engine/BGDatabaseQueue.swift +215 -0
  39. package/ios/engine/BGDateUtils.swift +26 -0
  40. package/ios/engine/BGDeviceInfo.swift +54 -0
  41. package/ios/engine/BGDeviceManager.swift +65 -0
  42. package/ios/engine/BGEnabledChangeEvent.swift +11 -0
  43. package/ios/engine/BGEnv.swift +17 -0
  44. package/ios/engine/BGEventBus.swift +83 -0
  45. package/ios/engine/BGEventManager.swift +169 -0
  46. package/ios/engine/BGEventNames.swift +51 -0
  47. package/ios/engine/BGGeofence.swift +233 -0
  48. package/ios/engine/BGGeofenceDAO.swift +152 -0
  49. package/ios/engine/BGGeofenceEvent.swift +42 -0
  50. package/ios/engine/BGGeofenceLocationRequest.swift +94 -0
  51. package/ios/engine/BGGeofenceManager.swift +315 -0
  52. package/ios/engine/BGGeofenceTransition.swift +97 -0
  53. package/ios/engine/BGGeofencesChangeEvent.swift +26 -0
  54. package/ios/engine/BGGeolocationConfig.swift +136 -0
  55. package/ios/engine/BGHeartbeatEvent.swift +31 -0
  56. package/ios/engine/BGHeartbeatService.swift +51 -0
  57. package/ios/engine/BGHttpConfig.swift +105 -0
  58. package/ios/engine/BGHttpErrorCodes.swift +63 -0
  59. package/ios/engine/BGHttpEvent.swift +34 -0
  60. package/ios/engine/BGHttpRequest.swift +126 -0
  61. package/ios/engine/BGHttpResponse.swift +93 -0
  62. package/ios/engine/BGHttpService.swift +428 -0
  63. package/ios/engine/BGKalmanFilter.swift +105 -0
  64. package/ios/engine/BGLMActionNames.swift +55 -0
  65. package/ios/engine/BGLicenseManager.swift +26 -0
  66. package/ios/engine/BGLiveActivityManager.swift +327 -0
  67. package/ios/engine/BGLocation.swift +311 -0
  68. package/ios/engine/BGLocationAuthorization.swift +427 -0
  69. package/ios/engine/BGLocationDAO.swift +252 -0
  70. package/ios/engine/BGLocationErrors.swift +28 -0
  71. package/ios/engine/BGLocationEvent.swift +43 -0
  72. package/ios/engine/BGLocationFilter.swift +82 -0
  73. package/ios/engine/BGLocationFilterConfig.swift +57 -0
  74. package/ios/engine/BGLocationHelper.swift +54 -0
  75. package/ios/engine/BGLocationManager.swift +662 -0
  76. package/ios/engine/BGLocationMetricsEngine.swift +116 -0
  77. package/ios/engine/BGLocationRequestService.swift +459 -0
  78. package/ios/engine/BGLocationSatisfier.swift +14 -0
  79. package/ios/engine/BGLocationStreamEvent.swift +27 -0
  80. package/ios/engine/BGLog.swift +337 -0
  81. package/ios/engine/BGLogLevel.swift +26 -0
  82. package/ios/engine/BGLoggerConfig.swift +60 -0
  83. package/ios/engine/BGMotionActivity.swift +31 -0
  84. package/ios/engine/BGMotionActivityClassifier.swift +108 -0
  85. package/ios/engine/BGMotionActivityManagerAdapter.swift +40 -0
  86. package/ios/engine/BGMotionActivitySource.swift +46 -0
  87. package/ios/engine/BGMotionDetector.swift +377 -0
  88. package/ios/engine/BGMotionPermissionManager.swift +50 -0
  89. package/ios/engine/BGNativeLogger.swift +48 -0
  90. package/ios/engine/BGNotificaitons.swift +37 -0
  91. package/ios/engine/BGOdometer.swift +66 -0
  92. package/ios/engine/BGPersistenceConfig.swift +29 -0
  93. package/ios/engine/BGPolygonStreamRequest.swift +48 -0
  94. package/ios/engine/BGPowerSaveChangeEvent.swift +12 -0
  95. package/ios/engine/BGPropertySpec.swift +29 -0
  96. package/ios/engine/BGProviderChangeEvent.swift +31 -0
  97. package/ios/engine/BGQueue.swift +50 -0
  98. package/ios/engine/BGRPC.swift +194 -0
  99. package/ios/engine/BGReachability.swift +58 -0
  100. package/ios/engine/BGResultSet.swift +157 -0
  101. package/ios/engine/BGSchedule.swift +228 -0
  102. package/ios/engine/BGScheduleEvent.swift +13 -0
  103. package/ios/engine/BGScheduler.swift +116 -0
  104. package/ios/engine/BGSingleLocationRequest.swift +49 -0
  105. package/ios/engine/BGStreamLocationRequest.swift +42 -0
  106. package/ios/engine/BGTemplate.swift +54 -0
  107. package/ios/engine/BGTimerService.swift +46 -0
  108. package/ios/engine/BGTrackingAudioManager.swift +286 -0
  109. package/ios/engine/BGTrackingService.swift +879 -0
  110. package/ios/engine/BGWatchPositionRequest.swift +63 -0
  111. package/ios/engine/DatabaseQueue.swift +47 -0
  112. package/ios/engine/LogQuery.swift +10 -0
  113. package/ios/engine/SQLQuery.swift +65 -0
  114. package/ios/engine/TransistorAuthorizationToken.swift +182 -0
  115. package/ios/liveactivity/BGLiveTrackingAttributes.swift +52 -0
  116. package/ios/locationpush/BGLocationPushDeliverer.swift +260 -0
  117. package/ios/locationpush/BGLocationPushService.swift +161 -0
  118. package/ios/locationpush/BGLocationPushShared.swift +98 -0
  119. package/ios/locationpush/BGLocationPushSocketClient.swift +198 -0
  120. package/lib/module/NativeBgGeolocation.js +5 -0
  121. package/lib/module/NativeBgGeolocation.js.map +1 -0
  122. package/lib/module/events.js +20 -0
  123. package/lib/module/events.js.map +1 -0
  124. package/lib/module/index.js +706 -0
  125. package/lib/module/index.js.map +1 -0
  126. package/lib/module/package.json +1 -0
  127. package/lib/module/types.js +2 -0
  128. package/lib/module/types.js.map +1 -0
  129. package/lib/typescript/package.json +1 -0
  130. package/lib/typescript/src/NativeBgGeolocation.d.ts +57 -0
  131. package/lib/typescript/src/NativeBgGeolocation.d.ts.map +1 -0
  132. package/lib/typescript/src/events.d.ts +18 -0
  133. package/lib/typescript/src/events.d.ts.map +1 -0
  134. package/lib/typescript/src/index.d.ts +238 -0
  135. package/lib/typescript/src/index.d.ts.map +1 -0
  136. package/lib/typescript/src/types.d.ts +229 -0
  137. package/lib/typescript/src/types.d.ts.map +1 -0
  138. package/package.json +141 -0
  139. package/src/NativeBgGeolocation.ts +236 -0
  140. package/src/events.ts +17 -0
  141. package/src/index.tsx +935 -0
  142. 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
+ }