diff --git a/Suohai/SuohaiController.swift b/Suohai/SuohaiController.swift index a19e782..d3da816 100644 --- a/Suohai/SuohaiController.swift +++ b/Suohai/SuohaiController.swift @@ -16,7 +16,7 @@ class SuohaiController: NSObject { override init() { super.init() - self.setupItems() + self.prepareItems() NotificationCenter.addObserver(observer: self, selector: #selector(reloadMenu), name: .audioDevicesDidChange) } @@ -24,22 +24,9 @@ class SuohaiController: NSObject { NotificationCenter.removeObserver(observer: self, name: .audioDevicesDidChange) } - private func setupItems() { - self.menu = { - let menu = NSMenu() - menu.delegate = self - return menu - }() - self.statusItem = { - let item = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) - item.image = #imageLiteral(resourceName: "StatusItem") - item.target = self - item.menu = self.menu - return item - }() - } - - @objc func reloadMenu() { + // Mark: Notification handler + @objc + func reloadMenu() { let listener = SuohaiListener.shared self.menu.removeAllItems() self.menu.addItem(NSMenuItem(title: NSLocalizedString("OutputDevices", comment: ""))) @@ -73,7 +60,8 @@ class SuohaiController: NSObject { } // MARK: Event method - @objc private func selectOutputDeviceAction(_ sender: NSMenuItem) { + @objc + private func selectOutputDeviceAction(_ sender: NSMenuItem) { let listener = SuohaiListener.shared guard let device = listener.devices.first(where: {$0.id == UInt32(sender.tag)}) else { return @@ -81,7 +69,8 @@ class SuohaiController: NSObject { listener.selectedOutputDeviceID = listener.selectedOutputDeviceID != device.id ? device.id : nil } - @objc private func selectInputDeviceAction(_ sender: NSMenuItem) { + @objc + private func selectInputDeviceAction(_ sender: NSMenuItem) { let listener = SuohaiListener.shared guard let device = listener.devices.first(where: {$0.id == UInt32(sender.tag)}) else { return @@ -89,9 +78,26 @@ class SuohaiController: NSObject { listener.selectedInputDeviceID = listener.selectedInputDeviceID != device.id ? device.id : nil } - @objc private func quitAction(_ sender: NSMenuItem) { + @objc + private func quitAction(_ sender: NSMenuItem) { NSApplication.shared.terminate(nil) } + + // MARK: UI method + private func prepareItems() { + self.menu = { + let menu = NSMenu() + menu.delegate = self + return menu + }() + self.statusItem = { + let item = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) + item.image = #imageLiteral(resourceName: "StatusItem") + item.target = self + item.menu = self.menu + return item + }() + } } extension SuohaiController: NSMenuDelegate { diff --git a/Suohai/SuohaiListener.swift b/Suohai/SuohaiListener.swift index 7a22ed1..a5d294c 100644 --- a/Suohai/SuohaiListener.swift +++ b/Suohai/SuohaiListener.swift @@ -24,6 +24,11 @@ enum SuohaiNotification: String { } } +struct SuohaiDefaultKeys { + static let audioInputDeviceID = "AudioInputDeviceID" + static let audioOutputDeviceID = "AudioOutputDeviceID" +} + enum AudioDeviceType { case output case input @@ -54,17 +59,17 @@ struct AudioAddress { } struct AudioListener { - static var devices: AudioObjectPropertyListenerProc = {_, _, _, _ in + static var devices: AudioObjectPropertyListenerProc = { _, _, _, _ in NotificationCenter.post(suohaiNotification: .audioDevicesDidChange) return 0 } - static var output: AudioObjectPropertyListenerProc = {_, _, _, _ in + static var output: AudioObjectPropertyListenerProc = { _, _, _, _ in NotificationCenter.post(suohaiNotification: .audioOutputDeviceDidChange) return 0 } - static var input: AudioObjectPropertyListenerProc = {_, _, _, _ in + static var input: AudioObjectPropertyListenerProc = { _, _, _, _ in NotificationCenter.post(suohaiNotification: .audioInputDeviceDidChange) return 0 } @@ -123,23 +128,26 @@ class SuohaiListener { var selectedOutputDeviceID: AudioDeviceID? { didSet { - guard var deviceID = self.selectedOutputDeviceID else { - return + if var deviceID = self.selectedOutputDeviceID { + self.setOutputDevice(id: &deviceID) } - self.setOutputDevice(id: &deviceID) + UserDefaults.standard.set(self.selectedOutputDeviceID, forKey: SuohaiDefaultKeys.audioOutputDeviceID) } } + var selectedInputDeviceID: AudioDeviceID? { didSet { - guard var deviceID = self.selectedInputDeviceID else { - return + if var deviceID = self.selectedInputDeviceID { + self.setInputDevice(id: &deviceID) } - self.setInputDevice(id: &deviceID) + UserDefaults.standard.set(self.selectedInputDeviceID, forKey: SuohaiDefaultKeys.audioInputDeviceID) } } // MARK: Lifecycle init() { + self.selectedOutputDeviceID = UserDefaults.standard.value(forKey: SuohaiDefaultKeys.audioOutputDeviceID) as? AudioDeviceID + self.selectedInputDeviceID = UserDefaults.standard.value(forKey: SuohaiDefaultKeys.audioInputDeviceID) as? AudioDeviceID NotificationCenter.addObserver(observer: self, selector: #selector(handleNotification(_:)), name: .audioDevicesDidChange) NotificationCenter.addObserver(observer: self, selector: #selector(handleNotification(_:)), name: .audioOutputDeviceDidChange) NotificationCenter.addObserver(observer: self, selector: #selector(handleNotification(_:)), name: .audioInputDeviceDidChange) @@ -164,8 +172,19 @@ class SuohaiListener { AudioObjectRemovePropertyListener(AudioObjectID(kAudioObjectSystemObject), &AudioAddress.inputDevice, AudioListener.input, nil) } + // MARK: Private method + private func setOutputDevice(id: inout AudioDeviceID) { + AudioObjectSetPropertyData(AudioObjectID(kAudioObjectSystemObject), &AudioAddress.outputDevice, 0, nil, UInt32(MemoryLayout.size), &id) + } + + private func setInputDevice(id: inout AudioDeviceID) { + AudioObjectSetPropertyData(AudioObjectID(kAudioObjectSystemObject), &AudioAddress.inputDevice, 0, nil, UInt32(MemoryLayout.size), &id) + UserDefaults.standard.set(id, forKey: SuohaiDefaultKeys.audioInputDeviceID) + } + // MARK: Notification handler - @objc private func handleNotification(_ notification: Notification) { + @objc + private func handleNotification(_ notification: Notification) { if notification.name == SuohaiNotification.audioDevicesDidChange.notificationName { if !self.devices.contains(where: {$0.id == self.selectedOutputDeviceID}) { self.selectedOutputDeviceID = nil @@ -185,12 +204,4 @@ class SuohaiListener { self.setInputDevice(id: &deviceID) } } - - private func setOutputDevice(id: inout AudioDeviceID) { - AudioObjectSetPropertyData(AudioObjectID(kAudioObjectSystemObject), &AudioAddress.outputDevice, 0, nil, UInt32(MemoryLayout.size), &id) - } - - private func setInputDevice(id: inout AudioDeviceID) { - AudioObjectSetPropertyData(AudioObjectID(kAudioObjectSystemObject), &AudioAddress.inputDevice, 0, nil, UInt32(MemoryLayout.size), &id) - } }