#include "QtCore/qurl.h" #include "config.h" #include #include #include #import #import Q_LOGGING_CATEGORY(lcMacSystray, "nextcloud.gui.macsystray") @interface NotificationCenterDelegate : NSObject @end @implementation NotificationCenterDelegate // Always show, even if app is active at the moment. - (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler { #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 110000 completionHandler(UNNotificationPresentationOptionSound + UNNotificationPresentationOptionBanner); #else completionHandler(UNNotificationPresentationOptionSound + UNNotificationPresentationOptionAlert); #endif } - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler { qCDebug(lcMacSystray()) << "Received notification with category identifier:" << response.notification.request.content.categoryIdentifier << "and action identifier" << response.actionIdentifier; UNNotificationContent* content = response.notification.request.content; if ([content.categoryIdentifier isEqualToString:@"UPDATE"]) { if ([response.actionIdentifier isEqualToString:@"DOWNLOAD_ACTION"] || [response.actionIdentifier isEqualToString:UNNotificationDefaultActionIdentifier]) { qCDebug(lcMacSystray()) << "Opening update download url in browser."; [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[content.userInfo objectForKey:@"webUrl"]]]; } } completionHandler(); } @end namespace OCC { enum MacNotificationAuthorizationOptions { Default = 0, Provisional }; double statusBarThickness() { return [NSStatusBar systemStatusBar].thickness; } // TODO: Get this to actually check for permissions bool canOsXSendUserNotification() { UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter]; return center != nil; } void registerNotificationCategories(const QString &localisedDownloadString) { UNNotificationCategory* generalCategory = [UNNotificationCategory categoryWithIdentifier:@"GENERAL" actions:@[] intentIdentifiers:@[] options:UNNotificationCategoryOptionCustomDismissAction]; // Create the custom actions for update notifications. UNNotificationAction* downloadAction = [UNNotificationAction actionWithIdentifier:@"DOWNLOAD_ACTION" title:localisedDownloadString.toNSString() options:UNNotificationActionOptionNone]; // Create the category with the custom actions. UNNotificationCategory* updateCategory = [UNNotificationCategory categoryWithIdentifier:@"UPDATE" actions:@[downloadAction] intentIdentifiers:@[] options:UNNotificationCategoryOptionNone]; [[UNUserNotificationCenter currentNotificationCenter] setNotificationCategories:[NSSet setWithObjects:generalCategory, updateCategory, nil]]; } void checkNotificationAuth(MacNotificationAuthorizationOptions additionalAuthOption = MacNotificationAuthorizationOptions::Provisional) { UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter]; UNAuthorizationOptions authOptions = UNAuthorizationOptionAlert + UNAuthorizationOptionSound; if(additionalAuthOption == MacNotificationAuthorizationOptions::Provisional) { authOptions += UNAuthorizationOptionProvisional; } [center requestAuthorizationWithOptions:(authOptions) completionHandler:^(BOOL granted, NSError * _Nullable error) { // Enable or disable features based on authorization. if(granted) { qCDebug(lcMacSystray) << "Authorization for notifications has been granted, can display notifications."; } else { qCDebug(lcMacSystray) << "Authorization for notifications not granted."; if(error) { QString errorDescription([error.localizedDescription UTF8String]); qCDebug(lcMacSystray) << "Error from notification center: " << errorDescription; } } }]; } void setUserNotificationCenterDelegate() { UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter]; static dispatch_once_t once; dispatch_once(&once, ^{ id delegate = [[NotificationCenterDelegate alloc] init]; [center setDelegate:delegate]; }); } UNMutableNotificationContent* basicNotificationContent(const QString &title, const QString &message) { UNMutableNotificationContent* content = [[UNMutableNotificationContent alloc] init]; content.title = title.toNSString(); content.body = message.toNSString(); content.sound = [UNNotificationSound defaultSound]; return content; } void sendOsXUserNotification(const QString &title, const QString &message) { UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter]; checkNotificationAuth(); UNMutableNotificationContent* content = basicNotificationContent(title, message); content.categoryIdentifier = @"GENERAL"; UNTimeIntervalNotificationTrigger* trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:1 repeats: NO]; UNNotificationRequest* request = [UNNotificationRequest requestWithIdentifier:@"NCUserNotification" content:content trigger:trigger]; [center addNotificationRequest:request withCompletionHandler:nil]; } void sendOsXUpdateNotification(const QString &title, const QString &message, const QUrl &webUrl) { UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter]; checkNotificationAuth(); UNMutableNotificationContent* content = basicNotificationContent(title, message); content.categoryIdentifier = @"UPDATE"; content.userInfo = [NSDictionary dictionaryWithObject:[webUrl.toNSURL() absoluteString] forKey:@"webUrl"]; UNTimeIntervalNotificationTrigger* trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:1 repeats: NO]; UNNotificationRequest* request = [UNNotificationRequest requestWithIdentifier:@"NCUpdateNotification" content:content trigger:trigger]; [center addNotificationRequest:request withCompletionHandler:nil]; } void setTrayWindowLevelAndVisibleOnAllSpaces(QWindow *window) { NSView *nativeView = (NSView *)window->winId(); NSWindow *nativeWindow = (NSWindow *)[nativeView window]; [nativeWindow setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces | NSWindowCollectionBehaviorIgnoresCycle | NSWindowCollectionBehaviorTransient]; [nativeWindow setLevel:NSMainMenuWindowLevel]; } bool osXInDarkMode() { NSString *osxMode = [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleInterfaceStyle"]; return [osxMode containsString:@"Dark"]; } }