react-native-bg-geolocation 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/BgGeolocation.podspec +39 -0
  2. package/LICENSE +20 -0
  3. package/README.md +366 -0
  4. package/android/build.gradle +69 -0
  5. package/android/src/main/AndroidManifest.xml +53 -0
  6. package/android/src/main/java/com/bggeolocation/BgGeolocationActivityRecognitionReceiver.kt +116 -0
  7. package/android/src/main/java/com/bggeolocation/BgGeolocationBootReceiver.kt +44 -0
  8. package/android/src/main/java/com/bggeolocation/BgGeolocationForegroundService.kt +373 -0
  9. package/android/src/main/java/com/bggeolocation/BgGeolocationGeofenceReceiver.kt +55 -0
  10. package/android/src/main/java/com/bggeolocation/BgGeolocationHeadlessTask.kt +138 -0
  11. package/android/src/main/java/com/bggeolocation/BgGeolocationModule.kt +1030 -0
  12. package/android/src/main/java/com/bggeolocation/BgGeolocationMotionStateMachine.kt +159 -0
  13. package/android/src/main/java/com/bggeolocation/BgGeolocationPackage.kt +31 -0
  14. package/android/src/main/res/drawable/bg_geo_notification.xml +9 -0
  15. package/ios/BgGeolocation.h +14 -0
  16. package/ios/BgGeolocation.mm +709 -0
  17. package/ios/engine/AtomicBoolean.swift +48 -0
  18. package/ios/engine/BGActivityChangeEvent.swift +20 -0
  19. package/ios/engine/BGActivityConfig.swift +71 -0
  20. package/ios/engine/BGAppConfig.swift +92 -0
  21. package/ios/engine/BGAppState.swift +147 -0
  22. package/ios/engine/BGAuthorization.swift +85 -0
  23. package/ios/engine/BGAuthorizationAlertPresenter.swift +39 -0
  24. package/ios/engine/BGAuthorizationConfig.swift +50 -0
  25. package/ios/engine/BGAuthorizationEvent.swift +40 -0
  26. package/ios/engine/BGBackgroundTaskManager.swift +143 -0
  27. package/ios/engine/BGCLRouter.swift +101 -0
  28. package/ios/engine/BGCallback.swift +19 -0
  29. package/ios/engine/BGConfig.swift +440 -0
  30. package/ios/engine/BGConfigModuleBase.swift +180 -0
  31. package/ios/engine/BGConfigOLD.swift +582 -0
  32. package/ios/engine/BGConnectivityChangeEvent.swift +15 -0
  33. package/ios/engine/BGCrashDetector.swift +122 -0
  34. package/ios/engine/BGCurrentPositionRequest.swift +87 -0
  35. package/ios/engine/BGDataStore.swift +75 -0
  36. package/ios/engine/BGDatabase.swift +677 -0
  37. package/ios/engine/BGDatabasePool.swift +220 -0
  38. package/ios/engine/BGDatabaseQueue.swift +215 -0
  39. package/ios/engine/BGDateUtils.swift +26 -0
  40. package/ios/engine/BGDeviceInfo.swift +54 -0
  41. package/ios/engine/BGDeviceManager.swift +65 -0
  42. package/ios/engine/BGEnabledChangeEvent.swift +11 -0
  43. package/ios/engine/BGEnv.swift +17 -0
  44. package/ios/engine/BGEventBus.swift +83 -0
  45. package/ios/engine/BGEventManager.swift +169 -0
  46. package/ios/engine/BGEventNames.swift +51 -0
  47. package/ios/engine/BGGeofence.swift +233 -0
  48. package/ios/engine/BGGeofenceDAO.swift +152 -0
  49. package/ios/engine/BGGeofenceEvent.swift +42 -0
  50. package/ios/engine/BGGeofenceLocationRequest.swift +94 -0
  51. package/ios/engine/BGGeofenceManager.swift +315 -0
  52. package/ios/engine/BGGeofenceTransition.swift +97 -0
  53. package/ios/engine/BGGeofencesChangeEvent.swift +26 -0
  54. package/ios/engine/BGGeolocationConfig.swift +136 -0
  55. package/ios/engine/BGHeartbeatEvent.swift +31 -0
  56. package/ios/engine/BGHeartbeatService.swift +51 -0
  57. package/ios/engine/BGHttpConfig.swift +105 -0
  58. package/ios/engine/BGHttpErrorCodes.swift +63 -0
  59. package/ios/engine/BGHttpEvent.swift +34 -0
  60. package/ios/engine/BGHttpRequest.swift +126 -0
  61. package/ios/engine/BGHttpResponse.swift +93 -0
  62. package/ios/engine/BGHttpService.swift +428 -0
  63. package/ios/engine/BGKalmanFilter.swift +105 -0
  64. package/ios/engine/BGLMActionNames.swift +55 -0
  65. package/ios/engine/BGLicenseManager.swift +26 -0
  66. package/ios/engine/BGLiveActivityManager.swift +327 -0
  67. package/ios/engine/BGLocation.swift +311 -0
  68. package/ios/engine/BGLocationAuthorization.swift +427 -0
  69. package/ios/engine/BGLocationDAO.swift +252 -0
  70. package/ios/engine/BGLocationErrors.swift +28 -0
  71. package/ios/engine/BGLocationEvent.swift +43 -0
  72. package/ios/engine/BGLocationFilter.swift +82 -0
  73. package/ios/engine/BGLocationFilterConfig.swift +57 -0
  74. package/ios/engine/BGLocationHelper.swift +54 -0
  75. package/ios/engine/BGLocationManager.swift +662 -0
  76. package/ios/engine/BGLocationMetricsEngine.swift +116 -0
  77. package/ios/engine/BGLocationRequestService.swift +459 -0
  78. package/ios/engine/BGLocationSatisfier.swift +14 -0
  79. package/ios/engine/BGLocationStreamEvent.swift +27 -0
  80. package/ios/engine/BGLog.swift +337 -0
  81. package/ios/engine/BGLogLevel.swift +26 -0
  82. package/ios/engine/BGLoggerConfig.swift +60 -0
  83. package/ios/engine/BGMotionActivity.swift +31 -0
  84. package/ios/engine/BGMotionActivityClassifier.swift +108 -0
  85. package/ios/engine/BGMotionActivityManagerAdapter.swift +40 -0
  86. package/ios/engine/BGMotionActivitySource.swift +46 -0
  87. package/ios/engine/BGMotionDetector.swift +377 -0
  88. package/ios/engine/BGMotionPermissionManager.swift +50 -0
  89. package/ios/engine/BGNativeLogger.swift +48 -0
  90. package/ios/engine/BGNotificaitons.swift +37 -0
  91. package/ios/engine/BGOdometer.swift +66 -0
  92. package/ios/engine/BGPersistenceConfig.swift +29 -0
  93. package/ios/engine/BGPolygonStreamRequest.swift +48 -0
  94. package/ios/engine/BGPowerSaveChangeEvent.swift +12 -0
  95. package/ios/engine/BGPropertySpec.swift +29 -0
  96. package/ios/engine/BGProviderChangeEvent.swift +31 -0
  97. package/ios/engine/BGQueue.swift +50 -0
  98. package/ios/engine/BGRPC.swift +194 -0
  99. package/ios/engine/BGReachability.swift +58 -0
  100. package/ios/engine/BGResultSet.swift +157 -0
  101. package/ios/engine/BGSchedule.swift +228 -0
  102. package/ios/engine/BGScheduleEvent.swift +13 -0
  103. package/ios/engine/BGScheduler.swift +116 -0
  104. package/ios/engine/BGSingleLocationRequest.swift +49 -0
  105. package/ios/engine/BGStreamLocationRequest.swift +42 -0
  106. package/ios/engine/BGTemplate.swift +54 -0
  107. package/ios/engine/BGTimerService.swift +46 -0
  108. package/ios/engine/BGTrackingAudioManager.swift +286 -0
  109. package/ios/engine/BGTrackingService.swift +879 -0
  110. package/ios/engine/BGWatchPositionRequest.swift +63 -0
  111. package/ios/engine/DatabaseQueue.swift +47 -0
  112. package/ios/engine/LogQuery.swift +10 -0
  113. package/ios/engine/SQLQuery.swift +65 -0
  114. package/ios/engine/TransistorAuthorizationToken.swift +182 -0
  115. package/ios/liveactivity/BGLiveTrackingAttributes.swift +52 -0
  116. package/ios/locationpush/BGLocationPushDeliverer.swift +260 -0
  117. package/ios/locationpush/BGLocationPushService.swift +161 -0
  118. package/ios/locationpush/BGLocationPushShared.swift +98 -0
  119. package/ios/locationpush/BGLocationPushSocketClient.swift +198 -0
  120. package/lib/module/NativeBgGeolocation.js +5 -0
  121. package/lib/module/NativeBgGeolocation.js.map +1 -0
  122. package/lib/module/events.js +20 -0
  123. package/lib/module/events.js.map +1 -0
  124. package/lib/module/index.js +706 -0
  125. package/lib/module/index.js.map +1 -0
  126. package/lib/module/package.json +1 -0
  127. package/lib/module/types.js +2 -0
  128. package/lib/module/types.js.map +1 -0
  129. package/lib/typescript/package.json +1 -0
  130. package/lib/typescript/src/NativeBgGeolocation.d.ts +57 -0
  131. package/lib/typescript/src/NativeBgGeolocation.d.ts.map +1 -0
  132. package/lib/typescript/src/events.d.ts +18 -0
  133. package/lib/typescript/src/events.d.ts.map +1 -0
  134. package/lib/typescript/src/index.d.ts +238 -0
  135. package/lib/typescript/src/index.d.ts.map +1 -0
  136. package/lib/typescript/src/types.d.ts +229 -0
  137. package/lib/typescript/src/types.d.ts.map +1 -0
  138. package/package.json +141 -0
  139. package/src/NativeBgGeolocation.ts +236 -0
  140. package/src/events.ts +17 -0
  141. package/src/index.tsx +935 -0
  142. package/src/types.ts +254 -0
