使用AppleScript为Mavericks上的应用程序启用辅助访问:本地化和优雅

使用AppleScript为Mavericks上的应用程序启用辅助访问:本地化和优雅,applescript,osx-mavericks,assistive-technology,Applescript,Osx Mavericks,Assistive Technology,从MacOS10.9Mavericks开始,苹果 对于给定的应用程序,我编写了以下AppleScript函数来处理此问题。该脚本适合我,但有两个缺陷: 当操作系统以英语以外的语言运行时,硬编码的按钮名称将出错,脚本将失败。我如何才能发现操作系统运行的语言,以及“单击锁进行更改”按钮在该语言中的名称?或者,是否有方法在不读取按钮名称的情况下确定此按钮是否处于锁定、验证或解锁状态 脚本在等待用户输入管理员用户名和密码时使用紧密的重复循环。是否有更好的策略可以用来等待对话框成功取消 ==== 将输出设

从MacOS10.9Mavericks开始,苹果

对于给定的应用程序,我编写了以下AppleScript函数来处理此问题。该脚本适合我,但有两个缺陷:

  • 当操作系统以英语以外的语言运行时,硬编码的按钮名称将出错,脚本将失败。我如何才能发现操作系统运行的语言,以及“单击锁进行更改”按钮在该语言中的名称?或者,是否有方法在不读取按钮名称的情况下确定此按钮是否处于
    锁定
    验证
    解锁
    状态

  • 脚本在等待用户输入管理员用户名和密码时使用紧密的重复循环。是否有更好的策略可以用来等待对话框成功取消

  • ====

    将输出设置为AllowAsistiveAccessfor(“Skype”)
    如果(输出的| quitWhenDone |),那么
    告诉应用程序“系统首选项”退出
    如果结束
    关于AllowAsistiveAccessfor(应用程序名称)
    将quitWhenDone设置为not(应用程序“系统首选项”正在运行)
    将输出设置为{quitWhenDone:quitWhenDone}
    告诉应用程序“系统首选项”
    激活
    显示窗格id“com.apple.preference.security”的主播“Privacy\u Accessibility”
    告诉应用程序“系统事件”
    告诉流程“系统首选项”
    --查找包含应用程序图标和复选框的表
    尝试
    将appTable设置为窗口“安全和隐私”选项卡组1的组1滚动区域1的表1
    关于错误消息
    返回输出&{state:-1,message:errorMessage}
    结束尝试
    将total设置为appTable的行数
    --查找引用applicationName的行
    重复上述步骤,使rowNumber从1到total
    如果(appTable行rowNumber的UI元素1的名称=applicationName),则
    将appCheckbox设置为appTable行rowNumber的UI元素1的复选框1
    如果(appCheckbox的值为布尔值),则
    --已为此应用程序启用辅助访问
    返回输出&{状态:0,消息:“已启用”}
    其他的
    --单击“单击锁进行更改”按钮。
    如果存在“安全和隐私”窗口的“单击锁进行更改”按钮,则
    单击“安全和隐私”窗口的“单击锁进行更改”按钮
    --用户现在必须输入管理员密码。这可能需要一些时间。
    --按钮的名称将更改为“身份验证”。。。
    将解锁设置为窗口“安全和隐私”的“身份验证…”按钮
    解锁时重复此操作
    结束重复
    -- ... 然后“单击锁以防止进一步更改…”。。。除非用户取消
    如果存在“安全和隐私”窗口的“单击锁进行更改”按钮,则
    返回输出&{state:-1,消息:“User cancelled”}
    如果结束
    如果结束
    --单击复选框。
    --如果我们必须解锁“安全和隐私”窗格,则可能无法立即单击
    --效果。尽可能多地尝试1秒,如果失败就放弃
    将failMessage设置为“无法允许”&applicationName&“应用程序控制您的计算机”
    将endDate设置为(当前日期)+1.0--从现在起1秒
    重复
    尝试
    如果((当前日期)>结束日期),则
    --时间到了
    返回输出&{state:-1,message:failMessage}
    如果结束
    单击应用复选框
    如果(appCheckbox的值为布尔值),则
    返回输出&{state:0,消息:“Success”}
    如果结束
    关于错误消息
    --可怕的事情发生了。继续努力,直到时间到了
    结束尝试
    结束重复
    如果结束
    如果结束
    结束重复
    结束语
    结束语
    结束语
    返回输出&{state:-1,消息:“Application”&applicationName&“notfound”}
    end AllowsIsistiveAccessfor
    
    不使用窗口和按钮名称,您可以将窗口称为“窗口1”,将锁定按钮称为“按钮4”。这样,系统使用的语言就不重要了

    tell application "System Preferences"
        activate
        reveal anchor "Privacy_Accessibility" of pane id "com.apple.preference.security"
        tell application "System Events"
            tell process "System Preferences"
                tell button 4 of window 1 to click
            end tell
        end tell
    end tell
    
    一旦用户通过身份验证,按钮4的名称就会更改。所以你可以循环直到你看到改变。以下不是一个完美的解决方案,因为它只适用于英语语言系统,但它可能有助于您更进一步

    tell application "System Preferences"
        activate
        reveal anchor "Privacy_Accessibility" of pane id "com.apple.preference.security"
        tell application "System Events"
            tell process "System Preferences"
                tell button 4 of window 1 to click
                with timeout of 180 seconds
                    repeat until button "Click the lock to prevent further changes." of window 1 exists
                    end repeat
                    display dialog "Preference pane unlocked." with title (the name as text) buttons {"OK"} default button "OK" giving up after 60
                end timeout
            end tell
        end tell
    end tell
    

    无论操作系统运行的语言是什么,我的脚本的这个版本都可以工作。用法语和俄语测试。它仍然使用紧密的重复循环来检查用户是否关闭了身份验证对话框,但这似乎不会影响性能
    tell application "System Preferences"
        activate
        reveal anchor "Privacy_Accessibility" of pane id "com.apple.preference.security"
        tell application "System Events"
            tell process "System Preferences"
                tell button 4 of window 1 to click
                with timeout of 180 seconds
                    repeat until button "Click the lock to prevent further changes." of window 1 exists
                    end repeat
                    display dialog "Preference pane unlocked." with title (the name as text) buttons {"OK"} default button "OK" giving up after 60
                end timeout
            end tell
        end tell
    end tell
    
    set output to allowAssistiveAccessFor("skype")
    
    if (the |quitWhenDone| of output) then
        tell application "System Preferences" to quit
    end if
    
    get output
    
    
    on allowAssistiveAccessFor(applicationName)
        set quitWhenDone to not (application "System Preferences" is running)
        set output to {quitWhenDone:quitWhenDone}
    
        tell application "System Preferences"
    
            reopen -- to ensure that the application will be open and window 1 will contain a tab group
            activate
    
            -- Open the Accessibility pane of the Security & Privacy Preferences
            reveal anchor "Privacy_Accessibility" of pane id "com.apple.preference.security"
    
            -- The "Security & Privacy" window and its contents are only available through System Events
            tell application "System Events" to tell process "System Preferences"
    
                -- Find the table that contains the application icons and checkboxes. This will be in
                -- window 1 which is called "Security & Privacy" in English. If assistive access is
                -- not enabled for AppleScript, then we will have no access to any window.
                try
                    set securityAndPrivacyWindow to window 1
                    set appTable to table 1 of scroll area 1 of group 1 of tab group 1 of securityAndPrivacyWindow
    
                on error errorMessage
                    return output & {state:-1, message:errorMessage}
                end try
    
                -- If we get here, then the assistive access is enabled for AppleScript.
                -- Let's find the row that refers to applicationName 
                set total to the number of rows of appTable
                repeat with rowNumber from 1 to total
                    if (name of UI element 1 of row rowNumber of appTable = applicationName) then
    
                        -- We have found the row containing the applicationName checkbox
                        set appCheckbox to checkbox 1 of UI element 1 of row rowNumber of appTable
    
                        if (value of appCheckbox as boolean) then
                            -- Assistive access is already enabled for this application
                            return output & {state:0, message:"Already enabled"}
    
                        else
                            (*  Attempt to click on appCheckbox. If its value changes then the
                            Security & Privacy window was already unlocked. If it fails, then we will
                            need to ask the user to enter an admin name and password
                        *)
    
                            click appCheckbox
    
                            if (value of appCheckbox as boolean) then
                                return output & {state:0, message:"Success"}
                            end if
    
                            (*  If we get here, then the click on appCheckbox had no effect, presumably
                            because this window is locked. We need to simulate a click on the 
                            “Click the lock to make changes.” button. This may have different names in
                            different languages. All we know for certain is:
                            •   It is button 4 (in Mavericks at least)
                            •   Its name will change to something meaning "Authenticating…" when it is
                                clicked
                            •   If it was in its locked state:
                                -   a dialog will now show, and it will take the user a significant amount of
                                    time to respond to the dialog
                                -   Its name will change again when the user clicks on one of the buttons
                                    in the dialog
                                -   If its name reverts to the name it had before, then the user canceled
                                    the dialog
                                -   If its name becomes something new, then the user successfully
                                    entered an admin name and password
                            •   If it were in its unlocked state, it would immediately lock and its name would
                                not change again, so we would wait forever in the `repeat while` loop
                                However, if it were in its unlocked state then we would already have changed
                                the state of the applicationName checkbox, so we would not be here.
                            *)
    
                            set lockButton to button 4 of securityAndPrivacyWindow
                            click lockButton
    
                            -- The name of the button will have changed to something like "Authenticating"...
                            set unlocking to button 4 of securityAndPrivacyWindow
    
                            -- The user will now have to enter an admin password. This can take some time.
                            repeat while exists unlocking
                                -- The user has not yet clicked on either the Cancel or the Unlock button yet
                            end repeat
    
                            (*  The user has closed the dialog. If s/he clicked Cancel then:
                                •   The original name of the button will have been restored
                                •   We cannot continue
                            *)
                            if exists lockButton then
                                return output & {state:-1, message:"User canceled"}
                            end if
    
                            -- If we get here, we can simulate a click on the <applicationName> checkbox.
                            click appCheckbox
    
                            if (value of appCheckbox as boolean) then
                                return output & {state:0, message:"Success after authentication"}
                            else
                                return output & {state:-1, message:"Failure after authentication"}
                            end if
                        end if
                    end if
                end repeat
            end tell
        end tell
    
        return output & {state:-1, message:"Application " & applicationName & " not found"}
    end allowAssistiveAccessFor