diff --git a/Signal/src/call/CallAudioService.swift b/Signal/src/call/CallAudioService.swift index 7ee45fe64..4e80bd1df 100644 --- a/Signal/src/call/CallAudioService.swift +++ b/Signal/src/call/CallAudioService.swift @@ -8,12 +8,13 @@ import Foundation private let TAG = "[CallAudioService]" private var vibrateTimer: Timer? private let soundPlayer = JSQSystemSoundPlayer.shared()! + private let handleRinging: Bool enum SoundFilenames: String { case incomingRing = "r" } - // Mark: Vibration config + // MARK: Vibration config private let vibrateRepeatDuration = 1.6 // Our ring buzz is a pair of vibrations. @@ -26,6 +27,14 @@ import Foundation } } + // MARK: - Initializers + + init(handleRinging: Bool) { + self.handleRinging = handleRinging + } + + // MARK: - Service action handlers + public func handleState(_ state: CallState) { switch state { case .idle: handleIdle() @@ -59,13 +68,8 @@ import Foundation } private func handleLocalRinging() { - Logger.debug("\(TAG) \(#function)") - - vibrateTimer = Timer.scheduledTimer(timeInterval: vibrateRepeatDuration, target: self, selector: #selector(ringVibration), userInfo: nil, repeats: true) - - // Stop other sounds and play ringer through external speaker - setAudioSession(category: AVAudioSessionCategorySoloAmbient) - soundPlayer.playSound(withFilename: SoundFilenames.incomingRing.rawValue, fileExtension: kJSQSystemSoundTypeCAF) + Logger.debug("\(TAG) in \(#function)") + startRinging() } private func handleConnected() { @@ -104,13 +108,36 @@ import Foundation } } - // MARK: Helpers + // MARK: - Ringing + + private func startRinging() { + guard handleRinging else { + Logger.debug("\(TAG) ignoring \(#function) since CallKit handles it's own ringing state") + return + } + + vibrateTimer = Timer.scheduledTimer(timeInterval: vibrateRepeatDuration, target: self, selector: #selector(ringVibration), userInfo: nil, repeats: true) + + // Stop other sounds and play ringer through external speaker + setAudioSession(category: AVAudioSessionCategorySoloAmbient) + + soundPlayer.playSound(withFilename: SoundFilenames.incomingRing.rawValue, fileExtension: kJSQSystemSoundTypeCAF) + } private func stopRinging() { + guard handleRinging else { + Logger.debug("\(TAG) ignoring \(#function) since CallKit handles it's own ringing state") + return + } + Logger.debug("\(TAG) in \(#function)") + + // Stop vibrating vibrateTimer?.invalidate() vibrateTimer = nil + soundPlayer.stopSound(withFilename: SoundFilenames.incomingRing.rawValue) - // Stop playing out of speaker + + // Stop solo audio, revert to default. setAudioSession(category: AVAudioSessionCategoryAmbient) } @@ -124,6 +151,8 @@ import Foundation } } + // MARK: - AVAudioSession Mgmt + private func setAudioSession(category: String, options: AVAudioSessionCategoryOptions) { do { try AVAudioSession.sharedInstance().setCategory(category, with: options) diff --git a/Signal/src/call/NonCallKitCallUIAdaptee.swift b/Signal/src/call/NonCallKitCallUIAdaptee.swift index 3f5a39742..ce0dbe315 100644 --- a/Signal/src/call/NonCallKitCallUIAdaptee.swift +++ b/Signal/src/call/NonCallKitCallUIAdaptee.swift @@ -14,6 +14,9 @@ class NonCallKitCallUIAdaptee: CallUIAdaptee { let notificationsAdapter: CallNotificationsAdapter let callService: CallService + // Starting/Stopping incoming call ringing is our apps responsibility for the non CallKit interface. + let hasManualRinger = true + required init(callService: CallService, notificationsAdapter: CallNotificationsAdapter) { self.callService = callService self.notificationsAdapter = notificationsAdapter diff --git a/Signal/src/call/Speakerbox/CallKitCallUIAdaptee.swift b/Signal/src/call/Speakerbox/CallKitCallUIAdaptee.swift index 94ec9bb59..a3019ec98 100644 --- a/Signal/src/call/Speakerbox/CallKitCallUIAdaptee.swift +++ b/Signal/src/call/Speakerbox/CallKitCallUIAdaptee.swift @@ -23,7 +23,10 @@ final class CallKitCallUIAdaptee: NSObject, CallUIAdaptee, CXProviderDelegate { internal let notificationsAdapter: CallNotificationsAdapter private let provider: CXProvider - /// The app's provider configuration, representing its CallKit capabilities + // CallKit handles incoming ringer stop/start for us. Yay! + let hasManualRinger = false + + // The app's provider configuration, representing its CallKit capabilities static var providerConfiguration: CXProviderConfiguration { let localizedName = NSLocalizedString("APPLICATION_NAME", comment: "Name of application") let providerConfiguration = CXProviderConfiguration(localizedName: localizedName) diff --git a/Signal/src/call/UserInterface/CallUIAdapter.swift b/Signal/src/call/UserInterface/CallUIAdapter.swift index 05cc90651..dded773a2 100644 --- a/Signal/src/call/UserInterface/CallUIAdapter.swift +++ b/Signal/src/call/UserInterface/CallUIAdapter.swift @@ -8,6 +8,7 @@ import CallKit protocol CallUIAdaptee { var notificationsAdapter: CallNotificationsAdapter { get } + var hasManualRinger: Bool { get } func startOutgoingCall(_ call: SignalCall) func reportIncomingCall(_ call: SignalCall, callerName: String) @@ -102,4 +103,9 @@ class CallUIAdapter { internal func setHasVideo(call: SignalCall, hasVideo: Bool) { adaptee.setHasVideo(call: call, hasVideo: hasVideo) } + + // CallKit handles ringing state on it's own. But for non-call kit we trigger ringing start/stop manually. + internal var hasManualRinger: Bool { + return adaptee.hasManualRinger + } } diff --git a/Signal/src/view controllers/CallViewController.swift b/Signal/src/view controllers/CallViewController.swift index 18653d144..e66336d3c 100644 --- a/Signal/src/view controllers/CallViewController.swift +++ b/Signal/src/view controllers/CallViewController.swift @@ -81,7 +81,7 @@ class CallViewController: UIViewController, CallDelegate { contactsManager = Environment.getCurrent().contactsManager let callService = Environment.getCurrent().callService! callUIAdapter = callService.callUIAdapter - audioService = CallAudioService() + audioService = CallAudioService(handleRinging: callUIAdapter.hasManualRinger) super.init(coder: aDecoder) } @@ -89,7 +89,7 @@ class CallViewController: UIViewController, CallDelegate { contactsManager = Environment.getCurrent().contactsManager let callService = Environment.getCurrent().callService! callUIAdapter = callService.callUIAdapter - audioService = CallAudioService() + audioService = CallAudioService(handleRinging: callUIAdapter.hasManualRinger) super.init(nibName: nil, bundle: nil) }