blob: c8680955991e23b99a05546801fbb1053fc935d3 [file] [log] [blame]
/*
* Copyright (C) 2018-2019 Savoir-faire Linux Inc.
*
* Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
import Foundation
import SwiftyBeaver
import RxSwift
enum OutputPortType: Int {
case builtinspk = 0
case bluetooth = 1
case headphones = 2
case receiver = 3
}
class AudioService {
private let disposeBag = DisposeBag()
private let log = SwiftyBeaver.self
private let audioAdapter: AudioAdapter
var isHeadsetConnected = Variable<Bool>(false)
var isOutputToSpeaker = Variable<Bool>(true)
var enableSwitchAudio: Observable<Bool> {
return self.isHeadsetConnected.asObservable()
}
init(withAudioAdapter audioAdapter: AudioAdapter) {
self.audioAdapter = audioAdapter
}
@objc private func audioRouteChangeListener(_ notification: Notification) {
guard let userInfo = notification.userInfo,
let reasonValue = userInfo[AVAudioSessionRouteChangeReasonKey] as? UInt else {
return
}
guard let reason = AVAudioSession.RouteChangeReason(rawValue: reasonValue),
(reason == .newDeviceAvailable || reason == .oldDeviceUnavailable || reason == .categoryChange) else {
return
}
overrideAudioRoute()
}
func connectAudioSignal() {
let bluetoothConnected = bluetoothAudioConnected()
let headphonesConnected = headphoneAudioConnected()
isHeadsetConnected.value = bluetoothConnected || headphonesConnected
// Listen for audio route changes
NotificationCenter.default.addObserver(
self,
selector: #selector(audioRouteChangeListener(_:)),
name: AVAudioSession.routeChangeNotification,
object: nil)
}
func overrideAudioRoute() {
let bluetoothConnected = bluetoothAudioConnected()
let headphonesConnected = headphoneAudioConnected()
isHeadsetConnected.value = bluetoothConnected || headphonesConnected
if bluetoothConnected {
setAudioOutputDevice(port: OutputPortType.bluetooth)
return
} else if headphonesConnected {
setAudioOutputDevice(port: OutputPortType.headphones)
return
}
let outputPort = isOutputToSpeaker.value ? OutputPortType.builtinspk : OutputPortType.receiver
setAudioOutputDevice(port: outputPort)
}
func switchSpeaker() {
guard let isSpeaker = self.speakerIsActive() else {
return
}
if isSpeaker {
overrideToReceiver()
} else {
overrideToSpeaker()
}
}
func setToRing() {
if !isHeadsetConnected.value {
setAudioOutputDevice(port: OutputPortType.builtinspk)
}
}
func overrideToSpeaker() {
isOutputToSpeaker.value = true
setAudioOutputDevice(port: OutputPortType.builtinspk)
}
func overrideToReceiver() {
isOutputToSpeaker.value = false
setAudioOutputDevice(port: OutputPortType.receiver)
}
func setDefaultOutput(toSpeaker: Bool, override: Bool = false) {
isOutputToSpeaker.value = toSpeaker
if override {
overrideAudioRoute()
}
}
func bluetoothAudioConnected() -> Bool {
let outputs = AVAudioSession.sharedInstance().currentRoute.outputs
for output in outputs {
if output.portType == AVAudioSession.Port.bluetoothA2DP ||
output.portType == AVAudioSession.Port.bluetoothHFP ||
output.portType == AVAudioSession.Port.bluetoothLE {
return true
}
}
return false
}
func headphoneAudioConnected() -> Bool {
let outputs = AVAudioSession.sharedInstance().currentRoute.outputs
for output in outputs where output.portType == AVAudioSession.Port.headphones {
return true
}
return false
}
func speakerIsActive() -> Bool? {
if let output = AVAudioSession.sharedInstance().currentRoute.outputs.first {
return output.uid == AVAudioSession.Port.builtInSpeaker.rawValue
}
return nil
}
func setAudioOutputDevice(port: OutputPortType) {
self.audioAdapter.setAudioOutputDevice(port.rawValue)
}
func setAudioRingtoneDevice(port: OutputPortType) {
self.audioAdapter.setAudioRingtoneDevice(port.rawValue)
}
func startAudio() {
self.audioAdapter.startAudio()
}
}