|
|
@@ -4,6 +4,7 @@
|
|
|
import AVFoundation
|
|
|
import Combine
|
|
|
import Foundation
|
|
|
+import MediaPlayer
|
|
|
import UIKit
|
|
|
|
|
|
class VolumeButtonHandler: NSObject {
|
|
|
@@ -30,6 +31,9 @@ class VolumeButtonHandler: NSObject {
|
|
|
private var lastSignificantVolumeChange: Date?
|
|
|
private var volumeChangePattern: [TimeInterval] = []
|
|
|
|
|
|
+ // Remote command center for handling bluetooth/CarPlay buttons
|
|
|
+ private var remoteCommandsEnabled = false
|
|
|
+
|
|
|
private var cancellables = Set<AnyCancellable>()
|
|
|
|
|
|
override private init() {
|
|
|
@@ -112,11 +116,127 @@ class VolumeButtonHandler: NSObject {
|
|
|
volumeChangePattern.removeAll()
|
|
|
}
|
|
|
|
|
|
+ private func setupRemoteCommandCenter() {
|
|
|
+ guard !remoteCommandsEnabled else { return }
|
|
|
+
|
|
|
+ let commandCenter = MPRemoteCommandCenter.shared()
|
|
|
+
|
|
|
+ // Log current audio route to help with debugging
|
|
|
+ let currentRoute = AVAudioSession.sharedInstance().currentRoute
|
|
|
+ LogManager.shared.log(category: .volumeButtonSnooze, message: "Audio route: \(currentRoute.outputs.map { $0.portName }.joined(separator: ", "))")
|
|
|
+
|
|
|
+ // Enable pause command - handles play/pause button on bluetooth devices and CarPlay
|
|
|
+ commandCenter.pauseCommand.isEnabled = true
|
|
|
+ commandCenter.pauseCommand.addTarget { [weak self] _ in
|
|
|
+ guard let self = self else { return .commandFailed }
|
|
|
+
|
|
|
+ LogManager.shared.log(category: .volumeButtonSnooze, message: "Pause command received from remote")
|
|
|
+
|
|
|
+ // Check if alarm is currently active and activation delay has passed
|
|
|
+ if let alarmStartTime = self.alarmStartTime {
|
|
|
+ let timeSinceAlarmStart = Date().timeIntervalSince(alarmStartTime)
|
|
|
+
|
|
|
+ if timeSinceAlarmStart > self.volumeButtonActivationDelay {
|
|
|
+ // Check cooldown
|
|
|
+ if let lastPress = self.lastVolumeButtonPressTime {
|
|
|
+ let timeSinceLastPress = Date().timeIntervalSince(lastPress)
|
|
|
+ if timeSinceLastPress < self.volumeButtonCooldown {
|
|
|
+ return .success
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ LogManager.shared.log(category: .volumeButtonSnooze, message: "Remote command pause received - snoozing alarm")
|
|
|
+ self.snoozeActiveAlarm()
|
|
|
+ return .success
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return .commandFailed
|
|
|
+ }
|
|
|
+
|
|
|
+ // Enable play command as well for symmetry
|
|
|
+ commandCenter.playCommand.isEnabled = true
|
|
|
+ commandCenter.playCommand.addTarget { [weak self] _ in
|
|
|
+ guard let self = self else { return .commandFailed }
|
|
|
+
|
|
|
+ LogManager.shared.log(category: .volumeButtonSnooze, message: "Play command received from remote")
|
|
|
+
|
|
|
+ if let alarmStartTime = self.alarmStartTime {
|
|
|
+ let timeSinceAlarmStart = Date().timeIntervalSince(alarmStartTime)
|
|
|
+
|
|
|
+ if timeSinceAlarmStart > self.volumeButtonActivationDelay {
|
|
|
+ if let lastPress = self.lastVolumeButtonPressTime {
|
|
|
+ let timeSinceLastPress = Date().timeIntervalSince(lastPress)
|
|
|
+ if timeSinceLastPress < self.volumeButtonCooldown {
|
|
|
+ return .success
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ LogManager.shared.log(category: .volumeButtonSnooze, message: "Remote command play received - snoozing alarm")
|
|
|
+ self.snoozeActiveAlarm()
|
|
|
+ return .success
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return .commandFailed
|
|
|
+ }
|
|
|
+
|
|
|
+ // Enable toggle play/pause command - common on many bluetooth devices
|
|
|
+ commandCenter.togglePlayPauseCommand.isEnabled = true
|
|
|
+ commandCenter.togglePlayPauseCommand.addTarget { [weak self] _ in
|
|
|
+ guard let self = self else { return .commandFailed }
|
|
|
+
|
|
|
+ LogManager.shared.log(category: .volumeButtonSnooze, message: "Toggle play/pause command received from remote")
|
|
|
+
|
|
|
+ if let alarmStartTime = self.alarmStartTime {
|
|
|
+ let timeSinceAlarmStart = Date().timeIntervalSince(alarmStartTime)
|
|
|
+
|
|
|
+ if timeSinceAlarmStart > self.volumeButtonActivationDelay {
|
|
|
+ if let lastPress = self.lastVolumeButtonPressTime {
|
|
|
+ let timeSinceLastPress = Date().timeIntervalSince(lastPress)
|
|
|
+ if timeSinceLastPress < self.volumeButtonCooldown {
|
|
|
+ return .success
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ LogManager.shared.log(category: .volumeButtonSnooze, message: "Remote command toggle play/pause received - snoozing alarm")
|
|
|
+ self.snoozeActiveAlarm()
|
|
|
+ return .success
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return .commandFailed
|
|
|
+ }
|
|
|
+
|
|
|
+ remoteCommandsEnabled = true
|
|
|
+ LogManager.shared.log(category: .volumeButtonSnooze, message: "Remote command center configured for bluetooth/CarPlay button handling")
|
|
|
+ }
|
|
|
+
|
|
|
+ private func disableRemoteCommandCenter() {
|
|
|
+ guard remoteCommandsEnabled else { return }
|
|
|
+
|
|
|
+ let commandCenter = MPRemoteCommandCenter.shared()
|
|
|
+ commandCenter.pauseCommand.isEnabled = false
|
|
|
+ commandCenter.playCommand.isEnabled = false
|
|
|
+ commandCenter.togglePlayPauseCommand.isEnabled = false
|
|
|
+
|
|
|
+ // Remove all targets
|
|
|
+ commandCenter.pauseCommand.removeTarget(nil)
|
|
|
+ commandCenter.playCommand.removeTarget(nil)
|
|
|
+ commandCenter.togglePlayPauseCommand.removeTarget(nil)
|
|
|
+
|
|
|
+ remoteCommandsEnabled = false
|
|
|
+ LogManager.shared.log(category: .volumeButtonSnooze, message: "Remote command center disabled")
|
|
|
+ }
|
|
|
+
|
|
|
func startMonitoring() {
|
|
|
guard !isMonitoring else { return }
|
|
|
|
|
|
isMonitoring = true
|
|
|
|
|
|
+ // Setup remote command center for bluetooth/CarPlay button handling
|
|
|
+ setupRemoteCommandCenter()
|
|
|
+
|
|
|
volumeObserver = AVAudioSession.sharedInstance().observe(\.outputVolume, options: [.new]) { [weak self] session, _ in
|
|
|
guard let self = self, let alarmStartTime = self.alarmStartTime else { return }
|
|
|
|
|
|
@@ -176,6 +296,9 @@ class VolumeButtonHandler: NSObject {
|
|
|
volumeObserver?.invalidate()
|
|
|
volumeObserver = nil
|
|
|
|
|
|
+ // Disable remote command center
|
|
|
+ disableRemoteCommandCenter()
|
|
|
+
|
|
|
isMonitoring = false
|
|
|
lastVolume = 0.0 // Reset for the next alarm.
|
|
|
}
|