Ответ 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];
}