Можно ли включить/выключить "не беспокоить" для OS X программно

Можно ли включить/выключить "Не беспокоить" для mac os x программно, что означает по коду. Я провел исследование google, например:

Ответы

Ответ 1

Unfortunatelly (но неудивительно), нет публичного API для обработки предпочтений уведомлений пользователей, одним из которых является режим Do Not Disturb (DND).

Тем не менее, если вы хотите включить функцию включения и отключения DND в своем приложении, вам не повезло: есть три способа выбора.

# 1. Запустите этот крошечный AppleScript и посмотрите результаты

Здесь AppleScript с помощью philastokes, который учитывает, что щелчок на значке Центра уведомлений в строке меню делает именно то, что мы хотим: он переключает режим DND!

(* Copyright © philastokes from applehelpwriter.com *)
(* Link: http://applehelpwriter.com/2014/12/10/applescript-toggle-notification-centre-yosemite *)

tell application "System Events"
    tell application process "SystemUIServer"
        try
            (* Replace "Notification Center" with "NotificationCenter"
            here if you're targeting OS X 10.10 *)
            if exists menu bar item "Notification Center, Do Not Disturb enabled" of menu bar 2 then
                key down option
                (* Replace "Notification Center" with "NotificationCenter"
                here if you're targeting OS X 10.10 *)
                click menu bar item "Notification Center, Do Not Disturb enabled" of menu bar 2
                key up option
            else
                key down option
                click menu bar item "Notification Center" of menu bar 2
                key up option
            end if
        on error
            key up option
        end try
    end tell
end tell

Обратите внимание, что вам нужно заменить "Notification Center" на "NotificationCenter" везде, если вы настроите OS X 10.10

Кроме того, для выполнения этого кода ваше приложение должно иметь доступность для него.

Последний шаг состоит в том, чтобы обернуть его в код Objctive-C/Swift:

NSString *source  = ... // the AppleScript code
NSAppleScript *script = [[NSAppleScript alloc] initWithSource: source];
NSDictionary *errorInfo = nil;
[script executeAndReturnError: &errorInfo];

# 2. Используйте API Accessibility напрямую

Вместо того, чтобы позволить AppleScript работать с пользовательскими взаимодействиями, мы могли бы сделать их ourselvs с помощью Accessibility API, доступных в системе:

Выполнение этого кода требует, чтобы ваше приложение имело доступность для него.

pid_t SystemUIServerPID = [[NSRunningApplication runningApplicationsWithBundleIdentifier:
                              @"com.apple.systemuiserver"].firstObject processIdentifier];
assert(SystemUIServerPID != 0);

AXUIElementRef target = AXUIElementCreateApplication(SystemUIServerPID);
assert(target != nil);

CFArrayRef attributes = nil;
AXUIElementCopyAttributeNames(target, &attributes);
assert([(__bridge NSArray *)attributes containsObject: @"AXExtrasMenuBar"]);

CFTypeRef menubar;
AXUIElementCopyAttributeValue(target, CFSTR("AXExtrasMenuBar"), &menubar);

CFTypeRef children;
AXUIElementCopyAttributeValue(menubar, CFSTR("AXChildren"), &children);

// XXX: I hate mixing CF and Objective-C like this but it just a PoC code.
// Anyway, I'm sorry
NSArray *items = (__bridge NSArray *)children;
for (id x in items) {
    AXUIElementRef child = (__bridge AXUIElementRef)x;
    CFTypeRef title;
    AXUIElementCopyAttributeValue(child, CFSTR("AXTitle"), &title);
    assert(CFGetTypeID(title) == CFStringGetTypeID());
    // XXX: the proper check would be to match the whole "Notification Center" string,
    // but on OS X 10.10 it "NotificationCenter" (without the space in-between) and
    // I don't feel like having two conditionals here
    if (CFStringHasPrefix(title, CFSTR("Notification"))) {
        optionKeyDown();
        AXUIElementPerformAction(child, kAXPressAction);
        optionKeyUp();
        break;
    }
}

где optionKeyDown() и optionKeyUp() являются

#define kOptionKeyCode (58)

static void optionKeyDown(void)
{
    CGEventRef e = CGEventCreateKeyboardEvent(NULL, kOptionKeyCode, true);
    CGEventPost(kCGSessionEventTap, e);
    CFRelease(e);
}

static void optionKeyUp(void)
{
    CGEventRef e = CGEventCreateKeyboardEvent(NULL, kOptionKeyCode, false);
    CGEventPost(kCGSessionEventTap, e);
    CFRelease(e);
}

# 3. Предположим, что мы являемся Notifications.prefPane

Возможно, вы заметили, что вы можете включить режим "Не беспокоить" на панели "Настройки уведомлений", установив диапазон режимов с 00:00 до 23:59. И отключить DND будет просто снятие флажка.

Вот что внутри Notifications.prefPane:

void turnDoNotDisturbOn(void)
{
    // The trick is to set DND time range from 00:00 (0 minutes) to 23:59 (1439 minutes),
    // so it will always be on
    CFPreferencesSetValue(CFSTR("dndStart"), (__bridge CFPropertyListRef)(@(0.0f)),
                          CFSTR("com.apple.notificationcenterui"),
                          kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);

    CFPreferencesSetValue(CFSTR("dndEnd"), (__bridge CFPropertyListRef)(@(1440.f)),
                          CFSTR("com.apple.notificationcenterui"),
                          kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);

    CFPreferencesSetValue(CFSTR("doNotDisturb"), (__bridge CFPropertyListRef)(@(YES)),
                          CFSTR("com.apple.notificationcenterui"),
                          kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);

    // Notify all the related daemons that we have changed Do Not Disturb preferences
    commitDoNotDisturbChanges();
}


void turnDoNotDisturbOff()
{
    CFPreferencesSetValue(CFSTR("dndStart"), NULL,
                        CFSTR("com.apple.notificationcenterui"),
                        kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);

    CFPreferencesSetValue(CFSTR("dndEnd"), NULL,
                          CFSTR("com.apple.notificationcenterui"),
                          kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);

    CFPreferencesSetValue(CFSTR("doNotDisturb"), (__bridge CFPropertyListRef)(@(NO)),
                          CFSTR("com.apple.notificationcenterui"),
                          kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);

    commitDoNotDisturbChanges();
}

void commitDoNotDisturbChanges(void)
{
    /// XXX: I'm using kCFPreferencesCurrentUser placeholder here which means that this code must
    /// be run under regular user account (not root/admin). If you're going to run this code
    /// from a privileged helper, use kCFPreferencesAnyUser in order to toggle DND for all users
    /// or drop privileges and use kCFPreferencesCurrentUser.
    CFPreferencesSynchronize(CFSTR("com.apple.notificationcenterui"), kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);
    [[NSDistributedNotificationCenter defaultCenter] postNotificationName: @"com.apple.notificationcenterui.dndprefs_changed"
                                                               object: nil userInfo: nil
                                                   deliverImmediately: YES];
}

Ответ 2

@Решение № 2 было единственным, которое правильно работало для меня.

Вот версия Swift 4:

    func enableDND(){

        CFPreferencesSetValue("dndStart" as CFString, CGFloat(0) as CFPropertyList, "com.apple.notificationcenterui" as CFString, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost)

        CFPreferencesSetValue("dndEnd" as CFString, CGFloat(1440) as CFPropertyList, "com.apple.notificationcenterui" as CFString, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost)

        CFPreferencesSetValue("doNotDisturb" as CFString, true as CFPropertyList, "com.apple.notificationcenterui" as CFString, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost)


        commitDNDChanges()
    }

    func disableDND(){
        CFPreferencesSetValue("dndStart" as CFString, nil, "com.apple.notificationcenterui" as CFString, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost)

        CFPreferencesSetValue("dndEnd" as CFString, nil, "com.apple.notificationcenterui" as CFString, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost)

        CFPreferencesSetValue("doNotDisturb" as CFString, false as CFPropertyList, "com.apple.notificationcenterui" as CFString, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost)
        commitDNDChanges()
    }

    func commitDNDChanges(){
        CFPreferencesSynchronize("com.apple.notificationcenterui" as CFString, kCFPreferencesCurrentUser, kCFPreferencesCurrentHost)
        DistributedNotificationCenter.default().postNotificationName(NSNotification.Name(rawValue: "com.apple.notificationcenterui.dndprefs_changed"), object: nil, userInfo: nil, deliverImmediately: true)
    }