@@ -0,0 +1,286 @@
1
+ import Foundation
2
+ import AVFoundation
3
+ import MediaPlayer
4
+ import UIKit
5
+
6
+ /// Owns the user-enabled audible background audio session for an active
7
+ /// tracking session and publishes user controls through Now Playing.
8
+ @objc public final class BGTrackingAudioManager: NSObject {
9
+ @objc public static let shared = BGTrackingAudioManager()
10
+
11
+ private let audioQueue = DispatchQueue(label: "BGTrackingAudioManager.audio")
12
+ private var engine: AVAudioEngine?
13
+ private var sourceNode: AVAudioSourceNode?
14
+ private var shouldBeRunning = false
15
+ private var active = false
16
+ private var lastError: String?
17
+ private var remoteCommandsConfigured = false
18
+
19
+ private override init() {
20
+ super.init()
21
+ NotificationCenter.default.addObserver(
22
+ self,
23
+ selector: #selector(applicationDidFinishLaunching),
24
+ name: UIApplication.didFinishLaunchingNotification,
25
+ object: nil
26
+ )
27
+ NotificationCenter.default.addObserver(
28
+ self,
29
+ selector: #selector(handleInterruption(_:)),
30
+ name: AVAudioSession.interruptionNotification,
31
+ object: AVAudioSession.sharedInstance()
32
+ )
33
+ NotificationCenter.default.addObserver(
34
+ self,
35
+ selector: #selector(handleMediaServicesReset),
36
+ name: AVAudioSession.mediaServicesWereResetNotification,
37
+ object: AVAudioSession.sharedInstance()
38
+ )
39
+ }
40
+
41
+ @objc public func startIfNeeded() {
42
+ guard BGConfig.sharedInstance().app.trackingAudioEnabled else { return }
43
+ audioQueue.sync {
44
+ self.shouldBeRunning = true
45
+ self.startLocked()
46
+ }
47
+ }
48
+
49
+ @objc public func stop() {
50
+ audioQueue.sync {
51
+ self.shouldBeRunning = false
52
+ self.stopLocked(deactivateSession: true)
53
+ }
54
+ }
55
+
56
+ @objc public func stateDictionary() -> [String: Any] {
57
+ audioQueue.sync {
58
+ var state: [String: Any] = [
59
+ "enabled": BGConfig.sharedInstance().app.trackingAudioEnabled,
60
+ "requested": shouldBeRunning,
61
+ "active": active,
62
+ "audible": true,
63
+ "volume": BGConfig.sharedInstance().app.trackingAudioVolume,
64
+ // iOS has no runtime permission for audio playback. The app
65
+ // must obtain user consent in its own UI and declare the audio
66
+ // background mode; microphone permission is unrelated.
67
+ "permissionRequired": false,
68
+ "authorizationStatus": "notRequired",
69
+ "backgroundModeDeclared": hasAudioBackgroundMode(),
70
+ "nowPlayingActive": MPNowPlayingInfoCenter.default().nowPlayingInfo != nil
71
+ ]
72
+ if let lastError {
73
+ state["error"] = lastError
74
+ }
75
+ return state
76
+ }
77
+ }
78
+
79
+ @objc private func applicationDidFinishLaunching() {
80
+ audioQueue.async {
81
+ if self.shouldBeRunning || BGConfig.sharedInstance().enabled {
82
+ self.startLocked()
83
+ }
84
+ }
85
+ }
86
+
87
+ @objc private func handleInterruption(_ notification: Notification) {
88
+ guard
89
+ let info = notification.userInfo,
90
+ let rawType = info[AVAudioSessionInterruptionTypeKey] as? UInt,
91
+ let type = AVAudioSession.InterruptionType(rawValue: rawType)
92
+ else { return }
93
+
94
+ audioQueue.async {
95
+ switch type {
96
+ case .began:
97
+ self.stopLocked(deactivateSession: false)
98
+ case .ended:
99
+ let rawOptions = info[AVAudioSessionInterruptionOptionKey] as? UInt ?? 0
100
+ let options = AVAudioSession.InterruptionOptions(rawValue: rawOptions)
101
+ if self.shouldBeRunning && options.contains(.shouldResume) {
102
+ self.startLocked()
103
+ }
104
+ @unknown default:
105
+ break
106
+ }
107
+ }
108
+ }
109
+
110
+ @objc private func handleMediaServicesReset() {
111
+ audioQueue.async {
112
+ self.stopLocked(deactivateSession: false)
113
+ if self.shouldBeRunning {
114
+ self.startLocked()
115
+ }
116
+ }
117
+ }
118
+
119
+ private func startLocked() {
120
+ guard shouldBeRunning || BGConfig.sharedInstance().enabled else { return }
121
+ guard BGConfig.sharedInstance().app.trackingAudioEnabled else { return }
122
+ guard !active else { return }
123
+
124
+ do {
125
+ let config = BGConfig.sharedInstance().app
126
+ let session = AVAudioSession.sharedInstance()
127
+ let options: AVAudioSession.CategoryOptions =
128
+ config.trackingAudioMixWithOthers ? [.mixWithOthers] : []
129
+ try session.setCategory(.playback, mode: .default, options: options)
130
+ try session.setActive(true)
131
+
132
+ let audioEngine = AVAudioEngine()
133
+ let sampleRate = 44_100.0
134
+ let frequency = 196.0
135
+ let amplitude = Float(min(max(config.trackingAudioVolume, 0.01), 1.0))
136
+ var phase = 0.0
137
+ let phaseIncrement = 2.0 * Double.pi * frequency / sampleRate
138
+
139
+ let node = AVAudioSourceNode { _, _, frameCount, audioBufferList -> OSStatus in
140
+ let buffers = UnsafeMutableAudioBufferListPointer(audioBufferList)
141
+ for frame in 0..<Int(frameCount) {
142
+ // A soft two-harmonic tracking tone. This is intentionally
143
+ // audible; the audio background mode must not be driven by
144
+ // silent playback.
145
+ let fundamental = sin(phase)
146
+ let harmonic = sin(phase * 2.0) * 0.18
147
+ let sample = Float(fundamental + harmonic) * amplitude
148
+ phase += phaseIncrement
149
+ if phase >= 2.0 * Double.pi {
150
+ phase -= 2.0 * Double.pi
151
+ }
152
+ for buffer in buffers {
153
+ guard let data = buffer.mData else { continue }
154
+ data.assumingMemoryBound(to: Float.self)[frame] = sample
155
+ }
156
+ }
157
+ return noErr
158
+ }
159
+
160
+ guard let format = AVAudioFormat(
161
+ standardFormatWithSampleRate: sampleRate,
162
+ channels: 1
163
+ ) else {
164
+ throw NSError(
165
+ domain: "BGTrackingAudioManager",
166
+ code: 1,
167
+ userInfo: [NSLocalizedDescriptionKey: "Unable to create audio format"]
168
+ )
169
+ }
170
+
171
+ audioEngine.attach(node)
172
+ audioEngine.connect(node, to: audioEngine.mainMixerNode, format: format)
173
+ audioEngine.prepare()
174
+ try audioEngine.start()
175
+
176
+ engine = audioEngine
177
+ sourceNode = node
178
+ active = true
179
+ lastError = nil
180
+ publishNowPlaying()
181
+ log("Tracking audio started")
182
+ } catch {
183
+ active = false
184
+ lastError = error.localizedDescription
185
+ log("Unable to start tracking audio: \(error.localizedDescription)")
186
+ }
187
+ }
188
+
189
+ private func stopLocked(deactivateSession: Bool) {
190
+ engine?.stop()
191
+ if let node = sourceNode {
192
+ engine?.disconnectNodeOutput(node)
193
+ engine?.detach(node)
194
+ }
195
+ sourceNode = nil
196
+ engine = nil
197
+ active = false
198
+ clearNowPlaying()
199
+
200
+ if deactivateSession {
201
+ do {
202
+ try AVAudioSession.sharedInstance().setActive(
203
+ false,
204
+ options: [.notifyOthersOnDeactivation]
205
+ )
206
+ } catch {
207
+ lastError = error.localizedDescription
208
+ }
209
+ }
210
+ log("Tracking audio stopped")
211
+ }
212
+
213
+ private func publishNowPlaying() {
214
+ let app = BGConfig.sharedInstance().app
215
+ let appName =
216
+ Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String
217
+ ?? Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String
218
+ ?? "Background Geolocation"
219
+
220
+ DispatchQueue.main.async {
221
+ self.configureRemoteCommandsIfNeeded()
222
+ MPNowPlayingInfoCenter.default().nowPlayingInfo = [
223
+ MPMediaItemPropertyTitle: app.liveActivityTitle,
224
+ MPMediaItemPropertyArtist: appName,
225
+ MPMediaItemPropertyAlbumTitle: app.liveActivitySubtitle,
226
+ MPNowPlayingInfoPropertyPlaybackRate: 1.0,
227
+ MPNowPlayingInfoPropertyElapsedPlaybackTime: 0.0,
228
+ MPNowPlayingInfoPropertyIsLiveStream: true,
229
+ MPNowPlayingInfoPropertyMediaType: MPNowPlayingInfoMediaType.audio.rawValue
230
+ ]
231
+ MPNowPlayingInfoCenter.default().playbackState = .playing
232
+ }
233
+ }
234
+
235
+ private func clearNowPlaying() {
236
+ DispatchQueue.main.async {
237
+ MPNowPlayingInfoCenter.default().playbackState = .stopped
238
+ MPNowPlayingInfoCenter.default().nowPlayingInfo = nil
239
+ }
240
+ }
241
+
242
+ private func configureRemoteCommandsIfNeeded() {
243
+ guard !remoteCommandsConfigured else { return }
244
+ remoteCommandsConfigured = true
245
+
246
+ let commands = MPRemoteCommandCenter.shared()
247
+ commands.playCommand.isEnabled = false
248
+ commands.nextTrackCommand.isEnabled = false
249
+ commands.previousTrackCommand.isEnabled = false
250
+ commands.changePlaybackPositionCommand.isEnabled = false
251
+
252
+ commands.pauseCommand.isEnabled = true
253
+ commands.pauseCommand.addTarget { [weak self] _ in
254
+ self?.endTrackingFromRemoteCommand()
255
+ return .success
256
+ }
257
+
258
+ commands.stopCommand.isEnabled = true
259
+ commands.stopCommand.addTarget { [weak self] _ in
260
+ self?.endTrackingFromRemoteCommand()
261
+ return .success
262
+ }
263
+
264
+ commands.togglePlayPauseCommand.isEnabled = true
265
+ commands.togglePlayPauseCommand.addTarget { [weak self] _ in
266
+ self?.endTrackingFromRemoteCommand()
267
+ return .success
268
+ }
269
+ }
270
+
271
+ private func endTrackingFromRemoteCommand() {
272
+ DispatchQueue.main.async {
273
+ BGLocationManager.sharedInstance().stop()
274
+ }
275
+ }
276
+
277
+ private func hasAudioBackgroundMode() -> Bool {
278
+ let modes = Bundle.main.object(forInfoDictionaryKey: "UIBackgroundModes") as? [String]
279
+ return modes?.contains("audio") == true
280
+ }
281
+
282
+ private func log(_ message: String) {
283
+ NSLog("[BGGEO][TrackingAudio] \(message)")
284
+ BGLog.sharedInstance().notify(message, debug: true)
285
+ }
286
+ }