nextcloud-desktop/src/gui/systray.mm

190 lines
7.4 KiB
Text
Raw Normal View History

#include "QtCore/qurl.h"
#include "config.h"
#include <QString>
#include <QWindow>
#include <QLoggingCategory>
#import <Cocoa/Cocoa.h>
#import <UserNotifications/UserNotifications.h>
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 menuBarThickness()
{
const NSMenu *mainMenu = [[NSApplication sharedApplication] mainMenu];
if (mainMenu == nil) {
// Return this educated guess if something goes wrong.
// As of macOS 12.4 this will always return 22, even on notched Macbooks.
qCWarning(lcMacSystray) << "Got nil for main menu. Going with reasonable menu bar height guess.";
return [[NSStatusBar systemStatusBar] thickness];
}
return mainMenu.menuBarHeight;
}
// 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"];
}
}