diff --git a/Apps/Keyty/Sources/Keyty/App/Composition/AppDependencies.swift b/Apps/Keyty/Sources/Keyty/App/Composition/AppDependencies.swift index 2e895fe6..1133d3b7 100644 --- a/Apps/Keyty/Sources/Keyty/App/Composition/AppDependencies.swift +++ b/Apps/Keyty/Sources/Keyty/App/Composition/AppDependencies.swift @@ -15,6 +15,7 @@ final class AppDependencies { let ui: AppUIContainer var captureController: CaptureController { self.services.captureController } + var permissionsService: any PermissionsService { self.services.permissionsService } var appSettings: any AppSettingsProtocol { self.settings.appSettings } var permissionsOnboardingWindowController: PermissionsOnboardingWindowController { self.ui.permissionsOnboardingWindowController } var aboutWindowController: AboutWindowController { self.ui.aboutWindowController } diff --git a/Apps/Keyty/Sources/Keyty/App/Lifecycle/AppController.swift b/Apps/Keyty/Sources/Keyty/App/Lifecycle/AppController.swift index cd1fade8..0f30a4d0 100644 --- a/Apps/Keyty/Sources/Keyty/App/Lifecycle/AppController.swift +++ b/Apps/Keyty/Sources/Keyty/App/Lifecycle/AppController.swift @@ -18,6 +18,7 @@ final class AppController: NSObject { private let statusItemController: StatusItemController private let dependencies: AppDependencies private let updateService: any UpdateService + private var permissionsObservationToken: PermissionObservationToken? override init() { self.updateService = UpdateServiceFactory.make() @@ -37,6 +38,13 @@ final class AppController: NSObject { self.dependencies.captureController.onCapturingChanged = { [weak self] isCapturing in self?.statusItemController.isCapturing = isCapturing } + self.statusItemController.isAccessibilityGranted = + self.dependencies.permissionsService.status(for: .accessibility) == .granted + self.permissionsObservationToken = self.dependencies.permissionsService.observeChanges { [weak self] in + Task { @MainActor [weak self] in + self?.updateAccessibilityStatus() + } + } } } @@ -88,6 +96,11 @@ extension AppController: NSApplicationDelegate { // MARK: - Settings Presentation private extension AppController { + func updateAccessibilityStatus() { + self.statusItemController.isAccessibilityGranted = + self.dependencies.permissionsService.status(for: .accessibility) == .granted + } + func checkForUpdatesAtLaunchIfNeeded() { guard self.updateService.automaticallyChecksForUpdates else { return } self.updateService.checkForUpdatesInBackground() diff --git a/Apps/Keyty/Sources/Keyty/App/Shell/StatusItemController.swift b/Apps/Keyty/Sources/Keyty/App/Shell/StatusItemController.swift index 4c5f8403..7cc33287 100644 --- a/Apps/Keyty/Sources/Keyty/App/Shell/StatusItemController.swift +++ b/Apps/Keyty/Sources/Keyty/App/Shell/StatusItemController.swift @@ -14,9 +14,11 @@ final class StatusItemController { private let statusItem: NSStatusItem private var statusItemImage: NSImage { - let image = self.isCapturing ? NSImage.statusItemEnabled : NSImage.statusItemDisabled - image.isTemplate = true - return image + if !self.isAccessibilityGranted { + return NSImage.statusItemPermissionRequired + } + + return self.isCapturing ? NSImage.statusItemEnabled : NSImage.statusItemDisabled } init(menu: NSMenu, shortcutItem: NSMenuItem) { @@ -24,16 +26,24 @@ final class StatusItemController { self.shortcutItem = shortcutItem let item = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength) item.menu = menu - let image = NSImage.statusItemDisabled - image.isTemplate = true - item.button?.image = image + item.button?.image = NSImage.statusItemDisabled self.statusItem = item } var isCapturing: Bool = false { didSet { self.shortcutItem.title = isCapturing ? L10n.General.stopCapturing : L10n.General.startCapturing - self.statusItem.button?.image = self.statusItemImage + self.updateStatusItemImage() + } + } + + var isAccessibilityGranted: Bool = false { + didSet { + self.updateStatusItemImage() } } + + private func updateStatusItemImage() { + self.statusItem.button?.image = self.statusItemImage + } } diff --git a/Apps/Keyty/Sources/Keyty/Resources/Assets.xcassets/status_item_disabled.imageset/status_item_disabled@2x.png b/Apps/Keyty/Sources/Keyty/Resources/Assets.xcassets/status_item_disabled.imageset/status_item_disabled@2x.png index dd4daedc..8b292b17 100644 Binary files a/Apps/Keyty/Sources/Keyty/Resources/Assets.xcassets/status_item_disabled.imageset/status_item_disabled@2x.png and b/Apps/Keyty/Sources/Keyty/Resources/Assets.xcassets/status_item_disabled.imageset/status_item_disabled@2x.png differ diff --git a/Apps/Keyty/Sources/Keyty/Resources/Assets.xcassets/status_item_enabled.imageset/status_item_enabled@2x.png b/Apps/Keyty/Sources/Keyty/Resources/Assets.xcassets/status_item_enabled.imageset/status_item_enabled@2x.png index a48f9b7b..a8660b21 100644 Binary files a/Apps/Keyty/Sources/Keyty/Resources/Assets.xcassets/status_item_enabled.imageset/status_item_enabled@2x.png and b/Apps/Keyty/Sources/Keyty/Resources/Assets.xcassets/status_item_enabled.imageset/status_item_enabled@2x.png differ diff --git a/Apps/Keyty/Sources/Keyty/Resources/Assets.xcassets/status_item_permission_required.imageset/Contents.json b/Apps/Keyty/Sources/Keyty/Resources/Assets.xcassets/status_item_permission_required.imageset/Contents.json new file mode 100644 index 00000000..421763e7 --- /dev/null +++ b/Apps/Keyty/Sources/Keyty/Resources/Assets.xcassets/status_item_permission_required.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "status_item_permission_required@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Apps/Keyty/Sources/Keyty/Resources/Assets.xcassets/status_item_permission_required.imageset/status_item_permission_required@2x.png b/Apps/Keyty/Sources/Keyty/Resources/Assets.xcassets/status_item_permission_required.imageset/status_item_permission_required@2x.png new file mode 100644 index 00000000..f1988c6b Binary files /dev/null and b/Apps/Keyty/Sources/Keyty/Resources/Assets.xcassets/status_item_permission_required.imageset/status_item_permission_required@2x.png differ diff --git a/Assets/Application/StatusItem/StatusItem.psd b/Assets/Application/StatusItem/StatusItem.psd new file mode 100644 index 00000000..90ecc39b Binary files /dev/null and b/Assets/Application/StatusItem/StatusItem.psd differ