Files
EdisonVoice/Sources/EdisonCore/Support/Permissions.swift
T

53 lines
1.9 KiB
Swift

import AVFoundation
import AppKit
import ApplicationServices
import Foundation
/// Edison Voice needs two grants, and neither can be worked around:
/// - **Microphone** — obviously.
/// - **Accessibility** — for both the `CGEventTap` (hotkey) and the AX text insert.
///
/// Accessibility has no programmatic request; the OS only shows the prompt, and the user
/// must toggle it in System Settings. TCC also keys on the code signature, so re-signing
/// the app resets the grant.
@MainActor
public enum Permissions {
public static var hasAccessibility: Bool {
AXIsProcessTrusted()
}
public static var hasMicrophone: Bool {
AVCaptureDevice.authorizationStatus(for: .audio) == .authorized
}
/// Shows the system Accessibility prompt if the app isn't yet trusted.
@discardableResult
public static func promptForAccessibility() -> Bool {
// Spelled out rather than using `kAXTrustedCheckOptionPrompt`, which imports as a
// mutable global and so isn't usable from concurrency-checked code.
let options = ["AXTrustedCheckOptionPrompt": true] as CFDictionary
return AXIsProcessTrustedWithOptions(options)
}
public static func requestMicrophone() async -> Bool {
switch AVCaptureDevice.authorizationStatus(for: .audio) {
case .authorized:
return true
case .notDetermined:
return await AVCaptureDevice.requestAccess(for: .audio)
default:
return false
}
}
public static func openAccessibilitySettings() {
let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility")!
NSWorkspace.shared.open(url)
}
public static func openMicrophoneSettings() {
let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_Microphone")!
NSWorkspace.shared.open(url)
}
}