mirror of
https://github.com/FBukovina/Atoll.git
synced 2026-10-29 08:04:32 +00:00
Partial BugFix: Bluetooth HUD
This commit is contained in:
parent
7e2e610f81
commit
42fe88f17d
1 changed files with 77 additions and 11 deletions
|
|
@ -44,6 +44,9 @@ class BluetoothAudioManager: ObservableObject {
|
||||||
private var isPmsetRefreshInFlight = false
|
private var isPmsetRefreshInFlight = false
|
||||||
private var lastPmsetRefreshDate: Date?
|
private var lastPmsetRefreshDate: Date?
|
||||||
private let pmsetRefreshCooldown: TimeInterval = 5
|
private let pmsetRefreshCooldown: TimeInterval = 5
|
||||||
|
private var hudBatteryWaitTasks: [UUID: Task<Void, Never>] = [:]
|
||||||
|
private let hudBatteryWaitInterval: TimeInterval = 0.3
|
||||||
|
private let hudBatteryWaitTimeout: TimeInterval = 1.8
|
||||||
|
|
||||||
// MARK: - Initialization
|
// MARK: - Initialization
|
||||||
private init() {
|
private init() {
|
||||||
|
|
@ -244,13 +247,16 @@ class BluetoothAudioManager: ObservableObject {
|
||||||
.compactMap { $0.addressString }
|
.compactMap { $0.addressString }
|
||||||
|
|
||||||
// Remove disconnected devices
|
// Remove disconnected devices
|
||||||
let previousCount = connectedDevices.count
|
let removedDevices = connectedDevices.filter { device in
|
||||||
|
!currentlyConnectedAddresses.contains(device.address)
|
||||||
|
}
|
||||||
connectedDevices.removeAll { device in
|
connectedDevices.removeAll { device in
|
||||||
!currentlyConnectedAddresses.contains(device.address)
|
!currentlyConnectedAddresses.contains(device.address)
|
||||||
}
|
}
|
||||||
|
|
||||||
if connectedDevices.count < previousCount {
|
if !removedDevices.isEmpty {
|
||||||
print("🎧 [BluetoothAudioManager] 👋 Audio device(s) disconnected")
|
print("🎧 [BluetoothAudioManager] 👋 Audio device(s) disconnected")
|
||||||
|
removedDevices.forEach { cancelHUDBatteryWait(for: $0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
isBluetoothAudioConnected = !connectedDevices.isEmpty
|
isBluetoothAudioConnected = !connectedDevices.isEmpty
|
||||||
|
|
@ -304,7 +310,9 @@ class BluetoothAudioManager: ObservableObject {
|
||||||
|
|
||||||
// Remove from connected devices
|
// Remove from connected devices
|
||||||
let address = device.addressString ?? "Unknown"
|
let address = device.addressString ?? "Unknown"
|
||||||
|
let removed = connectedDevices.filter { $0.address == address }
|
||||||
connectedDevices.removeAll { $0.address == address }
|
connectedDevices.removeAll { $0.address == address }
|
||||||
|
removed.forEach { cancelHUDBatteryWait(for: $0) }
|
||||||
isBluetoothAudioConnected = !connectedDevices.isEmpty
|
isBluetoothAudioConnected = !connectedDevices.isEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1324,6 +1332,20 @@ class BluetoothAudioManager: ObservableObject {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func cancelHUDBatteryWait(for device: BluetoothAudioDevice) {
|
||||||
|
let cancelBlock = { [weak self] in
|
||||||
|
guard let self else { return }
|
||||||
|
self.hudBatteryWaitTasks[device.id]?.cancel()
|
||||||
|
self.hudBatteryWaitTasks.removeValue(forKey: device.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
if Thread.isMainThread {
|
||||||
|
cancelBlock()
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.async(execute: cancelBlock)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func normalizeBluetoothIdentifier(_ value: String) -> String {
|
private func normalizeBluetoothIdentifier(_ value: String) -> String {
|
||||||
return value
|
return value
|
||||||
.lowercased()
|
.lowercased()
|
||||||
|
|
@ -1347,20 +1369,62 @@ class BluetoothAudioManager: ObservableObject {
|
||||||
private func showDeviceConnectedHUD(_ device: BluetoothAudioDevice) {
|
private func showDeviceConnectedHUD(_ device: BluetoothAudioDevice) {
|
||||||
guard Defaults[.showBluetoothDeviceConnections] else { return }
|
guard Defaults[.showBluetoothDeviceConnections] else { return }
|
||||||
|
|
||||||
|
cancelHUDBatteryWait(for: device)
|
||||||
|
|
||||||
|
if let battery = bestBatteryLevel(for: device) {
|
||||||
|
presentDeviceConnectedHUD(device: device, batteryLevel: battery)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
requestPmsetFallback(reason: "hud missing battery")
|
||||||
|
|
||||||
|
let task = Task.detached(priority: .utility) { [weak self] in
|
||||||
|
guard let self else { return }
|
||||||
|
let deadline = Date().addingTimeInterval(self.hudBatteryWaitTimeout)
|
||||||
|
while Date() < deadline {
|
||||||
|
try? await Task.sleep(nanoseconds: UInt64(self.hudBatteryWaitInterval * 1_000_000_000))
|
||||||
|
guard !Task.isCancelled else { return }
|
||||||
|
|
||||||
|
let batteryInfo = await MainActor.run { () -> (BluetoothAudioDevice, Int)? in
|
||||||
|
guard let refreshedDevice = self.connectedDevices.first(where: { $0.id == device.id }),
|
||||||
|
let battery = self.bestBatteryLevel(for: refreshedDevice) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return (refreshedDevice, battery)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let (refreshedDevice, battery) = batteryInfo {
|
||||||
|
await MainActor.run {
|
||||||
|
self.presentDeviceConnectedHUD(device: refreshedDevice, batteryLevel: battery)
|
||||||
|
}
|
||||||
|
self.cancelHUDBatteryWait(for: device)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
guard !Task.isCancelled else { return }
|
||||||
|
await MainActor.run {
|
||||||
|
self.presentDeviceConnectedHUD(device: device, batteryLevel: nil)
|
||||||
|
}
|
||||||
|
self.cancelHUDBatteryWait(for: device)
|
||||||
|
}
|
||||||
|
|
||||||
|
hudBatteryWaitTasks[device.id] = task
|
||||||
|
}
|
||||||
|
|
||||||
|
private func presentDeviceConnectedHUD(device: BluetoothAudioDevice, batteryLevel: Int?) {
|
||||||
|
guard Defaults[.showBluetoothDeviceConnections] else { return }
|
||||||
|
|
||||||
print("🎧 [BluetoothAudioManager] 📱 Showing device connected HUD")
|
print("🎧 [BluetoothAudioManager] 📱 Showing device connected HUD")
|
||||||
|
|
||||||
// Convert battery percentage to 0.0-1.0 value
|
let batteryValue: CGFloat = if let batteryLevel {
|
||||||
let reportedBattery = bestBatteryLevel(for: device)
|
CGFloat(clampBatteryPercentage(batteryLevel)) / 100.0
|
||||||
|
|
||||||
let batteryValue: CGFloat = if let battery = reportedBattery {
|
|
||||||
CGFloat(clampBatteryPercentage(battery)) / 100.0
|
|
||||||
} else {
|
} else {
|
||||||
0.0 // 0 means battery info not available
|
0.0
|
||||||
}
|
}
|
||||||
|
|
||||||
HUDSuppressionCoordinator.shared.suppressVolumeHUD(for: 1.5)
|
HUDSuppressionCoordinator.shared.suppressVolumeHUD(for: 1.5)
|
||||||
|
|
||||||
// Show HUD via coordinator
|
|
||||||
coordinator.toggleSneakPeek(
|
coordinator.toggleSneakPeek(
|
||||||
status: true,
|
status: true,
|
||||||
type: .bluetoothAudio,
|
type: .bluetoothAudio,
|
||||||
|
|
@ -1382,6 +1446,8 @@ class BluetoothAudioManager: ObservableObject {
|
||||||
dnc.removeObserver(self)
|
dnc.removeObserver(self)
|
||||||
observers.removeAll()
|
observers.removeAll()
|
||||||
cancellables.removeAll()
|
cancellables.removeAll()
|
||||||
|
hudBatteryWaitTasks.values.forEach { $0.cancel() }
|
||||||
|
hudBatteryWaitTasks.removeAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue