@capgo/capacitor-twilio-voice 8.2.9 → 8.2.11

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.
@@ -75,7 +75,7 @@ import org.json.JSONObject;
75
75
  )
76
76
  public class CapacitorTwilioVoicePlugin extends Plugin {
77
77
 
78
- private final String pluginVersion = "8.2.9";
78
+ private final String pluginVersion = "8.2.11";
79
79
 
80
80
  private static final String TAG = "CapacitorTwilioVoice";
81
81
  private static final String PREF_ACCESS_TOKEN = "twilio_access_token";
@@ -28,7 +28,7 @@ public protocol PushKitEventDelegate: AnyObject {
28
28
  */
29
29
  @objc(CapacitorTwilioVoicePlugin)
30
30
  public class CapacitorTwilioVoicePlugin: CAPPlugin, CAPBridgedPlugin, PushKitEventDelegate {
31
- private let pluginVersion: String = "8.2.9"
31
+ private let pluginVersion: String = "8.2.11"
32
32
 
33
33
  public let identifier = "CapacitorTwilioVoicePlugin"
34
34
  public let jsName = "CapacitorTwilioVoice"
@@ -57,6 +57,7 @@ public class CapacitorTwilioVoicePlugin: CAPPlugin, CAPBridgedPlugin, PushKitEve
57
57
  private var activeCall: Call?
58
58
  private var callKitProvider: CXProvider?
59
59
  private let callKitCallController = CXCallController()
60
+ private let callObserver = CXCallObserver()
60
61
  private var userInitiatedDisconnect: Bool = false
61
62
  private var audioDevice = DefaultAudioDevice()
62
63
  private var callKitCompletionCallback: ((Bool) -> Void)?
@@ -97,6 +98,7 @@ public class CapacitorTwilioVoicePlugin: CAPPlugin, CAPBridgedPlugin, PushKitEve
97
98
  setupCallKit()
98
99
  setupAudioDevice()
99
100
  setupNotifications()
101
+ callObserver.setDelegate(self, queue: nil)
100
102
  }
101
103
 
102
104
  private func setupCallKit() {
@@ -117,8 +119,7 @@ public class CapacitorTwilioVoicePlugin: CAPPlugin, CAPBridgedPlugin, PushKitEve
117
119
  try audioSession.setCategory(.playAndRecord,
118
120
  mode: .voiceChat,
119
121
  options: [.allowBluetooth, .allowBluetoothA2DP, .allowAirPlay])
120
- try audioSession.setActive(true)
121
- NSLog("Audio session configured successfully")
122
+ NSLog("Audio session category configured")
122
123
  } catch {
123
124
  NSLog("Failed to configure audio session: \(error.localizedDescription)")
124
125
  }
@@ -151,10 +152,10 @@ public class CapacitorTwilioVoicePlugin: CAPPlugin, CAPBridgedPlugin, PushKitEve
151
152
  NSLog("Media services were reset, reconfiguring audio session")
152
153
  setupAudioSession()
153
154
 
154
- // Re-enable audio device
155
- audioDevice.isEnabled = true
155
+ if let call = getActiveCall(), !call.isOnHold {
156
+ audioDevice.isEnabled = true
157
+ }
156
158
 
157
- // Notify listeners about the reset
158
159
  notifyListeners("audioSessionReset", data: nil)
159
160
  }
160
161
 
@@ -168,27 +169,13 @@ public class CapacitorTwilioVoicePlugin: CAPPlugin, CAPBridgedPlugin, PushKitEve
168
169
  switch type {
169
170
  case .began:
170
171
  NSLog("Audio session interruption began")
171
- audioDevice.isEnabled = false
172
+ if activeCalls.isEmpty && activeCallInvites.isEmpty {
173
+ audioDevice.isEnabled = false
174
+ }
172
175
  notifyListeners("audioSessionInterrupted", data: ["type": "began"])
173
176
 
174
177
  case .ended:
175
178
  NSLog("Audio session interruption ended")
176
-
177
- // Check if we should resume
178
- if let optionsValue = userInfo[AVAudioSessionInterruptionOptionKey] as? UInt {
179
- let options = AVAudioSession.InterruptionOptions(rawValue: optionsValue)
180
- if options.contains(.shouldResume) {
181
- do {
182
- try AVAudioSession.sharedInstance().setActive(true)
183
- audioDevice.isEnabled = true
184
- NSLog("Audio session resumed after interruption")
185
- notifyListeners("audioSessionResumed", data: nil)
186
- } catch {
187
- NSLog("Failed to resume audio session: \(error.localizedDescription)")
188
- }
189
- }
190
- }
191
-
192
179
  notifyListeners("audioSessionInterrupted", data: ["type": "ended"])
193
180
 
194
181
  @unknown default:
@@ -708,42 +695,14 @@ public class CapacitorTwilioVoicePlugin: CAPPlugin, CAPBridgedPlugin, PushKitEve
708
695
  private func toggleAudioRoute(toSpeaker: Bool) {
709
696
  audioDevice.block = {
710
697
  do {
711
- let audioSession = AVAudioSession.sharedInstance()
712
-
713
- // Ensure audio session is active before changing routing
714
- if !audioSession.isOtherAudioPlaying {
715
- try audioSession.setActive(true)
716
- }
717
-
718
698
  if toSpeaker {
719
- try audioSession.overrideOutputAudioPort(.speaker)
699
+ try AVAudioSession.sharedInstance().overrideOutputAudioPort(.speaker)
720
700
  } else {
721
- try audioSession.overrideOutputAudioPort(.none)
701
+ try AVAudioSession.sharedInstance().overrideOutputAudioPort(.none)
722
702
  }
723
-
724
703
  NSLog("Audio route changed to: \(toSpeaker ? "speaker" : "earpiece")")
725
704
  } catch {
726
705
  NSLog("Failed to change audio route: \(error.localizedDescription)")
727
-
728
- // Try to recover by reconfiguring the audio session
729
- do {
730
- let audioSession = AVAudioSession.sharedInstance()
731
- try audioSession.setCategory(.playAndRecord,
732
- mode: .voiceChat,
733
- options: [.allowBluetooth, .allowBluetoothA2DP, .allowAirPlay])
734
- try audioSession.setActive(true)
735
-
736
- // Retry the audio route change
737
- if toSpeaker {
738
- try audioSession.overrideOutputAudioPort(.speaker)
739
- } else {
740
- try audioSession.overrideOutputAudioPort(.none)
741
- }
742
-
743
- NSLog("Audio route recovered and changed to: \(toSpeaker ? "speaker" : "earpiece")")
744
- } catch {
745
- NSLog("Failed to recover audio route: \(error.localizedDescription)")
746
- }
747
706
  }
748
707
  }
749
708
  audioDevice.block()
@@ -1131,17 +1090,16 @@ extension CapacitorTwilioVoicePlugin: CXProviderDelegate {
1131
1090
  public func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) {
1132
1091
  NSLog("CallKit activated audio session")
1133
1092
 
1134
- // Configure the audio session for VoIP calls
1135
1093
  do {
1136
1094
  try audioSession.setCategory(.playAndRecord,
1137
1095
  mode: .voiceChat,
1138
1096
  options: [.allowBluetooth, .allowBluetoothA2DP, .allowAirPlay])
1139
- audioDevice.isEnabled = true
1140
- NSLog("Audio session activated and configured for call")
1141
1097
  } catch {
1142
1098
  NSLog("Failed to configure audio session during activation: \(error.localizedDescription)")
1143
- audioDevice.isEnabled = true // Still try to enable the device
1144
1099
  }
1100
+
1101
+ audioDevice.isEnabled = true
1102
+ NSLog("Audio device enabled after CallKit activation")
1145
1103
  }
1146
1104
 
1147
1105
  public func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) {
@@ -1265,6 +1223,46 @@ extension CapacitorTwilioVoicePlugin: CXProviderDelegate {
1265
1223
  }
1266
1224
  }
1267
1225
 
1226
+
1227
+ // MARK: - CXCallObserverDelegate
1228
+
1229
+ extension CapacitorTwilioVoicePlugin: CXCallObserverDelegate {
1230
+ public func callObserver(_ callObserver: CXCallObserver, callChanged call: CXCall) {
1231
+ guard let voipCall = getActiveCall(),
1232
+ let voipCallUuid = voipCall.uuid,
1233
+ activeCalls[voipCallUuid.uuidString] != nil,
1234
+ call.hasEnded,
1235
+ call.uuid != voipCallUuid,
1236
+ callObserver.calls.count == 1,
1237
+ voipCall.isOnHold,
1238
+ callObserver.calls.first?.uuid == voipCallUuid else {
1239
+ return
1240
+ }
1241
+
1242
+ NSLog("External call ended, scheduling VoIP call unhold")
1243
+
1244
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in
1245
+ guard let self = self,
1246
+ let call = self.getActiveCall(),
1247
+ call.isOnHold,
1248
+ let uuid = call.uuid else {
1249
+ return
1250
+ }
1251
+
1252
+ let unholdAction = CXSetHeldCallAction(call: uuid, onHold: false)
1253
+ let transaction = CXTransaction(action: unholdAction)
1254
+
1255
+ self.callKitCallController.request(transaction) { error in
1256
+ if let error = error {
1257
+ NSLog("Failed to unhold VoIP call after external call ended: \(error.localizedDescription)")
1258
+ } else {
1259
+ NSLog("Successfully unheld VoIP call after external call ended")
1260
+ }
1261
+ }
1262
+ }
1263
+ }
1264
+ }
1265
+
1268
1266
  // MARK: - AVAudioPlayerDelegate
1269
1267
 
1270
1268
  extension CapacitorTwilioVoicePlugin: AVAudioPlayerDelegate {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/capacitor-twilio-voice",
3
- "version": "8.2.9",
3
+ "version": "8.2.11",
4
4
  "description": "Integrates the Twilio Voice SDK into Capacitor",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",