Skip to content

Commit

Permalink
Merge pull request #146 from team-telnyx/fix/WEBRTC-2325-buttons
Browse files Browse the repository at this point in the history
[WEBRTC-2325] [FIX] Handle CallKit Mute / Unmute buttons
  • Loading branch information
gbattistel authored Jan 6, 2025
2 parents 68c179c + df01d4b commit 6b7a4cd
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 23 deletions.
7 changes: 7 additions & 0 deletions TelnyxRTC/Telnyx/WebRTC/Call.swift
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,16 @@ public class Call {
public var callInfo: TxCallInfo?
/// `CallState` The actual state of the Call.
public var callState: CallState = .NEW

/// `isMuted` Indicates if the call audio is muted based on the peer's audio track state.
public var isMuted: Bool {
return !(peer?.isAudioTrackEnabled ?? false)
}

private var ringTonePlayer: AVAudioPlayer?
private var ringbackPlayer: AVAudioPlayer?



// MARK: - Initializers
/// Constructor for incoming calls
Expand Down
29 changes: 26 additions & 3 deletions TelnyxRTC/Telnyx/WebRTC/Peer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,18 @@ class Peer : NSObject {
private var negotiationTimer: Timer?
private var negotiationEnded: Bool = false

public var isAudioTrackEnabled: Bool {
if self.connection?.configuration.sdpSemantics == .planB {
return self.connection?.senders
.compactMap { $0.track as? RTCAudioTrack }
.first?.isEnabled ?? false
} else {
return self.connection?.transceivers
.compactMap { $0.sender.track as? RTCAudioTrack }
.first?.isEnabled ?? false
}
}

// The `RTCPeerConnectionFactory` is in charge of creating new RTCPeerConnection instances.
// A new RTCPeerConnection should be created every new call, but the factory is shared.
private static let factory: RTCPeerConnectionFactory = {
Expand Down Expand Up @@ -366,9 +378,19 @@ class Peer : NSObject {
extension Peer {
//DO NOT USE THIS FOR planB
private func setTrackEnabled<T: RTCMediaStreamTrack>(_ type: T.Type, isEnabled: Bool) {
self.connection?.transceivers
.compactMap { return $0.sender.track as? T }
.forEach { $0.isEnabled = isEnabled }
Logger.log.i(message: "setTrackEnabled: \(isEnabled)")

if let transceivers = self.connection?.transceivers {
Logger.log.i(message:"setTrackEnabled: transeivers \(transceivers)")

transceivers.compactMap { return $0.sender.track as? T }
.forEach {
$0.isEnabled = isEnabled
Logger.log.i(message:"setTrackEnabled IsEnabled: \(isEnabled)")
}
} else {
Logger.log.i(message:"setTrackEnabled transeivers empty")
}
}
}

Expand All @@ -387,6 +409,7 @@ extension Peer {
self.setTrackEnabled(RTCAudioTrack.self, isEnabled: !mute)
}
}

}
// MARK: -RTCPeerConnectionDelegate
/**
Expand Down
29 changes: 27 additions & 2 deletions TelnyxWebRTCDemo/Extensions/AppDelegateCallKitExtension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,21 @@ extension AppDelegate : CXProviderDelegate {
self.callKitUUID = nil
}
}
// MARK: - CXProviderDelegate -

func executeMuteUnmuteAction(uuid: UUID, mute: Bool) {
let muteAction = CXSetMutedCallAction(call: uuid, muted: mute)
let transaction = CXTransaction(action: muteAction)

callKitCallController.request(transaction) { error in
if let error = error {
print("Error executing mute/unmute action: \(error.localizedDescription)")
} else {
print("Successfully executed mute/unmute action. Mute: \(mute)")
}
}
}

// MARK: - CXProviderDelegate -
func provider(_ provider: CXProvider, perform action: CXStartCallAction) {
print("AppDelegate:: START call action: callKitUUID [\(String(describing: self.callKitUUID))] action [\(action.callUUID)]")
self.callKitUUID = action.callUUID
Expand Down Expand Up @@ -214,7 +228,18 @@ extension AppDelegate : CXProviderDelegate {
}

func provider(_ provider: CXProvider, perform action: CXSetMutedCallAction) {
print("provider:performSetMutedAction:")
print("provider:performSetMutedAction: \(action.isMuted)")
if let call = currentCall {
if action.isMuted {
print("provider:performSetMutedAction: incoming action to mute call")
call.muteAudio()
} else {
print("provider:performSetMutedAction: incoming action to unmute call")
call.unmuteAudio()
}
print("provider:performSetMutedAction: call.isMuted \(call.isMuted)")
}
action.fulfill()
}

func processVoIPNotification(callUUID: UUID,pushMetaData:[String: Any]) {
Expand Down
26 changes: 20 additions & 6 deletions TelnyxWebRTCDemo/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,21 @@ class ViewController: UIViewController {
self.telnyxClient = appDelegate.telnyxClient
self.initViews()

// Register notifications
NotificationCenter.default.addObserver(self,
selector: #selector(appWillEnterForeground),
name: UIApplication.willEnterForegroundNotification,
object: nil)
}

deinit {
NotificationCenter.default.removeObserver(self)
}

func initViews() {
print("ViewController:: initViews()")
self.callView.isHidden = true
self.callView.isMuted = self.appDelegate.currentCall?.isMuted ?? false
self.callView.delegate = self
self.callView.hideEndButton(hide: true)

Expand Down Expand Up @@ -131,6 +141,11 @@ class ViewController: UIViewController {
self.showHiddenOptions()
}
}

@objc func appWillEnterForeground() {
print("ViewController:: App is about to enter the foreground")
self.callView.isMuted = self.appDelegate.currentCall?.isMuted ?? false
}

func showHiddenOptions() {
let alert = UIAlertController(title: "Options", message: "", preferredStyle: .actionSheet)
Expand Down Expand Up @@ -251,8 +266,8 @@ class ViewController: UIViewController {
self.incomingCall = false
self.incomingCallView.isHidden = true
self.callView.isHidden = false
self.callView.resetMuteUnmuteState()
self.callView.resetHoldUnholdState()
self.callView.isMuted = self.appDelegate.currentCall?.isMuted ?? false
self.callView.resetSpeakerState()
}

Expand Down Expand Up @@ -300,12 +315,11 @@ extension ViewController : UICallScreenDelegate {
appDelegate.executeEndCallAction(uuid: uuid)
}

func onMuteUnmuteSwitch(isMuted: Bool) {
if (isMuted) {
self.appDelegate.currentCall?.muteAudio()
} else {
self.appDelegate.currentCall?.unmuteAudio()
func onMuteUnmuteSwitch(mute: Bool) {
guard let callId = self.appDelegate.currentCall?.callInfo?.callId else {
return
}
self.appDelegate.executeMuteUnmuteAction(uuid: callId, mute: mute)
}

func onHoldUnholdSwitch(isOnHold: Bool) {
Expand Down
22 changes: 10 additions & 12 deletions TelnyxWebRTCDemo/Views/UICallScreen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import TelnyxRTC
protocol UICallScreenDelegate: AnyObject {
func onCallButton()
func onEndCallButton()
func onMuteUnmuteSwitch(isMuted: Bool)
func onMuteUnmuteSwitch(mute: Bool)
func onHoldUnholdSwitch(isOnHold: Bool)
func onToggleSpeaker(isSpeakerActive: Bool)
}
Expand All @@ -37,6 +37,13 @@ class UICallScreen: UIView {
@IBOutlet weak var holdUnholdLabel: UILabel!
@IBOutlet weak var speakerOnOffSwitch: UISwitch!

var isMuted: Bool = false {
didSet {
muteUnmuteSwitch.isOn = isMuted
muteUnmuteLabel.text = isMuted ? "Unmute" : "Mute"
}
}


override init(frame: CGRect) {
super.init(frame: frame)
Expand Down Expand Up @@ -123,11 +130,6 @@ class UICallScreen: UIView {
self.holdUnholdLabel.text = "Hold"
}

func resetMuteUnmuteState() {
self.muteUnmuteSwitch.setOn(false, animated: false)
self.muteUnmuteLabel.text = "Mute"
}

@IBAction func callButtonTapped(_ sender: Any) {
self.delegate?.onCallButton()
}
Expand All @@ -137,12 +139,8 @@ class UICallScreen: UIView {
}

@IBAction func muteUnmuteTapped(_ sender: Any) {
if (muteUnmuteSwitch.isOn) {
self.muteUnmuteLabel.text = "Unmute"
} else {
self.muteUnmuteLabel.text = "Mute"
}
self.delegate?.onMuteUnmuteSwitch(isMuted: muteUnmuteSwitch.isOn)
let shouldMute = muteUnmuteSwitch.isOn
self.delegate?.onMuteUnmuteSwitch(mute: shouldMute)
}

@IBAction func holdUnholdTapped(_ sender: Any) {
Expand Down

0 comments on commit 6b7a4cd

Please sign in to comment.