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

时间:2014-08-02 01:49:51

标签: applescript osx-mavericks assistive-technology

从Mac OS 10.9 Mavericks开始,Apple requires that Assistive Access must be enabled explicitly for each application that wants to use it

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

  1. 当操作系统以非英语语言运行时,硬编码按钮名称将出错,脚本将失败。我怎样才能发现操作系统运行的语言,以及"点击锁定进行更改的名称。"按钮会有那种语言吗?或者,有没有办法确定此按钮是否处于lockedauthenticatingunlocked状态,而不读取按钮的名称?

  2. 脚本在等待用户输入管理员用户名和密码时使用紧密重复循环。有没有更好的策略可以用来等待对话框成功解除?

  3. ====

    set output to allowAssistiveAccessFor("Skype")
    
    if (the |quitWhenDone| of output) then
      tell application "System Preferences" to quit
    end if
    
    on allowAssistiveAccessFor(applicationName)
        set quitWhenDone to not (application "System Preferences" is running)
        set output to {quitWhenDone:quitWhenDone}
    
        tell application "System Preferences"
    
            activate
            reveal anchor "Privacy_Accessibility" of pane id "com.apple.preference.security"
    
            tell application "System Events"
                tell process "System Preferences"
    
                    -- Find the table that contains the application icons and checkboxes
                    try
                        set appTable to table 1 of scroll area 1 of group 1 of tab group 1 of window "Security & Privacy"
                    on error errorMessage
                        return output & {state:-1, message:errorMessage}
                    end try
    
                    set total to the number of rows of appTable
    
                    -- Find the row that refers to applicationName 
                    repeat with rowNumber from 1 to total
                        if (name of UI element 1 of row rowNumber of appTable = applicationName) then
                            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
                                -- Click the “Click the lock to make changes.” button.
                                if exists button "Click the lock to make changes." of window "Security & Privacy" then
                                    click button "Click the lock to make changes." of window "Security & Privacy"
    
                                    -- The user will now have to enter an admin password. This can take some time.
                                    -- The name of the button will change to "Authenticating"...
                                    set unlocking to button "Authenticating…" of window "Security & Privacy"
                                    repeat while exists unlocking
                                    end repeat
                                    -- ... and then to "Click the lock to prevent further changes." ... unless the user cancelled
    
                                    if exists button "Click the lock to make changes." of window "Security & Privacy" then
                                        return output & {state:-1, message:"User cancelled"}
                                    end if
                                end if
    
                                -- Click the <applicationName> checkbox.
                                -- If we had to unlock the Security & Privacy pane, then an immediate click might not have
                                -- an effect. Try as many times as possible for 1 second, and give up if unsuccessful
                                set failMessage to "Cannot allow the " & applicationName & " application to control your computer"
                                set endDate to (current date) + 1.0 -- 1 second from now
    
                                repeat
                                    try
                                        if ((current date) > endDate) then
                                            -- Time's up
                                            return output & {state:-1, message:failMessage}
    
                                        end if
    
                                        click appCheckbox
    
                                        if (value of appCheckbox as boolean) then
                                            return output & {state:0, message:"Success"}
                                        end if
    
                                    on error errorMessage
                                        -- Something dreadful happened. Keep trying until time is up
                                    end try
                                end repeat
                            end if
                        end if
                    end repeat
                end tell
            end tell
        end tell
    
        return output & {state:-1, message:"Application " & applicationName & " not found"}
    end allowAssistiveAccessFor
    

2 个答案:

答案 0 :(得分:2)

您可以将窗口称为“窗口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

答案 1 :(得分:0)

无论操作系统使用何种语言,此版本的脚本都能正常运行。使用法语和俄语进行测试。它仍然使用紧密的重复循环来检查用户是否已关闭身份验证对话框,但这似乎不会影响性能。

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