Respect silent switch in and out of app.

// FREEBIE
This commit is contained in:
Michael Kirk 2017-01-18 11:46:29 -05:00
parent a89bde933d
commit 4374e431a2
3 changed files with 160 additions and 111 deletions

View File

@ -102,6 +102,8 @@
45F170AC1E2F0351003FC1F2 /* CallAudioSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45F170AB1E2F0351003FC1F2 /* CallAudioSession.swift */; };
45F170AD1E2F0351003FC1F2 /* CallAudioSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45F170AB1E2F0351003FC1F2 /* CallAudioSession.swift */; };
45F170AF1E2F0393003FC1F2 /* CallAudioSessionTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45F170AE1E2F0393003FC1F2 /* CallAudioSessionTest.swift */; };
45F170BB1E2FC5D3003FC1F2 /* CallAudioService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45F170BA1E2FC5D3003FC1F2 /* CallAudioService.swift */; };
45F170BC1E2FC5D3003FC1F2 /* CallAudioService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45F170BA1E2FC5D3003FC1F2 /* CallAudioService.swift */; };
45F2B1941D9C9F48000D2C69 /* OWSOutgoingMessageCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 45F2B1931D9C9F48000D2C69 /* OWSOutgoingMessageCollectionViewCell.m */; };
45F2B1971D9CA207000D2C69 /* OWSIncomingMessageCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 45F2B1951D9CA207000D2C69 /* OWSIncomingMessageCollectionViewCell.xib */; };
45F2B1981D9CA207000D2C69 /* OWSOutgoingMessageCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 45F2B1961D9CA207000D2C69 /* OWSOutgoingMessageCollectionViewCell.xib */; };
@ -696,6 +698,7 @@
45F170AB1E2F0351003FC1F2 /* CallAudioSession.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallAudioSession.swift; sourceTree = "<group>"; };
45F170AE1E2F0393003FC1F2 /* CallAudioSessionTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CallAudioSessionTest.swift; path = test/call/CallAudioSessionTest.swift; sourceTree = "<group>"; };
45F170B31E2F0A6A003FC1F2 /* RTCAudioSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RTCAudioSession.h; sourceTree = "<group>"; };
45F170BA1E2FC5D3003FC1F2 /* CallAudioService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallAudioService.swift; sourceTree = "<group>"; };
45F2B1921D9C9F48000D2C69 /* OWSOutgoingMessageCollectionViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSOutgoingMessageCollectionViewCell.h; sourceTree = "<group>"; };
45F2B1931D9C9F48000D2C69 /* OWSOutgoingMessageCollectionViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSOutgoingMessageCollectionViewCell.m; sourceTree = "<group>"; };
45F2B1951D9CA207000D2C69 /* OWSIncomingMessageCollectionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = OWSIncomingMessageCollectionViewCell.xib; sourceTree = "<group>"; };
@ -1535,6 +1538,7 @@
458DE9D51DEE3FD00071BB03 /* PeerConnectionClient.swift */,
4574A5D51DD6704700C6B692 /* CallService.swift */,
45F170AB1E2F0351003FC1F2 /* CallAudioSession.swift */,
45F170BA1E2FC5D3003FC1F2 /* CallAudioService.swift */,
);
path = call;
sourceTree = "<group>";
@ -3182,6 +3186,7 @@
453D28B71D32BA5F00D523F0 /* OWSDisplayedMessage.m in Sources */,
76EB05DC18170B33006006FC /* StreamPair.m in Sources */,
76EB064618170B33006006FC /* TimeUtil.m in Sources */,
45F170BB1E2FC5D3003FC1F2 /* CallAudioService.swift in Sources */,
70BAFD5D190584BE00FA5E0B /* NotificationTracker.m in Sources */,
76EB05A418170B33006006FC /* PacketHandler.m in Sources */,
E197B62118BBF12700F073E5 /* AppAudioManager.m in Sources */,
@ -3272,6 +3277,7 @@
B660F70C1C29988E00687D6E /* StretchFactorController.m in Sources */,
B660F70D1C29988E00687D6E /* AnonymousAudioCallbackHandler.m in Sources */,
45F170AD1E2F0351003FC1F2 /* CallAudioSession.swift in Sources */,
45F170BC1E2FC5D3003FC1F2 /* CallAudioService.swift in Sources */,
B660F70E1C29988E00687D6E /* RemoteIOAudio.m in Sources */,
B660F70F1C29988E00687D6E /* RemoteIOBufferListWrapper.m in Sources */,
456F6E2F1E261D1000FD2210 /* PeerConnectionClientTest.swift in Sources */,

