Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backport/6498/stable 3.12 #6534

Merged
merged 7 commits into from
Mar 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion NEXTCLOUD.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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()
2 changes: 1 addition & 1 deletion cmake/modules/MacOSXBundleInfo.plist.in
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>LSMinimumSystemVersion</key>
<string>12.0.0</string>
<string>10.13.0</string>
<key>LSUIElement</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
Expand Down
10 changes: 8 additions & 2 deletions src/gui/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()

Expand Down
8 changes: 4 additions & 4 deletions src/gui/systray.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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")));
Expand Down Expand Up @@ -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
Expand All @@ -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);
Expand All @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions src/gui/systray.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
55 changes: 55 additions & 0 deletions src/gui/systray_mac_common.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright (C) 2023 by Claudio Cambra <[email protected]>
*
* 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 <QWindow>

#import <Cocoa/Cocoa.h>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ clang-diagnostic-error ⚠️
Cocoa/Cocoa.h file not found


#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"];
}

}
98 changes: 43 additions & 55 deletions src/gui/systray.mm → src/gui/systray_mac_usernotifications.mm
Original file line number Diff line number Diff line change
@@ -1,18 +1,32 @@
#include "QtCore/qurl.h"
/*
* Copyright (C) 2023 by Claudio Cambra <[email protected]>
*
* 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 <QLoggingCategory>
#include <QString>
#include <QUrl>

#import <Cocoa/Cocoa.h>
#import <UserNotifications/UserNotifications.h>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ clang-diagnostic-error ⚠️
Cocoa/Cocoa.h file not found


#include "account.h"
#include "accountstate.h"
#include "accountmanager.h"
#include "config.h"
#include "systray.h"
#include "tray/talkreply.h"
#include <QString>
#include <QWindow>
#include <QLoggingCategory>

#import <Cocoa/Cocoa.h>
#import <UserNotifications/UserNotifications.h>

Q_LOGGING_CATEGORY(lcMacSystray, "nextcloud.gui.macsystray")
Q_LOGGING_CATEGORY(lcMacSystrayUserNotifications, "nextcloud.gui.macsystrayusernotifications")

/************************* Private utility functions *************************/

Expand All @@ -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;
}

Expand All @@ -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());
Expand All @@ -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);
Expand All @@ -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"]) {
Expand All @@ -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()
{
Expand Down Expand Up @@ -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;
}
}
}];
Expand Down Expand Up @@ -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
Loading