()
| 4227 | } |
| 4228 | |
| 4229 | func startAccessibilityPolling() { |
| 4230 | // Keep polling until macOS reports the current process is trusted. The restart guard |
| 4231 | // only prevents restart loops; it must not prevent the UI from noticing permission changes. |
| 4232 | guard !self.accessibilityEnabled else { return } |
| 4233 | |
| 4234 | // Cancel any existing polling task |
| 4235 | self.accessibilityPollingTask?.cancel() |
| 4236 | |
| 4237 | // Start background polling |
| 4238 | self.accessibilityPollingTask = Task { |
| 4239 | while !Task.isCancelled { |
| 4240 | try? await Task.sleep(nanoseconds: 2_000_000_000) // Poll every 2 seconds |
| 4241 | |
| 4242 | // Check if permission was granted |
| 4243 | let nowTrusted = AXIsProcessTrusted() |
| 4244 | if nowTrusted && !self.accessibilityEnabled { |
| 4245 | await MainActor.run { |
| 4246 | DebugLogger.shared.info("Accessibility permission granted", source: "ContentView") |
| 4247 | self.refreshAccessibilityPermissionState() |
| 4248 | self.finishAccessibilityPermissionFlow() |
| 4249 | |
| 4250 | guard !UserDefaults.standard.bool(forKey: self.hasAutoRestartedForAccessibilityKey) else { |
| 4251 | self.hotkeyManager?.reinitialize() |
| 4252 | return |
| 4253 | } |
| 4254 | |
| 4255 | // Mark that we've auto-restarted to prevent loops. |
| 4256 | UserDefaults.standard.set(true, forKey: self.hasAutoRestartedForAccessibilityKey) |
| 4257 | DebugLogger.shared.info("Auto-restarting app after accessibility grant", source: "ContentView") |
| 4258 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { |
| 4259 | self.restartApp() |
| 4260 | } |
| 4261 | } |
| 4262 | break // Stop polling after triggering restart |
| 4263 | } |
| 4264 | } |
| 4265 | } |
| 4266 | } |
| 4267 | |
| 4268 | private func stopAccessibilityPolling() { |
| 4269 | self.accessibilityPollingTask?.cancel() |
no test coverage detected