From be6aa6e81b44be67518731d67bb2b72268d351e8 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Thu, 3 Sep 2015 18:01:14 +0200 Subject: Initial support for OS X Notification Center This implements basic support for OS X Notification Center notifications with simple banner notifications showing type, contact and message/presence information. A click on the notification opens the chat view to the contact. The Notification Center backend will be used on OS X if the Growl notification backend is not used. This code requires OS X version 10.8 or later. Test-Information: Tested presence and message notifications between multiple Swift accounts and instances on OS X 10.9.5. Change-Id: I7b9e2132cab25e086e0912191562cad8f4cbae38 diff --git a/SwifTools/Notifier/NotificationCenterNotifier.h b/SwifTools/Notifier/NotificationCenterNotifier.h new file mode 100644 index 0000000..0d43c5b --- /dev/null +++ b/SwifTools/Notifier/NotificationCenterNotifier.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#pragma once + +#include + +#include + +namespace Swift { + +/** + * @brief The NotificationCenterNotifier class implmenents the notification interface for the + * OS X Notification Center API. + */ +class NotificationCenterNotifier : public Notifier { +public: + NotificationCenterNotifier(); + virtual ~NotificationCenterNotifier(); + + virtual void showMessage(Type type, const std::string& subject, const std::string& description, const boost::filesystem::path& picture, boost::function callback); + virtual void purgeCallbacks(); + + /** + * @brief The handleUserNotificationActivated is called by the delegate, when a user activates/clicks on a notification. + * @param identifier The std::string UUID identifiying the notification. + */ + void handleUserNotificationActivated(const std::string& identifier); + +private: + class Private; + boost::shared_ptr p; +}; + +} diff --git a/SwifTools/Notifier/NotificationCenterNotifier.mm b/SwifTools/Notifier/NotificationCenterNotifier.mm new file mode 100644 index 0000000..df092ff --- /dev/null +++ b/SwifTools/Notifier/NotificationCenterNotifier.mm @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#include + +#include +#include + +#include + +#include + +#import + +#include +#include + +namespace { + struct Context { + Context(const boost::function& callback) : callback(new boost::function(callback)) { + } + + ~Context() { + delete callback; + } + + boost::function* callback; + }; +} + +namespace Swift { + +class NotificationCenterNotifier::Private { + public: + std::map > callbacksForNotifications; + boost::intrusive_ptr delegate; +}; + +NotificationCenterNotifier::NotificationCenterNotifier() { + p = boost::make_shared(); + p->delegate = boost::intrusive_ptr([[NotificationCenterNotifierDelegate alloc] init], false); + [p->delegate.get() setNotifier: this]; + + [[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate: p->delegate.get()]; +} + +NotificationCenterNotifier::~NotificationCenterNotifier() { + [[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate: nil]; + p->callbacksForNotifications.clear(); +} + +void NotificationCenterNotifier::showMessage(Type type, const std::string& subject, const std::string& description, const boost::filesystem::path& picture, boost::function callback) { + NSUserNotification* notification = [[NSUserNotification alloc] init]; + notification.title = STD2NSSTRING(typeToString(type)); + notification.subtitle = STD2NSSTRING(subject); + notification.informativeText = STD2NSSTRING(description); + notification.contentImage = [[NSImage alloc] initWithContentsOfFile: STD2NSSTRING(picture.string())]; + + // The OS X Notification Center API does not allow to attach custom data, like a pointer to a callback function, + // to the NSUserNotification object. Therefore we maintain a mapping from a NSUserNotification instance's identification + // to their respective callbacks. + notification.identifier = [[NSUUID UUID] UUIDString]; + + /// \todo Currently the elements are only removed on application exit. Ideally the notifications not required anymore + /// are removed from the map; e.g. when visiting a chat view, all notifications from that view can be removed from + /// the map and the NSUserNotificationCenter. + p->callbacksForNotifications[NS2STDSTRING(notification.identifier)] = boost::make_shared(callback); + [[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification]; +} + +void NotificationCenterNotifier::purgeCallbacks() { + p->callbacksForNotifications.clear(); +} + +void NotificationCenterNotifier::handleUserNotificationActivated(const std::string& identifier) { + if (p->callbacksForNotifications.find(identifier) != p->callbacksForNotifications.end()) { + (*p->callbacksForNotifications[identifier]->callback)(); + } + else { + SWIFT_LOG(warning) << "Missing callback entry for activated notification. The activate notification may come from another instance." << std::endl; + } +} + +} diff --git a/SwifTools/Notifier/NotificationCenterNotifierDelegate.h b/SwifTools/Notifier/NotificationCenterNotifierDelegate.h new file mode 100644 index 0000000..ea8fae0 --- /dev/null +++ b/SwifTools/Notifier/NotificationCenterNotifierDelegate.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#pragma once + +#import + +namespace Swift { + class NotificationCenterNotifier; +} + +@interface NotificationCenterNotifierDelegate : NSObject { +} + +@property (nonatomic) Swift::NotificationCenterNotifier* notifier; + +- (void)userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification; + +@end diff --git a/SwifTools/Notifier/NotificationCenterNotifierDelegate.mm b/SwifTools/Notifier/NotificationCenterNotifierDelegate.mm new file mode 100644 index 0000000..617619c --- /dev/null +++ b/SwifTools/Notifier/NotificationCenterNotifierDelegate.mm @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#import "SwifTools/Notifier/NotificationCenterNotifierDelegate.h" + +#include + +#include +#include + +@implementation NotificationCenterNotifierDelegate + +using namespace Swift; + +@synthesize notifier; + +- (void)userNotificationCenter:(NSUserNotificationCenter *) center didActivateNotification:(NSUserNotification *)notification { + (void)center; + std::string identifier = NS2STDSTRING(notification.identifier); + notifier->handleUserNotificationActivated(identifier); +} + +@end diff --git a/SwifTools/Notifier/SConscript b/SwifTools/Notifier/SConscript index 98b5400..e60937b 100644 --- a/SwifTools/Notifier/SConscript +++ b/SwifTools/Notifier/SConscript @@ -11,6 +11,12 @@ if swiftools_env.get("HAVE_GROWL", False) : "GrowlNotifier.mm", "GrowlNotifierDelegate.mm", ] +elif myenv["PLATFORM"] == "darwin" : + sources += [ + "NotificationCenterNotifier.mm", + "NotificationCenterNotifierDelegate.mm", + ] + if swiftools_env.get("HAVE_SNARL", False) : myenv.MergeFlags(myenv["SNARL_FLAGS"]) sources += [ diff --git a/Swift/QtUI/QtSwift.cpp b/Swift/QtUI/QtSwift.cpp index 6d8ac7b..756f530 100644 --- a/Swift/QtUI/QtSwift.cpp +++ b/Swift/QtUI/QtSwift.cpp @@ -56,6 +56,8 @@ #include #elif defined(SWIFTEN_PLATFORM_LINUX) #include +#elif defined(SWIFTEN_PLATFORM_MACOSX) +#include #else #include #endif @@ -196,6 +198,8 @@ QtSwift::QtSwift(const po::variables_map& options) : networkFactories_(&clientMa notifier_ = new WindowsNotifier(SWIFT_APPLICATION_NAME, applicationPathProvider_->getResourcePath("/images/logo-icon-32.png"), systemTray->getQSystemTrayIcon()); #elif defined(SWIFTEN_PLATFORM_LINUX) notifier_ = new FreeDesktopNotifier(SWIFT_APPLICATION_NAME); +#elif defined(SWIFTEN_PLATFORM_MACOSX) + notifier_ = new NotificationCenterNotifier(); #else notifier_ = new NullNotifier(); #endif -- cgit v0.10.2-6-g49f6