diff --git a/CMakeLists.txt b/CMakeLists.txt index 3217aaf30f14e..a196dbba5ce67 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,10 @@ endif() project(client) +if(APPLE) + set(CMAKE_OSX_DEPLOYMENT_TARGET "12.0" CACHE STRING "Minimum OSX deployment version") +endif() + include(FeatureSummary) set(CMAKE_XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME YES) diff --git a/NEXTCLOUD.cmake b/NEXTCLOUD.cmake index 40d0f2ff403ca..4b49a32b300b1 100644 --- a/NEXTCLOUD.cmake +++ b/NEXTCLOUD.cmake @@ -77,6 +77,6 @@ if(WIN32) option( BUILD_WIN_TOOLS "Build Win32 migration tools" OFF ) endif() -if (APPLE) +if (APPLE AND CMAKE_OSX_DEPLOYMENT_TARGET VERSION_GREATER_EQUAL 11.0) option( BUILD_FILE_PROVIDER_MODULE "Build the macOS virtual files File Provider module" OFF ) endif() diff --git a/cmake/modules/MacOSXBundleInfo.plist.in b/cmake/modules/MacOSXBundleInfo.plist.in index e0dba2ef62355..8327c22976671 100644 --- a/cmake/modules/MacOSXBundleInfo.plist.in +++ b/cmake/modules/MacOSXBundleInfo.plist.in @@ -5,7 +5,7 @@ NSPrincipalClass NSApplication LSMinimumSystemVersion - 12.0.0 + 10.13.0 LSUIElement CFBundleDevelopmentRegion diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 1968683e23415..85115b0a9c9de 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -282,7 +282,11 @@ endif() IF( APPLE ) list(APPEND client_SRCS cocoainitializer_mac.mm) - list(APPEND client_SRCS systray.mm) + list(APPEND client_SRCS systray_mac_common.mm) + + if (CMAKE_OSX_DEPLOYMENT_TARGET VERSION_GREATER_EQUAL 10.14) + list(APPEND client_SRCS systray_mac_usernotifications.mm) + endif() if (BUILD_FILE_PROVIDER_MODULE) list(APPEND client_SRCS @@ -678,8 +682,10 @@ if (APPLE) if (BUILD_FILE_PROVIDER_MODULE) target_link_libraries(nextcloudCore PUBLIC Qt5::MacExtras "-framework UserNotifications -framework FileProvider") - else() + elseif(CMAKE_OSX_DEPLOYMENT_TARGET VERSION_GREATER_EQUAL 10.14) target_link_libraries(nextcloudCore PUBLIC Qt5::MacExtras "-framework UserNotifications") + else() + target_link_libraries(nextcloudCore PUBLIC Qt5::MacExtras) endif() endif() diff --git a/src/gui/systray.cpp b/src/gui/systray.cpp index 510435ebf1b02..5529964341b52 100644 --- a/src/gui/systray.cpp +++ b/src/gui/systray.cpp @@ -74,7 +74,7 @@ void Systray::setTrayEngine(QQmlApplicationEngine *trayEngine) Systray::Systray() : QSystemTrayIcon(nullptr) { -#if defined(Q_OS_MACOS) && defined(BUILD_OWNCLOUD_OSX_BUNDLE) +#if defined(Q_OS_MACOS) && __MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_14 && defined(BUILD_OWNCLOUD_OSX_BUNDLE) setUserNotificationCenterDelegate(); checkNotificationAuth(MacNotificationAuthorizationOptions::Default); // No provisional auth, ask user explicitly first time registerNotificationCategories(QString(tr("Download"))); @@ -517,7 +517,7 @@ void Systray::showMessage(const QString &title, const QString &message, MessageI QDBusConnection::sessionBus().asyncCall(method); } else #endif -#if defined(Q_OS_MACOS) && defined(BUILD_OWNCLOUD_OSX_BUNDLE) +#if defined(Q_OS_MACOS) && __MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_14 && defined(BUILD_OWNCLOUD_OSX_BUNDLE) if (canOsXSendUserNotification()) { sendOsXUserNotification(title, message); } else @@ -529,7 +529,7 @@ void Systray::showMessage(const QString &title, const QString &message, MessageI void Systray::showUpdateMessage(const QString &title, const QString &message, const QUrl &webUrl) { -#if defined(Q_OS_MACOS) && defined(BUILD_OWNCLOUD_OSX_BUNDLE) +#if defined(Q_OS_MACOS) && __MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_14 && defined(BUILD_OWNCLOUD_OSX_BUNDLE) sendOsXUpdateNotification(title, message, webUrl); #else // TODO: Implement custom notifications (i.e. actionable) for other OSes Q_UNUSED(webUrl); @@ -539,7 +539,7 @@ void Systray::showUpdateMessage(const QString &title, const QString &message, co void Systray::showTalkMessage(const QString &title, const QString &message, const QString &token, const QString &replyTo, const AccountStatePtr &accountState) { -#if defined(Q_OS_MACOS) && defined(BUILD_OWNCLOUD_OSX_BUNDLE) +#if defined(Q_OS_MACOS) && __MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_14 && defined(BUILD_OWNCLOUD_OSX_BUNDLE) sendOsXTalkNotification(title, message, token, replyTo, accountState); #else // TODO: Implement custom notifications (i.e. actionable) for other OSes Q_UNUSED(replyTo) diff --git a/src/gui/systray.h b/src/gui/systray.h index 87592e935dc4d..2056b7175eee7 100644 --- a/src/gui/systray.h +++ b/src/gui/systray.h @@ -40,6 +40,7 @@ class AccessManagerFactory : public QQmlNetworkAccessManagerFactory }; #ifdef Q_OS_MACOS +#if __MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_14 enum MacNotificationAuthorizationOptions { Default = 0, Provisional @@ -52,6 +53,7 @@ bool canOsXSendUserNotification(); void sendOsXUserNotification(const QString &title, const QString &message); void sendOsXUpdateNotification(const QString &title, const QString &message, const QUrl &webUrl); void sendOsXTalkNotification(const QString &title, const QString &message, const QString &token, const QString &replyTo, const AccountStatePtr accountState); +#endif void setTrayWindowLevelAndVisibleOnAllSpaces(QWindow *window); double menuBarThickness(); #endif diff --git a/src/gui/systray_mac_common.mm b/src/gui/systray_mac_common.mm new file mode 100644 index 0000000000000..8189acd10951e --- /dev/null +++ b/src/gui/systray_mac_common.mm @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2023 by Claudio Cambra + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include + +#import + +#include "systray.h" + +Q_LOGGING_CATEGORY(lcMacSystrayCommon, "nextcloud.gui.macsystraycommon") + +namespace OCC { + +double menuBarThickness() +{ + NSMenu * const 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(lcMacSystrayCommon) << "Got nil for main menu. " + << "Going with reasonable menu bar height guess."; + return NSStatusBar.systemStatusBar.thickness; + } + + return mainMenu.menuBarHeight; +} + +void setTrayWindowLevelAndVisibleOnAllSpaces(QWindow *const window) +{ + NSView * const nativeView = (NSView *)window->winId(); + NSWindow * const nativeWindow = (NSWindow *)(nativeView.window); + [nativeWindow setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces | NSWindowCollectionBehaviorIgnoresCycle | + NSWindowCollectionBehaviorTransient]; + [nativeWindow setLevel:NSMainMenuWindowLevel]; +} + +bool osXInDarkMode() +{ + NSString * const osxMode = [NSUserDefaults.standardUserDefaults stringForKey:@"AppleInterfaceStyle"]; + return [osxMode containsString:@"Dark"]; +} + +} diff --git a/src/gui/systray.mm b/src/gui/systray_mac_usernotifications.mm similarity index 78% rename from src/gui/systray.mm rename to src/gui/systray_mac_usernotifications.mm index 707e3269fd429..3d02272c6b312 100644 --- a/src/gui/systray.mm +++ b/src/gui/systray_mac_usernotifications.mm @@ -1,18 +1,32 @@ -#include "QtCore/qurl.h" +/* + * Copyright (C) 2023 by Claudio Cambra + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include +#include +#include + +#import +#import + #include "account.h" #include "accountstate.h" #include "accountmanager.h" #include "config.h" #include "systray.h" #include "tray/talkreply.h" -#include -#include -#include - -#import -#import -Q_LOGGING_CATEGORY(lcMacSystray, "nextcloud.gui.macsystray") +Q_LOGGING_CATEGORY(lcMacSystrayUserNotifications, "nextcloud.gui.macsystrayusernotifications") /************************* Private utility functions *************************/ @@ -21,16 +35,16 @@ void sendTalkReply(UNNotificationResponse *response, UNNotificationContent* content) { if (!response || !content) { - qCWarning(lcMacSystray()) << "Invalid notification response or content." - << "Can't send talk reply."; + qCWarning(lcMacSystrayUserNotifications) << "Invalid notification response or content." + << "Can't send talk reply."; return; } UNTextInputNotificationResponse * const textInputResponse = (UNTextInputNotificationResponse*)response; if (!textInputResponse) { - qCWarning(lcMacSystray()) << "Notification response was not a text input response." - << "Can't send talk reply."; + qCWarning(lcMacSystrayUserNotifications) << "Notification response was not a text input response." + << "Can't send talk reply."; return; } @@ -47,16 +61,16 @@ void sendTalkReply(UNNotificationResponse *response, UNNotificationContent* cont const auto accountState = OCC::AccountManager::instance()->accountFromUserId(qAccount); if (!accountState) { - qCWarning(lcMacSystray()) << "Could not find account matching" << qAccount - << "Can't send talk reply."; + qCWarning(lcMacSystrayUserNotifications) << "Could not find account matching" << qAccount + << "Can't send talk reply."; return; } - qCDebug(lcMacSystray()) << "Sending talk reply from macOS notification." - << "Reply is:" << qReply - << "Replying to:" << qReplyTo - << "Token:" << qToken - << "Account:" << qAccount; + qCDebug(lcMacSystrayUserNotifications) << "Sending talk reply from macOS notification." + << "Reply is:" << qReply + << "Replying to:" << qReplyTo + << "Token:" << qToken + << "Account:" << qAccount; // OCC::TalkReply deletes itself once it's done, fire and forget const auto talkReply = new OCC::TalkReply(accountState.data(), OCC::Systray::instance()); @@ -76,7 +90,7 @@ - (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler { -#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 110000 +#if __MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_11_0 completionHandler(UNNotificationPresentationOptionSound + UNNotificationPresentationOptionBanner); #else completionHandler(UNNotificationPresentationOptionSound + UNNotificationPresentationOptionAlert); @@ -87,13 +101,16 @@ - (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; + qCDebug(lcMacSystrayUserNotifications) << "Received notification with category identifier:" + << response.notification.request.content.categoryIdentifier + << "and action identifier" + << response.actionIdentifier; + UNNotificationContent * const 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."; + qCDebug(lcMacSystrayUserNotifications) << "Opening update download url in browser."; [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:[content.userInfo objectForKey:@"webUrl"]]]; } } else if ([content.categoryIdentifier isEqualToString:@"TALK_MESSAGE"]) { @@ -111,20 +128,6 @@ - (void)userNotificationCenter:(UNUserNotificationCenter *)center namespace OCC { -double menuBarThickness() -{ - NSMenu * const 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() { @@ -181,12 +184,12 @@ void checkNotificationAuth(MacNotificationAuthorizationOptions additionalAuthOpt [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."; + qCDebug(lcMacSystrayUserNotifications) << "Authorization for notifications has been granted, can display notifications."; } else { - qCDebug(lcMacSystray) << "Authorization for notifications not granted."; + qCDebug(lcMacSystrayUserNotifications) << "Authorization for notifications not granted."; if (error) { const auto errorDescription = QString::fromNSString(error.localizedDescription); - qCDebug(lcMacSystray) << "Error from notification center: " << errorDescription; + qCDebug(lcMacSystrayUserNotifications) << "Error from notification center: " << errorDescription; } } }]; @@ -266,19 +269,4 @@ void sendOsXTalkNotification(const QString &title, const QString &message, const [center addNotificationRequest:request withCompletionHandler:nil]; } -void setTrayWindowLevelAndVisibleOnAllSpaces(QWindow *window) -{ - NSView * const nativeView = (NSView *)window->winId(); - NSWindow * const nativeWindow = (NSWindow *)(nativeView.window); - [nativeWindow setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces | NSWindowCollectionBehaviorIgnoresCycle | - NSWindowCollectionBehaviorTransient]; - [nativeWindow setLevel:NSMainMenuWindowLevel]; -} - -bool osXInDarkMode() -{ - NSString * const osxMode = [NSUserDefaults.standardUserDefaults stringForKey:@"AppleInterfaceStyle"]; - return [osxMode containsString:@"Dark"]; -} - } // OCC namespace