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,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
|
+
}
|