Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Enable access for assistive devices programmatically on 10.9

I want to enable access for assistive devices programatically on 10.9. On 10.8 and lower I was using following Applescript to enable access for assistive devices:

tell application "System Events" if UI elements enabled is false then     set UI elements enabled to true end if end tell 

With 10.9, Apple has moved the accessibility options to System Preferences ➞ Security & Privacy ➞ Privacy ➞ Accessibility. Unlike previous versions of OS X, which used a universal checkbox for all applications, the new functionality in 10.9 allows users to individually choose which apps can gain control of the system to perform their various scripted functions.

The new system preferences regarding accessibility

Apple has NOT provided any API to developers to programmatically enable accessibility for an app. So Mac OS 10.9 will prompt a dialog for end user permission to enable Accessibility when application uses accessibility APIs. Additionally User has to Relaunch the application after enabling Accessibility.

Default prompt dialog put up by 10.9 OS for Xcode

Can we enable access for assistive devices programmatically on 10.9 using Applescript or any other APIs? Any help to fix this issue would be greatly appreciated.

like image 616
Vinpai Avatar asked Jul 17 '13 07:07

Vinpai


People also ask

What are accessibility permissions?

Android Accessibility Services was created to help developers enhance their apps to cater to and assist individuals with disabilities in overcoming their challenges when using their smartphones. When users download these apps, they need to enable 'Accessibility Permissions' in order to take advantage of these benefits.


2 Answers

This doesn’t answer your question, but it’s good to know about a new API call that appeared in 10.9 and lets you display the authorization screen or bypass it:

NSDictionary *options = @{(id)kAXTrustedCheckOptionPrompt: @YES}; BOOL accessibilityEnabled = AXIsProcessTrustedWithOptions((CFDictionaryRef)options); 

Passing YES will force the authorization screen to appear, passing NO will silently skip it. The return value is the same as the one returned by AXAPIEnabled(), which is getting deprecated in 10.9. To make sure that the function is available on your system, just compare it to NULL:

if (AXIsProcessTrustedWithOptions != NULL) {     // 10.9 and later } else {     // 10.8 and older } 

You'll need to add ApplicationServices.framework to your project, and import to your .m or .h file:

#import <ApplicationServices/ApplicationServices.h> 

It’s quite a pity that the authorization screen doesn’t let the user to authorize the app directly, it just opens the right part of the System Preferences. Which, by the way, you can do directly without going through the useless system dialogue:

tell application "System Preferences"     set securityPane to pane id "com.apple.preference.security"     tell securityPane to reveal anchor "Privacy_Accessibility"     activate end tell 

or using Objective C:

NSString *urlString = @"x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility"; [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:urlString]]; 

This can be paired with the first code snippet to test whether accessibilityEnabled by passing @NO to kAXTrustedCheckOptionPrompt while preventing the system pop-up to appear and instead opening the Accessibility preferences pane directly:

NSDictionary *options = @{(id)kAXTrustedCheckOptionPrompt: @NO}; BOOL accessibilityEnabled = AXIsProcessTrustedWithOptions((CFDictionaryRef)options); if (!accessibilityEnabled) {     NSString *urlString = @"x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility";     [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:urlString]]; } 
like image 157
zoul Avatar answered Sep 23 '22 02:09

zoul


I'd recommend against using all the sqlite3 and AppleScript hacks as they might stop working in the future, there's also just a proper api for this.

To add on to this, you can actually monitor if the user clicks the accessibility setting for your app so you can do some actions when the user grants the permission.

(Swift 5, tested on Mojave, Catalina, Big Sur)

reading privileges:

private func readPrivileges(prompt: Bool) -> Bool {     let options: NSDictionary = [kAXTrustedCheckOptionPrompt.takeRetainedValue() as NSString: prompt]     let status = AXIsProcessTrustedWithOptions(options)     return status } 

Monitoring for changes in accessibility:

DistributedNotificationCenter.default().addObserver(forName: NSNotification.Name("com.apple.accessibility.api"), object: nil, queue: nil) { _ in   DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {     self.updatePrivileges()   } } 

It is best to read the privileges again after getting the notification as the notification itself doesn't really work in my experience. So inside the updatePrivileges(), run readPrivileges() to get the new status.

You need the delay because it takes some time for the changes to be reflected.

Another thing you need to keep in mind while monitoring is that a notification will be fired for any app that gets different permissions, so if the user grants or revokes a different app you'll still get a notification.

Also, don't forget to remove the observer when you don't need it anymore.

edit:

Source: Accessbility Testbench by Piddlesoft

like image 21
JoniVR Avatar answered Sep 25 '22 02:09

JoniVR