39 lines
1.4 KiB
Swift
39 lines
1.4 KiB
Swift
import Foundation
|
|
|
|
/// Whether anyone is looking at the app: the phone UI in the foreground, or CarPlay connected.
|
|
/// Work that only feeds a screen (stemd polling, a shallow output buffer for snappy faders)
|
|
/// checks this, so a locked phone playing music does as little as it can.
|
|
enum AppActivity {
|
|
private static let lock = UnfairLock()
|
|
nonisolated(unsafe) private static var phone = true
|
|
nonisolated(unsafe) private static var car = false
|
|
nonisolated(unsafe) private static var waiters: [CheckedContinuation<Void, Never>] = []
|
|
|
|
static var isActive: Bool { lock.with { phone || car } }
|
|
|
|
static func setPhone(_ on: Bool) { update { phone = on } }
|
|
static func setCar(_ on: Bool) { update { car = on } }
|
|
|
|
private static func update(_ change: () -> Void) {
|
|
let ready = lock.with { () -> [CheckedContinuation<Void, Never>] in
|
|
change()
|
|
guard phone || car else { return [] }
|
|
defer { waiters = [] }
|
|
return waiters
|
|
}
|
|
ready.forEach { $0.resume() }
|
|
}
|
|
|
|
/// Returns once the app is active, at once if it already is.
|
|
static func untilActive() async {
|
|
await withCheckedContinuation { (c: CheckedContinuation<Void, Never>) in
|
|
let now = lock.with { () -> Bool in
|
|
if phone || car { return true }
|
|
waiters.append(c)
|
|
return false
|
|
}
|
|
if now { c.resume() }
|
|
}
|
|
}
|
|
}
|