View File

@ -0,0 +1,154 @@
//
// Copyright © 2017 Open Whisper Systems. All rights reserved.
//
import Foundation
@objc class CallAudioService: NSObject {
private let TAG = "[CallAudioService]"
private var vibrateTimer: Timer?
private let audioManager = AppAudioManager.sharedInstance()
private let soundPlayer = JSQSystemSoundPlayer.shared()!
enum SoundFilenames: String {
case incomingRing = "r"
}
// Mark: Vibration config
private let vibrateRepeatDuration = 1.6
// Our ring buzz is a pair of vibrations.
// `pulseDuration` is the small pause between the two vibrations in the pair.
private let pulseDuration = 0.2
public var isSpeakerphoneEnabled = false {
didSet {
handleUpdatedSpeakerphone()
}
}
public func handleState(_ state: CallState) {
switch state {
case .idle: handleIdle()
case .dialing: handleDialing()
case .answering: handleAnswering()
case .remoteRinging: handleRemoteRinging()
case .localRinging: handleLocalRinging()
case .connected: handleConnected()
case .localFailure: handleLocalFailure()
case .localHangup: handleLocalHangup()
case .remoteHangup: handleRemoteHangup()
case .remoteBusy: handleBusy()
}
}
private func handleIdle() {
Logger.debug("\(TAG) \(#function)")
}
private func handleDialing() {
Logger.debug("\(TAG) \(#function)")
}
private func handleAnswering() {
Logger.debug("\(TAG) \(#function)")
stopRinging()
}
private func handleRemoteRinging() {
Logger.debug("\(TAG) \(#function)")
}
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)
}
private func handleConnected() {
Logger.debug("\(TAG) \(#function)")
stopRinging()
// disable start recording to transmit call audio.
setAudioSession(category: AVAudioSessionCategoryPlayAndRecord)
}
private func handleLocalFailure() {
Logger.debug("\(TAG) \(#function)")
stopRinging()
}
private func handleLocalHangup() {
Logger.debug("\(TAG) \(#function)")
stopRinging()
}
private func handleRemoteHangup() {
Logger.debug("\(TAG) \(#function)")
stopRinging()
}
private func handleBusy() {
Logger.debug("\(TAG) \(#function)")
stopRinging()
}
private func handleUpdatedSpeakerphone() {
// TODO
// let category = AVAudioSession.sharedInstance().getCategory()
// if isSpeakerphoneEnabled {
// AVAudioSession.sharedInstance().setCategory(category, option: AVAudioSessionCategoryOptionDefaultToSpeaker)
// } else {
// Will this disable speaker?
// AVAudioSession.sharedInstance().setCategory(category)
// }
audioManager.toggleSpeakerPhone(isEnabled: isSpeakerphoneEnabled)
}
// MARK: Helpers
private func stopRinging() {
vibrateTimer?.invalidate()
vibrateTimer = nil
soundPlayer.stopSound(withFilename: SoundFilenames.incomingRing.rawValue)
// Stop playing out of speaker
setAudioSession(category: AVAudioSessionCategoryAmbient)
}
// public so it can be called by timer via selector
public func ringVibration() {
// Since a call notification is more urgent than a message notifaction, we
// vibrate twice, like a pulse, to differentiate from a normal notification vibration.
soundPlayer.playVibrateSound()
DispatchQueue.default.asyncAfter(deadline: DispatchTime.now() + pulseDuration) {
self.soundPlayer.playVibrateSound()
}
}
private func setAudioSession(category: String, options: AVAudioSessionCategoryOptions) {
do {
try AVAudioSession.sharedInstance().setCategory(category, with: options)
Logger.debug("\(self.TAG) set category: \(category) options: \(options)")
} catch {
let message = "\(self.TAG) in \(#function) failed to set category: \(category) with error: \(error)"
assertionFailure(message)
Logger.error(message)
}
}
private func setAudioSession(category: String) {
do {
try AVAudioSession.sharedInstance().setCategory(category)
Logger.debug("\(self.TAG) set category: \(category)")
} catch {
let message = "\(self.TAG) in \(#function) failed to set category: \(category) with error: \(error)"
assertionFailure(message)
Logger.error(message)
}
}
}

