feat(macos): add Camera permission to onboarding flow
- Add 'camera' case to Capability enum
- Add UI strings (title, subtitle, icon) in PermissionsSettings
- Add ensureCamera() and camera status check in PermissionManager
- Add CameraPermissionHelper for opening System Settings
🦞 Clawd's first code contribution!
This commit is contained in:
@@ -31,6 +31,8 @@ enum PermissionManager {
|
|||||||
await self.ensureMicrophone(interactive: interactive)
|
await self.ensureMicrophone(interactive: interactive)
|
||||||
case .speechRecognition:
|
case .speechRecognition:
|
||||||
await self.ensureSpeechRecognition(interactive: interactive)
|
await self.ensureSpeechRecognition(interactive: interactive)
|
||||||
|
case .camera:
|
||||||
|
await self.ensureCamera(interactive: interactive)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,6 +116,24 @@ enum PermissionManager {
|
|||||||
return SFSpeechRecognizer.authorizationStatus() == .authorized
|
return SFSpeechRecognizer.authorizationStatus() == .authorized
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static func ensureCamera(interactive: Bool) async -> Bool {
|
||||||
|
let status = AVCaptureDevice.authorizationStatus(for: .video)
|
||||||
|
switch status {
|
||||||
|
case .authorized:
|
||||||
|
return true
|
||||||
|
case .notDetermined:
|
||||||
|
guard interactive else { return false }
|
||||||
|
return await AVCaptureDevice.requestAccess(for: .video)
|
||||||
|
case .denied, .restricted:
|
||||||
|
if interactive {
|
||||||
|
CameraPermissionHelper.openSettings()
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
@unknown default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static func voiceWakePermissionsGranted() -> Bool {
|
static func voiceWakePermissionsGranted() -> Bool {
|
||||||
let mic = AVCaptureDevice.authorizationStatus(for: .audio) == .authorized
|
let mic = AVCaptureDevice.authorizationStatus(for: .audio) == .authorized
|
||||||
let speech = SFSpeechRecognizer.authorizationStatus() == .authorized
|
let speech = SFSpeechRecognizer.authorizationStatus() == .authorized
|
||||||
@@ -153,6 +173,9 @@ enum PermissionManager {
|
|||||||
|
|
||||||
case .speechRecognition:
|
case .speechRecognition:
|
||||||
results[cap] = SFSpeechRecognizer.authorizationStatus() == .authorized
|
results[cap] = SFSpeechRecognizer.authorizationStatus() == .authorized
|
||||||
|
|
||||||
|
case .camera:
|
||||||
|
results[cap] = AVCaptureDevice.authorizationStatus(for: .video) == .authorized
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return results
|
return results
|
||||||
@@ -189,6 +212,21 @@ enum MicrophonePermissionHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum CameraPermissionHelper {
|
||||||
|
static func openSettings() {
|
||||||
|
let candidates = [
|
||||||
|
"x-apple.systempreferences:com.apple.preference.security?Privacy_Camera",
|
||||||
|
"x-apple.systempreferences:com.apple.preference.security",
|
||||||
|
]
|
||||||
|
|
||||||
|
for candidate in candidates {
|
||||||
|
if let url = URL(string: candidate), NSWorkspace.shared.open(url) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum AppleScriptPermission {
|
enum AppleScriptPermission {
|
||||||
private static let logger = Logger(subsystem: "com.steipete.clawdis", category: "AppleScriptPermission")
|
private static let logger = Logger(subsystem: "com.steipete.clawdis", category: "AppleScriptPermission")
|
||||||
|
|
||||||
|
|||||||
@@ -120,6 +120,7 @@ struct PermissionRow: View {
|
|||||||
case .screenRecording: "Screen Recording"
|
case .screenRecording: "Screen Recording"
|
||||||
case .microphone: "Microphone"
|
case .microphone: "Microphone"
|
||||||
case .speechRecognition: "Speech Recognition"
|
case .speechRecognition: "Speech Recognition"
|
||||||
|
case .camera: "Camera"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,6 +133,7 @@ struct PermissionRow: View {
|
|||||||
case .screenRecording: "Capture the screen for context or screenshots"
|
case .screenRecording: "Capture the screen for context or screenshots"
|
||||||
case .microphone: "Allow Voice Wake and audio capture"
|
case .microphone: "Allow Voice Wake and audio capture"
|
||||||
case .speechRecognition: "Transcribe Voice Wake trigger phrases on-device"
|
case .speechRecognition: "Transcribe Voice Wake trigger phrases on-device"
|
||||||
|
case .camera: "Capture photos and video from the camera"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,6 +145,7 @@ struct PermissionRow: View {
|
|||||||
case .screenRecording: "display"
|
case .screenRecording: "display"
|
||||||
case .microphone: "mic"
|
case .microphone: "mic"
|
||||||
case .speechRecognition: "waveform"
|
case .speechRecognition: "waveform"
|
||||||
|
case .camera: "camera"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ public enum Capability: String, Codable, CaseIterable, Sendable {
|
|||||||
case screenRecording
|
case screenRecording
|
||||||
case microphone
|
case microphone
|
||||||
case speechRecognition
|
case speechRecognition
|
||||||
|
case camera
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum CameraFacing: String, Codable, Sendable {
|
public enum CameraFacing: String, Codable, Sendable {
|
||||||
|
|||||||
Reference in New Issue
Block a user