From 72fd1ef2d68224150a4390837a94b0553bbc8aae Mon Sep 17 00:00:00 2001 From: loveuer Date: Tue, 2 Dec 2025 18:10:53 +0800 Subject: [PATCH] Fix: Auto-initialize app after accessibility permission grant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: - When user first launches the app without accessibility permissions, the system shows a permission dialog - After granting permissions, the app was already running but not fully initialized - User couldn't reopen the app (already running) and functionality wasn't available Solution: - Implemented permission monitoring with Timer (checks every 1 second) - Deferred app initialization until permissions are granted - Split initialization into initializeApp() method - Automatically initialize once permissions are detected User Experience: - User grants permission in System Preferences - App automatically detects permission change - App initializes and starts working without restart - Updated alert message to inform user about auto-start Technical Changes: - Added permissionCheckTimer for monitoring - Added initializeApp() for deferred initialization - Store config and keyMapper as instance variables - Use Task { @MainActor } for timer callback - Changed alert style from warning to informational 🤖 Generated with [Qoder](https://qoder.com) --- .gitignore | 1 + Sources/AppDelegate.swift | 62 +++++++++++++++++++++++++++++++-------- 2 files changed, 50 insertions(+), 13 deletions(-) diff --git a/.gitignore b/.gitignore index e5bc211..7ef31a9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.qoder # macOS .DS_Store .AppleDouble diff --git a/Sources/AppDelegate.swift b/Sources/AppDelegate.swift index 65595bd..a31436b 100644 --- a/Sources/AppDelegate.swift +++ b/Sources/AppDelegate.swift @@ -5,6 +5,9 @@ class AppDelegate: NSObject, NSApplicationDelegate { private var statusBarController: StatusBarController? private var eventTapManager: EventTapManager? + private var config: Config? + private var keyMapper: KeyMapper? + private var permissionCheckTimer: Timer? func applicationDidFinishLaunching(_ notification: Notification) { Logger.setup() @@ -13,19 +16,37 @@ class AppDelegate: NSObject, NSApplicationDelegate { Logger.info("================================") Logger.info("App bundle path: \(Bundle.main.bundlePath)") - let config = loadConfig() - Logger.logLevel = config.log.level - - let keyMapper = KeyMapper(fromConfig: config) - keyMapper.printMappings() + config = loadConfig() + Logger.logLevel = config!.log.level Logger.info("Checking accessibility permissions...") if !checkAccessibilityPermissions() { - Logger.error("Accessibility permissions not granted!") + Logger.warning("Accessibility permissions not granted, waiting for user to grant permissions...") showAccessibilityAlert() + startPermissionMonitoring() return } + Logger.info("Accessibility permissions granted") + initializeApp() + } + + func applicationWillTerminate(_ notification: Notification) { + permissionCheckTimer?.invalidate() + eventTapManager?.stop() + Logger.info("Application terminated") + Logger.cleanup() + } + + private func initializeApp() { + guard let config = config else { + Logger.error("Configuration not loaded") + return + } + + let keyMapper = KeyMapper(fromConfig: config) + self.keyMapper = keyMapper + keyMapper.printMappings() eventTapManager = EventTapManager(keyMapper: keyMapper) statusBarController = StatusBarController(eventTapManager: eventTapManager!, config: config) @@ -42,10 +63,22 @@ class AppDelegate: NSObject, NSApplicationDelegate { Logger.info("Application ready") } - func applicationWillTerminate(_ notification: Notification) { - eventTapManager?.stop() - Logger.info("Application terminated") - Logger.cleanup() + private func startPermissionMonitoring() { + Logger.info("Starting permission monitoring...") + + permissionCheckTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in + Task { @MainActor in + guard let self = self else { return } + + let trusted = AXIsProcessTrusted() + if trusted { + Logger.info("Accessibility permissions granted! Initializing app...") + self.permissionCheckTimer?.invalidate() + self.permissionCheckTimer = nil + self.initializeApp() + } + } + } } private func loadConfig() -> Config { @@ -87,9 +120,12 @@ class AppDelegate: NSObject, NSApplicationDelegate { private func showAccessibilityAlert() { let alert = NSAlert() alert.messageText = "Accessibility Permissions Required" - alert.informativeText = "uskey needs accessibility permissions to remap keyboard keys.\n\nPlease grant permissions in:\nSystem Preferences > Privacy & Security > Accessibility\n\nAfter granting permissions, restart the app." - alert.alertStyle = .warning + alert.informativeText = "uskey needs accessibility permissions to remap keyboard keys.\n\nPlease:\n1. Click 'Open System Preferences' in the dialog that appeared\n2. Enable uskey in the Accessibility list\n\nThe app will automatically start once permissions are granted." + alert.alertStyle = .informational alert.addButton(withTitle: "OK") - alert.runModal() + + DispatchQueue.main.async { + alert.runModal() + } } } \ No newline at end of file