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,377 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import CoreMotion
|
|
3
|
+
import CoreLocation
|
|
4
|
+
|
|
5
|
+
@objc public class BGMotionDetector: NSObject, BGMotionActivitySourceDelegate {
|
|
6
|
+
|
|
7
|
+
private static var _sharedInstance: BGMotionDetector?
|
|
8
|
+
private static let instanceLock = NSLock()
|
|
9
|
+
|
|
10
|
+
@objc public class func sharedInstance() -> BGMotionDetector {
|
|
11
|
+
instanceLock.lock()
|
|
12
|
+
defer { instanceLock.unlock() }
|
|
13
|
+
if _sharedInstance == nil { _sharedInstance = BGMotionDetector() }
|
|
14
|
+
return _sharedInstance!
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
public class func motionAuthorizationStatus() -> CMAuthorizationStatus {
|
|
18
|
+
return CMMotionActivityManager.authorizationStatus()
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@objc public class func motionHardwareAvailable() -> Bool {
|
|
22
|
+
return CMMotionActivityManager.isActivityAvailable()
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// MARK: - State
|
|
26
|
+
|
|
27
|
+
@objc public var enabled: Bool = false
|
|
28
|
+
@objc public var debug: Bool = false
|
|
29
|
+
@objc public var isMoving: Bool = false
|
|
30
|
+
@objc public var isUpdatingMotionActivity: Bool = false
|
|
31
|
+
@objc public var useM7IfAvailable: Bool = true
|
|
32
|
+
@objc public var useAccelerometerFallback: Bool = true
|
|
33
|
+
@objc public var autoRequestMotionPermission: Bool = true
|
|
34
|
+
|
|
35
|
+
// Activity state
|
|
36
|
+
@objc public var motionType: String = "unknown"
|
|
37
|
+
@objc public var previousMotionType: String = "unknown"
|
|
38
|
+
@objc public var statedActivity: BGMotionActivity?
|
|
39
|
+
@objc public var currentActivity: BGMotionActivity?
|
|
40
|
+
@objc public var motionActivity: CMMotionActivity?
|
|
41
|
+
@objc public var detectorState: String = "stopped"
|
|
42
|
+
@objc public var history: [BGMotionActivity] = []
|
|
43
|
+
@objc public var diagnostics: [String: Any] = [:]
|
|
44
|
+
|
|
45
|
+
// Speed/location state
|
|
46
|
+
@objc public var location: CLLocation?
|
|
47
|
+
@objc public var currentSpeed: Double = 0
|
|
48
|
+
|
|
49
|
+
// Confidence tracking
|
|
50
|
+
@objc public var lastConfidence: Int = 0
|
|
51
|
+
@objc public var lastEmittedType: String = "unknown"
|
|
52
|
+
@objc public var lastEmittedConfidence: Int = 0
|
|
53
|
+
@objc public var lastTypeChangeAt: Date?
|
|
54
|
+
@objc public var confidenceDeltaThreshold: Int = 1
|
|
55
|
+
@objc public var minimumMotionActivityConfidence: Int = 1
|
|
56
|
+
|
|
57
|
+
// Sensor state
|
|
58
|
+
public var acceleration: CMAcceleration = CMAcceleration()
|
|
59
|
+
@objc public var accelerometerUpdateInterval: TimeInterval = 0.1
|
|
60
|
+
@objc public var samplesPerInterval: Int = 50
|
|
61
|
+
@objc public var detectMotionWindow: TimeInterval = 5.0
|
|
62
|
+
|
|
63
|
+
// M7 detection thresholds
|
|
64
|
+
@objc public var isUsingM7: Bool = false
|
|
65
|
+
@objc public var M7Authorized: Bool = false
|
|
66
|
+
|
|
67
|
+
// Motion probe settings (adaptive polling)
|
|
68
|
+
@objc public var probeMinInterval: TimeInterval = 5.0
|
|
69
|
+
@objc public var probeCooldownBase: TimeInterval = 15.0
|
|
70
|
+
@objc public var probeCooldownMax: TimeInterval = 120.0
|
|
71
|
+
@objc public var probeFreshSpeedAge: TimeInterval = 10.0
|
|
72
|
+
@objc public var probeFreshSpeedThreshold: Double = 0.5
|
|
73
|
+
@objc public var probeHighConfidence: Int = 75
|
|
74
|
+
@objc public var probeDwellWins: Int = 3
|
|
75
|
+
|
|
76
|
+
// Stop detection
|
|
77
|
+
@objc public var stopDwellWins: Int = 3
|
|
78
|
+
@objc public var stopHighConfidence: Int = 90
|
|
79
|
+
@objc public var sdConsecutiveStillWins: Int = 0
|
|
80
|
+
@objc public var mdConsecutiveMovingWins: Int = 0
|
|
81
|
+
@objc public var mdBackoffLevel: Int = 0
|
|
82
|
+
@objc public var mdLastProbeAt: Date?
|
|
83
|
+
@objc public var mdLastSpeedAt: Date?
|
|
84
|
+
@objc public var mdLastSpeedValue: Double = 0
|
|
85
|
+
@objc public var mdSuppressUntil: Date?
|
|
86
|
+
|
|
87
|
+
// Type debounce
|
|
88
|
+
@objc public var motionDebounceInterval: TimeInterval = 1.0
|
|
89
|
+
@objc public var typeDebounceInterval: TimeInterval = 2.0
|
|
90
|
+
@objc public var motionDetectionInterval: TimeInterval = 10.0
|
|
91
|
+
|
|
92
|
+
// Speed thresholds
|
|
93
|
+
@objc public var minimumSpeed: Double = 0.5
|
|
94
|
+
@objc public var maximumWalkingSpeed: Double = 3.0
|
|
95
|
+
@objc public var maximumRunningSpeed: Double = 10.0
|
|
96
|
+
|
|
97
|
+
// Sensors
|
|
98
|
+
@objc public var motionManager: CMMotionManager?
|
|
99
|
+
@objc public var motionActivityManager: CMMotionActivityManager?
|
|
100
|
+
@objc public var activitySource: BGMotionActivitySource?
|
|
101
|
+
@objc public var activityClassifier: BGMotionActivityClassifier?
|
|
102
|
+
@objc public var permissionMgr: BGMotionPermissionManager?
|
|
103
|
+
@objc public var activityProvider: AnyObject?
|
|
104
|
+
|
|
105
|
+
var accelerometerQueue: OperationQueue?
|
|
106
|
+
var windowTimer: Timer?
|
|
107
|
+
var stateQueue: DispatchQueue?
|
|
108
|
+
|
|
109
|
+
@objc public var accelerationChangedBlock: ((CMAccelerometerData) -> Void)?
|
|
110
|
+
@objc public var motionActivityChangedBlock: ((CMMotionActivity) -> Void)?
|
|
111
|
+
|
|
112
|
+
@objc public override init() {
|
|
113
|
+
super.init()
|
|
114
|
+
stateQueue = DispatchQueue(label: "BGMotionDetector.state")
|
|
115
|
+
activityClassifier = BGMotionActivityClassifier()
|
|
116
|
+
activityClassifier?.configureWithSampleInterval(0.1, windowSeconds: 5.0)
|
|
117
|
+
permissionMgr = BGMotionPermissionManager()
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// MARK: - Authorization
|
|
121
|
+
|
|
122
|
+
@objc public func isAccelerometerAvailable() -> Bool {
|
|
123
|
+
return motionManager?.isAccelerometerAvailable ?? false
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
@objc public func isGyroAvailable() -> Bool {
|
|
127
|
+
return motionManager?.isGyroAvailable ?? false
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
@objc public func isDeviceMotionAvailable() -> Bool {
|
|
131
|
+
return motionManager?.isDeviceMotionAvailable ?? false
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
@objc public func isMagnetometerAvailable() -> Bool {
|
|
135
|
+
return motionManager?.isMagnetometerAvailable ?? false
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
@objc public func isConfidentlyStationary() -> Bool {
|
|
139
|
+
guard motionType == "still" else { return false }
|
|
140
|
+
return sdConsecutiveStillWins >= stopDwellWins
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// MARK: - Lifecycle
|
|
144
|
+
|
|
145
|
+
@objc public func start() {
|
|
146
|
+
guard !enabled else { return }
|
|
147
|
+
enabled = true
|
|
148
|
+
detectorState = "running"
|
|
149
|
+
|
|
150
|
+
motionManager = CMMotionManager()
|
|
151
|
+
motionManager?.accelerometerUpdateInterval = accelerometerUpdateInterval
|
|
152
|
+
|
|
153
|
+
if useM7IfAvailable && BGMotionDetector.motionHardwareAvailable() {
|
|
154
|
+
startM7Detection()
|
|
155
|
+
} else if useAccelerometerFallback {
|
|
156
|
+
startSensorDetection()
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
@objc public func stop() {
|
|
161
|
+
guard enabled else { return }
|
|
162
|
+
enabled = false
|
|
163
|
+
detectorState = "stopped"
|
|
164
|
+
stopSensorDetection()
|
|
165
|
+
activitySource?.stop()
|
|
166
|
+
windowTimer?.invalidate()
|
|
167
|
+
windowTimer = nil
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
@objc public func startSensorDetection() {
|
|
171
|
+
guard let mgr = motionManager, mgr.isAccelerometerAvailable else { return }
|
|
172
|
+
accelerometerQueue = OperationQueue()
|
|
173
|
+
accelerometerQueue?.name = "BGMotionDetector.accelerometer"
|
|
174
|
+
mgr.startAccelerometerUpdates(to: accelerometerQueue!) { [weak self] data, error in
|
|
175
|
+
guard let self = self, let data = data else { return }
|
|
176
|
+
self.activityClassifier?.ingestAcceleration(data)
|
|
177
|
+
self.accelerationChangedBlock?(data)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if mgr.isGyroAvailable {
|
|
181
|
+
mgr.startGyroUpdates(to: accelerometerQueue!) { [weak self] data, error in
|
|
182
|
+
guard let self = self, let data = data else { return }
|
|
183
|
+
self.activityClassifier?.ingestRotationRate(data)
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
scheduleWindowTimer()
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
@objc public func stopSensorDetection() {
|
|
191
|
+
motionManager?.stopAccelerometerUpdates()
|
|
192
|
+
motionManager?.stopGyroUpdates()
|
|
193
|
+
accelerometerQueue?.cancelAllOperations()
|
|
194
|
+
accelerometerQueue = nil
|
|
195
|
+
windowTimer?.invalidate()
|
|
196
|
+
windowTimer = nil
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
private func startM7Detection() {
|
|
200
|
+
isUsingM7 = true
|
|
201
|
+
let queue = DispatchQueue(label: "BGMotionDetector.M7")
|
|
202
|
+
activitySource = BGMotionActivitySource()
|
|
203
|
+
activitySource?.delegate = self
|
|
204
|
+
activitySource?.start()
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
private func scheduleWindowTimer() {
|
|
208
|
+
DispatchQueue.main.async {
|
|
209
|
+
self.windowTimer?.invalidate()
|
|
210
|
+
self.windowTimer = Timer.scheduledTimer(withTimeInterval: self.detectMotionWindow, repeats: true) { [weak self] _ in
|
|
211
|
+
self?._calculateMotionTypeLocked()
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// MARK: - Motion type calculation
|
|
217
|
+
|
|
218
|
+
@objc public func calculateMotionType() {
|
|
219
|
+
stateQueue?.async { self._calculateMotionTypeLocked() }
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
@objc public func _calculateMotionTypeLocked() {
|
|
223
|
+
guard let classifier = activityClassifier, classifier.isWindowReady() else { return }
|
|
224
|
+
let newType = classifier.classifyWithSpeed(currentSpeed)
|
|
225
|
+
_applyMotionType(fromClassifier: newType, confidence: 80, emit: true)
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
@objc public func _applyMotionType(fromClassifier type: String, confidence: Int, emit: Bool) {
|
|
229
|
+
previousMotionType = motionType
|
|
230
|
+
motionType = type
|
|
231
|
+
|
|
232
|
+
if type != previousMotionType {
|
|
233
|
+
lastTypeChangeAt = Date()
|
|
234
|
+
sdConsecutiveStillWins = 0
|
|
235
|
+
mdConsecutiveMovingWins = 0
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if type == "still" {
|
|
239
|
+
sdConsecutiveStillWins += 1
|
|
240
|
+
mdConsecutiveMovingWins = 0
|
|
241
|
+
} else {
|
|
242
|
+
mdConsecutiveMovingWins += 1
|
|
243
|
+
sdConsecutiveStillWins = 0
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
lastEmittedType = type
|
|
247
|
+
lastEmittedConfidence = confidence
|
|
248
|
+
lastConfidence = confidence
|
|
249
|
+
|
|
250
|
+
if emit {
|
|
251
|
+
let activity = BGMotionActivity(type: type, confidence: confidence)
|
|
252
|
+
currentActivity = activity
|
|
253
|
+
motionActivityChangedBlock.map { _ in }
|
|
254
|
+
NotificationCenter.default.post(name: NSNotification.Name("BGMotionDetectorDidUpdateActivity"), object: activity)
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
@objc public func _applySamplingIntervalLocked(_ interval: TimeInterval) {
|
|
259
|
+
accelerometerUpdateInterval = interval
|
|
260
|
+
motionManager?.accelerometerUpdateInterval = interval
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
@objc public func _recalcFromM7Locked() {
|
|
264
|
+
guard let activity = motionActivity else { return }
|
|
265
|
+
var type = "unknown"
|
|
266
|
+
if activity.stationary { type = "still" }
|
|
267
|
+
else if activity.walking { type = "walking" }
|
|
268
|
+
else if activity.running { type = "running" }
|
|
269
|
+
else if activity.automotive { type = "in_vehicle" }
|
|
270
|
+
else if activity.cycling { type = "on_bicycle" }
|
|
271
|
+
_applyMotionType(fromClassifier: type, confidence: Int(activity.confidence.rawValue) * 25, emit: true)
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
@objc public func _maybeAdjustSamplingForActivity(_ type: String) {
|
|
275
|
+
switch type {
|
|
276
|
+
case "still":
|
|
277
|
+
_applySamplingIntervalLocked(0.5)
|
|
278
|
+
case "walking", "running":
|
|
279
|
+
_applySamplingIntervalLocked(0.1)
|
|
280
|
+
default:
|
|
281
|
+
_applySamplingIntervalLocked(0.2)
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// MARK: - BGMotionActivitySourceDelegate
|
|
286
|
+
|
|
287
|
+
@objc public func activitySource(_ source: BGMotionActivitySource, didUpdate activity: CMMotionActivity) {
|
|
288
|
+
stateQueue?.async {
|
|
289
|
+
self.motionActivity = activity
|
|
290
|
+
self._recalcFromM7Locked()
|
|
291
|
+
self._maybeAdjustSamplingForActivity(self.motionType)
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
@objc public func activitySourceDidUpdate(_ activity: BGMotionActivity) {
|
|
296
|
+
stateQueue?.async {
|
|
297
|
+
self.motionActivity = nil
|
|
298
|
+
self._recalcFromM7Locked()
|
|
299
|
+
self._maybeAdjustSamplingForActivity(self.motionType)
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// MARK: - Location / speed
|
|
304
|
+
|
|
305
|
+
@objc public func setLocation(_ loc: CLLocation, isMoving moving: Bool) {
|
|
306
|
+
location = loc
|
|
307
|
+
currentSpeed = max(0, loc.speed)
|
|
308
|
+
isMoving = moving
|
|
309
|
+
activityClassifier?.updateSpeed(currentSpeed)
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
@objc public func updateSpeed(_ speed: Double) {
|
|
313
|
+
currentSpeed = speed
|
|
314
|
+
activityClassifier?.updateSpeed(speed)
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
@objc public func isMoving(_ moving: Bool) -> Bool {
|
|
318
|
+
return isMoving
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
@objc public func threadSafeMotionType() -> String {
|
|
322
|
+
var result = "unknown"
|
|
323
|
+
stateQueue?.sync { result = self.motionType }
|
|
324
|
+
return result
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
@objc public func threadSafeMotionActivity() -> BGMotionActivity? {
|
|
328
|
+
var result: BGMotionActivity?
|
|
329
|
+
stateQueue?.sync { result = self.currentActivity }
|
|
330
|
+
return result
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
@objc public func classifyWithSpeed(_ speed: Double) -> String {
|
|
334
|
+
return activityClassifier?.classifyWithSpeed(speed) ?? "unknown"
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
@objc public func shouldRequestLocationProbe() -> Bool {
|
|
338
|
+
guard enabled else { return false }
|
|
339
|
+
if let suppressUntil = mdSuppressUntil, Date() < suppressUntil { return false }
|
|
340
|
+
if let lastProbe = mdLastProbeAt {
|
|
341
|
+
return Date().timeIntervalSince(lastProbe) >= probeMinInterval
|
|
342
|
+
}
|
|
343
|
+
return true
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
@objc public func noteWillRequestLocationProbe() {
|
|
347
|
+
mdLastProbeAt = Date()
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
public func requestMotionPermission(_ completion: @escaping (CMAuthorizationStatus) -> Void) {
|
|
351
|
+
permissionMgr?.requestPermission(completion: completion)
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
@objc public func queryMotionActivityHistory() {
|
|
355
|
+
guard let mgr = motionActivityManager else { return }
|
|
356
|
+
let end = Date()
|
|
357
|
+
let start = end.addingTimeInterval(-3600)
|
|
358
|
+
mgr.queryActivityStarting(from: start, to: end, to: OperationQueue.main) { [weak self] (activities: [CMMotionActivity]?, error: Error?) in
|
|
359
|
+
guard let self = self, let acts = activities else { return }
|
|
360
|
+
for act in acts {
|
|
361
|
+
self.motionActivityChangedBlock?(act)
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
@objc public func getDiagnosticsData() -> [String: Any] {
|
|
367
|
+
return [
|
|
368
|
+
"motionType": motionType,
|
|
369
|
+
"isMoving": isMoving,
|
|
370
|
+
"enabled": enabled,
|
|
371
|
+
"lastEmittedType": lastEmittedType,
|
|
372
|
+
"lastEmittedConfidence": lastEmittedConfidence,
|
|
373
|
+
"sdConsecutiveStillWins": sdConsecutiveStillWins,
|
|
374
|
+
"mdConsecutiveMovingWins": mdConsecutiveMovingWins
|
|
375
|
+
]
|
|
376
|
+
}
|
|
377
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import CoreMotion
|
|
3
|
+
|
|
4
|
+
@objc public class BGMotionPermissionManager: NSObject {
|
|
5
|
+
|
|
6
|
+
private static var _sharedInstance: BGMotionPermissionManager?
|
|
7
|
+
private static let lock = NSLock()
|
|
8
|
+
|
|
9
|
+
private let motionManager = CMMotionActivityManager()
|
|
10
|
+
|
|
11
|
+
@objc public class func sharedInstance() -> BGMotionPermissionManager {
|
|
12
|
+
lock.lock()
|
|
13
|
+
defer { lock.unlock() }
|
|
14
|
+
if _sharedInstance == nil {
|
|
15
|
+
_sharedInstance = BGMotionPermissionManager()
|
|
16
|
+
}
|
|
17
|
+
return _sharedInstance!
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
@objc public override init() {
|
|
21
|
+
super.init()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
public class func authorizationStatus() -> CMAuthorizationStatus {
|
|
25
|
+
return CMMotionActivityManager.authorizationStatus()
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public func requestPermission(completion: @escaping (_ status: CMAuthorizationStatus) -> Void) {
|
|
29
|
+
motionManager.queryActivityStarting(from: Date(), to: Date(), to: .main) { _, _ in
|
|
30
|
+
completion(CMMotionActivityManager.authorizationStatus())
|
|
31
|
+
self.motionManager.stopActivityUpdates()
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@objc public func isAuthorized() -> Bool {
|
|
36
|
+
return CMMotionActivityManager.authorizationStatus() == .authorized
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@objc public func isDenied() -> Bool {
|
|
40
|
+
return CMMotionActivityManager.authorizationStatus() == .denied
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@objc public func isRestricted() -> Bool {
|
|
44
|
+
return CMMotionActivityManager.authorizationStatus() == .restricted
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
@objc public func notDetermined() -> Bool {
|
|
48
|
+
return CMMotionActivityManager.authorizationStatus() == .notDetermined
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import os.log
|
|
3
|
+
|
|
4
|
+
@objc public class BGNativeLogger: NSObject {
|
|
5
|
+
|
|
6
|
+
private static let subsystem = "com.transistorsoft.BGLocationManager"
|
|
7
|
+
private static let category = "general"
|
|
8
|
+
|
|
9
|
+
@objc public class func log(_ level: Int, tag: String, message: String) {
|
|
10
|
+
if #available(iOS 14.0, *) {
|
|
11
|
+
let logger = Logger(subsystem: subsystem, category: tag)
|
|
12
|
+
switch level {
|
|
13
|
+
case 0: break
|
|
14
|
+
case 1: logger.error("\(message)")
|
|
15
|
+
case 2: logger.warning("\(message)")
|
|
16
|
+
case 3: logger.info("\(message)")
|
|
17
|
+
case 4: logger.debug("\(message)")
|
|
18
|
+
default: logger.log("\(message)")
|
|
19
|
+
}
|
|
20
|
+
} else {
|
|
21
|
+
let prefix: String
|
|
22
|
+
switch level {
|
|
23
|
+
case 1: prefix = "ERROR"
|
|
24
|
+
case 2: prefix = "WARN"
|
|
25
|
+
case 3: prefix = "INFO"
|
|
26
|
+
case 4: prefix = "DEBUG"
|
|
27
|
+
default: prefix = "LOG"
|
|
28
|
+
}
|
|
29
|
+
print("[\(prefix)] [\(tag)] \(message)")
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@objc public class func error(_ tag: String, message: String) {
|
|
34
|
+
log(1, tag: tag, message: message)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@objc public class func warn(_ tag: String, message: String) {
|
|
38
|
+
log(2, tag: tag, message: message)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@objc public class func info(_ tag: String, message: String) {
|
|
42
|
+
log(3, tag: tag, message: message)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@objc public class func debug(_ tag: String, message: String) {
|
|
46
|
+
log(4, tag: tag, message: message)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import UserNotifications
|
|
3
|
+
|
|
4
|
+
@objc public final class BGNotifications: NSObject {
|
|
5
|
+
|
|
6
|
+
@objc public var prepared: Bool = false
|
|
7
|
+
|
|
8
|
+
@objc public static let shared = BGNotifications()
|
|
9
|
+
|
|
10
|
+
@objc public func prepareIfNeeded(_ context: Any?) {
|
|
11
|
+
guard !prepared else { return }
|
|
12
|
+
prepared = true
|
|
13
|
+
let center = UNUserNotificationCenter.current()
|
|
14
|
+
center.delegate = self
|
|
15
|
+
center.getNotificationSettings { settings in
|
|
16
|
+
if settings.authorizationStatus != .notDetermined {
|
|
17
|
+
NSLog("[TS] notif status=%ld", settings.authorizationStatus.rawValue)
|
|
18
|
+
} else {
|
|
19
|
+
center.requestAuthorization(options: [.alert, .badge, .sound]) { granted, error in
|
|
20
|
+
NSLog("[TS] notif auth: granted=%d err=%@", granted, error?.localizedDescription ?? "")
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
public func userNotificationCenter(_ center: UNUserNotificationCenter,
|
|
27
|
+
willPresent notification: UNNotification,
|
|
28
|
+
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
|
|
29
|
+
if #available(iOS 14.0, *) {
|
|
30
|
+
completionHandler([.banner, .list, .sound])
|
|
31
|
+
} else {
|
|
32
|
+
completionHandler([.alert, .badge, .sound])
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
extension BGNotifications: UNUserNotificationCenterDelegate {}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import CoreLocation
|
|
3
|
+
|
|
4
|
+
@objc public class BGOdometer: NSObject {
|
|
5
|
+
|
|
6
|
+
private static var _sharedInstance: BGOdometer?
|
|
7
|
+
private static let lock = NSLock()
|
|
8
|
+
|
|
9
|
+
@objc public var value: Double = 0
|
|
10
|
+
private var lastLocation: CLLocation?
|
|
11
|
+
private let userDefaultsKey = "BGLocationManager_odometer"
|
|
12
|
+
|
|
13
|
+
@objc public class func sharedInstance() -> BGOdometer {
|
|
14
|
+
lock.lock()
|
|
15
|
+
defer { lock.unlock() }
|
|
16
|
+
if _sharedInstance == nil {
|
|
17
|
+
_sharedInstance = BGOdometer()
|
|
18
|
+
}
|
|
19
|
+
return _sharedInstance!
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@objc public override init() {
|
|
23
|
+
super.init()
|
|
24
|
+
value = UserDefaults.standard.double(forKey: userDefaultsKey)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@objc public func addDistance(from location: CLLocation) -> Double {
|
|
28
|
+
guard let last = lastLocation else {
|
|
29
|
+
lastLocation = location
|
|
30
|
+
return value
|
|
31
|
+
}
|
|
32
|
+
let delta = location.distance(from: last)
|
|
33
|
+
value += delta
|
|
34
|
+
lastLocation = location
|
|
35
|
+
persist()
|
|
36
|
+
return value
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@objc public func setOdometer(_ newValue: Double, location: CLLocation?) {
|
|
40
|
+
value = newValue
|
|
41
|
+
lastLocation = location
|
|
42
|
+
persist()
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@objc public func reset() {
|
|
46
|
+
value = 0
|
|
47
|
+
lastLocation = nil
|
|
48
|
+
persist()
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@objc public func getOdometer() -> Double {
|
|
52
|
+
return value
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@objc public func setLastLocation(_ location: CLLocation?) {
|
|
56
|
+
lastLocation = location
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@objc public func lastKnownLocation() -> CLLocation? {
|
|
60
|
+
return lastLocation
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private func persist() {
|
|
64
|
+
UserDefaults.standard.set(value, forKey: userDefaultsKey)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
@objc public class BGPersistenceConfig: BGConfigModuleBase {
|
|
4
|
+
|
|
5
|
+
@objc public var maxDaysToPersist: Int = 1
|
|
6
|
+
@objc public var maxRecordsToPersist: Int = -1
|
|
7
|
+
@objc public var persistMode: Int = 2
|
|
8
|
+
@objc public var locationsOrderDirection: String = "ASC"
|
|
9
|
+
|
|
10
|
+
@objc public override func applyDefaults() {
|
|
11
|
+
maxDaysToPersist = 1
|
|
12
|
+
maxRecordsToPersist = -1
|
|
13
|
+
persistMode = 2
|
|
14
|
+
locationsOrderDirection = "ASC"
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
@objc public override func propertySpecs() -> [BGPropertySpecImpl] {
|
|
18
|
+
return [
|
|
19
|
+
BGPropertySpec(name: "maxDaysToPersist", type: "int"),
|
|
20
|
+
BGPropertySpec(name: "maxRecordsToPersist", type: "int"),
|
|
21
|
+
BGPropertySpec(name: "persistMode", type: "int"),
|
|
22
|
+
BGPropertySpec(name: "locationsOrderDirection", type: "string")
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@objc public override var description: String {
|
|
27
|
+
return "<BGPersistenceConfig maxDaysToPersist=\(maxDaysToPersist) maxRecordsToPersist=\(maxRecordsToPersist)>"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import CoreLocation
|
|
3
|
+
|
|
4
|
+
@objc public class BGPolygonStreamRequest: NSObject {
|
|
5
|
+
|
|
6
|
+
@objc public var identifier: String
|
|
7
|
+
@objc public var vertices: [[CLLocationDegrees]]
|
|
8
|
+
@objc public var interval: Double
|
|
9
|
+
@objc public var success: ((Any?) -> Void)?
|
|
10
|
+
@objc public var failure: ((Int) -> Void)?
|
|
11
|
+
|
|
12
|
+
@objc public init(
|
|
13
|
+
identifier: String,
|
|
14
|
+
vertices: [[CLLocationDegrees]],
|
|
15
|
+
interval: Double,
|
|
16
|
+
success: @escaping (Any?) -> Void,
|
|
17
|
+
failure: @escaping (Int) -> Void
|
|
18
|
+
) {
|
|
19
|
+
self.identifier = identifier
|
|
20
|
+
self.vertices = vertices
|
|
21
|
+
self.interval = interval
|
|
22
|
+
self.success = success
|
|
23
|
+
self.failure = failure
|
|
24
|
+
super.init()
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@objc public override init() {
|
|
28
|
+
identifier = UUID().uuidString
|
|
29
|
+
vertices = []
|
|
30
|
+
interval = 1000
|
|
31
|
+
super.init()
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@objc public func containsCoordinate(_ coord: CLLocationCoordinate2D) -> Bool {
|
|
35
|
+
guard vertices.count >= 3 else { return false }
|
|
36
|
+
var inside = false
|
|
37
|
+
var j = vertices.count - 1
|
|
38
|
+
for i in 0..<vertices.count {
|
|
39
|
+
let xi = vertices[i][0], yi = vertices[i][1]
|
|
40
|
+
let xj = vertices[j][0], yj = vertices[j][1]
|
|
41
|
+
let intersect = ((yi > coord.latitude) != (yj > coord.latitude)) &&
|
|
42
|
+
(coord.longitude < (xj - xi) * (coord.latitude - yi) / (yj - yi) + xi)
|
|
43
|
+
if intersect { inside = !inside }
|
|
44
|
+
j = i
|
|
45
|
+
}
|
|
46
|
+
return inside
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
@objc public final class BGPowerSaveChangeEvent: NSObject {
|
|
4
|
+
|
|
5
|
+
@objc public private(set) var isPowerSaveMode: Bool
|
|
6
|
+
|
|
7
|
+
@objc public override init() {
|
|
8
|
+
self.isPowerSaveMode = false
|
|
9
|
+
super.init()
|
|
10
|
+
self.isPowerSaveMode = ProcessInfo.processInfo.isLowPowerModeEnabled
|
|
11
|
+
}
|
|
12
|
+
}
|