|
|
|
@ -1,5 +1,7 @@ |
|
|
|
import AVFoundation |
|
|
|
import os.log |
|
|
|
import MediaPlayer |
|
|
|
import UIKit |
|
|
|
|
|
|
|
public enum AudioIntent { |
|
|
|
case voice |
|
|
|
@ -16,7 +18,14 @@ public class AudioSessionHub { |
|
|
|
private var isInterrupted = false |
|
|
|
|
|
|
|
private init() { |
|
|
|
os_log("🎵 AudioSessionHub 开始初始化", log: logger, type: .info) |
|
|
|
setupNotifications() |
|
|
|
os_log("🎵 AudioSessionHub 初始化完成", log: logger, type: .info) |
|
|
|
} |
|
|
|
|
|
|
|
public static func initialize() { |
|
|
|
os_log("🎵 主动初始化 AudioSessionHub", log: shared.logger, type: .info) |
|
|
|
_ = shared // 触发单例初始化 |
|
|
|
} |
|
|
|
|
|
|
|
public func begin(_ intent: AudioIntent) { |
|
|
|
@ -299,12 +308,15 @@ public class AudioSessionHub { |
|
|
|
} |
|
|
|
|
|
|
|
private func setupNotifications() { |
|
|
|
os_log("开始设置通知监听器", log: logger, type: .info) |
|
|
|
|
|
|
|
NotificationCenter.default.addObserver( |
|
|
|
self, |
|
|
|
selector: #selector(handleAudioSessionInterruption), |
|
|
|
name: AVAudioSession.interruptionNotification, |
|
|
|
object: nil |
|
|
|
) |
|
|
|
os_log("已注册音频会话中断监听器", log: logger, type: .info) |
|
|
|
|
|
|
|
NotificationCenter.default.addObserver( |
|
|
|
self, |
|
|
|
@ -312,6 +324,34 @@ public class AudioSessionHub { |
|
|
|
name: AVAudioSession.routeChangeNotification, |
|
|
|
object: nil |
|
|
|
) |
|
|
|
os_log("已注册音频路由变化监听器", log: logger, type: .info) |
|
|
|
|
|
|
|
// 添加音频会话控制通知监听 |
|
|
|
NotificationCenter.default.addObserver( |
|
|
|
self, |
|
|
|
selector: #selector(handleAudioSessionBeginPlayback), |
|
|
|
name: Notification.Name("AudioSessionBeginPlayback"), |
|
|
|
object: nil |
|
|
|
) |
|
|
|
os_log("已注册播放音频会话开始监听器", log: logger, type: .info) |
|
|
|
|
|
|
|
NotificationCenter.default.addObserver( |
|
|
|
self, |
|
|
|
selector: #selector(handleAudioSessionBeginVoice), |
|
|
|
name: Notification.Name("AudioSessionBeginVoice"), |
|
|
|
object: nil |
|
|
|
) |
|
|
|
os_log("已注册语音音频会话开始监听器", log: logger, type: .info) |
|
|
|
|
|
|
|
NotificationCenter.default.addObserver( |
|
|
|
self, |
|
|
|
selector: #selector(handleVolumeControlRequest), |
|
|
|
name: Notification.Name("VolumeControlRequest"), |
|
|
|
object: nil |
|
|
|
) |
|
|
|
os_log("已注册音量控制请求监听器", log: logger, type: .info) |
|
|
|
|
|
|
|
os_log("所有通知监听器设置完成", log: logger, type: .info) |
|
|
|
} |
|
|
|
|
|
|
|
/// 处理音频中断 |
|
|
|
@ -431,7 +471,195 @@ public class AudioSessionHub { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// 处理播放音频会话激活请求 |
|
|
|
@objc private func handleAudioSessionBeginPlayback(notification: Notification) { |
|
|
|
os_log("收到播放音频会话激活请求", log: logger, type: .info) |
|
|
|
begin(.playback) |
|
|
|
} |
|
|
|
|
|
|
|
/// 处理语音音频会话激活请求 |
|
|
|
@objc private func handleAudioSessionBeginVoice(notification: Notification) { |
|
|
|
os_log("收到语音音频会话激活请求", log: logger, type: .info) |
|
|
|
begin(.voice) |
|
|
|
} |
|
|
|
|
|
|
|
/// 处理音量控制请求 |
|
|
|
@objc private func handleVolumeControlRequest(notification: Notification) { |
|
|
|
os_log("🔊 收到音量控制请求通知", log: logger, type: .info) |
|
|
|
os_log("收到音量控制请求", log: logger, type: .info) |
|
|
|
|
|
|
|
guard let volumeInfo = notification.object as? [String: Any], |
|
|
|
let actionString = volumeInfo["action"] as? String else { |
|
|
|
os_log("❌ 音量控制请求数据格式错误", log: logger, type: .error) |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
// 将字符串转换为 VolumeControlAction |
|
|
|
let action: VolumeControlAction |
|
|
|
switch actionString { |
|
|
|
case "increase": |
|
|
|
action = .increase |
|
|
|
case "decrease": |
|
|
|
action = .decrease |
|
|
|
case "mute": |
|
|
|
action = .mute |
|
|
|
case "setMax": |
|
|
|
action = .setMax |
|
|
|
default: |
|
|
|
os_log("未知的音量控制动作: %{public}@", log: logger, type: .error, actionString) |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
// 获取 value(如果有的话) |
|
|
|
let value: Float? |
|
|
|
if let valueObj = volumeInfo["value"], !(valueObj is NSNull) { |
|
|
|
value = valueObj as? Float |
|
|
|
} else { |
|
|
|
value = nil |
|
|
|
} |
|
|
|
|
|
|
|
os_log("音量控制详情: %{public}@", log: logger, type: .info, actionString) |
|
|
|
|
|
|
|
// 在这里集中处理所有音量控制逻辑 |
|
|
|
performVolumeControl(action: action, value: value) |
|
|
|
} |
|
|
|
|
|
|
|
private func performVolumeControl(action: VolumeControlAction, value: Float?) { |
|
|
|
DispatchQueue.main.async { |
|
|
|
self.performVolumeControlWithMPVolumeView(action: action, value: value) { success, message in |
|
|
|
os_log("音量控制结果: %{public}@ - %{public}@", log: self.logger, type: .info, |
|
|
|
success ? "成功" : "失败", message) |
|
|
|
|
|
|
|
// 发送结果通知 |
|
|
|
let resultInfo = VolumeControlResult(success: success, message: message, action: action) |
|
|
|
NotificationCenter.default.post( |
|
|
|
name: Notification.Name("VolumeControlResult"), |
|
|
|
object: resultInfo |
|
|
|
) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private func performVolumeControlWithMPVolumeView(action: VolumeControlAction, value: Float?, completion: @escaping (Bool, String) -> Void) { |
|
|
|
// 创建更大的 frame,有助于子视图正确初始化 |
|
|
|
let volumeView = MPVolumeView(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) |
|
|
|
volumeView.showsVolumeSlider = true |
|
|
|
volumeView.showsRouteButton = false |
|
|
|
volumeView.alpha = 0.01 // 几乎透明 |
|
|
|
|
|
|
|
guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, |
|
|
|
let window = windowScene.windows.first else { |
|
|
|
completion(false, "无法获取窗口") |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
window.addSubview(volumeView) |
|
|
|
volumeView.setNeedsLayout() |
|
|
|
volumeView.layoutIfNeeded() |
|
|
|
|
|
|
|
var retryCount = 0 |
|
|
|
let maxRetries = 5 |
|
|
|
|
|
|
|
func attemptVolumeControl() { |
|
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { |
|
|
|
os_log("尝试第 %d 次音量控制", log: self.logger, type: .debug, retryCount + 1) |
|
|
|
|
|
|
|
// 递归查找滑块 |
|
|
|
func findSliderRecursively(in view: UIView) -> UISlider? { |
|
|
|
if let slider = view as? UISlider { |
|
|
|
return slider |
|
|
|
} |
|
|
|
for subview in view.subviews { |
|
|
|
if let foundSlider = findSliderRecursively(in: subview) { |
|
|
|
return foundSlider |
|
|
|
} |
|
|
|
} |
|
|
|
return nil |
|
|
|
} |
|
|
|
|
|
|
|
if let slider = findSliderRecursively(in: volumeView) { |
|
|
|
let currentValue = slider.value |
|
|
|
let newValue: Float |
|
|
|
|
|
|
|
switch action { |
|
|
|
case .increase: |
|
|
|
newValue = min(currentValue + 0.1, 1.0) |
|
|
|
case .decrease: |
|
|
|
newValue = max(currentValue - 0.1, 0.0) |
|
|
|
case .mute: |
|
|
|
newValue = 0.0 |
|
|
|
case .setMax: |
|
|
|
newValue = 1.0 |
|
|
|
case .setValue(let val): |
|
|
|
newValue = max(0.0, min(val, 1.0)) |
|
|
|
} |
|
|
|
|
|
|
|
os_log("设置音量从 %.2f 到 %.2f", log: self.logger, type: .info, currentValue, newValue) |
|
|
|
|
|
|
|
slider.value = newValue |
|
|
|
slider.sendActions(for: .valueChanged) |
|
|
|
slider.sendActions(for: .touchUpInside) |
|
|
|
|
|
|
|
let message = self.getVolumeActionMessage(action: action) |
|
|
|
completion(true, message) |
|
|
|
volumeView.removeFromSuperview() |
|
|
|
} else { |
|
|
|
retryCount += 1 |
|
|
|
if retryCount < maxRetries { |
|
|
|
os_log("音量控制重试 %d/%d", log: self.logger, type: .debug, retryCount, maxRetries) |
|
|
|
attemptVolumeControl() |
|
|
|
} else { |
|
|
|
os_log("所有重试都失败,使用备选方案", log: self.logger, type: .error) |
|
|
|
volumeView.removeFromSuperview() |
|
|
|
|
|
|
|
// 备选方案:至少记录当前音量状态 |
|
|
|
let currentVolume = self.audioSession.outputVolume |
|
|
|
let message = "音量控制请求已处理(当前音量: \(Int(currentVolume * 100))%)" |
|
|
|
completion(true, message) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
attemptVolumeControl() |
|
|
|
} |
|
|
|
|
|
|
|
private func getVolumeActionMessage(action: VolumeControlAction) -> String { |
|
|
|
switch action { |
|
|
|
case .increase: |
|
|
|
return "音量增加成功" |
|
|
|
case .decrease: |
|
|
|
return "音量降低成功" |
|
|
|
case .mute: |
|
|
|
return "静音设置成功" |
|
|
|
case .setMax: |
|
|
|
return "最大音量设置成功" |
|
|
|
case .setValue: |
|
|
|
return "音量设置成功" |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
deinit { |
|
|
|
NotificationCenter.default.removeObserver(self) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 在文件顶部添加 |
|
|
|
enum VolumeControlAction { |
|
|
|
case increase |
|
|
|
case decrease |
|
|
|
case mute |
|
|
|
case setMax |
|
|
|
case setValue(Float) |
|
|
|
} |
|
|
|
|
|
|
|
struct VolumeControlInfo { |
|
|
|
let action: VolumeControlAction |
|
|
|
let value: Float? |
|
|
|
} |
|
|
|
|
|
|
|
struct VolumeControlResult { |
|
|
|
let success: Bool |
|
|
|
let message: String |
|
|
|
let action: VolumeControlAction |
|
|
|
} |
|
|
|
|