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,337 @@
1
+ import Foundation
2
+ import MessageUI
3
+ import CoreLocation
4
+
5
+ @objc public class BGLog: NSObject {
6
+
7
+ private static var _sharedInstance: BGLog?
8
+ private static let lock = NSLock()
9
+
10
+ @objc public var logLevel: Int = 5
11
+ @objc public var maxAge: Int = 3
12
+ @objc public var deviceInfo: [String: Any] = [:]
13
+ @objc public var pendingEmailSuccess: (() -> Void)?
14
+
15
+ @objc public var locationCompleteListener: AnyObject?
16
+ @objc public var locationErrorListener: AnyObject?
17
+ @objc public var locationSampleListener: AnyObject?
18
+ @objc public var locationTrackingListener: AnyObject?
19
+ @objc public var heartbeatListener: AnyObject?
20
+ @objc public var motionChangeCompleteListener: AnyObject?
21
+ @objc public var motionChangeSampleListener: AnyObject?
22
+ @objc public var motionChangeErrorListener: AnyObject?
23
+ @objc public var geofenceCompleteListener: AnyObject?
24
+ @objc public var watchPositionCompleteListener: AnyObject?
25
+
26
+ private let logQueue = DispatchQueue(label: "BGLog.queue", attributes: .concurrent)
27
+ private var logEntries: [String] = []
28
+ private var dbQueue: BGDatabaseQueue?
29
+
30
+ @objc public class func sharedInstance() -> BGLog {
31
+ lock.lock()
32
+ defer { lock.unlock() }
33
+ if _sharedInstance == nil {
34
+ _sharedInstance = BGLog()
35
+ }
36
+ return _sharedInstance!
37
+ }
38
+
39
+ @objc public override init() {
40
+ super.init()
41
+ }
42
+
43
+ @objc public func configure() {
44
+ setupDatabase()
45
+ subscribeEventBus()
46
+ }
47
+
48
+ private func setupDatabase() {
49
+ let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
50
+ guard let docPath = paths.first else { return }
51
+ let dbPath = docPath.appendingPathComponent("BGLog.db")
52
+ dbQueue = BGDatabaseQueue(path: dbPath.path)
53
+ dbQueue?.inDatabase { db in
54
+ _ = db.executeUpdate("""
55
+ CREATE TABLE IF NOT EXISTS logs (
56
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
57
+ timestamp REAL NOT NULL,
58
+ level INTEGER NOT NULL,
59
+ tag TEXT,
60
+ message TEXT NOT NULL
61
+ )
62
+ """)
63
+ _ = db.executeUpdate("CREATE INDEX IF NOT EXISTS logs_timestamp ON logs (timestamp)")
64
+ }
65
+ }
66
+
67
+ @objc public func subscribeEventBus() {
68
+ let bus = BGEventBus.sharedInstance()
69
+ locationCompleteListener = bus.on(BGEventNames.locationComplete) { [weak self] _ in
70
+ self?.onLocationComplete()
71
+ } as AnyObject
72
+ locationErrorListener = bus.on(BGEventNames.locationError) { [weak self] _ in
73
+ self?.onLocationError()
74
+ } as AnyObject
75
+ locationSampleListener = bus.on(BGEventNames.locationSample) { [weak self] _ in
76
+ self?.onLocationSample()
77
+ } as AnyObject
78
+ heartbeatListener = bus.on(BGEventNames.heartbeat) { [weak self] payload in
79
+ self?.onHeartbeat(payload as? [String: Any])
80
+ } as AnyObject
81
+ geofenceCompleteListener = bus.on(BGEventNames.geofenceComplete) { [weak self] payload in
82
+ self?.onGeofence(payload as? [String: Any])
83
+ } as AnyObject
84
+ motionChangeCompleteListener = bus.on(BGEventNames.motionChangeComplete) { [weak self] payload in
85
+ self?.onMotionChangeComplete(payload as? [String: Any])
86
+ } as AnyObject
87
+ }
88
+
89
+ @objc public func unsubscribeEventBus() {
90
+ let bus = BGEventBus.sharedInstance()
91
+ if let token = locationCompleteListener as? String { bus.off(BGEventNames.locationComplete, token: token) }
92
+ if let token = locationErrorListener as? String { bus.off(BGEventNames.locationError, token: token) }
93
+ if let token = locationSampleListener as? String { bus.off(BGEventNames.locationSample, token: token) }
94
+ if let token = heartbeatListener as? String { bus.off(BGEventNames.heartbeat, token: token) }
95
+ if let token = geofenceCompleteListener as? String { bus.off(BGEventNames.geofenceComplete, token: token) }
96
+ if let token = motionChangeCompleteListener as? String { bus.off(BGEventNames.motionChangeComplete, token: token) }
97
+ }
98
+
99
+ public func setLogLevel(_ level: Int) {
100
+ self.logLevel = level
101
+ }
102
+
103
+ public func setMaxAge(_ days: Int) {
104
+ self.maxAge = days
105
+ purgeOldEntries()
106
+ }
107
+
108
+ @objc public func notify(_ message: String, debug: Bool) {
109
+ let level = debug ? 5 : 3
110
+ guard level <= logLevel else { return }
111
+ let entry = "[BGLocationManager \(timestamp())] \(message)"
112
+ logQueue.async(flags: .barrier) {
113
+ self.logEntries.append(entry)
114
+ self.writeToDatabase(level: level, tag: "BGLocationManager", message: message)
115
+ }
116
+ }
117
+
118
+ @objc public func alert(_ tag: String, message: String) {
119
+ guard 2 <= logLevel else { return }
120
+ let entry = "[\(timestamp())] [\(tag)] \(message)"
121
+ logQueue.async(flags: .barrier) {
122
+ self.logEntries.append(entry)
123
+ self.writeToDatabase(level: 2, tag: tag, message: message)
124
+ }
125
+ }
126
+
127
+ @objc public func commit() {
128
+ logQueue.sync(flags: .barrier) {
129
+ self.logEntries.removeAll()
130
+ }
131
+ }
132
+
133
+ @objc public func destroy() {
134
+ logQueue.async(flags: .barrier) {
135
+ self.logEntries.removeAll()
136
+ }
137
+ dbQueue?.inDatabase { db in
138
+ _ = db.executeUpdate("DELETE FROM logs")
139
+ }
140
+ }
141
+
142
+ @objc public func getLog(_ query: LogQuery) -> [[String: Any]] {
143
+ var results: [[String: Any]] = []
144
+ dbQueue?.inDatabase { db in
145
+ let sql = buildQuery(query)
146
+ guard let rs = db.executeQuery(sql, withArgumentsInArray: query.arguments as! [Any]) else { return }
147
+ defer { rs.close() }
148
+ while rs.next() {
149
+ var entry: [String: Any] = [:]
150
+ if let ts = rs.string(forColumn: "timestamp") { entry["timestamp"] = ts }
151
+ if let level = rs.string(forColumn: "level") { entry["level"] = level }
152
+ if let tag = rs.string(forColumn: "tag") { entry["tag"] = tag }
153
+ if let msg = rs.string(forColumn: "message") { entry["message"] = msg }
154
+ results.append(entry)
155
+ }
156
+ }
157
+ return results
158
+ }
159
+
160
+ @objc public func emailLog(_ to: String, query: LogQuery, success: (() -> Void)?, failure: ((Error) -> Void)?) {
161
+ guard MFMailComposeViewController.canSendMail() else {
162
+ failure?(NSError(domain: "BGLog", code: -1, userInfo: [NSLocalizedDescriptionKey: "Mail not available"]))
163
+ return
164
+ }
165
+ let entries = getLog(query)
166
+ let body = entries.map { entry -> String in
167
+ let ts = entry["timestamp"] as? String ?? ""
168
+ let tag = entry["tag"] as? String ?? ""
169
+ let msg = entry["message"] as? String ?? ""
170
+ return "[\(ts)] [\(tag)] \(msg)"
171
+ }.joined(separator: "\n")
172
+
173
+ DispatchQueue.main.async {
174
+ let vc = MFMailComposeViewController()
175
+ vc.mailComposeDelegate = self
176
+ vc.setToRecipients([to])
177
+ vc.setSubject("BGLocationManager Log")
178
+ vc.setMessageBody(body, isHTML: false)
179
+ self.pendingEmailSuccess = success
180
+ // Present from top-most view controller
181
+ if let rootVC = UIApplication.shared.windows.first?.rootViewController {
182
+ var topVC = rootVC
183
+ while let presented = topVC.presentedViewController { topVC = presented }
184
+ topVC.present(vc, animated: true)
185
+ }
186
+ }
187
+ }
188
+
189
+ @objc public func uploadLog(_ url: String, query: LogQuery, success: (([String: Any]) -> Void)?, failure: ((Error) -> Void)?) {
190
+ let entries = getLog(query)
191
+ guard let requestURL = URL(string: url) else {
192
+ failure?(NSError(domain: "BGLog", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid URL"]))
193
+ return
194
+ }
195
+ var request = URLRequest(url: requestURL)
196
+ request.httpMethod = "POST"
197
+ request.setValue("application/json", forHTTPHeaderField: "Content-Type")
198
+ let body: [String: Any] = ["logs": entries, "deviceInfo": deviceInfo]
199
+ request.httpBody = try? JSONSerialization.data(withJSONObject: body)
200
+ URLSession.shared.dataTask(with: request) { data, response, error in
201
+ if let error = error {
202
+ failure?(error)
203
+ return
204
+ }
205
+ if let data = data, let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] {
206
+ success?(json)
207
+ } else {
208
+ success?([:])
209
+ }
210
+ }.resume()
211
+ }
212
+
213
+ @objc public func gzip(_ data: Data) -> Data? {
214
+ return data
215
+ }
216
+
217
+ @objc public func playSound(_ soundId: SystemSoundID) {
218
+ AudioServicesPlaySystemSound(soundId)
219
+ }
220
+
221
+ @objc public func playSound(_ soundId: SystemSoundID, debug: Bool) {
222
+ guard debug else { return }
223
+ playSound(soundId)
224
+ }
225
+
226
+ // MARK: - Event handlers
227
+
228
+ @objc public func onLocationComplete() {
229
+ notify("Location complete", debug: true)
230
+ }
231
+
232
+ @objc public func onLocationError() {
233
+ notify("Location error", debug: false)
234
+ }
235
+
236
+ @objc public func onLocationSample() {
237
+ notify("Location sample", debug: true)
238
+ }
239
+
240
+ @objc public func onHeartbeat(_ payload: [String: Any]?) {
241
+ notify("Heartbeat", debug: true)
242
+ }
243
+
244
+ @objc public func onGeofence(_ payload: [String: Any]?) {
245
+ notify("Geofence event", debug: true)
246
+ }
247
+
248
+ @objc public func onMotionChangeComplete(_ payload: [String: Any]?) {
249
+ notify("Motion change complete", debug: true)
250
+ }
251
+
252
+ @objc public func onChangeDebug(_ debug: Bool) {
253
+ }
254
+
255
+ @objc public func onChangeLogLevel(_ level: Int) {
256
+ logLevel = level
257
+ }
258
+
259
+ @objc public func onChangeLogMaxDays(_ days: Int) {
260
+ maxAge = days
261
+ purgeOldEntries()
262
+ }
263
+
264
+
265
+ // MARK: - Logging interface
266
+
267
+ @objc public func shouldLog(_ level: Int) -> Bool {
268
+ return level <= logLevel
269
+ }
270
+
271
+ @objc public func log(_ level: Int, tag: Int, function: String, message: String) {
272
+ guard shouldLog(level) else { return }
273
+ let tagName = "tag:\(tag)"
274
+ let entry = "[\(timestamp())] [\(tagName)] [\(function)] \(message)"
275
+ logQueue.async(flags: .barrier) {
276
+ self.logEntries.append(entry)
277
+ self.writeToDatabase(level: level, tag: tagName, message: "\(function): \(message)")
278
+ }
279
+ }
280
+
281
+ // MARK: - Private helpers
282
+
283
+ private func timestamp() -> String {
284
+ let fmt = DateFormatter()
285
+ fmt.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
286
+ return fmt.string(from: Date())
287
+ }
288
+
289
+ private func writeToDatabase(level: Int, tag: String, message: String) {
290
+ dbQueue?.inDatabase { db in
291
+ _ = db.executeUpdate(
292
+ "INSERT INTO logs (timestamp, level, tag, message) VALUES (?, ?, ?, ?)",
293
+ withArgumentsInArray: [Date().timeIntervalSince1970, level, tag, message]
294
+ )
295
+ }
296
+ }
297
+
298
+ private func purgeOldEntries() {
299
+ let cutoff = Date().timeIntervalSince1970 - Double(maxAge) * 86400
300
+ dbQueue?.inDatabase { db in
301
+ _ = db.executeUpdate("DELETE FROM logs WHERE timestamp < ?", withArgumentsInArray: [cutoff])
302
+ }
303
+ }
304
+
305
+ private func buildQuery(_ query: LogQuery) -> String {
306
+ var conditions: [String] = []
307
+ if query.start > 0 {
308
+ conditions.append("timestamp >= \(query.start)")
309
+ }
310
+ if query.end > 0 {
311
+ conditions.append("timestamp <= \(query.end)")
312
+ }
313
+ var sql = "SELECT * FROM logs"
314
+ if !conditions.isEmpty {
315
+ sql += " WHERE " + conditions.joined(separator: " AND ")
316
+ }
317
+ sql += " ORDER BY timestamp \(query.order == 0 ? "DESC" : "ASC")"
318
+ if query.limit > 0 {
319
+ sql += " LIMIT \(query.limit)"
320
+ }
321
+ return sql
322
+ }
323
+ }
324
+
325
+ import AudioToolbox
326
+ import UIKit
327
+
328
+ extension BGLog: MFMailComposeViewControllerDelegate {
329
+ public func mailComposeController(_ controller: MFMailComposeViewController,
330
+ didFinishWith result: MFMailComposeResult,
331
+ error: Error?) {
332
+ controller.dismiss(animated: true) {
333
+ if result == .sent { self.pendingEmailSuccess?() }
334
+ self.pendingEmailSuccess = nil
335
+ }
336
+ }
337
+ }
@@ -0,0 +1,26 @@
1
+ import Foundation
2
+
3
+ public func BGLogLevelToString(_ level: Int) -> String {
4
+ switch level {
5
+ case 0: return "off"
6
+ case 1: return "error"
7
+ case 2: return "warning"
8
+ case 3: return "info"
9
+ case 4: return "debug"
10
+ case 5: return "verbose"
11
+ default: return "unknown"
12
+ }
13
+ }
14
+
15
+ public func BGLogLevelFromString(_ string: String?, _ defaultLevel: Int) -> Int {
16
+ guard let string = string, !string.isEmpty else { return defaultLevel }
17
+ switch string.lowercased() {
18
+ case "off": return 0
19
+ case "error": return 1
20
+ case "warning": return 2
21
+ case "info": return 3
22
+ case "debug": return 4
23
+ case "verbose": return 5
24
+ default: return defaultLevel
25
+ }
26
+ }
@@ -0,0 +1,60 @@
1
+ import Foundation
2
+
3
+ @objc public class BGLoggerConfig: BGConfigModuleBase {
4
+
5
+ @objc public var logLevel: Int = 5
6
+ @objc public var logMaxDays: Int = 3
7
+ @objc public var debug: Bool = false
8
+
9
+ @objc public class func logLevel(fromString s: String) -> Int {
10
+ switch s.uppercased() {
11
+ case "OFF": return 0
12
+ case "ERROR": return 1
13
+ case "WARN", "WARNING": return 2
14
+ case "INFO": return 3
15
+ case "DEBUG": return 4
16
+ case "VERBOSE", "TRACE": return 5
17
+ default: return 3
18
+ }
19
+ }
20
+
21
+ @objc public class func string(forLogLevel level: Int) -> String {
22
+ switch level {
23
+ case 0: return "OFF"
24
+ case 1: return "ERROR"
25
+ case 2: return "WARN"
26
+ case 3: return "INFO"
27
+ case 4: return "DEBUG"
28
+ case 5: return "VERBOSE"
29
+ default: return "INFO"
30
+ }
31
+ }
32
+
33
+ @objc public override func applyDefaults() {
34
+ logLevel = 5
35
+ logMaxDays = 3
36
+ debug = false
37
+ }
38
+
39
+ @objc public override func contributeDeprecatedProperties(_ dict: NSMutableDictionary, redact: Bool) {
40
+ dict["logLevel"] = logLevel
41
+ dict["logMaxDays"] = logMaxDays
42
+ dict["debug"] = debug
43
+ }
44
+
45
+ @objc public override func propertySpecs() -> [BGPropertySpecImpl] {
46
+ return [
47
+ BGPropertySpec(name: "logLevel", type: "int"),
48
+ BGPropertySpec(name: "logMaxDays", type: "int"),
49
+ BGPropertySpec(name: "debug", type: "bool")
50
+ ]
51
+ }
52
+
53
+ @objc public override func validateConfiguration() -> Bool {
54
+ return true
55
+ }
56
+
57
+ @objc public override var description: String {
58
+ return "<BGLoggerConfig logLevel=\(logLevel) logMaxDays=\(logMaxDays) debug=\(debug)>"
59
+ }
60
+ }
@@ -0,0 +1,31 @@
1
+ import Foundation
2
+
3
+ @objc public final class BGMotionActivity: NSObject {
4
+
5
+ @objc public var type: String = "unknown"
6
+ @objc public var confidence: Int = 0
7
+ @objc public var name: String { return type }
8
+
9
+ @objc public override convenience init() {
10
+ self.init(type: "unknown", confidence: 0)
11
+ }
12
+
13
+ @objc public init(type: String, confidence: Int) {
14
+ super.init()
15
+ self.type = type
16
+ self.confidence = confidence
17
+ }
18
+
19
+ // Integer-based type value for legacy ObjC compatibility
20
+ @objc public var typeCode: Int {
21
+ switch type {
22
+ case "still": return 1
23
+ case "on_foot": return 2
24
+ case "running": return 3
25
+ case "in_vehicle": return 4
26
+ case "on_bicycle": return 5
27
+ case "moving": return 7
28
+ default: return 6
29
+ }
30
+ }
31
+ }
@@ -0,0 +1,108 @@
1
+ import Foundation
2
+ import CoreMotion
3
+
4
+ @objc public class BGMotionActivityClassifier: NSObject {
5
+
6
+ @objc public var sampleInterval: Double = 0.1
7
+ @objc public var samplesPerWindow: Int = 50
8
+ @objc public var windowSeconds: Double = 5.0
9
+ @objc public var n: Int = 0
10
+ @objc public var decay: Double = 0.95
11
+
12
+ @objc public var sumMag: Double = 0
13
+ @objc public var sumMag2: Double = 0
14
+ @objc public var sumGyro2: Double = 0
15
+ @objc public var lastMag: Double = 0
16
+ @objc public var hasLastMag: Bool = false
17
+ @objc public var jerkSum: Double = 0
18
+ @objc public var jerkStill: Double = 0.05
19
+ @objc public var jerkShakeThreshold: Double = 2.0
20
+ @objc public var varStill: Double = 0.01
21
+ @objc public var gyroShake: Double = 0.5
22
+ @objc public var gyroStill: Double = 0.05
23
+
24
+ @objc public var gzWalk_c: Double = 0
25
+ @objc public var gzWalk_s: Double = 0
26
+ @objc public var gzWalk_q1: Double = 0
27
+ @objc public var gzWalk_q2: Double = 0
28
+ @objc public var gzWalk_pow: Double = 0
29
+
30
+ @objc public var gzRun_c: Double = 0
31
+ @objc public var gzRun_s: Double = 0
32
+ @objc public var gzRun_q1: Double = 0
33
+ @objc public var gzRun_q2: Double = 0
34
+ @objc public var gzRun_pow: Double = 0
35
+
36
+ @objc public var walkWins: Int = 0
37
+ @objc public var runWins: Int = 0
38
+ @objc public var lastSpeed: Double = 0
39
+ @objc public var maxWalkSpeed: Double = 3.0
40
+ @objc public var maxRunSpeed: Double = 12.0
41
+ @objc public var minSpeed: Double = 0.5
42
+ @objc public var cadenceConfirmWindows: Int = 3
43
+
44
+ @objc public override init() {
45
+ super.init()
46
+ }
47
+
48
+ @objc public func configureWithSampleInterval(_ interval: Double, windowSeconds: Double) {
49
+ self.sampleInterval = interval
50
+ self.windowSeconds = windowSeconds
51
+ self.samplesPerWindow = Int(windowSeconds / interval)
52
+ reset()
53
+ }
54
+
55
+ @objc public func ingestAcceleration(_ data: CMAccelerometerData) {
56
+ let ax = data.acceleration.x
57
+ let ay = data.acceleration.y
58
+ let az = data.acceleration.z
59
+ let mag = sqrt(ax*ax + ay*ay + az*az)
60
+
61
+ if hasLastMag {
62
+ let jerk = abs(mag - lastMag) / sampleInterval
63
+ jerkSum = jerkSum * decay + jerk
64
+ sumGyro2 = sumGyro2 * decay
65
+ }
66
+
67
+ sumMag = sumMag * decay + mag
68
+ sumMag2 = sumMag2 * decay + mag * mag
69
+ lastMag = mag
70
+ hasLastMag = true
71
+ n = min(n + 1, samplesPerWindow)
72
+ }
73
+
74
+ @objc public func ingestRotationRate(_ data: CMGyroData) {
75
+ let rx = data.rotationRate.x
76
+ let ry = data.rotationRate.y
77
+ let rz = data.rotationRate.z
78
+ let gyroMag = sqrt(rx*rx + ry*ry + rz*rz)
79
+ sumGyro2 = sumGyro2 * decay + gyroMag * gyroMag
80
+ }
81
+
82
+ @objc public func isWindowReady() -> Bool {
83
+ return n >= samplesPerWindow
84
+ }
85
+
86
+ @objc public func classifyWithSpeed(_ speed: Double) -> String {
87
+ lastSpeed = speed
88
+ if speed < minSpeed { return "still" }
89
+ if speed <= maxWalkSpeed { return "walking" }
90
+ if speed <= maxRunSpeed { return "running" }
91
+ return "in_vehicle"
92
+ }
93
+
94
+ @objc public func updateSpeed(_ speed: Double) {
95
+ lastSpeed = speed
96
+ }
97
+
98
+ @objc public func reset() {
99
+ n = 0
100
+ sumMag = 0
101
+ sumMag2 = 0
102
+ sumGyro2 = 0
103
+ jerkSum = 0
104
+ hasLastMag = false
105
+ walkWins = 0
106
+ runWins = 0
107
+ }
108
+ }
@@ -0,0 +1,40 @@
1
+ import Foundation
2
+ import CoreMotion
3
+
4
+ @objc public class BGMotionActivityManagerAdapter: NSObject {
5
+
6
+ @objc public var mgr: CMMotionActivityManager?
7
+
8
+ @objc public class func isActivityAvailable() -> Bool {
9
+ return CMMotionActivityManager.isActivityAvailable()
10
+ }
11
+
12
+ public class func authorizationStatus() -> CMAuthorizationStatus {
13
+ return CMMotionActivityManager.authorizationStatus()
14
+ }
15
+
16
+ @objc public override init() {
17
+ mgr = CMMotionActivityManager()
18
+ super.init()
19
+ }
20
+
21
+ @objc public func startActivityUpdates(
22
+ toQueue queue: OperationQueue,
23
+ withHandler handler: @escaping CMMotionActivityHandler
24
+ ) {
25
+ mgr?.startActivityUpdates(to: queue, withHandler: handler)
26
+ }
27
+
28
+ @objc public func stopActivityUpdates() {
29
+ mgr?.stopActivityUpdates()
30
+ }
31
+
32
+ @objc public func queryActivityStarting(
33
+ fromDate start: Date,
34
+ toDate end: Date,
35
+ toQueue queue: OperationQueue,
36
+ withHandler handler: @escaping CMMotionActivityQueryHandler
37
+ ) {
38
+ mgr?.queryActivityStarting(from: start, to: end, to: queue, withHandler: handler)
39
+ }
40
+ }
@@ -0,0 +1,46 @@
1
+ import Foundation
2
+ import CoreMotion
3
+
4
+ @objc public protocol BGMotionActivitySourceDelegate: AnyObject {
5
+ func activitySource(_ source: BGMotionActivitySource, didUpdate activity: CMMotionActivity)
6
+ }
7
+
8
+ @objc public class BGMotionActivitySource: NSObject {
9
+
10
+ @objc public weak var delegate: BGMotionActivitySourceDelegate?
11
+ @objc public var manager: BGMotionActivityManagerAdapter?
12
+ @objc public var queue: OperationQueue?
13
+ @objc private(set) var running: Bool = false
14
+
15
+ @objc public class func isAvailable() -> Bool {
16
+ return CMMotionActivityManager.isActivityAvailable()
17
+ }
18
+
19
+ @objc public init(queue: OperationQueue) {
20
+ self.queue = queue
21
+ self.manager = BGMotionActivityManagerAdapter()
22
+ super.init()
23
+ }
24
+
25
+ @objc public override init() {
26
+ self.queue = OperationQueue()
27
+ self.manager = BGMotionActivityManagerAdapter()
28
+ super.init()
29
+ }
30
+
31
+ @objc public func start() {
32
+ guard !running, let mgr = manager, let q = queue else { return }
33
+ running = true
34
+ mgr.startActivityUpdates(toQueue: q) { [weak self] activity in
35
+ guard let self = self, let activity = activity else { return }
36
+ self.delegate?.activitySource(self, didUpdate: activity)
37
+ }
38
+ }
39
+
40
+ @objc public func stop() {
41
+ manager?.stopActivityUpdates()
42
+ running = false
43
+ }
44
+
45
+ public func setRunning(_ value: Bool) { running = value }
46
+ }