View File

@ -6,117 +6,6 @@ import Foundation
import WebRTC
import PromiseKit
// TODO move this somewhere else.
@objc class CallAudioService: NSObject {
private let TAG = "[CallAudioService]"
private var vibrateTimer: Timer?
private let audioManager = AppAudioManager.sharedInstance()
private let soundPlayer = JSQSystemSoundPlayer.shared()!
enum SoundFilenames: String {
case incomingRing = "r"
}
// Mark: Vibration config
private let vibrateRepeatDuration = 1.6
// Our ring buzz is a pair of vibrations.
// `pulseDuration` is the small pause between the two vibrations in the pair.
private let pulseDuration = 0.2
public var isSpeakerphoneEnabled = false {
didSet {
handleUpdatedSpeakerphone()
}
}
public func handleState(_ state: CallState) {
switch state {
case .idle: handleIdle()
case .dialing: handleDialing()
case .answering: handleAnswering()
case .remoteRinging: handleRemoteRinging()
case .localRinging: handleLocalRinging()
case .connected: handleConnected()
case .localFailure: handleLocalFailure()
case .localHangup: handleLocalHangup()
case .remoteHangup: handleRemoteHangup()
case .remoteBusy: handleBusy()
}
}
private func handleIdle() {
Logger.debug("\(TAG) \(#function)")
}
private func handleDialing() {
Logger.debug("\(TAG) \(#function)")
}
private func handleAnswering() {
Logger.debug("\(TAG) \(#function)")
stopRinging()
}
private func handleRemoteRinging() {
Logger.debug("\(TAG) \(#function)")
}
private func handleLocalRinging() {
Logger.debug("\(TAG) \(#function)")
vibrateTimer = Timer.scheduledTimer(timeInterval: vibrateRepeatDuration, target: self, selector: #selector(ringVibration), userInfo: nil, repeats: true)
soundPlayer.playSound(withFilename: SoundFilenames.incomingRing.rawValue, fileExtension: kJSQSystemSoundTypeCAF)
}
private func handleConnected() {
Logger.debug("\(TAG) \(#function)")
stopRinging()
}
private func handleLocalFailure() {
Logger.debug("\(TAG) \(#function)")
stopRinging()
}
private func handleLocalHangup() {
Logger.debug("\(TAG) \(#function)")
stopRinging()
}
private func handleRemoteHangup() {
Logger.debug("\(TAG) \(#function)")
stopRinging()
}
private func handleBusy() {
Logger.debug("\(TAG) \(#function)")
stopRinging()
}
private func handleUpdatedSpeakerphone() {
audioManager.toggleSpeakerPhone(isEnabled: isSpeakerphoneEnabled)
}
// MARK: Helpers
private func stopRinging() {
vibrateTimer?.invalidate()
vibrateTimer = nil
soundPlayer.stopSound(withFilename: SoundFilenames.incomingRing.rawValue)
}
public func ringVibration() {
// Since a call notification is more urgent than a message notifaction, we
// vibrate twice, like a pulse, to differentiate from a normal notification vibration.
soundPlayer.playVibrateSound()
DispatchQueue.default.asyncAfter(deadline: DispatchTime.now() + pulseDuration) {
self.soundPlayer.playVibrateSound()
}
}
}
// TODO: Add category so that button handlers can be defined where button is created.
// TODO: Add logic to button handlers.
// TODO: Ensure buttons enabled & disabled as necessary.