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,94 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import CoreLocation
|
|
3
|
+
|
|
4
|
+
@objc public class BGGeofenceLocationRequest: BGSingleLocationRequest {
|
|
5
|
+
|
|
6
|
+
@objc public var geofenceEvent: BGGeofenceEvent?
|
|
7
|
+
|
|
8
|
+
@objc public class func request(
|
|
9
|
+
event: BGGeofenceEvent,
|
|
10
|
+
maximumAge: Double,
|
|
11
|
+
timeout: Double,
|
|
12
|
+
desiredAccuracy: CLLocationAccuracy,
|
|
13
|
+
allowStale: Bool,
|
|
14
|
+
samples: Int,
|
|
15
|
+
label: String?,
|
|
16
|
+
persist: Bool,
|
|
17
|
+
success: @escaping (Any?) -> Void,
|
|
18
|
+
failure: @escaping (Int) -> Void
|
|
19
|
+
) -> BGGeofenceLocationRequest {
|
|
20
|
+
let req = BGGeofenceLocationRequest()
|
|
21
|
+
req.geofenceEvent = event
|
|
22
|
+
req.maximumAge = maximumAge
|
|
23
|
+
req.timeout = timeout
|
|
24
|
+
req.desiredAccuracy = desiredAccuracy
|
|
25
|
+
req.allowStale = allowStale
|
|
26
|
+
req.samples = samples
|
|
27
|
+
req.label = label
|
|
28
|
+
req.persist = persist
|
|
29
|
+
req.success = success
|
|
30
|
+
req.failure = failure
|
|
31
|
+
req.type = "geofence"
|
|
32
|
+
return req
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@objc public init(
|
|
36
|
+
event: BGGeofenceEvent,
|
|
37
|
+
maximumAge: Double,
|
|
38
|
+
timeout: Double,
|
|
39
|
+
desiredAccuracy: CLLocationAccuracy,
|
|
40
|
+
allowStale: Bool,
|
|
41
|
+
samples: Int,
|
|
42
|
+
label: String?,
|
|
43
|
+
persist: Bool,
|
|
44
|
+
success: @escaping (Any?) -> Void,
|
|
45
|
+
failure: @escaping (Int) -> Void
|
|
46
|
+
) {
|
|
47
|
+
super.init()
|
|
48
|
+
self.geofenceEvent = event
|
|
49
|
+
self.maximumAge = maximumAge
|
|
50
|
+
self.timeout = timeout
|
|
51
|
+
self.desiredAccuracy = desiredAccuracy
|
|
52
|
+
self.allowStale = allowStale
|
|
53
|
+
self.samples = samples
|
|
54
|
+
self.label = label
|
|
55
|
+
self.persist = persist
|
|
56
|
+
self.success = success
|
|
57
|
+
self.failure = failure
|
|
58
|
+
self.type = "geofence"
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
@objc public override init() {
|
|
62
|
+
super.init()
|
|
63
|
+
self.type = "geofence"
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@objc public func copy(with zone: NSZone? = nil) -> Any {
|
|
67
|
+
let copy = BGGeofenceLocationRequest()
|
|
68
|
+
copy.geofenceEvent = geofenceEvent
|
|
69
|
+
copy.maximumAge = maximumAge
|
|
70
|
+
copy.timeout = timeout
|
|
71
|
+
copy.desiredAccuracy = desiredAccuracy
|
|
72
|
+
copy.allowStale = allowStale
|
|
73
|
+
copy.samples = samples
|
|
74
|
+
copy.label = label
|
|
75
|
+
copy.persist = persist
|
|
76
|
+
copy.success = success
|
|
77
|
+
copy.failure = failure
|
|
78
|
+
return copy
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
@objc public func didComplete(withLocation location: CLLocation) {
|
|
82
|
+
success?(location)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
public func setType(_ type: String) {
|
|
86
|
+
self.type = type
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
@objc public func toDictionary() -> [String: Any] {
|
|
90
|
+
var dict: [String: Any] = ["type": type]
|
|
91
|
+
if let event = geofenceEvent { dict["geofenceEvent"] = event.toDictionary() }
|
|
92
|
+
return dict
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import CoreLocation
|
|
3
|
+
|
|
4
|
+
@objc public class BGGeofenceManager: NSObject, CLLocationManagerDelegate {
|
|
5
|
+
|
|
6
|
+
private static var _sharedInstance: BGGeofenceManager?
|
|
7
|
+
private static let instanceLock = NSLock()
|
|
8
|
+
|
|
9
|
+
@objc public class func sharedInstance() -> BGGeofenceManager {
|
|
10
|
+
instanceLock.lock()
|
|
11
|
+
defer { instanceLock.unlock() }
|
|
12
|
+
if _sharedInstance == nil { _sharedInstance = BGGeofenceManager() }
|
|
13
|
+
return _sharedInstance!
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// MARK: - State
|
|
17
|
+
|
|
18
|
+
@objc public var locationManager: CLLocationManager?
|
|
19
|
+
@objc public var enabled: Bool = false
|
|
20
|
+
@objc public var isMoving: Bool = false
|
|
21
|
+
@objc public var count: Int = 0
|
|
22
|
+
@objc public var proximityRadius: CLLocationDistance = 1000
|
|
23
|
+
@objc public var evaluationInterval: TimeInterval = 5.0
|
|
24
|
+
@objc public var bufferInterval: TimeInterval = 0.5
|
|
25
|
+
@objc public var evaluated: Bool = false
|
|
26
|
+
@objc public var lastLocation: CLLocation?
|
|
27
|
+
@objc public var lastEvent: BGGeofenceEvent?
|
|
28
|
+
@objc public var lastAction: String?
|
|
29
|
+
@objc public var lastEvaluatedAt: Date?
|
|
30
|
+
@objc public var didScheduleColdStartReconcile: Bool = false
|
|
31
|
+
@objc public var isMonitoringSignificantLocationChanges: Bool = false
|
|
32
|
+
@objc public var entryStates: [String: String] = [:]
|
|
33
|
+
@objc public var monitoredGeofences: [String: BGGeofence] = [:]
|
|
34
|
+
@objc public var preventSuspendTask: Any?
|
|
35
|
+
@objc public var willEvaluateProximity: Bool = false
|
|
36
|
+
|
|
37
|
+
var bufferTimer: Timer?
|
|
38
|
+
var clLocationListener: AnyObject?
|
|
39
|
+
var eventQueue: DispatchQueue?
|
|
40
|
+
|
|
41
|
+
private let stateQueue = DispatchQueue(label: "BGGeofenceManager.state", attributes: .concurrent)
|
|
42
|
+
private var clMutationQueue = DispatchQueue(label: "BGGeofenceManager.cl")
|
|
43
|
+
|
|
44
|
+
@objc public override init() {
|
|
45
|
+
super.init()
|
|
46
|
+
eventQueue = DispatchQueue(label: "BGGeofenceManager.events")
|
|
47
|
+
registerConfigChangeHandlers()
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@objc public func registerConfigChangeHandlers() {
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// MARK: - Start/Stop
|
|
54
|
+
|
|
55
|
+
@objc public func start() {
|
|
56
|
+
guard !enabled else { return }
|
|
57
|
+
enabled = true
|
|
58
|
+
// NOTE: the CLLocationManager delegate is owned solely by BGCLRouter
|
|
59
|
+
// (see BGLocationManager.setupCoreLocation), which forwards region and
|
|
60
|
+
// location callbacks here. Do NOT assign mgr.delegate.
|
|
61
|
+
reconcileMonitoredCacheFromCoreLocation()
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
@objc public func stop() {
|
|
65
|
+
guard enabled else { return }
|
|
66
|
+
enabled = false
|
|
67
|
+
stopMonitoringGeofences()
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// MARK: - CL mutation
|
|
71
|
+
|
|
72
|
+
@objc public func _mutateCL(_ block: @escaping (CLLocationManager) -> Void) {
|
|
73
|
+
clMutationQueue.async {
|
|
74
|
+
guard let mgr = self.locationManager else { return }
|
|
75
|
+
DispatchQueue.main.async { block(mgr) }
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// MARK: - Geofence management
|
|
80
|
+
|
|
81
|
+
@objc public func create(_ geofence: BGGeofence, success: (() -> Void)?, failure: ((Error) -> Void)?) {
|
|
82
|
+
BGGeofenceDAO.sharedInstance().create(geofence)
|
|
83
|
+
startMonitoringGeofence(geofence)
|
|
84
|
+
count = BGGeofenceDAO.sharedInstance().count()
|
|
85
|
+
success?()
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
@objc public func destroy(_ identifier: String, success: (() -> Void)?, failure: ((Error) -> Void)?) {
|
|
89
|
+
if let geofence = BGGeofenceDAO.sharedInstance().find(identifier) {
|
|
90
|
+
BGGeofenceDAO.sharedInstance().destroy(identifier)
|
|
91
|
+
stopMonitoringGeofence(geofence)
|
|
92
|
+
}
|
|
93
|
+
count = BGGeofenceDAO.sharedInstance().count()
|
|
94
|
+
success?()
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
@objc public func isInfiniteMonitoring() -> Bool {
|
|
98
|
+
return true
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
@objc public func startMonitoringGeofence(_ geofence: BGGeofence) {
|
|
102
|
+
_mutateCL { mgr in
|
|
103
|
+
geofence.startMonitoring(withLocationManager: mgr, prefix: "BGGeofence:")
|
|
104
|
+
self.stateQueue.async(flags: .barrier) {
|
|
105
|
+
self.monitoredGeofences[geofence.identifier] = geofence
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
@objc public func stopMonitoringGeofence(_ geofence: BGGeofence) {
|
|
111
|
+
_mutateCL { mgr in
|
|
112
|
+
for region in mgr.monitoredRegions {
|
|
113
|
+
if region.identifier.hasSuffix(geofence.identifier) {
|
|
114
|
+
mgr.stopMonitoring(for: region)
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
self.stateQueue.async(flags: .barrier) {
|
|
118
|
+
self.monitoredGeofences.removeValue(forKey: geofence.identifier)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
@objc public func stopMonitoringGeofenceByIdentifier(_ identifier: String) {
|
|
124
|
+
if let geofence = monitoredGeofences[identifier] {
|
|
125
|
+
stopMonitoringGeofence(geofence)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
@objc public func stopMonitoringGeofences() {
|
|
130
|
+
_mutateCL { mgr in
|
|
131
|
+
for region in mgr.monitoredRegions {
|
|
132
|
+
if region.identifier.hasPrefix("BGGeofence:") {
|
|
133
|
+
mgr.stopMonitoring(for: region)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
stateQueue.async(flags: .barrier) {
|
|
138
|
+
self.monitoredGeofences.removeAll()
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
@objc public func isMonitoringRegion(_ identifier: String) -> Bool {
|
|
143
|
+
return monitoredGeofences[identifier] != nil
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
@objc public func getMonitoredGeofenceByIdentifier(_ identifier: String) -> BGGeofence? {
|
|
147
|
+
var result: BGGeofence?
|
|
148
|
+
stateQueue.sync { result = monitoredGeofences[identifier] }
|
|
149
|
+
return result
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
@objc public func identifierFor(_ region: CLRegion) -> String {
|
|
153
|
+
let id = region.identifier
|
|
154
|
+
if id.hasPrefix("BGGeofence:") {
|
|
155
|
+
return String(id.dropFirst("BGGeofence:".count))
|
|
156
|
+
}
|
|
157
|
+
return id
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
@objc public func reconcileMonitoredCacheFromCoreLocation() {
|
|
161
|
+
guard let mgr = locationManager else { return }
|
|
162
|
+
var coreLocationIds = Set<String>()
|
|
163
|
+
for region in mgr.monitoredRegions {
|
|
164
|
+
if region.identifier.hasPrefix("BGGeofence:") {
|
|
165
|
+
coreLocationIds.insert(identifierFor(region))
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
let dao = BGGeofenceDAO.sharedInstance()
|
|
169
|
+
let all = dao.all()
|
|
170
|
+
for geofence in all {
|
|
171
|
+
if !coreLocationIds.contains(geofence.identifier) {
|
|
172
|
+
startMonitoringGeofence(geofence)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// MARK: - Evaluation
|
|
178
|
+
|
|
179
|
+
@objc public func setLocation(_ location: CLLocation, isMoving moving: Bool) {
|
|
180
|
+
lastLocation = location
|
|
181
|
+
isMoving = moving
|
|
182
|
+
if moving {
|
|
183
|
+
evaluateProximity(location)
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
@objc public func evaluateProximity(_ location: CLLocation) {
|
|
188
|
+
evaluateProximity(location, delay: false)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
@objc public func evaluateProximity(_ location: CLLocation, delay: Bool) {
|
|
192
|
+
guard enabled else { return }
|
|
193
|
+
if delay {
|
|
194
|
+
startBufferTimer()
|
|
195
|
+
} else {
|
|
196
|
+
onEvaluate()
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
@objc public func startBufferTimer() {
|
|
201
|
+
bufferTimer?.invalidate()
|
|
202
|
+
DispatchQueue.main.async {
|
|
203
|
+
self.bufferTimer = Timer.scheduledTimer(withTimeInterval: self.bufferInterval, repeats: false) { [weak self] _ in
|
|
204
|
+
self?.onEvaluate()
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
@objc public func onEvaluate() {
|
|
210
|
+
guard let location = lastLocation else { return }
|
|
211
|
+
lastEvaluatedAt = Date()
|
|
212
|
+
evaluated = true
|
|
213
|
+
|
|
214
|
+
let dao = BGGeofenceDAO.sharedInstance()
|
|
215
|
+
let nearby = dao.allWithinRadius(proximityRadius, latitude: location.coordinate.latitude, longitude: location.coordinate.longitude, limit: 20)
|
|
216
|
+
|
|
217
|
+
_mutateCL { mgr in
|
|
218
|
+
var needsStartSLC = false
|
|
219
|
+
for geofence in nearby {
|
|
220
|
+
if !self.isMonitoringRegion(geofence.identifier) {
|
|
221
|
+
geofence.startMonitoring(withLocationManager: mgr, prefix: "BGGeofence:")
|
|
222
|
+
self.stateQueue.async(flags: .barrier) {
|
|
223
|
+
self.monitoredGeofences[geofence.identifier] = geofence
|
|
224
|
+
}
|
|
225
|
+
needsStartSLC = true
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
if needsStartSLC && !self.isMonitoringSignificantLocationChanges {
|
|
229
|
+
self.startMonitoringSignificantLocationChanges()
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// MARK: - Significant location changes
|
|
235
|
+
|
|
236
|
+
@objc public func startMonitoringSignificantLocationChanges() {
|
|
237
|
+
guard !isMonitoringSignificantLocationChanges else { return }
|
|
238
|
+
_mutateCL { mgr in
|
|
239
|
+
mgr.startMonitoringSignificantLocationChanges()
|
|
240
|
+
self.isMonitoringSignificantLocationChanges = true
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
@objc public func stopMonitoringSignificantLocationChanges() {
|
|
245
|
+
guard isMonitoringSignificantLocationChanges else { return }
|
|
246
|
+
_mutateCL { mgr in
|
|
247
|
+
mgr.stopMonitoringSignificantLocationChanges()
|
|
248
|
+
self.isMonitoringSignificantLocationChanges = false
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
@objc public func onStopMonitoringSLC() {
|
|
253
|
+
stopMonitoringSignificantLocationChanges()
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// MARK: - Event handling
|
|
257
|
+
|
|
258
|
+
@objc public func handleGeofenceEvent(_ geofence: BGGeofence, action: String) {
|
|
259
|
+
createEvent(geofence, geofence: geofence, action: action)
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
@objc public func createEvent(_ sender: BGGeofence, geofence: BGGeofence, action: String) {
|
|
263
|
+
let identifier = geofence.identifier
|
|
264
|
+
let event = BGGeofenceEvent(identifier: identifier, action: action, timestamp: Date(), geofence: geofence, location: lastLocation, extras: geofence.extras)
|
|
265
|
+
lastEvent = event
|
|
266
|
+
lastAction = action
|
|
267
|
+
geofence.fireEvent(action, location: lastLocation)
|
|
268
|
+
BGGeofenceDAO.sharedInstance().updateState(forIdentifier: identifier, entryState: action == "ENTER" ? "ENTER" : "EXIT", hits: geofence.hits + 1)
|
|
269
|
+
BGEventBus.sharedInstance().emit(BGEventNames.geofence, payload: event.toDictionary())
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// MARK: - CLLocationManagerDelegate
|
|
273
|
+
|
|
274
|
+
@objc public func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
|
|
275
|
+
guard region.identifier.hasPrefix("BGGeofence:") else { return }
|
|
276
|
+
let id = identifierFor(region)
|
|
277
|
+
if let geofence = BGGeofenceDAO.sharedInstance().find(id) {
|
|
278
|
+
if geofence.notifyOnEntry {
|
|
279
|
+
handleGeofenceEvent(geofence, action: "ENTER")
|
|
280
|
+
}
|
|
281
|
+
if geofence.notifyOnDwell && geofence.loiteringDelay > 0 {
|
|
282
|
+
geofence.startLoitering()
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
@objc public func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
|
|
288
|
+
guard region.identifier.hasPrefix("BGGeofence:") else { return }
|
|
289
|
+
let id = identifierFor(region)
|
|
290
|
+
if let geofence = BGGeofenceDAO.sharedInstance().find(id) {
|
|
291
|
+
geofence.cancelLoitering()
|
|
292
|
+
if geofence.notifyOnExit {
|
|
293
|
+
handleGeofenceEvent(geofence, action: "EXIT")
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
@objc public func locationManager(_ manager: CLLocationManager, didStartMonitoringFor region: CLRegion) {
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
@objc public func locationManager(_ manager: CLLocationManager, monitoringDidFailFor region: CLRegion?, withError error: Error) {
|
|
302
|
+
BGNativeLogger.error("BGGeofenceManager", message: error.localizedDescription)
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
@objc public func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
|
|
306
|
+
guard let location = locations.last else { return }
|
|
307
|
+
setLocation(location, isMoving: isMoving)
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// MARK: - Info
|
|
311
|
+
|
|
312
|
+
@objc public func launchedInBackground() -> Bool {
|
|
313
|
+
return BGAppState.sharedInstance().didLaunchInBackground
|
|
314
|
+
}
|
|
315
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import CoreLocation
|
|
3
|
+
|
|
4
|
+
@objc public final class BGGeofenceTransition: NSObject {
|
|
5
|
+
|
|
6
|
+
@objc public var location: CLLocation?
|
|
7
|
+
@objc public var triggerLocation: CLLocation?
|
|
8
|
+
@objc public var triggerLocationRequest: BGCurrentPositionRequest?
|
|
9
|
+
@objc public var loiteringRegion: CLCircularRegion?
|
|
10
|
+
@objc public var isLoitering: Bool = false
|
|
11
|
+
@objc public var didLoiter: Bool = false
|
|
12
|
+
@objc public var didComplete: Bool = false
|
|
13
|
+
@objc public var onComplete: ((NSError?) -> Void)?
|
|
14
|
+
@objc public var geofence: BGGeofence?
|
|
15
|
+
@objc public var region: CLRegion?
|
|
16
|
+
@objc public var timestamp: Date?
|
|
17
|
+
@objc public var action: String?
|
|
18
|
+
|
|
19
|
+
@objc public init(geofence: BGGeofence?,
|
|
20
|
+
region: CLRegion?,
|
|
21
|
+
action: String?,
|
|
22
|
+
onComplete: ((NSError?) -> Void)?) {
|
|
23
|
+
self.geofence = geofence
|
|
24
|
+
self.region = region
|
|
25
|
+
self.action = action
|
|
26
|
+
self.onComplete = onComplete
|
|
27
|
+
self.isLoitering = false
|
|
28
|
+
self.didLoiter = false
|
|
29
|
+
self.didComplete = false
|
|
30
|
+
self.timestamp = Date()
|
|
31
|
+
super.init()
|
|
32
|
+
requestTriggerLocation()
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@objc public convenience init(geofence: BGGeofence?,
|
|
36
|
+
action: String?,
|
|
37
|
+
onComplete: ((NSError?) -> Void)?) {
|
|
38
|
+
self.init(geofence: geofence, region: nil, action: action, onComplete: onComplete)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@objc public convenience init(geofence: BGGeofence?,
|
|
42
|
+
action: String?,
|
|
43
|
+
triggerLocation: CLLocation?) {
|
|
44
|
+
self.init(geofence: geofence, action: action, onComplete: nil)
|
|
45
|
+
if let triggerLocation = triggerLocation {
|
|
46
|
+
self.triggerLocation = triggerLocation
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@objc public func getLoiteringRegion(_ location: CLLocation) -> CLCircularRegion? {
|
|
51
|
+
if let existing = loiteringRegion {
|
|
52
|
+
return existing
|
|
53
|
+
}
|
|
54
|
+
guard let region = region as? CLCircularRegion else { return nil }
|
|
55
|
+
let center = CLLocation(latitude: region.center.latitude,
|
|
56
|
+
longitude: region.center.longitude)
|
|
57
|
+
let distance = location.distance(from: center)
|
|
58
|
+
let accuracy = location.horizontalAccuracy
|
|
59
|
+
let radius = distance + max(accuracy * 1.5, 10.0)
|
|
60
|
+
let loitering = CLCircularRegion(center: region.center,
|
|
61
|
+
radius: radius,
|
|
62
|
+
identifier: "loitering-region")
|
|
63
|
+
self.loiteringRegion = loitering
|
|
64
|
+
return loitering
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
@objc public func requestTriggerLocation() {
|
|
68
|
+
let label = String(format: "BGGeofenceTransition:%@:%@",
|
|
69
|
+
action ?? "", geofence?.identifier ?? "")
|
|
70
|
+
weak var weakSelf = self
|
|
71
|
+
let request = BGCurrentPositionRequest(
|
|
72
|
+
type: "geofence",
|
|
73
|
+
maximumAge: 30.0,
|
|
74
|
+
timeout: 10000,
|
|
75
|
+
desiredAccuracy: 1,
|
|
76
|
+
allowStale: true,
|
|
77
|
+
samples: 3,
|
|
78
|
+
label: label,
|
|
79
|
+
persist: true,
|
|
80
|
+
extras: nil,
|
|
81
|
+
success: { _ in },
|
|
82
|
+
failure: { (code: Int) in
|
|
83
|
+
guard let strongSelf = weakSelf else { return }
|
|
84
|
+
let error = NSError(domain: "BGGeofenceTransition", code: code)
|
|
85
|
+
strongSelf.onComplete?(error)
|
|
86
|
+
})
|
|
87
|
+
self.triggerLocationRequest = request
|
|
88
|
+
BGLocationRequestService.sharedInstance().requestLocation(request)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
@objc public func cancelLoitering() {
|
|
92
|
+
guard let request = triggerLocationRequest else { return }
|
|
93
|
+
BGLocationRequestService.sharedInstance().cancelRequest(request.label ?? "")
|
|
94
|
+
BGLog.sharedInstance().notify("Geofence DWELL cancelled", debug: BGConfig.sharedInstance().logger.debug)
|
|
95
|
+
triggerLocationRequest = nil
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
@objc public final class BGGeofencesChangeEvent: NSObject {
|
|
4
|
+
|
|
5
|
+
@objc public private(set) var on: [Any]
|
|
6
|
+
@objc public private(set) var off: [Any]
|
|
7
|
+
|
|
8
|
+
@objc public init(on: [Any]?, off: [Any]?) {
|
|
9
|
+
self.on = on ?? []
|
|
10
|
+
self.off = off ?? []
|
|
11
|
+
super.init()
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
@objc public func toDictionary() -> [String: Any] {
|
|
15
|
+
var onDictionaries: [Any] = []
|
|
16
|
+
for geofence in on {
|
|
17
|
+
if let g = geofence as? BGGeofence {
|
|
18
|
+
onDictionaries.append(g.toDictionary())
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return [
|
|
22
|
+
"on": onDictionaries,
|
|
23
|
+
"off": off
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import CoreLocation
|
|
3
|
+
|
|
4
|
+
@objc public class BGGeolocationConfig: BGConfigModuleBase {
|
|
5
|
+
|
|
6
|
+
@objc public var desiredAccuracy: CLLocationAccuracy = kCLLocationAccuracyBest
|
|
7
|
+
@objc public var distanceFilter: CLLocationDistance = 10.0
|
|
8
|
+
@objc public var stationaryRadius: CLLocationDistance = 25.0
|
|
9
|
+
@objc public var locationTimeout: Double = 60.0
|
|
10
|
+
@objc public var stopTimeout: Double = 5.0
|
|
11
|
+
@objc public var stopAfterElapsedMinutes: Double = 0
|
|
12
|
+
@objc public var activityType: CLActivityType = .automotiveNavigation
|
|
13
|
+
@objc public var pausesLocationUpdatesAutomatically: Bool = false
|
|
14
|
+
@objc public var showsBackgroundLocationIndicator: Bool = false
|
|
15
|
+
@objc public var useSignificantChangesOnly: Bool = false
|
|
16
|
+
// When true, the engine never powers GPS down to the stationary state — it
|
|
17
|
+
// keeps continuous location updates running for the whole tracking session.
|
|
18
|
+
// Combined with showsBackgroundLocationIndicator this is the "ride app"
|
|
19
|
+
// behavior: the app stays alive in the background (location indicator shown)
|
|
20
|
+
// and tracks continuously instead of relying on motion/region wakeups.
|
|
21
|
+
// Higher battery cost; far more reliable background tracking.
|
|
22
|
+
@objc public var disableStopDetection: Bool = false
|
|
23
|
+
@objc public var locationAuthorizationRequest: String = "Always"
|
|
24
|
+
@objc public var locationAuthorizationAlert: [String: String] = [:]
|
|
25
|
+
@objc public var disableLocationAuthorizationAlert: Bool = false
|
|
26
|
+
@objc public var geofenceProximityRadius: CLLocationDistance = 1000.0
|
|
27
|
+
@objc public var geofenceInitialTriggerEntry: Bool = true
|
|
28
|
+
@objc public var enableTimestampMeta: Bool = false
|
|
29
|
+
@objc public var disableElasticity: Bool = false
|
|
30
|
+
@objc public var elasticityMultiplier: Double = 1.0
|
|
31
|
+
@objc public var filter: BGLocationFilterConfig?
|
|
32
|
+
|
|
33
|
+
@objc public class func activityType(fromString s: String) -> CLActivityType {
|
|
34
|
+
switch s.lowercased() {
|
|
35
|
+
case "automotive_navigation", "automotivenavigation": return .automotiveNavigation
|
|
36
|
+
case "fitness": return .fitness
|
|
37
|
+
case "other_navigation", "othernavigation": return .otherNavigation
|
|
38
|
+
case "other": return .other
|
|
39
|
+
default: return .other
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@objc public class func string(forActivityType type: CLActivityType) -> String {
|
|
44
|
+
switch type {
|
|
45
|
+
case .automotiveNavigation: return "automotive_navigation"
|
|
46
|
+
case .fitness: return "fitness"
|
|
47
|
+
case .otherNavigation: return "other_navigation"
|
|
48
|
+
case .other: return "other"
|
|
49
|
+
default: return "other"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
@objc public class func decodeDesiredAccuracy(_ value: Any?) -> CLLocationAccuracy {
|
|
54
|
+
if let n = value as? NSNumber {
|
|
55
|
+
let d = n.doubleValue
|
|
56
|
+
switch d {
|
|
57
|
+
case -1: return kCLLocationAccuracyBestForNavigation
|
|
58
|
+
case -2: return kCLLocationAccuracyBest
|
|
59
|
+
case 10: return kCLLocationAccuracyNearestTenMeters
|
|
60
|
+
case 100: return kCLLocationAccuracyHundredMeters
|
|
61
|
+
case 1000: return kCLLocationAccuracyKilometer
|
|
62
|
+
case 3000: return kCLLocationAccuracyThreeKilometers
|
|
63
|
+
default: return d
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return kCLLocationAccuracyBest
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
@objc public override func applyDefaults() {
|
|
70
|
+
desiredAccuracy = kCLLocationAccuracyBest
|
|
71
|
+
distanceFilter = 10.0
|
|
72
|
+
stationaryRadius = 25.0
|
|
73
|
+
locationTimeout = 60.0
|
|
74
|
+
stopTimeout = 5.0
|
|
75
|
+
stopAfterElapsedMinutes = 0
|
|
76
|
+
activityType = .automotiveNavigation
|
|
77
|
+
pausesLocationUpdatesAutomatically = false
|
|
78
|
+
showsBackgroundLocationIndicator = false
|
|
79
|
+
useSignificantChangesOnly = false
|
|
80
|
+
disableStopDetection = false
|
|
81
|
+
locationAuthorizationRequest = "Always"
|
|
82
|
+
disableLocationAuthorizationAlert = false
|
|
83
|
+
geofenceProximityRadius = 1000.0
|
|
84
|
+
geofenceInitialTriggerEntry = true
|
|
85
|
+
enableTimestampMeta = false
|
|
86
|
+
disableElasticity = false
|
|
87
|
+
elasticityMultiplier = 1.0
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
@objc public var requestsAlwaysAuthorization: Bool {
|
|
91
|
+
return locationAuthorizationRequest == "Always"
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
@objc public var usesHighAccuracyGPS: Bool {
|
|
95
|
+
return desiredAccuracy <= kCLLocationAccuracyNearestTenMeters
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
@objc public var hasValidGeofenceProximityRadius: Bool {
|
|
99
|
+
return geofenceProximityRadius > 0
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
@objc public func getLocationAuthorizationAlertStrings() -> [String: String] {
|
|
103
|
+
return locationAuthorizationAlert
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
@objc public override func propertySpecs() -> [BGPropertySpecImpl] {
|
|
107
|
+
return [
|
|
108
|
+
BGPropertySpec(name: "desiredAccuracy", type: "double"),
|
|
109
|
+
BGPropertySpec(name: "distanceFilter", type: "double"),
|
|
110
|
+
BGPropertySpec(name: "stationaryRadius", type: "double"),
|
|
111
|
+
BGPropertySpec(name: "locationTimeout", type: "double"),
|
|
112
|
+
BGPropertySpec(name: "stopTimeout", type: "double"),
|
|
113
|
+
BGPropertySpec(name: "stopAfterElapsedMinutes", type: "double"),
|
|
114
|
+
BGPropertySpec(name: "activityType", type: "string"),
|
|
115
|
+
BGPropertySpec(name: "pausesLocationUpdatesAutomatically", type: "bool"),
|
|
116
|
+
BGPropertySpec(name: "showsBackgroundLocationIndicator", type: "bool"),
|
|
117
|
+
BGPropertySpec(name: "useSignificantChangesOnly", type: "bool"),
|
|
118
|
+
BGPropertySpec(name: "disableStopDetection", type: "bool"),
|
|
119
|
+
BGPropertySpec(name: "locationAuthorizationRequest", type: "string"),
|
|
120
|
+
BGPropertySpec(name: "disableLocationAuthorizationAlert", type: "bool"),
|
|
121
|
+
BGPropertySpec(name: "geofenceProximityRadius", type: "double"),
|
|
122
|
+
BGPropertySpec(name: "geofenceInitialTriggerEntry", type: "bool"),
|
|
123
|
+
BGPropertySpec(name: "enableTimestampMeta", type: "bool"),
|
|
124
|
+
BGPropertySpec(name: "disableElasticity", type: "bool"),
|
|
125
|
+
BGPropertySpec(name: "elasticityMultiplier", type: "double")
|
|
126
|
+
]
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
@objc public override func validateConfiguration() -> Bool {
|
|
130
|
+
return true
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
@objc public override var description: String {
|
|
134
|
+
return "<BGGeolocationConfig desiredAccuracy=\(desiredAccuracy) distanceFilter=\(distanceFilter) stationaryRadius=\(stationaryRadius)>"
|
|
135
|
+
}
|
|
136
|
+
}
|