@poolse/react-native-calls 0.4.3 → 0.4.4
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.
|
@@ -224,6 +224,7 @@ final class CallKitController: NSObject, CXProviderDelegate {
|
|
|
224
224
|
}
|
|
225
225
|
|
|
226
226
|
func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
|
|
227
|
+
pcTrace("CallKit answer action")
|
|
227
228
|
guard let t = calls[action.callUUID], !t.ended else { action.fail(); return }
|
|
228
229
|
let fromUs = t.answered
|
|
229
230
|
t.answered = true
|
|
@@ -259,10 +260,12 @@ final class CallKitController: NSObject, CXProviderDelegate {
|
|
|
259
260
|
}
|
|
260
261
|
|
|
261
262
|
func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) {
|
|
263
|
+
pcTrace("CallKit didActivate \(pcSessionDescription(audioSession))")
|
|
262
264
|
actions?.callKitAudioActivated(audioSession)
|
|
263
265
|
}
|
|
264
266
|
|
|
265
267
|
func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) {
|
|
268
|
+
pcTrace("CallKit didDeactivate")
|
|
266
269
|
actions?.callKitAudioDeactivated(audioSession)
|
|
267
270
|
}
|
|
268
271
|
}
|
|
@@ -26,6 +26,21 @@ protocol MediaEvents: AnyObject {
|
|
|
26
26
|
func onLocalCamera(on: Bool, facing: String?)
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
/// Step-by-step trace of the audio path, in the device log as
|
|
30
|
+
/// "[PoolseCalls] trace …" — the order of activation, engine availability,
|
|
31
|
+
/// connect and publish is what cold-answer audio failures hinge on.
|
|
32
|
+
func pcTrace(_ message: @autoclosure () -> String) {
|
|
33
|
+
NSLog("[PoolseCalls] trace %@", message())
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
func pcSessionDescription(_ s: AVAudioSession = .sharedInstance()) -> String {
|
|
37
|
+
let outs = s.currentRoute.outputs.map { $0.portType.rawValue }.joined(separator: ",")
|
|
38
|
+
let ins = s.currentRoute.inputs.map { $0.portType.rawValue }.joined(separator: ",")
|
|
39
|
+
return "category=\(s.category.rawValue) mode=\(s.mode.rawValue) in=[\(ins)] out=[\(outs)] " +
|
|
40
|
+
"otherAudio=\(s.isOtherAudioPlaying) inputAvailable=\(s.isInputAvailable) " +
|
|
41
|
+
"record=\(s.recordPermission.rawValue)"
|
|
42
|
+
}
|
|
43
|
+
|
|
29
44
|
/// The shipped MediaPort over LiveKit's Swift SDK — LiveKitMediaPort.kt
|
|
30
45
|
/// for iOS. Joins with the server-minted token, publishes the mic (and
|
|
31
46
|
/// the camera for video), tracks the roster and reports room transitions.
|
|
@@ -83,6 +98,7 @@ final class LiveKitMediaPort: NSObject, MediaPort {
|
|
|
83
98
|
func prepareAudio() {
|
|
84
99
|
guard !audioPrepared else { return }
|
|
85
100
|
audioPrepared = true
|
|
101
|
+
pcTrace("prepareAudio (automatic config off, engine .none)")
|
|
86
102
|
AudioManager.shared.audioSession.isAutomaticConfigurationEnabled = false
|
|
87
103
|
do { try AudioManager.shared.setEngineAvailability(.none) } catch {
|
|
88
104
|
NSLog("[PoolseCalls] setEngineAvailability(.none) failed: %@", "\(error)")
|
|
@@ -91,6 +107,7 @@ final class LiveKitMediaPort: NSObject, MediaPort {
|
|
|
91
107
|
|
|
92
108
|
/// CallKit activated the session (or, without CallKit, we did).
|
|
93
109
|
func audioActivated(_ session: AVAudioSession, media: String) {
|
|
110
|
+
pcTrace("audioActivated media=\(media) room=\(room != nil) connected=\(connectedRoom != nil) \(pcSessionDescription(session))")
|
|
94
111
|
prepareAudio()
|
|
95
112
|
audioActive = true
|
|
96
113
|
if media != self.media && room == nil { self.media = media }
|
|
@@ -115,6 +132,7 @@ final class LiveKitMediaPort: NSObject, MediaPort {
|
|
|
115
132
|
audioKicked != ObjectIdentifier(r), !mutedWanted else { return }
|
|
116
133
|
guard r.localParticipant.isMicrophoneEnabled() else { return }
|
|
117
134
|
audioKicked = ObjectIdentifier(r)
|
|
135
|
+
pcTrace("kick: cycling engine availability + mic")
|
|
118
136
|
do {
|
|
119
137
|
try AudioManager.shared.setEngineAvailability(.none)
|
|
120
138
|
try AudioManager.shared.setEngineAvailability(.default)
|
|
@@ -134,6 +152,7 @@ final class LiveKitMediaPort: NSObject, MediaPort {
|
|
|
134
152
|
} catch {
|
|
135
153
|
NSLog("[PoolseCalls] mic cycle failed: %@", "\(error)")
|
|
136
154
|
}
|
|
155
|
+
pcTrace("kick done enabled=\(r.localParticipant.isMicrophoneEnabled()) \(pcSessionDescription())")
|
|
137
156
|
await MainActor.run { self?.reassertAudio() }
|
|
138
157
|
}
|
|
139
158
|
}
|
|
@@ -147,6 +166,7 @@ final class LiveKitMediaPort: NSObject, MediaPort {
|
|
|
147
166
|
/// no-CallKit fallback) holds the session active.
|
|
148
167
|
func reassertAudio(_ session: AVAudioSession = .sharedInstance()) {
|
|
149
168
|
guard audioActive else { return }
|
|
169
|
+
pcTrace("reassertAudio before: \(pcSessionDescription(session))")
|
|
150
170
|
let mode: AVAudioSession.Mode = media == "video" && speakerWanted != false ? .videoChat : .voiceChat
|
|
151
171
|
if session.category != .playAndRecord || session.mode != mode {
|
|
152
172
|
do {
|
|
@@ -163,6 +183,7 @@ final class LiveKitMediaPort: NSObject, MediaPort {
|
|
|
163
183
|
}
|
|
164
184
|
|
|
165
185
|
func audioDeactivated() {
|
|
186
|
+
pcTrace("audioDeactivated")
|
|
166
187
|
audioActive = false
|
|
167
188
|
guard audioPrepared else { return }
|
|
168
189
|
do { try AudioManager.shared.setEngineAvailability(.none) } catch {
|
|
@@ -181,6 +202,7 @@ final class LiveKitMediaPort: NSObject, MediaPort {
|
|
|
181
202
|
// MARK: - MediaPort
|
|
182
203
|
|
|
183
204
|
func join(bundle: CallSessionBundle) {
|
|
205
|
+
pcTrace("join call=\(bundle.callId) media=\(bundle.media) audioActive=\(audioActive)")
|
|
184
206
|
prepareAudio()
|
|
185
207
|
if let joined = joinedCallId, joined != bundle.callId { resetWanted() }
|
|
186
208
|
joinedCallId = bundle.callId
|
|
@@ -224,10 +246,13 @@ final class LiveKitMediaPort: NSObject, MediaPort {
|
|
|
224
246
|
return
|
|
225
247
|
}
|
|
226
248
|
let muted = await MainActor.run { self.mutedWanted }
|
|
249
|
+
let activeNow = await MainActor.run { self.audioActive }
|
|
250
|
+
pcTrace("connected; publishing mic muted=\(muted) audioActive=\(activeNow)")
|
|
227
251
|
do { try await r.localParticipant.setMicrophone(enabled: !muted) } catch {
|
|
228
252
|
NSLog("[PoolseCalls] microphone publish failed: %@", "\(error)")
|
|
229
253
|
}
|
|
230
254
|
// Connected and publishing: make sure nothing changed the session
|
|
255
|
+
pcTrace("mic published enabled=\(r.localParticipant.isMicrophoneEnabled()) \(pcSessionDescription())")
|
|
231
256
|
// while we connected (a cold answer boots JS in exactly this window),
|
|
232
257
|
// then make sure the engine is actually carrying the mic.
|
|
233
258
|
await MainActor.run {
|
|
@@ -375,18 +400,21 @@ final class LiveKitMediaPort: NSObject, MediaPort {
|
|
|
375
400
|
/// An interruption (another app's audio, Siri) ended: the session may
|
|
376
401
|
/// have been reconfigured; restore the call's.
|
|
377
402
|
@objc private func interrupted(_ note: Notification) {
|
|
403
|
+
pcTrace("interruption \(note.userInfo ?? [:])")
|
|
378
404
|
guard let raw = note.userInfo?[AVAudioSessionInterruptionTypeKey] as? UInt,
|
|
379
405
|
AVAudioSession.InterruptionType(rawValue: raw) == .ended else { return }
|
|
380
406
|
DispatchQueue.main.async { [weak self] in self?.reassertAudio() }
|
|
381
407
|
}
|
|
382
408
|
|
|
383
409
|
@objc private func mediaServicesReset(_ note: Notification) {
|
|
410
|
+
pcTrace("mediaServicesWereReset")
|
|
384
411
|
DispatchQueue.main.async { [weak self] in self?.reassertAudio() }
|
|
385
412
|
}
|
|
386
413
|
|
|
387
414
|
@objc private func routeChanged(_ note: Notification) {
|
|
388
415
|
let reason = (note.userInfo?[AVAudioSessionRouteChangeReasonKey] as? UInt)
|
|
389
416
|
.flatMap(AVAudioSession.RouteChangeReason.init(rawValue:))
|
|
417
|
+
pcTrace("routeChange reason=\(reason.map { String($0.rawValue) } ?? "?") \(pcSessionDescription())")
|
|
390
418
|
DispatchQueue.main.async { [weak self] in
|
|
391
419
|
guard let self = self, self.audioActive else { return }
|
|
392
420
|
// Something reconfigured the shared session mid-call: put it back.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import CallKit
|
|
2
2
|
import Foundation
|
|
3
3
|
import PushKit
|
|
4
|
+
import UIKit
|
|
4
5
|
|
|
5
6
|
/// Native PushKit intake — the iOS twin of CallMessagingService. The
|
|
6
7
|
/// registry is created at launch (PoolseCallsAppDelegateSubscriber →
|
|
@@ -59,6 +60,7 @@ final class VoipPush: NSObject, PKPushRegistryDelegate {
|
|
|
59
60
|
|
|
60
61
|
func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload,
|
|
61
62
|
for type: PKPushType, completion: @escaping () -> Void) {
|
|
63
|
+
pcTrace("VoIP push received appState=\(UIApplication.shared.applicationState.rawValue)")
|
|
62
64
|
if type == .voIP { handle(payload.dictionaryPayload) }
|
|
63
65
|
completion()
|
|
64
66
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@poolse/react-native-calls",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.4",
|
|
4
4
|
"description": "Native call core for poolse — Kotlin/Swift calling engine with a thin React Native bridge. Ring, answer and carry audio with the app killed and the phone locked.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|