From 5dd4d6a96199889a592a9f7987d0542e7877f66e Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Sun, 23 Apr 2017 22:16:19 +1000 Subject: [PATCH 01/22] macOS implementation of notifications in the main process --- atom/browser/api/atom_api_notification.cc | 126 ++++++++++++++++++ atom/browser/api/atom_api_notification.h | 78 +++++++++++ atom/browser/api/atom_api_notification_mac.mm | 97 ++++++++++++++ atom/browser/ui/notification_observer.h | 22 +++ atom/common/node_bindings.cc | 1 + default_app/default_app.js | 16 ++- filenames.gypi | 5 + lib/browser/api/module-list.js | 1 + lib/browser/api/notification.js | 6 + 9 files changed, 351 insertions(+), 1 deletion(-) create mode 100644 atom/browser/api/atom_api_notification.cc create mode 100644 atom/browser/api/atom_api_notification.h create mode 100644 atom/browser/api/atom_api_notification_mac.mm create mode 100644 atom/browser/ui/notification_observer.h create mode 100644 lib/browser/api/notification.js diff --git a/atom/browser/api/atom_api_notification.cc b/atom/browser/api/atom_api_notification.cc new file mode 100644 index 000000000000..4cedaa85d612 --- /dev/null +++ b/atom/browser/api/atom_api_notification.cc @@ -0,0 +1,126 @@ +// Copyright (c) 2014 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "atom/browser/api/atom_api_notification.h" + +#include + +#include "atom/browser/api/atom_api_menu.h" +#include "atom/browser/browser.h" +#include "atom/common/api/atom_api_native_image.h" +#include "atom/common/native_mate_converters/gfx_converter.h" +#include "atom/common/native_mate_converters/image_converter.h" +#include "atom/common/native_mate_converters/string16_converter.h" +#include "atom/common/node_includes.h" +#include "base/threading/thread_task_runner_handle.h" +#include "native_mate/constructor.h" +#include "native_mate/dictionary.h" +#include "ui/gfx/image/image.h" + +namespace atom { + +namespace api { + +int id_counter = 1; +std::map notifications_; + +Notification::Notification(v8::Isolate* isolate, v8::Local wrapper, mate::Arguments* args) { + InitWith(isolate, wrapper); + + mate::Dictionary opts; + if (args->GetNext(&opts)) { + opts.Get("title", &title_); + opts.Get("body", &body_); + has_icon_ = opts.Get("icon", &icon_); + opts.Get("silent", &silent_); + opts.Get("replyPlaceholder", &reply_placeholder_); + opts.Get("hasReply", &has_reply_); + id_ = id_counter++; + } + notifications_[id_] = this; + OnInitialProps(); +} + +Notification::~Notification() {} + +// static +mate::WrappableBase* Notification::New(mate::Arguments* args) { + if (!Browser::Get()->is_ready()) { + args->ThrowError("Cannot create Notification before app is ready"); + return nullptr; + } + return new Notification(args->isolate(), args->GetThis(), args); +} + +bool Notification::HasID(int id) { + return notifications_.find(id) != notifications_.end(); +} + +Notification* Notification::FromID(int id) { + return notifications_[id]; +} + +// Getters +int Notification::GetID() { return id_; } +std::string Notification::GetTitle() { return title_; } +std::string Notification::GetBody() { return body_; } +bool Notification::GetSilent() { return silent_; } +std::string Notification::GetReplyPlaceholder() { return reply_placeholder_; } +bool Notification::GetHasReply() { return has_reply_; } + +// Setters +void Notification::SetTitle(std::string new_title) { title_ = new_title; NotifyPropsUpdated(); } +void Notification::SetBody(std::string new_body) { body_ = new_body; NotifyPropsUpdated(); } +void Notification::SetSilent(bool new_silent) { silent_ = new_silent; NotifyPropsUpdated(); } +void Notification::SetReplyPlaceholder(std::string new_reply_placeholder) { reply_placeholder_ = new_reply_placeholder; NotifyPropsUpdated(); } +void Notification::SetHasReply(bool new_has_reply) { has_reply_ = new_has_reply; NotifyPropsUpdated(); } + +void Notification::OnClicked() { + Emit("click"); +} + +void Notification::OnReplied(std::string reply) { + Emit("reply", reply); +} + +void Notification::OnShown() { + Emit("show"); +} + +// static +void Notification::BuildPrototype(v8::Isolate* isolate, + v8::Local prototype) { + prototype->SetClassName(mate::StringToV8(isolate, "Notification")); + mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate()) + .MakeDestroyable() + .SetMethod("show", &Notification::Show) + .SetProperty("id", &Notification::GetID) + .SetProperty("title", &Notification::GetTitle, &Notification::SetTitle) + .SetProperty("body", &Notification::GetBody, &Notification::SetBody) + .SetProperty("silent", &Notification::GetSilent, &Notification::SetSilent) + .SetProperty("replyPlaceholder", &Notification::GetReplyPlaceholder, &Notification::SetReplyPlaceholder) + .SetProperty("hasReply", &Notification::GetHasReply, &Notification::SetHasReply); +} + +} // namespace api + +} // namespace atom + + +namespace { + +using atom::api::Notification; + +void Initialize(v8::Local exports, v8::Local unused, + v8::Local context, void* priv) { + v8::Isolate* isolate = context->GetIsolate(); + Notification::SetConstructor(isolate, base::Bind(&Notification::New)); + + mate::Dictionary dict(isolate, exports); + dict.Set("Notification", Notification::GetConstructor(isolate)->GetFunction()); +} + +} // namespace + +NODE_MODULE_CONTEXT_AWARE_BUILTIN(atom_common_notification, Initialize) diff --git a/atom/browser/api/atom_api_notification.h b/atom/browser/api/atom_api_notification.h new file mode 100644 index 000000000000..030dee21b49d --- /dev/null +++ b/atom/browser/api/atom_api_notification.h @@ -0,0 +1,78 @@ +// Copyright (c) 2014 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef ATOM_BROWSER_API_ATOM_API_NOTIFICATION_H_ +#define ATOM_BROWSER_API_ATOM_API_NOTIFICATION_H_ + +#include +#include +#include + +#include "atom/browser/api/trackable_object.h" +#include "atom/browser/ui/notification_observer.h" +#include "native_mate/handle.h" +#include "ui/gfx/image/image.h" + +namespace atom { + +namespace api { + +class Notification : public mate::TrackableObject, + public NotifictionObserver { + public: + static mate::WrappableBase* New(mate::Arguments* args); + static bool HasID(int id); + static Notification* FromID(int id); + + static void BuildPrototype(v8::Isolate* isolate, + v8::Local prototype); + + // NotificationObserver: + void OnClicked() override; + void OnReplied(std::string reply) override; + void OnShown() override; + + protected: + Notification(v8::Isolate* isolate, v8::Local wrapper, mate::Arguments* args); + ~Notification() override; + + void OnInitialProps(); + void Show(); + + // Prop Getters + int GetID(); + std::string GetTitle(); + std::string GetBody(); + bool GetSilent(); + std::string GetReplyPlaceholder(); + bool GetHasReply(); + + // Prop Setters + void SetTitle(std::string new_title); + void SetBody(std::string new_body); + void SetSilent(bool new_silent); + void SetReplyPlaceholder(std::string new_reply_placeholder); + void SetHasReply(bool new_has_reply); + + void NotifyPropsUpdated(); + + private: + std::string title_ = ""; + std::string body_ = ""; + gfx::Image icon_; + bool has_icon_ = false; + bool silent_ = false; + std::string reply_placeholder_ = ""; + bool has_reply_ = false; + + int id_; + + DISALLOW_COPY_AND_ASSIGN(Notification); +}; + +} // namespace api + +} // namespace atom + +#endif // ATOM_BROWSER_API_ATOM_API_NOTIFICATION_H_ diff --git a/atom/browser/api/atom_api_notification_mac.mm b/atom/browser/api/atom_api_notification_mac.mm new file mode 100644 index 000000000000..1c3699aa21fa --- /dev/null +++ b/atom/browser/api/atom_api_notification_mac.mm @@ -0,0 +1,97 @@ +// Copyright (c) 2014 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "atom/browser/api/atom_api_notification.h" + +#import + +#include "atom/browser/browser.h" +#include "base/mac/mac_util.h" +#include "base/mac/scoped_nsobject.h" +#include "base/strings/sys_string_conversions.h" + +std::map> native_notifications_; + +@interface AtomNotificationCenter : NSObject { +} +@end + +@implementation AtomNotificationCenter +- (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center + shouldPresentNotification:(NSUserNotification *)notification { + return YES; + } + +- (void)userNotificationCenter:(NSUserNotificationCenter *)center + didActivateNotification:(NSUserNotification *)notification { + int n_id = [[notification.userInfo objectForKey:@"id"] intValue]; + if (atom::api::Notification::HasID(n_id)) { + auto atomNotification = atom::api::Notification::FromID(n_id); + + if (notification.activationType == NSUserNotificationActivationTypeReplied){ + atomNotification->OnReplied([notification.response.string UTF8String]); + } else { + atomNotification->OnClicked(); + } + } + } +@end + +namespace atom { + +namespace api { + +AtomNotificationCenter* del = [[AtomNotificationCenter alloc] init]; +bool set_del_ = false; + +void Notification::Show() { + base::scoped_nsobject notification_ = native_notifications_[id_]; + [NSUserNotificationCenter.defaultUserNotificationCenter + deliverNotification:notification_]; + OnShown(); +} + +void Notification::OnInitialProps() { + if (!set_del_) { + [[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:del]; + set_del_ = true; + } + + base::scoped_nsobject notification_; + notification_.reset([[NSUserNotification alloc] init]); + + native_notifications_[id_] = notification_; + + NotifyPropsUpdated(); +} + +void Notification::NotifyPropsUpdated() { + base::scoped_nsobject notification_ = native_notifications_[id_]; + + [notification_ setTitle:base::SysUTF8ToNSString(title_)]; + [notification_ setInformativeText:base::SysUTF8ToNSString(body_)]; + + NSDictionary * userInfo = [NSMutableDictionary dictionary]; + [userInfo setValue:[NSNumber numberWithInt:id_] forKey:@"id"]; + [notification_ setUserInfo:userInfo]; + + if ([notification_ respondsToSelector:@selector(setContentImage:)] && has_icon_) { + [notification_ setContentImage:icon_.AsNSImage()]; + } + + if (has_reply_) { + [notification_ setResponsePlaceholder:base::SysUTF8ToNSString(reply_placeholder_)]; + [notification_ setHasReplyButton:true]; + } + + if (silent_) { + [notification_ setSoundName:nil]; + } else { + [notification_ setSoundName:NSUserNotificationDefaultSoundName]; + } +} + +} // namespace api + +} // namespace atom \ No newline at end of file diff --git a/atom/browser/ui/notification_observer.h b/atom/browser/ui/notification_observer.h new file mode 100644 index 000000000000..8619e5d3609b --- /dev/null +++ b/atom/browser/ui/notification_observer.h @@ -0,0 +1,22 @@ +// Copyright (c) 2014 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef ATOM_BROWSER_UI_NOTIFICATION_OBSERVER_H_ +#define ATOM_BROWSER_UI_NOTIFICATION_OBSERVER_H_ + +namespace atom { + +class NotifictionObserver { + public: + virtual void OnClicked() {} + virtual void OnReplied(std::string reply) {} + virtual void OnShown() {} + + protected: + virtual ~NotifictionObserver() {} +}; + +} // namespace atom + +#endif // ATOM_BROWSER_UI_NOTIFICATION_OBSERVER_H_ diff --git a/atom/common/node_bindings.cc b/atom/common/node_bindings.cc index 2a7be5762769..03e96ae8a4f3 100644 --- a/atom/common/node_bindings.cc +++ b/atom/common/node_bindings.cc @@ -56,6 +56,7 @@ REFERENCE_MODULE(atom_common_asar); REFERENCE_MODULE(atom_common_clipboard); REFERENCE_MODULE(atom_common_crash_reporter); REFERENCE_MODULE(atom_common_native_image); +REFERENCE_MODULE(atom_common_notification); REFERENCE_MODULE(atom_common_screen); REFERENCE_MODULE(atom_common_shell); REFERENCE_MODULE(atom_common_v8_util); diff --git a/default_app/default_app.js b/default_app/default_app.js index 2a3ce3a85ec1..446c159b6c1a 100644 --- a/default_app/default_app.js +++ b/default_app/default_app.js @@ -1,4 +1,4 @@ -const {app, BrowserWindow} = require('electron') +const {app, BrowserWindow, Notification} = require('electron') const path = require('path') let mainWindow = null @@ -27,5 +27,19 @@ exports.load = (appUrl) => { mainWindow = new BrowserWindow(options) mainWindow.loadURL(appUrl) mainWindow.focus() + + const n = new Notification({ + title: 'Hello World', + body: 'This is the long and complicated body for this notification that just goes on and on and on and never really seems to stop', + silent: true, + icon: '/Users/samuel/Downloads/ninja.png', + hasReply: true, + replyPlacehodler: 'Type Here!!' + }); + n.on('show', () => console.log('showed')); + n.on('click', () => console.info('clicked!!')); + n.on('reply', (e, reply) => console.log('Replied:', reply)); + + n.show(); }) } diff --git a/filenames.gypi b/filenames.gypi index 63b46cb40e57..e670f3c8f0da 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -26,6 +26,7 @@ 'lib/browser/api/module-list.js', 'lib/browser/api/navigation-controller.js', 'lib/browser/api/net.js', + 'lib/browser/api/notification.js', 'lib/browser/api/power-monitor.js', 'lib/browser/api/power-save-blocker.js', 'lib/browser/api/protocol.js', @@ -127,6 +128,9 @@ 'atom/browser/api/atom_api_menu_views.h', 'atom/browser/api/atom_api_net.cc', 'atom/browser/api/atom_api_net.h', + 'atom/browser/api/atom_api_notification.cc', + 'atom/browser/api/atom_api_notification.h', + 'atom/browser/api/atom_api_notification_mac.mm', 'atom/browser/api/atom_api_power_monitor.cc', 'atom/browser/api/atom_api_power_monitor.h', 'atom/browser/api/atom_api_power_save_blocker.cc', @@ -310,6 +314,7 @@ 'atom/browser/ui/message_box_gtk.cc', 'atom/browser/ui/message_box_mac.mm', 'atom/browser/ui/message_box_win.cc', + 'atom/browser/ui/notification_observer.h', 'atom/browser/ui/tray_icon.cc', 'atom/browser/ui/tray_icon.h', 'atom/browser/ui/tray_icon_gtk.cc', diff --git a/lib/browser/api/module-list.js b/lib/browser/api/module-list.js index 64b2829064b6..d8b20c5bec19 100644 --- a/lib/browser/api/module-list.js +++ b/lib/browser/api/module-list.js @@ -11,6 +11,7 @@ module.exports = [ {name: 'Menu', file: 'menu'}, {name: 'MenuItem', file: 'menu-item'}, {name: 'net', file: 'net'}, + {name: 'Notification', file: 'notification'}, {name: 'powerMonitor', file: 'power-monitor'}, {name: 'powerSaveBlocker', file: 'power-save-blocker'}, {name: 'protocol', file: 'protocol'}, diff --git a/lib/browser/api/notification.js b/lib/browser/api/notification.js new file mode 100644 index 000000000000..953f7d8d4987 --- /dev/null +++ b/lib/browser/api/notification.js @@ -0,0 +1,6 @@ +const {EventEmitter} = require('events') +const {Notification} = process.atomBinding('notification') + +Object.setPrototypeOf(Notification.prototype, EventEmitter.prototype) + +module.exports = Notification From 7c38633d1e6c1062b87696d6c2ab7b048741b684 Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Mon, 24 Apr 2017 00:18:31 +1000 Subject: [PATCH 02/22] Initial semi-working windows implementation --- atom/browser/api/atom_api_notification.cc | 15 +- atom/browser/api/atom_api_notification.h | 19 +- atom/browser/api/atom_api_notification_mac.mm | 6 +- atom/browser/api/atom_api_notification_win.cc | 48 ++ atom/browser/ui/win/toast_handler.cc | 28 + atom/browser/ui/win/toast_handler.h | 25 + atom/browser/ui/win/toast_lib.cc | 537 ++++++++++++++++++ atom/browser/ui/win/toast_lib.h | 168 ++++++ filenames.gypi | 5 + 9 files changed, 833 insertions(+), 18 deletions(-) create mode 100644 atom/browser/api/atom_api_notification_win.cc create mode 100644 atom/browser/ui/win/toast_handler.cc create mode 100644 atom/browser/ui/win/toast_handler.h create mode 100644 atom/browser/ui/win/toast_lib.cc create mode 100644 atom/browser/ui/win/toast_lib.h diff --git a/atom/browser/api/atom_api_notification.cc b/atom/browser/api/atom_api_notification.cc index 4cedaa85d612..9812852ce623 100644 --- a/atom/browser/api/atom_api_notification.cc +++ b/atom/browser/api/atom_api_notification.cc @@ -33,6 +33,9 @@ Notification::Notification(v8::Isolate* isolate, v8::Local wrapper, opts.Get("title", &title_); opts.Get("body", &body_); has_icon_ = opts.Get("icon", &icon_); + if (has_icon_) { + opts.Get("icon", &icon_path_); + } opts.Get("silent", &silent_); opts.Get("replyPlaceholder", &reply_placeholder_); opts.Get("hasReply", &has_reply_); @@ -63,17 +66,17 @@ Notification* Notification::FromID(int id) { // Getters int Notification::GetID() { return id_; } -std::string Notification::GetTitle() { return title_; } -std::string Notification::GetBody() { return body_; } +base::string16 Notification::GetTitle() { return title_; } +base::string16 Notification::GetBody() { return body_; } bool Notification::GetSilent() { return silent_; } -std::string Notification::GetReplyPlaceholder() { return reply_placeholder_; } +base::string16 Notification::GetReplyPlaceholder() { return reply_placeholder_; } bool Notification::GetHasReply() { return has_reply_; } // Setters -void Notification::SetTitle(std::string new_title) { title_ = new_title; NotifyPropsUpdated(); } -void Notification::SetBody(std::string new_body) { body_ = new_body; NotifyPropsUpdated(); } +void Notification::SetTitle(base::string16 new_title) { title_ = new_title; NotifyPropsUpdated(); } +void Notification::SetBody(base::string16 new_body) { body_ = new_body; NotifyPropsUpdated(); } void Notification::SetSilent(bool new_silent) { silent_ = new_silent; NotifyPropsUpdated(); } -void Notification::SetReplyPlaceholder(std::string new_reply_placeholder) { reply_placeholder_ = new_reply_placeholder; NotifyPropsUpdated(); } +void Notification::SetReplyPlaceholder(base::string16 new_reply_placeholder) { reply_placeholder_ = new_reply_placeholder; NotifyPropsUpdated(); } void Notification::SetHasReply(bool new_has_reply) { has_reply_ = new_has_reply; NotifyPropsUpdated(); } void Notification::OnClicked() { diff --git a/atom/browser/api/atom_api_notification.h b/atom/browser/api/atom_api_notification.h index 030dee21b49d..fcfc86701486 100644 --- a/atom/browser/api/atom_api_notification.h +++ b/atom/browser/api/atom_api_notification.h @@ -42,28 +42,29 @@ class Notification : public mate::TrackableObject, // Prop Getters int GetID(); - std::string GetTitle(); - std::string GetBody(); + base::string16 GetTitle(); + base::string16 GetBody(); bool GetSilent(); - std::string GetReplyPlaceholder(); + base::string16 GetReplyPlaceholder(); bool GetHasReply(); // Prop Setters - void SetTitle(std::string new_title); - void SetBody(std::string new_body); + void SetTitle(base::string16 new_title); + void SetBody(base::string16 new_body); void SetSilent(bool new_silent); - void SetReplyPlaceholder(std::string new_reply_placeholder); + void SetReplyPlaceholder(base::string16 new_reply_placeholder); void SetHasReply(bool new_has_reply); void NotifyPropsUpdated(); private: - std::string title_ = ""; - std::string body_ = ""; + base::string16 title_ = L""; + base::string16 body_ = L""; gfx::Image icon_; + base::string16 icon_path_ = L""; bool has_icon_ = false; bool silent_ = false; - std::string reply_placeholder_ = ""; + base::string16 reply_placeholder_ = L""; bool has_reply_ = false; int id_; diff --git a/atom/browser/api/atom_api_notification_mac.mm b/atom/browser/api/atom_api_notification_mac.mm index 1c3699aa21fa..c56955da2e02 100644 --- a/atom/browser/api/atom_api_notification_mac.mm +++ b/atom/browser/api/atom_api_notification_mac.mm @@ -69,8 +69,8 @@ void Notification::OnInitialProps() { void Notification::NotifyPropsUpdated() { base::scoped_nsobject notification_ = native_notifications_[id_]; - [notification_ setTitle:base::SysUTF8ToNSString(title_)]; - [notification_ setInformativeText:base::SysUTF8ToNSString(body_)]; + [notification_ setTitle:base::SysUTF16ToNSString(title_)]; + [notification_ setInformativeText:base::SysUTF16ToNSString(body_)]; NSDictionary * userInfo = [NSMutableDictionary dictionary]; [userInfo setValue:[NSNumber numberWithInt:id_] forKey:@"id"]; @@ -81,7 +81,7 @@ void Notification::NotifyPropsUpdated() { } if (has_reply_) { - [notification_ setResponsePlaceholder:base::SysUTF8ToNSString(reply_placeholder_)]; + [notification_ setResponsePlaceholder:base::SysUTF16ToNSString(reply_placeholder_)]; [notification_ setHasReplyButton:true]; } diff --git a/atom/browser/api/atom_api_notification_win.cc b/atom/browser/api/atom_api_notification_win.cc new file mode 100644 index 000000000000..fb27fb6e7a55 --- /dev/null +++ b/atom/browser/api/atom_api_notification_win.cc @@ -0,0 +1,48 @@ +// Copyright (c) 2014 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "atom/browser/api/atom_api_notification.h" + +#include "atom/browser/ui/win/toast_handler.h" +#include "atom/browser/ui/win/toast_lib.h" +#include "base/strings/utf_string_conversions.h" +#include "common/string_conversion.h" + +namespace atom { + +namespace api { + +bool can_toast_ = true; +bool initialized_ = false; + +void Notification::Show() { + atom::AtomToastHandler* handler = new atom::AtomToastHandler(this); + WinToastLib::WinToastTemplate toast = WinToastLib::WinToastTemplate(WinToastLib::WinToastTemplate::TextTwoLines); + // toast.setImagePath(L"C:\example.png"); + toast.setTextField(title_, WinToastLib::WinToastTemplate::TextField::FirstLine); + toast.setTextField(body_, WinToastLib::WinToastTemplate::TextField::SecondLine); + toast.setSilent(silent_); + + WinToastLib::WinToast::instance()->showToast(toast, handler); + + OnShown(); +} + +void Notification::OnInitialProps() { + if (!initialized_) { + WinToastLib::WinToast::instance()->setAppName(L"WinToastExample"); + WinToastLib::WinToast::instance()->setAppUserModelId( + WinToastLib::WinToast::configureAUMI(L"mohabouje", L"wintoast", L"wintoastexample", L"20161006") + ); + can_toast_ = WinToastLib::WinToast::instance()->initialize(); + } +} + +void Notification::NotifyPropsUpdated() { + +} + +} + +} \ No newline at end of file diff --git a/atom/browser/ui/win/toast_handler.cc b/atom/browser/ui/win/toast_handler.cc new file mode 100644 index 000000000000..5108c630208c --- /dev/null +++ b/atom/browser/ui/win/toast_handler.cc @@ -0,0 +1,28 @@ +// Copyright (c) 2014 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "atom/browser/ui/win/toast_handler.h" + +#include "atom/browser/ui/win/toast_lib.h" +#include "atom/browser/api/atom_api_notification.h" + +namespace atom { + +AtomToastHandler::AtomToastHandler(atom::api::Notification* target) { + observer_ = target; +} + +void AtomToastHandler::toastActivated() { + observer_->OnClicked(); +}; + +void AtomToastHandler::toastDismissed(WinToastLib::WinToastHandler::WinToastDismissalReason state) { + // observer_->OnDismissed(); +}; + +void AtomToastHandler::toastFailed() { + // observer_->OnErrored(); +}; + +} diff --git a/atom/browser/ui/win/toast_handler.h b/atom/browser/ui/win/toast_handler.h new file mode 100644 index 000000000000..d88c21d32dc0 --- /dev/null +++ b/atom/browser/ui/win/toast_handler.h @@ -0,0 +1,25 @@ +// Copyright (c) 2014 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "atom/browser/ui/win/toast_lib.h" +#include "atom/browser/api/atom_api_notification.h" + +#ifndef ATOM_BROWSER_UI_TOAST_HANDLER_H_ +#define ATOM_BROWSER_UI_TOAST_HANDLER_H_ + +namespace atom { + +class AtomToastHandler : public WinToastLib::WinToastHandler { + public: + atom::api::Notification* observer_; + AtomToastHandler(atom::api::Notification* target); + + void toastActivated() override; + void toastDismissed(WinToastLib::WinToastHandler::WinToastDismissalReason state); + void toastFailed(); +}; + +} + +#endif // ATOM_BROWSER_UI_TOAST_HANDLER_H_ diff --git a/atom/browser/ui/win/toast_lib.cc b/atom/browser/ui/win/toast_lib.cc new file mode 100644 index 000000000000..3fdd657a26cd --- /dev/null +++ b/atom/browser/ui/win/toast_lib.cc @@ -0,0 +1,537 @@ +// MIT License + +// Copyright (c) 2016 Mohammed Boujemaoui Boulaghmoudi + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include "toast_lib.h" +#include "browser/win/scoped_hstring.h" +#pragma comment(lib,"shlwapi") +#pragma comment(lib,"user32") + +using namespace WinToastLib; +namespace DllImporter { + + // Function load a function from library + template + HRESULT loadFunctionFromLibrary(HINSTANCE library, LPCSTR name, Function &func) { + if (!library) + return false; + + func = reinterpret_cast(GetProcAddress(library, name)); + return (func != nullptr) ? S_OK : E_FAIL; + } + + typedef HRESULT(FAR STDAPICALLTYPE *f_SetCurrentProcessExplicitAppUserModelID)(__in PCWSTR AppID); + typedef HRESULT(FAR STDAPICALLTYPE *f_PropVariantToString)(_In_ REFPROPVARIANT propvar, _Out_writes_(cch) PWSTR psz, _In_ UINT cch); + typedef HRESULT(FAR STDAPICALLTYPE *f_RoGetActivationFactory)(_In_ HSTRING activatableClassId, _In_ REFIID iid, _COM_Outptr_ void ** factory); + typedef HRESULT(FAR STDAPICALLTYPE *f_WindowsCreateStringReference)(_In_reads_opt_(length + 1) PCWSTR sourceString, UINT32 length, _Out_ HSTRING_HEADER * hstringHeader, _Outptr_result_maybenull_ _Result_nullonfailure_ HSTRING * string); + typedef HRESULT(FAR STDAPICALLTYPE *f_WindowsDeleteString)(_In_opt_ HSTRING string); + + f_SetCurrentProcessExplicitAppUserModelID SetCurrentProcessExplicitAppUserModelID; + f_PropVariantToString PropVariantToString; + f_RoGetActivationFactory RoGetActivationFactory; + f_WindowsCreateStringReference WindowsCreateStringReference; + f_WindowsDeleteString WindowsDeleteString; + + + template + _Check_return_ __inline HRESULT _1_GetActivationFactory(_In_ HSTRING activatableClassId, _COM_Outptr_ T** factory) { + return RoGetActivationFactory(activatableClassId, IID_INS_ARGS(factory)); + } + + template + inline HRESULT Wrap_GetActivationFactory(_In_ HSTRING activatableClassId, _Inout_ Details::ComPtrRef factory) throw() { + return _1_GetActivationFactory(activatableClassId, factory.ReleaseAndGetAddressOf()); + } + + inline HRESULT initialize() { + HINSTANCE LibShell32 = LoadLibrary(L"SHELL32.DLL"); + HRESULT hr = loadFunctionFromLibrary(LibShell32, "SetCurrentProcessExplicitAppUserModelID", SetCurrentProcessExplicitAppUserModelID); + if (SUCCEEDED(hr)) { + HINSTANCE LibPropSys = LoadLibrary(L"PROPSYS.DLL"); + hr = loadFunctionFromLibrary(LibPropSys, "PropVariantToString", PropVariantToString); + if (SUCCEEDED(hr)) { + HINSTANCE LibComBase = LoadLibrary(L"COMBASE.DLL"); + return SUCCEEDED(loadFunctionFromLibrary(LibComBase, "RoGetActivationFactory", RoGetActivationFactory)) + && SUCCEEDED(loadFunctionFromLibrary(LibComBase, "WindowsCreateStringReference", WindowsCreateStringReference)) + && SUCCEEDED(loadFunctionFromLibrary(LibComBase, "WindowsDeleteString", WindowsDeleteString)); + } + } + return hr; + } +} + +namespace Util { + inline HRESULT defaultExecutablePath(_In_ WCHAR* path, _In_ DWORD nSize = MAX_PATH) { + DWORD written = GetModuleFileNameEx(GetCurrentProcess(), nullptr, path, nSize); + return (written > 0) ? S_OK : E_FAIL; + } + + + inline HRESULT defaultShellLinksDirectory(_In_ WCHAR* path, _In_ DWORD nSize = MAX_PATH) { + DWORD written = GetEnvironmentVariable(L"APPDATA", path, nSize); + HRESULT hr = written > 0 ? S_OK : E_INVALIDARG; + if (SUCCEEDED(hr)) { + errno_t result = wcscat_s(path, nSize, DEFAULT_SHELL_LINKS_PATH); + hr = (result == 0) ? S_OK : E_INVALIDARG; + } + return hr; + } + + inline HRESULT defaultShellLinkPath(const std::wstring& appname, _In_ WCHAR* path, _In_ DWORD nSize = MAX_PATH) { + HRESULT hr = defaultShellLinksDirectory(path, nSize); + if (SUCCEEDED(hr)) { + const std::wstring appLink(appname + DEFAULT_LINK_FORMAT); + errno_t result = wcscat_s(path, nSize, appLink.c_str()); + hr = (result == 0) ? S_OK : E_INVALIDARG; + } + return hr; + } + + + inline HRESULT setNodeStringValue(const std::wstring& string, IXmlNode *node, IXmlDocument *xml) { + ComPtr textNode; + HRESULT hr = xml->CreateTextNode( WinToastStringWrapper(string).Get(), &textNode); + if (SUCCEEDED(hr)) { + ComPtr stringNode; + hr = textNode.As(&stringNode); + if (SUCCEEDED(hr)) { + ComPtr appendedChild; + hr = node->AppendChild(stringNode.Get(), &appendedChild); + } + } + return hr; + } + + inline HRESULT setEventHandlers(_In_ IToastNotification* notification, _In_ WinToastHandler* eventHandler) { + EventRegistrationToken activatedToken, dismissedToken, failedToken; + HRESULT hr = notification->add_Activated( + Callback < Implements < RuntimeClassFlags, + ITypedEventHandler> >( + [eventHandler](IToastNotification*, IInspectable*) + { + eventHandler->toastActivated(); + return S_OK; + }).Get(), &activatedToken); + + if (SUCCEEDED(hr)) { + hr = notification->add_Dismissed(Callback < Implements < RuntimeClassFlags, + ITypedEventHandler> >( + [eventHandler](IToastNotification*, IToastDismissedEventArgs* e) + { + ToastDismissalReason reason; + if (SUCCEEDED(e->get_Reason(&reason))) + { + eventHandler->toastDismissed(static_cast(reason)); + } + return S_OK; + }).Get(), &dismissedToken); + if (SUCCEEDED(hr)) { + hr = notification->add_Failed(Callback < Implements < RuntimeClassFlags, + ITypedEventHandler> >( + [eventHandler](IToastNotification*, IToastFailedEventArgs*) + { + eventHandler->toastFailed(); + return S_OK; + }).Get(), &failedToken); + } + } + return hr; + } +} + +WinToast* WinToast::_instance = nullptr; +WinToast* WinToast::instance() { + if (_instance == nullptr) { + _instance = new WinToast(); + } + return _instance; +} + +WinToast::WinToast() : _isInitialized(false) +{ + DllImporter::initialize(); +} + +void WinToast::setAppName(_In_ const std::wstring& appName) { + _appName = appName; +} + +std::wstring WinToast::appName() const { + return _appName; +} + +std::wstring WinToast::appUserModelId() const { + return _aumi; +} +void WinToast::setAppUserModelId(_In_ const std::wstring& aumi) { + _aumi = aumi; +} + +bool WinToast::isCompatible() { + return !((DllImporter::SetCurrentProcessExplicitAppUserModelID == nullptr) + || (DllImporter::PropVariantToString == nullptr) + || (DllImporter::RoGetActivationFactory == nullptr) + || (DllImporter::WindowsCreateStringReference == nullptr) + || (DllImporter::WindowsDeleteString == nullptr)); +} + +std::wstring WinToast::configureAUMI(_In_ const std::wstring &company, + _In_ const std::wstring &name, + _In_ const std::wstring &surname, + _In_ const std::wstring &versionInfo) +{ + std::wstring aumi = company; + aumi += L"." + name; + aumi += L"." + surname; + aumi += L"." + versionInfo; + + return aumi; +} + +bool WinToast::initialize() { + if (_aumi.empty() || _appName.empty()) { + return _isInitialized = false; + } + + if (!isCompatible()) { + return _isInitialized = false; + } + + if (FAILED(DllImporter::SetCurrentProcessExplicitAppUserModelID(_aumi.c_str()))) { + return _isInitialized = false; + } + + + HRESULT hr = validateShellLink(); + if (FAILED(hr)) { + hr = createShellLink(); + } + + if (SUCCEEDED(hr)) { + hr = DllImporter::Wrap_GetActivationFactory(WinToastStringWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(), &_notificationManager); + if (SUCCEEDED(hr)) { + hr = notificationManager()->CreateToastNotifierWithId(WinToastStringWrapper(_aumi).Get(), &_notifier); + if (SUCCEEDED(hr)) { + hr = DllImporter::Wrap_GetActivationFactory(WinToastStringWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotification).Get(), &_notificationFactory); + } + } + } + + return _isInitialized = SUCCEEDED(hr); +} + +HRESULT WinToast::validateShellLink() { + + WCHAR _path[MAX_PATH]; + Util::defaultShellLinkPath(_appName, _path); + // Check if the file exist + DWORD attr = GetFileAttributes(_path); + if (attr >= 0xFFFFFFF) { + std::wcout << "Error, shell link not found. Try to create a new one in: " << _path << std::endl; + return E_FAIL; + } + + // Let's load the file as shell link to validate. + // - Create a shell link + // - Create a persistant file + // - Load the path as data for the persistant file + // - Read the property AUMI and validate with the current + // - Review if AUMI is equal. + ComPtr shellLink; + HRESULT hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink)); + if (SUCCEEDED(hr)) { + ComPtr persistFile; + hr = shellLink.As(&persistFile); + if (SUCCEEDED(hr)) { + hr = persistFile->Load(_path, STGM_READWRITE); + if (SUCCEEDED(hr)) { + ComPtr propertyStore; + hr = shellLink.As(&propertyStore); + if (SUCCEEDED(hr)) { + PROPVARIANT appIdPropVar; + hr = propertyStore->GetValue(PKEY_AppUserModel_ID, &appIdPropVar); + if (SUCCEEDED(hr)) { + WCHAR AUMI[MAX_PATH]; + hr = DllImporter::PropVariantToString(appIdPropVar, AUMI, MAX_PATH); + if (SUCCEEDED(hr)) { + hr = (_aumi == AUMI) ? S_OK : E_FAIL; + } else { // AUMI Changed for the same app, let's update the current value! =) + PropVariantClear(&appIdPropVar); + hr = InitPropVariantFromString(_aumi.c_str(), &appIdPropVar); + if (SUCCEEDED(hr)) { + hr = propertyStore->SetValue(PKEY_AppUserModel_ID, appIdPropVar); + if (SUCCEEDED(hr)) { + hr = propertyStore->Commit(); + if (SUCCEEDED(hr) && SUCCEEDED(persistFile->IsDirty())) { + hr = persistFile->Save(_path, TRUE); + } + } + } + } + PropVariantClear(&appIdPropVar); + } + } + } + } + } + return hr; +} + + + +HRESULT WinToast::createShellLink() { + WCHAR exePath[MAX_PATH]; + WCHAR slPath[MAX_PATH]; + Util::defaultShellLinkPath(_appName, slPath); + Util::defaultExecutablePath(exePath); + ComPtr shellLink; + HRESULT hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink)); + if (SUCCEEDED(hr)) { + hr = shellLink->SetPath(exePath); + if (SUCCEEDED(hr)) { + hr = shellLink->SetArguments(L""); + if (SUCCEEDED(hr)) { + hr = shellLink->SetWorkingDirectory(exePath); + if (SUCCEEDED(hr)) { + ComPtr propertyStore; + hr = shellLink.As(&propertyStore); + if (SUCCEEDED(hr)) { + PROPVARIANT appIdPropVar; + hr = InitPropVariantFromString(_aumi.c_str(), &appIdPropVar); + if (SUCCEEDED(hr)) { + hr = propertyStore->SetValue(PKEY_AppUserModel_ID, appIdPropVar); + if (SUCCEEDED(hr)) { + hr = propertyStore->Commit(); + if (SUCCEEDED(hr)) { + ComPtr persistFile; + hr = shellLink.As(&persistFile); + if (SUCCEEDED(hr)) { + hr = persistFile->Save(slPath, TRUE); + } + } + } + } + PropVariantClear(&appIdPropVar); + } + } + } + } + } + CoTaskMemFree(exePath); + CoTaskMemFree(slPath); + return hr; +} + + + +bool WinToast::showToast(_In_ const WinToastTemplate& toast, _In_ WinToastHandler* handler) { + if (!isInitialized()) { + return _isInitialized; + } + + HRESULT hr = _notificationManager->GetTemplateContent(ToastTemplateType(toast.type()), &_xmlDocument); + if (SUCCEEDED(hr)) { + const int fieldsCount = toast.textFieldsCount(); + for (int i = 0; i < fieldsCount && SUCCEEDED(hr); i++) { + hr = setTextField(toast.textField(WinToastTemplate::TextField(i)), i); + } + if (makeSilent(toast.isSilent())) { + if (SUCCEEDED(hr)) { + if (SUCCEEDED(hr)) { + hr = toast.hasImage() ? setImageField(toast.imagePath()) : hr; + if (SUCCEEDED(hr)) { + hr = _notificationFactory->CreateToastNotification(xmlDocument(), &_notification); + if (SUCCEEDED(hr)) { + hr = Util::setEventHandlers(notification(), handler); + if (SUCCEEDED(hr)) { + hr = _notifier->Show(notification()); + } + } + } + } + } + } else { + return false; + } + } + + return SUCCEEDED(hr); +} + + +HRESULT WinToast::setTextField(_In_ const std::wstring& text, _In_ int pos) { + ComPtr nodeList; + HRESULT hr = _xmlDocument->GetElementsByTagName(WinToastStringWrapper(L"text").Get(), &nodeList); + if (SUCCEEDED(hr)) { + ComPtr node; + hr = nodeList->Item(pos, &node); + if (SUCCEEDED(hr)) { + hr = Util::setNodeStringValue(text, node.Get(), xmlDocument()); + } + } + return hr; +} + +bool WinToast::makeSilent(bool is_silent) { + if (!is_silent) return true; + ScopedHString tag(L"toast"); + if (!tag.success()) + return false; + + ComPtr node_list; + if (FAILED(xmlDocument()->GetElementsByTagName(tag, &node_list))) + return false; + + ComPtr root; + if (FAILED(node_list->Item(0, &root))) + return false; + + ComPtr audio_element; + ScopedHString audio_str(L"audio"); + if (FAILED(xmlDocument()->CreateElement(audio_str, &audio_element))) + return false; + + ComPtr audio_node_tmp; + if (FAILED(audio_element.As(&audio_node_tmp))) + return false; + + // Append audio node to toast xml + ComPtr audio_node; + if (FAILED(root->AppendChild(audio_node_tmp.Get(), &audio_node))) + return false; + + // Create silent attribute + ComPtr attributes; + if (FAILED(audio_node->get_Attributes(&attributes))) + return false; + + ComPtr silent_attribute; + ScopedHString silent_str(L"silent"); + if (FAILED(xmlDocument()->CreateAttribute(silent_str, &silent_attribute))) + return false; + + ComPtr silent_attribute_node; + if (FAILED(silent_attribute.As(&silent_attribute_node))) + return false; + + // Set silent attribute to true + ScopedHString silent_value(L"true"); + if (!silent_value.success()) + return false; + + ComPtr silent_text; + if (FAILED(xmlDocument()->CreateTextNode(silent_value, &silent_text))) + return false; + + ComPtr silent_node; + if (FAILED(silent_text.As(&silent_node))) + return false; + + ComPtr child_node; + if (FAILED( + silent_attribute_node->AppendChild(silent_node.Get(), &child_node))) + return false; + + ComPtr silent_attribute_pnode; + return SUCCEEDED(attributes.Get()->SetNamedItem(silent_attribute_node.Get(), + &silent_attribute_pnode)); +} + + +HRESULT WinToast::setImageField(_In_ const std::wstring& path) { + wchar_t imagePath[MAX_PATH] = L"file:///"; + HRESULT hr = StringCchCat(imagePath, MAX_PATH, path.c_str()); + if (SUCCEEDED(hr)) { + ComPtr nodeList; + hr = _xmlDocument->GetElementsByTagName(WinToastStringWrapper(L"image").Get(), &nodeList); + if (SUCCEEDED(hr)) { + ComPtr node; + hr = nodeList->Item(0, &node); + if (SUCCEEDED(hr)) { + ComPtr attributes; + hr = node->get_Attributes(&attributes); + if (SUCCEEDED(hr)) { + ComPtr editedNode; + hr = attributes->GetNamedItem(WinToastStringWrapper(L"src").Get(), &editedNode); + if (SUCCEEDED(hr)) { + Util::setNodeStringValue(imagePath, editedNode.Get(), xmlDocument()); + } + } + } + } + } + return hr; +} + +WinToastTemplate::WinToastTemplate(const WinToastTemplateType& type) : + _type(type) +{ + initComponentsFromType(); +} + + +WinToastTemplate::~WinToastTemplate() +{ + _textFields.clear(); +} + +void WinToastTemplate::setTextField(_In_ const std::wstring& txt, _In_ const WinToastTemplate::TextField& pos) { + _textFields[pos] = txt; +} +void WinToastTemplate::setImagePath(_In_ const std::wstring& imgPath) { + if (!_hasImage) + return; + _imagePath = imgPath; +} + +void WinToastTemplate::setSilent(bool is_silent) { + _silent = is_silent; +} + +int WinToastTemplate::TextFieldsCount[WinToastTemplateTypeCount] = { 1, 2, 2, 3, 1, 2, 2, 3}; +void WinToastTemplate::initComponentsFromType() { + _hasImage = _type < ToastTemplateType_ToastText01; + _textFields = std::vector(TextFieldsCount[_type], L""); +} + +WinToastStringWrapper::WinToastStringWrapper(_In_reads_(length) PCWSTR stringRef, _In_ UINT32 length) throw() { + HRESULT hr = DllImporter::WindowsCreateStringReference(stringRef, length, &_header, &_hstring); + if (!SUCCEEDED(hr)) { + RaiseException(static_cast(STATUS_INVALID_PARAMETER), EXCEPTION_NONCONTINUABLE, 0, nullptr); + } +} + + +WinToastStringWrapper::WinToastStringWrapper(const std::wstring &stringRef) +{ + HRESULT hr = DllImporter::WindowsCreateStringReference(stringRef.c_str(), static_cast(stringRef.length()), &_header, &_hstring); + if (FAILED(hr)) { + RaiseException(static_cast(STATUS_INVALID_PARAMETER), EXCEPTION_NONCONTINUABLE, 0, nullptr); + } +} + +WinToastStringWrapper::~WinToastStringWrapper() { + DllImporter::WindowsDeleteString(_hstring); +} + +HSTRING WinToastStringWrapper::Get() const { + return _hstring; +} \ No newline at end of file diff --git a/atom/browser/ui/win/toast_lib.h b/atom/browser/ui/win/toast_lib.h new file mode 100644 index 000000000000..c7fd0f81d626 --- /dev/null +++ b/atom/browser/ui/win/toast_lib.h @@ -0,0 +1,168 @@ +// MIT License + +// Copyright (c) 2016 Mohammed Boujemaoui Boulaghmoudi + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#ifndef WINTOASTLIB_H +#define WINTOASTLIB_H +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +using namespace Microsoft::WRL; +using namespace ABI::Windows::Data::Xml::Dom; +using namespace ABI::Windows::Foundation; +using namespace ABI::Windows::UI::Notifications; +using namespace Windows::Foundation; + +#define DEFAULT_SHELL_LINKS_PATH L"\\Microsoft\\Windows\\Start Menu\\Programs\\" +#define DEFAULT_LINK_FORMAT L".lnk" + +namespace WinToastLib { + class WinToastStringWrapper { + public: + WinToastStringWrapper(_In_reads_(length) PCWSTR stringRef, _In_ UINT32 length) throw(); + WinToastStringWrapper(_In_ const std::wstring &stringRef) throw(); + ~WinToastStringWrapper(); + HSTRING Get() const throw(); + private: + HSTRING _hstring; + HSTRING_HEADER _header; + + }; + + + class WinToastHandler { + public: + enum WinToastDismissalReason { + UserCanceled = ToastDismissalReason::ToastDismissalReason_UserCanceled, + ApplicationHidden = ToastDismissalReason::ToastDismissalReason_ApplicationHidden, + TimedOut = ToastDismissalReason::ToastDismissalReason_TimedOut + }; + + virtual void toastActivated() {}; + virtual void toastDismissed(WinToastDismissalReason state) {}; + virtual void toastFailed() {}; + }; + + class WinToastTemplate + { + public: + enum TextField { + FirstLine = 0, + SecondLine, + ThirdLine, + LineCount + }; + + enum WinToastTemplateType { + ImageWithOneLine = ToastTemplateType::ToastTemplateType_ToastImageAndText01, + ImageWithTwoLines = ToastTemplateType::ToastTemplateType_ToastImageAndText02, + ImageWithThreeLines = ToastTemplateType::ToastTemplateType_ToastImageAndText03, + ImageWithFourLines = ToastTemplateType::ToastTemplateType_ToastImageAndText04, + TextOneLine = ToastTemplateType::ToastTemplateType_ToastText01, + TextTwoLines = ToastTemplateType::ToastTemplateType_ToastText02, + TextThreeLines = ToastTemplateType::ToastTemplateType_ToastText03, + TextFourLines = ToastTemplateType::ToastTemplateType_ToastText04, + WinToastTemplateTypeCount + }; + + WinToastTemplate(_In_ const WinToastTemplateType& type = ImageWithTwoLines); + ~WinToastTemplate(); + + int textFieldsCount() const { return _textFields.size(); } + bool hasImage() const { return _hasImage; } + std::vector textFields() const { return _textFields; } + std::wstring textField(_In_ const TextField& pos) const { return _textFields[pos]; } + std::wstring imagePath() const { return _imagePath; } + WinToastTemplateType type() const { return _type; } + void setTextField(_In_ const std::wstring& txt, _In_ const TextField& pos); + void setImagePath(_In_ const std::wstring& imgPath); + void setSilent(bool is_silent); + bool isSilent() const { return _silent; } + private: + static int TextFieldsCount[WinToastTemplateTypeCount]; + bool _hasImage; + bool _silent = false; + std::vector _textFields; + std::wstring _imagePath; + WinToastTemplateType _type; + void initComponentsFromType(); + }; + + class WinToast { + public: + static WinToast* instance(); + static bool isCompatible(); + static std::wstring configureAUMI(_In_ const std::wstring& company, + _In_ const std::wstring& name, + _In_ const std::wstring& surname, + _In_ const std::wstring& versionInfo + ); + bool initialize(); + bool isInitialized() const { return _isInitialized; } + bool showToast(_In_ const WinToastTemplate& toast, _In_ WinToastHandler* handler); + std::wstring appName() const; + std::wstring appUserModelId() const; + void setAppUserModelId(_In_ const std::wstring& appName); + void setAppName(_In_ const std::wstring& appName); + private: + bool _isInitialized; + std::wstring _appName; + std::wstring _aumi; + ComPtr _xmlDocument; + ComPtr _notificationManager; + ComPtr _notifier; + ComPtr _notificationFactory; + ComPtr _notification; + static WinToast* _instance; + + WinToast(void); + IXmlDocument* xmlDocument() const { return _xmlDocument.Get(); } + IToastNotifier* notifier() const { return _notifier.Get(); } + IToastNotificationFactory* notificationFactory() const { return _notificationFactory.Get(); } + IToastNotificationManagerStatics* notificationManager() const { return _notificationManager.Get(); } + IToastNotification* notification() const { return _notification.Get(); } + + HRESULT validateShellLink(); + HRESULT createShellLink(); + HRESULT setImageField(_In_ const std::wstring& path); + HRESULT setTextField(_In_ const std::wstring& text, _In_ int pos); + bool makeSilent(bool is_silent); + }; + + + +} + +#endif // WINTOASTLIB_H \ No newline at end of file diff --git a/filenames.gypi b/filenames.gypi index e670f3c8f0da..526fa3ad353f 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -131,6 +131,7 @@ 'atom/browser/api/atom_api_notification.cc', 'atom/browser/api/atom_api_notification.h', 'atom/browser/api/atom_api_notification_mac.mm', + 'atom/browser/api/atom_api_notification_win.cc', 'atom/browser/api/atom_api_power_monitor.cc', 'atom/browser/api/atom_api_power_monitor.h', 'atom/browser/api/atom_api_power_save_blocker.cc', @@ -357,6 +358,10 @@ 'atom/browser/ui/win/notify_icon.h', 'atom/browser/ui/win/taskbar_host.cc', 'atom/browser/ui/win/taskbar_host.h', + 'atom/browser/ui/win/toast_handler.cc', + 'atom/browser/ui/win/toast_handler.h', + 'atom/browser/ui/win/toast_lib.cc', + 'atom/browser/ui/win/toast_lib.h', 'atom/browser/ui/x/event_disabler.cc', 'atom/browser/ui/x/event_disabler.h', 'atom/browser/ui/x/window_state_watcher.cc', From a4bd26fab0e00743f1f5ff8a55a73e0995e236b7 Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Mon, 24 Apr 2017 08:14:35 +1000 Subject: [PATCH 03/22] Fix string16 init on macOS --- atom/browser/api/atom_api_notification.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/atom/browser/api/atom_api_notification.h b/atom/browser/api/atom_api_notification.h index fcfc86701486..6704b84f469b 100644 --- a/atom/browser/api/atom_api_notification.h +++ b/atom/browser/api/atom_api_notification.h @@ -11,6 +11,7 @@ #include "atom/browser/api/trackable_object.h" #include "atom/browser/ui/notification_observer.h" +#include "base/strings/utf_string_conversions.h" #include "native_mate/handle.h" #include "ui/gfx/image/image.h" @@ -58,13 +59,13 @@ class Notification : public mate::TrackableObject, void NotifyPropsUpdated(); private: - base::string16 title_ = L""; - base::string16 body_ = L""; + base::string16 title_ = base::UTF8ToUTF16(""); + base::string16 body_ = base::UTF8ToUTF16(""); gfx::Image icon_; - base::string16 icon_path_ = L""; + base::string16 icon_path_ = base::UTF8ToUTF16(""); bool has_icon_ = false; bool silent_ = false; - base::string16 reply_placeholder_ = L""; + base::string16 reply_placeholder_ = base::UTF8ToUTF16(""); bool has_reply_ = false; int id_; From 6bbc4c3113461b268576647bd51dec02d90b6f04 Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Mon, 24 Apr 2017 09:47:18 +1000 Subject: [PATCH 04/22] Support Windows 7 notifications from brightray --- atom/browser/api/atom_api_notification_win.cc | 41 +++++++++++++++---- .../ui/notification_delegate_adapter.cc | 21 ++++++++++ .../ui/notification_delegate_adapter.h | 28 +++++++++++++ default_app/default_app.js | 2 +- filenames.gypi | 2 + 5 files changed, 85 insertions(+), 9 deletions(-) create mode 100644 atom/browser/ui/notification_delegate_adapter.cc create mode 100644 atom/browser/ui/notification_delegate_adapter.h diff --git a/atom/browser/api/atom_api_notification_win.cc b/atom/browser/api/atom_api_notification_win.cc index fb27fb6e7a55..93482d481624 100644 --- a/atom/browser/api/atom_api_notification_win.cc +++ b/atom/browser/api/atom_api_notification_win.cc @@ -4,10 +4,16 @@ #include "atom/browser/api/atom_api_notification.h" +#include "atom/browser/ui/notification_delegate_adapter.h" #include "atom/browser/ui/win/toast_handler.h" #include "atom/browser/ui/win/toast_lib.h" #include "base/strings/utf_string_conversions.h" +#include "browser/notification.h" +#include "browser/notification_presenter.h" +#include "browser/win/notification_presenter_win7.h" #include "common/string_conversion.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "url/gurl.h" namespace atom { @@ -15,18 +21,33 @@ namespace api { bool can_toast_ = true; bool initialized_ = false; +brightray::NotificationPresenter* presenter; void Notification::Show() { - atom::AtomToastHandler* handler = new atom::AtomToastHandler(this); - WinToastLib::WinToastTemplate toast = WinToastLib::WinToastTemplate(WinToastLib::WinToastTemplate::TextTwoLines); - // toast.setImagePath(L"C:\example.png"); - toast.setTextField(title_, WinToastLib::WinToastTemplate::TextField::FirstLine); - toast.setTextField(body_, WinToastLib::WinToastTemplate::TextField::SecondLine); - toast.setSilent(silent_); + if (can_toast_) { + atom::AtomToastHandler* handler = new atom::AtomToastHandler(this); + WinToastLib::WinToastTemplate toast = WinToastLib::WinToastTemplate(WinToastLib::WinToastTemplate::TextTwoLines); + // toast.setImagePath(L"C:\example.png"); + toast.setTextField(title_, WinToastLib::WinToastTemplate::TextField::FirstLine); + toast.setTextField(body_, WinToastLib::WinToastTemplate::TextField::SecondLine); + toast.setSilent(silent_); - WinToastLib::WinToast::instance()->showToast(toast, handler); + WinToastLib::WinToast::instance()->showToast(toast, handler); - OnShown(); + OnShown(); + } else { + AtomNotificationDelegateAdapter* adapter = new AtomNotificationDelegateAdapter(this); + auto notif = presenter->CreateNotification(adapter); + GURL* u = new GURL; + notif->Show( + title_, + body_, + "", + u->Resolve(""), + *(new SkBitmap), + true + ); + } } void Notification::OnInitialProps() { @@ -37,6 +58,10 @@ void Notification::OnInitialProps() { ); can_toast_ = WinToastLib::WinToast::instance()->initialize(); } + can_toast_ = false; + if (!can_toast_) { + presenter = new brightray::NotificationPresenterWin7; + } } void Notification::NotifyPropsUpdated() { diff --git a/atom/browser/ui/notification_delegate_adapter.cc b/atom/browser/ui/notification_delegate_adapter.cc new file mode 100644 index 000000000000..bea8f3f296e9 --- /dev/null +++ b/atom/browser/ui/notification_delegate_adapter.cc @@ -0,0 +1,21 @@ +#include "atom/browser/ui/notification_delegate_adapter.h" + +#include "atom/browser/api/atom_api_notification.h" +#include "browser/notification_delegate.h" + +namespace atom { + +AtomNotificationDelegateAdapter::AtomNotificationDelegateAdapter(atom::api::Notification* target) { + observer_ = target; +} +void AtomNotificationDelegateAdapter::NotificationDisplayed() { + observer_->OnShown(); +} +void AtomNotificationDelegateAdapter::NotificationClosed() {} +void AtomNotificationDelegateAdapter::NotificationClick() { + observer_->OnClicked(); +} +void AtomNotificationDelegateAdapter::NotificationDestroyed() {} +void AtomNotificationDelegateAdapter::NotificationFailed() {} + +} \ No newline at end of file diff --git a/atom/browser/ui/notification_delegate_adapter.h b/atom/browser/ui/notification_delegate_adapter.h new file mode 100644 index 000000000000..2bbbc0512a6c --- /dev/null +++ b/atom/browser/ui/notification_delegate_adapter.h @@ -0,0 +1,28 @@ +// Copyright (c) 2014 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "browser/notification_delegate.h" + +#include "atom/browser/api/atom_api_notification.h" + +#ifndef ATOM_BROWSER_UI_NOTIFICATION_DELEGATE_ADAPTER_H_ +#define ATOM_BROWSER_UI_NOTIFICATION_DELEGATE_ADAPTER_H_ + +namespace atom { + +class AtomNotificationDelegateAdapter : public brightray::NotificationDelegate { + public: + atom::api::Notification* observer_; + AtomNotificationDelegateAdapter(atom::api::Notification* target); + + void NotificationDisplayed(); + void NotificationClosed(); + void NotificationClick(); + void NotificationDestroyed(); + void NotificationFailed(); +}; + +} + +#endif // ATOM_BROWSER_UI_NOTIFICATION_DELEGATE_ADAPTER_H_ \ No newline at end of file diff --git a/default_app/default_app.js b/default_app/default_app.js index 446c159b6c1a..d002a8d24aee 100644 --- a/default_app/default_app.js +++ b/default_app/default_app.js @@ -34,7 +34,7 @@ exports.load = (appUrl) => { silent: true, icon: '/Users/samuel/Downloads/ninja.png', hasReply: true, - replyPlacehodler: 'Type Here!!' + replyPlaceholder: 'Type Here!!' }); n.on('show', () => console.log('showed')); n.on('click', () => console.info('clicked!!')); diff --git a/filenames.gypi b/filenames.gypi index 526fa3ad353f..fb67697965a9 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -315,6 +315,8 @@ 'atom/browser/ui/message_box_gtk.cc', 'atom/browser/ui/message_box_mac.mm', 'atom/browser/ui/message_box_win.cc', + 'atom/browser/ui/notification_delegate_adapter.h', + 'atom/browser/ui/notification_delegate_adapter.cc', 'atom/browser/ui/notification_observer.h', 'atom/browser/ui/tray_icon.cc', 'atom/browser/ui/tray_icon.h', From 03688b9415cedd7b417610bb266c8edc7d5abcd1 Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Mon, 24 Apr 2017 12:03:05 +1000 Subject: [PATCH 05/22] Get image notifications working on Win7 + Win10 --- atom/browser/api/atom_api_notification_win.cc | 64 +++++++++++++++---- 1 file changed, 52 insertions(+), 12 deletions(-) diff --git a/atom/browser/api/atom_api_notification_win.cc b/atom/browser/api/atom_api_notification_win.cc index 93482d481624..c92a1b2ce7fe 100644 --- a/atom/browser/api/atom_api_notification_win.cc +++ b/atom/browser/api/atom_api_notification_win.cc @@ -4,15 +4,20 @@ #include "atom/browser/api/atom_api_notification.h" +#include "atom/browser/browser.h" #include "atom/browser/ui/notification_delegate_adapter.h" #include "atom/browser/ui/win/toast_handler.h" #include "atom/browser/ui/win/toast_lib.h" +#include "base/files/file_util.h" +#include "base/md5.h" #include "base/strings/utf_string_conversions.h" #include "browser/notification.h" #include "browser/notification_presenter.h" +#include "browser/win/notification_presenter_win.h" #include "browser/win/notification_presenter_win7.h" #include "common/string_conversion.h" #include "third_party/skia/include/core/SkBitmap.h" +#include "ui/gfx/codec/png_codec.h" #include "url/gurl.h" namespace atom { @@ -21,13 +26,49 @@ namespace api { bool can_toast_ = true; bool initialized_ = false; -brightray::NotificationPresenter* presenter; +brightray::NotificationPresenterWin7* presenter; + +base::ScopedTempDir temp_dir_; + +bool SaveIconToPath(const SkBitmap& bitmap, const base::FilePath& path) { + std::vector png_data; + if (!gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, false, &png_data)) + return false; + + char* data = reinterpret_cast(&png_data[0]); + int size = static_cast(png_data.size()); + return base::WriteFile(path, data, size) == size; +} void Notification::Show() { + SkBitmap image = *(new SkBitmap); + if (has_icon_) { + image = *(icon_.ToSkBitmap()); + } + if (can_toast_) { atom::AtomToastHandler* handler = new atom::AtomToastHandler(this); - WinToastLib::WinToastTemplate toast = WinToastLib::WinToastTemplate(WinToastLib::WinToastTemplate::TextTwoLines); - // toast.setImagePath(L"C:\example.png"); + WinToastLib::WinToastTemplate::WinToastTemplateType toastType = WinToastLib::WinToastTemplate::TextOneLine; + if (!has_icon_) { + if (body_ != L"") { + toastType = WinToastLib::WinToastTemplate::TextTwoLines; + } else { + toastType = WinToastLib::WinToastTemplate::TextOneLine; + } + } else { + if (body_ != L"") { + toastType = WinToastLib::WinToastTemplate::ImageWithTwoLines; + } else { + toastType = WinToastLib::WinToastTemplate::ImageWithOneLine; + } + } + WinToastLib::WinToastTemplate toast = WinToastLib::WinToastTemplate(toastType); + + std::string filename = base::MD5String(base::UTF16ToUTF8(icon_path_)) + ".png"; + base::FilePath savePath = temp_dir_.GetPath().Append(base::UTF8ToUTF16(filename)); + if (has_icon_ && SaveIconToPath(image, savePath)) { + toast.setImagePath(savePath.value()); + } toast.setTextField(title_, WinToastLib::WinToastTemplate::TextField::FirstLine); toast.setTextField(body_, WinToastLib::WinToastTemplate::TextField::SecondLine); toast.setSilent(silent_); @@ -38,27 +79,26 @@ void Notification::Show() { } else { AtomNotificationDelegateAdapter* adapter = new AtomNotificationDelegateAdapter(this); auto notif = presenter->CreateNotification(adapter); - GURL* u = new GURL; + GURL nullUrl = *(new GURL); notif->Show( title_, body_, "", - u->Resolve(""), - *(new SkBitmap), - true + nullUrl, + image, + silent_ ); } } void Notification::OnInitialProps() { if (!initialized_) { - WinToastLib::WinToast::instance()->setAppName(L"WinToastExample"); - WinToastLib::WinToast::instance()->setAppUserModelId( - WinToastLib::WinToast::configureAUMI(L"mohabouje", L"wintoast", L"wintoastexample", L"20161006") - ); + Browser* browser = Browser::Get(); + WinToastLib::WinToast::instance()->setAppName(base::UTF8ToUTF16(browser->GetName())); + WinToastLib::WinToast::instance()->setAppUserModelId(browser->GetAppUserModelID()); can_toast_ = WinToastLib::WinToast::instance()->initialize(); + temp_dir_.CreateUniqueTempDir(); } - can_toast_ = false; if (!can_toast_) { presenter = new brightray::NotificationPresenterWin7; } From c6196810a655a85fda76fcfde0c87fb76f123a99 Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Mon, 24 Apr 2017 13:07:42 +1000 Subject: [PATCH 06/22] Code cleanup --- atom/browser/api/atom_api_notification.cc | 72 +- atom/browser/api/atom_api_notification.h | 6 +- atom/browser/api/atom_api_notification_win.cc | 46 +- .../ui/notification_delegate_adapter.cc | 15 +- .../ui/notification_delegate_adapter.h | 20 +- atom/browser/ui/notification_observer.h | 2 + atom/browser/ui/win/toast_handler.cc | 11 +- atom/browser/ui/win/toast_handler.h | 13 +- atom/browser/ui/win/toast_lib.cc | 881 ++++++++++-------- atom/browser/ui/win/toast_lib.h | 294 +++--- default_app/default_app.js | 12 +- 11 files changed, 758 insertions(+), 614 deletions(-) diff --git a/atom/browser/api/atom_api_notification.cc b/atom/browser/api/atom_api_notification.cc index 9812852ce623..502184c2eb9f 100644 --- a/atom/browser/api/atom_api_notification.cc +++ b/atom/browser/api/atom_api_notification.cc @@ -4,6 +4,7 @@ #include "atom/browser/api/atom_api_notification.h" +#include #include #include "atom/browser/api/atom_api_menu.h" @@ -25,7 +26,9 @@ namespace api { int id_counter = 1; std::map notifications_; -Notification::Notification(v8::Isolate* isolate, v8::Local wrapper, mate::Arguments* args) { +Notification::Notification(v8::Isolate* isolate, + v8::Local wrapper, + mate::Arguments* args) { InitWith(isolate, wrapper); mate::Dictionary opts; @@ -65,19 +68,46 @@ Notification* Notification::FromID(int id) { } // Getters -int Notification::GetID() { return id_; } -base::string16 Notification::GetTitle() { return title_; } -base::string16 Notification::GetBody() { return body_; } -bool Notification::GetSilent() { return silent_; } -base::string16 Notification::GetReplyPlaceholder() { return reply_placeholder_; } -bool Notification::GetHasReply() { return has_reply_; } +int Notification::GetID() { + return id_; +} +base::string16 Notification::GetTitle() { + return title_; +} +base::string16 Notification::GetBody() { + return body_; +} +bool Notification::GetSilent() { + return silent_; +} +base::string16 Notification::GetReplyPlaceholder() { + return reply_placeholder_; +} +bool Notification::GetHasReply() { + return has_reply_; +} // Setters -void Notification::SetTitle(base::string16 new_title) { title_ = new_title; NotifyPropsUpdated(); } -void Notification::SetBody(base::string16 new_body) { body_ = new_body; NotifyPropsUpdated(); } -void Notification::SetSilent(bool new_silent) { silent_ = new_silent; NotifyPropsUpdated(); } -void Notification::SetReplyPlaceholder(base::string16 new_reply_placeholder) { reply_placeholder_ = new_reply_placeholder; NotifyPropsUpdated(); } -void Notification::SetHasReply(bool new_has_reply) { has_reply_ = new_has_reply; NotifyPropsUpdated(); } +void Notification::SetTitle(base::string16 new_title) { + title_ = new_title; + NotifyPropsUpdated(); +} +void Notification::SetBody(base::string16 new_body) { + body_ = new_body; + NotifyPropsUpdated(); +} +void Notification::SetSilent(bool new_silent) { + silent_ = new_silent; + NotifyPropsUpdated(); +} +void Notification::SetReplyPlaceholder(base::string16 new_reply_placeholder) { + reply_placeholder_ = new_reply_placeholder; + NotifyPropsUpdated(); +} +void Notification::SetHasReply(bool new_has_reply) { + has_reply_ = new_has_reply; + NotifyPropsUpdated(); +} void Notification::OnClicked() { Emit("click"); @@ -93,7 +123,7 @@ void Notification::OnShown() { // static void Notification::BuildPrototype(v8::Isolate* isolate, - v8::Local prototype) { + v8::Local prototype) { prototype->SetClassName(mate::StringToV8(isolate, "Notification")); mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate()) .MakeDestroyable() @@ -102,26 +132,30 @@ void Notification::BuildPrototype(v8::Isolate* isolate, .SetProperty("title", &Notification::GetTitle, &Notification::SetTitle) .SetProperty("body", &Notification::GetBody, &Notification::SetBody) .SetProperty("silent", &Notification::GetSilent, &Notification::SetSilent) - .SetProperty("replyPlaceholder", &Notification::GetReplyPlaceholder, &Notification::SetReplyPlaceholder) - .SetProperty("hasReply", &Notification::GetHasReply, &Notification::SetHasReply); + .SetProperty("replyPlaceholder", &Notification::GetReplyPlaceholder, + &Notification::SetReplyPlaceholder) + .SetProperty("hasReply", &Notification::GetHasReply, + &Notification::SetHasReply); } } // namespace api } // namespace atom - namespace { using atom::api::Notification; -void Initialize(v8::Local exports, v8::Local unused, - v8::Local context, void* priv) { +void Initialize(v8::Local exports, + v8::Local unused, + v8::Local context, + void* priv) { v8::Isolate* isolate = context->GetIsolate(); Notification::SetConstructor(isolate, base::Bind(&Notification::New)); mate::Dictionary dict(isolate, exports); - dict.Set("Notification", Notification::GetConstructor(isolate)->GetFunction()); + dict.Set("Notification", + Notification::GetConstructor(isolate)->GetFunction()); } } // namespace diff --git a/atom/browser/api/atom_api_notification.h b/atom/browser/api/atom_api_notification.h index 6704b84f469b..7882c145f757 100644 --- a/atom/browser/api/atom_api_notification.h +++ b/atom/browser/api/atom_api_notification.h @@ -20,7 +20,7 @@ namespace atom { namespace api { class Notification : public mate::TrackableObject, - public NotifictionObserver { + public NotifictionObserver { public: static mate::WrappableBase* New(mate::Arguments* args); static bool HasID(int id); @@ -35,7 +35,9 @@ class Notification : public mate::TrackableObject, void OnShown() override; protected: - Notification(v8::Isolate* isolate, v8::Local wrapper, mate::Arguments* args); + Notification(v8::Isolate* isolate, + v8::Local wrapper, + mate::Arguments* args); ~Notification() override; void OnInitialProps(); diff --git a/atom/browser/api/atom_api_notification_win.cc b/atom/browser/api/atom_api_notification_win.cc index c92a1b2ce7fe..c740fa9a9e22 100644 --- a/atom/browser/api/atom_api_notification_win.cc +++ b/atom/browser/api/atom_api_notification_win.cc @@ -48,7 +48,8 @@ void Notification::Show() { if (can_toast_) { atom::AtomToastHandler* handler = new atom::AtomToastHandler(this); - WinToastLib::WinToastTemplate::WinToastTemplateType toastType = WinToastLib::WinToastTemplate::TextOneLine; + WinToastLib::WinToastTemplate::WinToastTemplateType toastType = + WinToastLib::WinToastTemplate::TextOneLine; if (!has_icon_) { if (body_ != L"") { toastType = WinToastLib::WinToastTemplate::TextTwoLines; @@ -62,40 +63,41 @@ void Notification::Show() { toastType = WinToastLib::WinToastTemplate::ImageWithOneLine; } } - WinToastLib::WinToastTemplate toast = WinToastLib::WinToastTemplate(toastType); + WinToastLib::WinToastTemplate toast = + WinToastLib::WinToastTemplate(toastType); - std::string filename = base::MD5String(base::UTF16ToUTF8(icon_path_)) + ".png"; - base::FilePath savePath = temp_dir_.GetPath().Append(base::UTF8ToUTF16(filename)); + std::string filename = + base::MD5String(base::UTF16ToUTF8(icon_path_)) + ".png"; + base::FilePath savePath = + temp_dir_.GetPath().Append(base::UTF8ToUTF16(filename)); if (has_icon_ && SaveIconToPath(image, savePath)) { toast.setImagePath(savePath.value()); } - toast.setTextField(title_, WinToastLib::WinToastTemplate::TextField::FirstLine); - toast.setTextField(body_, WinToastLib::WinToastTemplate::TextField::SecondLine); + toast.setTextField(title_, + WinToastLib::WinToastTemplate::TextField::FirstLine); + toast.setTextField(body_, + WinToastLib::WinToastTemplate::TextField::SecondLine); toast.setSilent(silent_); WinToastLib::WinToast::instance()->showToast(toast, handler); OnShown(); } else { - AtomNotificationDelegateAdapter* adapter = new AtomNotificationDelegateAdapter(this); + AtomNotificationDelegateAdapter* adapter = + new AtomNotificationDelegateAdapter(this); auto notif = presenter->CreateNotification(adapter); GURL nullUrl = *(new GURL); - notif->Show( - title_, - body_, - "", - nullUrl, - image, - silent_ - ); + notif->Show(title_, body_, "", nullUrl, image, silent_); } } void Notification::OnInitialProps() { if (!initialized_) { Browser* browser = Browser::Get(); - WinToastLib::WinToast::instance()->setAppName(base::UTF8ToUTF16(browser->GetName())); - WinToastLib::WinToast::instance()->setAppUserModelId(browser->GetAppUserModelID()); + WinToastLib::WinToast::instance()->setAppName( + base::UTF8ToUTF16(browser->GetName())); + WinToastLib::WinToast::instance()->setAppUserModelId( + browser->GetAppUserModelID()); can_toast_ = WinToastLib::WinToast::instance()->initialize(); temp_dir_.CreateUniqueTempDir(); } @@ -104,10 +106,6 @@ void Notification::OnInitialProps() { } } -void Notification::NotifyPropsUpdated() { - -} - -} - -} \ No newline at end of file +void Notification::NotifyPropsUpdated() {} +} // namespace api +} // namespace atom diff --git a/atom/browser/ui/notification_delegate_adapter.cc b/atom/browser/ui/notification_delegate_adapter.cc index bea8f3f296e9..d141be43ea64 100644 --- a/atom/browser/ui/notification_delegate_adapter.cc +++ b/atom/browser/ui/notification_delegate_adapter.cc @@ -1,3 +1,7 @@ +// Copyright (c) 2014 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + #include "atom/browser/ui/notification_delegate_adapter.h" #include "atom/browser/api/atom_api_notification.h" @@ -5,17 +9,18 @@ namespace atom { -AtomNotificationDelegateAdapter::AtomNotificationDelegateAdapter(atom::api::Notification* target) { - observer_ = target; +AtomNotificationDelegateAdapter::AtomNotificationDelegateAdapter( + atom::api::Notification* target) { + observer_ = target; } void AtomNotificationDelegateAdapter::NotificationDisplayed() { - observer_->OnShown(); + observer_->OnShown(); } void AtomNotificationDelegateAdapter::NotificationClosed() {} void AtomNotificationDelegateAdapter::NotificationClick() { - observer_->OnClicked(); + observer_->OnClicked(); } void AtomNotificationDelegateAdapter::NotificationDestroyed() {} void AtomNotificationDelegateAdapter::NotificationFailed() {} -} \ No newline at end of file +} // namespace atom diff --git a/atom/browser/ui/notification_delegate_adapter.h b/atom/browser/ui/notification_delegate_adapter.h index 2bbbc0512a6c..89e06e3ad1eb 100644 --- a/atom/browser/ui/notification_delegate_adapter.h +++ b/atom/browser/ui/notification_delegate_adapter.h @@ -12,17 +12,17 @@ namespace atom { class AtomNotificationDelegateAdapter : public brightray::NotificationDelegate { - public: - atom::api::Notification* observer_; - AtomNotificationDelegateAdapter(atom::api::Notification* target); + public: + atom::api::Notification* observer_; + explicit AtomNotificationDelegateAdapter(atom::api::Notification* target); - void NotificationDisplayed(); - void NotificationClosed(); - void NotificationClick(); - void NotificationDestroyed(); - void NotificationFailed(); + void NotificationDisplayed(); + void NotificationClosed(); + void NotificationClick(); + void NotificationDestroyed(); + void NotificationFailed(); }; -} +} // namespace atom -#endif // ATOM_BROWSER_UI_NOTIFICATION_DELEGATE_ADAPTER_H_ \ No newline at end of file +#endif // ATOM_BROWSER_UI_NOTIFICATION_DELEGATE_ADAPTER_H_ diff --git a/atom/browser/ui/notification_observer.h b/atom/browser/ui/notification_observer.h index 8619e5d3609b..5b1ba053ec50 100644 --- a/atom/browser/ui/notification_observer.h +++ b/atom/browser/ui/notification_observer.h @@ -5,6 +5,8 @@ #ifndef ATOM_BROWSER_UI_NOTIFICATION_OBSERVER_H_ #define ATOM_BROWSER_UI_NOTIFICATION_OBSERVER_H_ +#include + namespace atom { class NotifictionObserver { diff --git a/atom/browser/ui/win/toast_handler.cc b/atom/browser/ui/win/toast_handler.cc index 5108c630208c..6ce01ef37136 100644 --- a/atom/browser/ui/win/toast_handler.cc +++ b/atom/browser/ui/win/toast_handler.cc @@ -15,14 +15,15 @@ AtomToastHandler::AtomToastHandler(atom::api::Notification* target) { void AtomToastHandler::toastActivated() { observer_->OnClicked(); -}; +} -void AtomToastHandler::toastDismissed(WinToastLib::WinToastHandler::WinToastDismissalReason state) { +void AtomToastHandler::toastDismissed( + WinToastLib::WinToastHandler::WinToastDismissalReason state) { // observer_->OnDismissed(); -}; +} void AtomToastHandler::toastFailed() { // observer_->OnErrored(); -}; - } + +} // namespace atom diff --git a/atom/browser/ui/win/toast_handler.h b/atom/browser/ui/win/toast_handler.h index d88c21d32dc0..70b8ad604890 100644 --- a/atom/browser/ui/win/toast_handler.h +++ b/atom/browser/ui/win/toast_handler.h @@ -5,21 +5,22 @@ #include "atom/browser/ui/win/toast_lib.h" #include "atom/browser/api/atom_api_notification.h" -#ifndef ATOM_BROWSER_UI_TOAST_HANDLER_H_ -#define ATOM_BROWSER_UI_TOAST_HANDLER_H_ +#ifndef ATOM_BROWSER_UI_WIN_TOAST_HANDLER_H_ +#define ATOM_BROWSER_UI_WIN_TOAST_HANDLER_H_ namespace atom { class AtomToastHandler : public WinToastLib::WinToastHandler { public: atom::api::Notification* observer_; - AtomToastHandler(atom::api::Notification* target); + explicit AtomToastHandler(atom::api::Notification* target); void toastActivated() override; - void toastDismissed(WinToastLib::WinToastHandler::WinToastDismissalReason state); + void toastDismissed( + WinToastLib::WinToastHandler::WinToastDismissalReason state); void toastFailed(); }; -} +} // namespace atom -#endif // ATOM_BROWSER_UI_TOAST_HANDLER_H_ +#endif // ATOM_BROWSER_UI_WIN_TOAST_HANDLER_H_ diff --git a/atom/browser/ui/win/toast_lib.cc b/atom/browser/ui/win/toast_lib.cc index 3fdd657a26cd..1141a3d73a0e 100644 --- a/atom/browser/ui/win/toast_lib.cc +++ b/atom/browser/ui/win/toast_lib.cc @@ -9,8 +9,8 @@ // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, @@ -20,518 +20,589 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#include "toast_lib.h" -#include "browser/win/scoped_hstring.h" -#pragma comment(lib,"shlwapi") -#pragma comment(lib,"user32") +#include "atom/browser/ui/win/toast_lib.h" + +#include + +#include "browser/win/scoped_hstring.h" + +#pragma comment(lib, "shlwapi") +#pragma comment(lib, "user32") + +using WinToastLib::WinToast; +using WinToastLib::WinToastHandler; +using WinToastLib::WinToastStringWrapper; +using WinToastLib::WinToastTemplate; -using namespace WinToastLib; namespace DllImporter { - // Function load a function from library - template - HRESULT loadFunctionFromLibrary(HINSTANCE library, LPCSTR name, Function &func) { - if (!library) - return false; +// Function load a function from library +template +HRESULT loadFunctionFromLibrary(HINSTANCE library, + LPCSTR name, + Function& func) { // NOLINT + if (!library) + return false; - func = reinterpret_cast(GetProcAddress(library, name)); - return (func != nullptr) ? S_OK : E_FAIL; - } - - typedef HRESULT(FAR STDAPICALLTYPE *f_SetCurrentProcessExplicitAppUserModelID)(__in PCWSTR AppID); - typedef HRESULT(FAR STDAPICALLTYPE *f_PropVariantToString)(_In_ REFPROPVARIANT propvar, _Out_writes_(cch) PWSTR psz, _In_ UINT cch); - typedef HRESULT(FAR STDAPICALLTYPE *f_RoGetActivationFactory)(_In_ HSTRING activatableClassId, _In_ REFIID iid, _COM_Outptr_ void ** factory); - typedef HRESULT(FAR STDAPICALLTYPE *f_WindowsCreateStringReference)(_In_reads_opt_(length + 1) PCWSTR sourceString, UINT32 length, _Out_ HSTRING_HEADER * hstringHeader, _Outptr_result_maybenull_ _Result_nullonfailure_ HSTRING * string); - typedef HRESULT(FAR STDAPICALLTYPE *f_WindowsDeleteString)(_In_opt_ HSTRING string); - - f_SetCurrentProcessExplicitAppUserModelID SetCurrentProcessExplicitAppUserModelID; - f_PropVariantToString PropVariantToString; - f_RoGetActivationFactory RoGetActivationFactory; - f_WindowsCreateStringReference WindowsCreateStringReference; - f_WindowsDeleteString WindowsDeleteString; - - - template - _Check_return_ __inline HRESULT _1_GetActivationFactory(_In_ HSTRING activatableClassId, _COM_Outptr_ T** factory) { - return RoGetActivationFactory(activatableClassId, IID_INS_ARGS(factory)); - } - - template - inline HRESULT Wrap_GetActivationFactory(_In_ HSTRING activatableClassId, _Inout_ Details::ComPtrRef factory) throw() { - return _1_GetActivationFactory(activatableClassId, factory.ReleaseAndGetAddressOf()); - } - - inline HRESULT initialize() { - HINSTANCE LibShell32 = LoadLibrary(L"SHELL32.DLL"); - HRESULT hr = loadFunctionFromLibrary(LibShell32, "SetCurrentProcessExplicitAppUserModelID", SetCurrentProcessExplicitAppUserModelID); - if (SUCCEEDED(hr)) { - HINSTANCE LibPropSys = LoadLibrary(L"PROPSYS.DLL"); - hr = loadFunctionFromLibrary(LibPropSys, "PropVariantToString", PropVariantToString); - if (SUCCEEDED(hr)) { - HINSTANCE LibComBase = LoadLibrary(L"COMBASE.DLL"); - return SUCCEEDED(loadFunctionFromLibrary(LibComBase, "RoGetActivationFactory", RoGetActivationFactory)) - && SUCCEEDED(loadFunctionFromLibrary(LibComBase, "WindowsCreateStringReference", WindowsCreateStringReference)) - && SUCCEEDED(loadFunctionFromLibrary(LibComBase, "WindowsDeleteString", WindowsDeleteString)); - } - } - return hr; - } + func = reinterpret_cast(GetProcAddress(library, name)); + return (func != nullptr) ? S_OK : E_FAIL; } +typedef HRESULT(FAR STDAPICALLTYPE* f_SetCurrentProcessExplicitAppUserModelID)( + __in PCWSTR AppID); +typedef HRESULT(FAR STDAPICALLTYPE* f_PropVariantToString)( + _In_ REFPROPVARIANT propvar, + _Out_writes_(cch) PWSTR psz, + _In_ UINT cch); +typedef HRESULT(FAR STDAPICALLTYPE* f_RoGetActivationFactory)( + _In_ HSTRING activatableClassId, + _In_ REFIID iid, + _COM_Outptr_ void** factory); +typedef HRESULT(FAR STDAPICALLTYPE* f_WindowsCreateStringReference)( + _In_reads_opt_(length + 1) PCWSTR sourceString, + UINT32 length, + _Out_ HSTRING_HEADER* hstringHeader, + _Outptr_result_maybenull_ _Result_nullonfailure_ HSTRING* string); +typedef HRESULT(FAR STDAPICALLTYPE* f_WindowsDeleteString)( + _In_opt_ HSTRING string); + +f_SetCurrentProcessExplicitAppUserModelID + SetCurrentProcessExplicitAppUserModelID; +f_PropVariantToString PropVariantToString; +f_RoGetActivationFactory RoGetActivationFactory; +f_WindowsCreateStringReference WindowsCreateStringReference; +f_WindowsDeleteString WindowsDeleteString; + +template +_Check_return_ __inline HRESULT _1_GetActivationFactory( + _In_ HSTRING activatableClassId, + _COM_Outptr_ T** factory) { + return RoGetActivationFactory(activatableClassId, IID_INS_ARGS(factory)); +} + +template +inline HRESULT Wrap_GetActivationFactory( + _In_ HSTRING activatableClassId, + _Inout_ ComPtrRef factory) throw() { + return _1_GetActivationFactory(activatableClassId, + factory.ReleaseAndGetAddressOf()); +} + +inline HRESULT initialize() { + HINSTANCE LibShell32 = LoadLibrary(L"SHELL32.DLL"); + HRESULT hr = loadFunctionFromLibrary( + LibShell32, "SetCurrentProcessExplicitAppUserModelID", + SetCurrentProcessExplicitAppUserModelID); + if (SUCCEEDED(hr)) { + HINSTANCE LibPropSys = LoadLibrary(L"PROPSYS.DLL"); + hr = loadFunctionFromLibrary(LibPropSys, "PropVariantToString", + PropVariantToString); + if (SUCCEEDED(hr)) { + HINSTANCE LibComBase = LoadLibrary(L"COMBASE.DLL"); + return SUCCEEDED(loadFunctionFromLibrary(LibComBase, + "RoGetActivationFactory", + RoGetActivationFactory)) && + SUCCEEDED(loadFunctionFromLibrary(LibComBase, + "WindowsCreateStringReference", + WindowsCreateStringReference)) && + SUCCEEDED(loadFunctionFromLibrary( + LibComBase, "WindowsDeleteString", WindowsDeleteString)); + } + } + return hr; +} + +} // namespace DllImporter + namespace Util { - inline HRESULT defaultExecutablePath(_In_ WCHAR* path, _In_ DWORD nSize = MAX_PATH) { - DWORD written = GetModuleFileNameEx(GetCurrentProcess(), nullptr, path, nSize); - return (written > 0) ? S_OK : E_FAIL; - } - - - inline HRESULT defaultShellLinksDirectory(_In_ WCHAR* path, _In_ DWORD nSize = MAX_PATH) { - DWORD written = GetEnvironmentVariable(L"APPDATA", path, nSize); - HRESULT hr = written > 0 ? S_OK : E_INVALIDARG; - if (SUCCEEDED(hr)) { - errno_t result = wcscat_s(path, nSize, DEFAULT_SHELL_LINKS_PATH); - hr = (result == 0) ? S_OK : E_INVALIDARG; - } - return hr; - } - - inline HRESULT defaultShellLinkPath(const std::wstring& appname, _In_ WCHAR* path, _In_ DWORD nSize = MAX_PATH) { - HRESULT hr = defaultShellLinksDirectory(path, nSize); - if (SUCCEEDED(hr)) { - const std::wstring appLink(appname + DEFAULT_LINK_FORMAT); - errno_t result = wcscat_s(path, nSize, appLink.c_str()); - hr = (result == 0) ? S_OK : E_INVALIDARG; - } - return hr; - } - - - inline HRESULT setNodeStringValue(const std::wstring& string, IXmlNode *node, IXmlDocument *xml) { - ComPtr textNode; - HRESULT hr = xml->CreateTextNode( WinToastStringWrapper(string).Get(), &textNode); - if (SUCCEEDED(hr)) { - ComPtr stringNode; - hr = textNode.As(&stringNode); - if (SUCCEEDED(hr)) { - ComPtr appendedChild; - hr = node->AppendChild(stringNode.Get(), &appendedChild); - } - } - return hr; - } - - inline HRESULT setEventHandlers(_In_ IToastNotification* notification, _In_ WinToastHandler* eventHandler) { - EventRegistrationToken activatedToken, dismissedToken, failedToken; - HRESULT hr = notification->add_Activated( - Callback < Implements < RuntimeClassFlags, - ITypedEventHandler> >( - [eventHandler](IToastNotification*, IInspectable*) - { - eventHandler->toastActivated(); - return S_OK; - }).Get(), &activatedToken); - - if (SUCCEEDED(hr)) { - hr = notification->add_Dismissed(Callback < Implements < RuntimeClassFlags, - ITypedEventHandler> >( - [eventHandler](IToastNotification*, IToastDismissedEventArgs* e) - { - ToastDismissalReason reason; - if (SUCCEEDED(e->get_Reason(&reason))) - { - eventHandler->toastDismissed(static_cast(reason)); - } - return S_OK; - }).Get(), &dismissedToken); - if (SUCCEEDED(hr)) { - hr = notification->add_Failed(Callback < Implements < RuntimeClassFlags, - ITypedEventHandler> >( - [eventHandler](IToastNotification*, IToastFailedEventArgs*) - { - eventHandler->toastFailed(); - return S_OK; - }).Get(), &failedToken); - } - } - return hr; - } +inline HRESULT defaultExecutablePath(_In_ WCHAR* path, + _In_ DWORD nSize = MAX_PATH) { + DWORD written = + GetModuleFileNameEx(GetCurrentProcess(), nullptr, path, nSize); + return (written > 0) ? S_OK : E_FAIL; } +inline HRESULT defaultShellLinksDirectory(_In_ WCHAR* path, + _In_ DWORD nSize = MAX_PATH) { + DWORD written = GetEnvironmentVariable(L"APPDATA", path, nSize); + HRESULT hr = written > 0 ? S_OK : E_INVALIDARG; + if (SUCCEEDED(hr)) { + errno_t result = wcscat_s(path, nSize, DEFAULT_SHELL_LINKS_PATH); + hr = (result == 0) ? S_OK : E_INVALIDARG; + } + return hr; +} + +inline HRESULT defaultShellLinkPath(const std::wstring& appname, + _In_ WCHAR* path, + _In_ DWORD nSize = MAX_PATH) { + HRESULT hr = defaultShellLinksDirectory(path, nSize); + if (SUCCEEDED(hr)) { + const std::wstring appLink(appname + DEFAULT_LINK_FORMAT); + errno_t result = wcscat_s(path, nSize, appLink.c_str()); + hr = (result == 0) ? S_OK : E_INVALIDARG; + } + return hr; +} + +inline HRESULT setNodeStringValue(const std::wstring& string, + IXmlNode* node, + IXmlDocument* xml) { + ComPtr textNode; + HRESULT hr = + xml->CreateTextNode(WinToastStringWrapper(string).Get(), &textNode); + if (SUCCEEDED(hr)) { + ComPtr stringNode; + hr = textNode.As(&stringNode); + if (SUCCEEDED(hr)) { + ComPtr appendedChild; + hr = node->AppendChild(stringNode.Get(), &appendedChild); + } + } + return hr; +} + +inline HRESULT setEventHandlers(_In_ IToastNotification* notification, + _In_ WinToastHandler* eventHandler) { + EventRegistrationToken activatedToken, dismissedToken, failedToken; + HRESULT hr = notification->add_Activated( + Callback< + Implements, + ITypedEventHandler>>( + [eventHandler](IToastNotification*, IInspectable*) { + eventHandler->toastActivated(); + return S_OK; + }) + .Get(), + &activatedToken); + + if (SUCCEEDED(hr)) { + hr = notification->add_Dismissed( + Callback, + ITypedEventHandler>>( + [eventHandler](IToastNotification*, IToastDismissedEventArgs* e) { + ToastDismissalReason reason; + if (SUCCEEDED(e->get_Reason(&reason))) { + eventHandler->toastDismissed( + static_cast( + reason)); + } + return S_OK; + }) + .Get(), + &dismissedToken); + if (SUCCEEDED(hr)) { + hr = notification->add_Failed( + Callback, + ITypedEventHandler>>( + [eventHandler](IToastNotification*, IToastFailedEventArgs*) { + eventHandler->toastFailed(); + return S_OK; + }) + .Get(), + &failedToken); + } + } + return hr; +} + +} // namespace Util + WinToast* WinToast::_instance = nullptr; WinToast* WinToast::instance() { - if (_instance == nullptr) { - _instance = new WinToast(); - } - return _instance; + if (_instance == nullptr) { + _instance = new WinToast(); + } + return _instance; } -WinToast::WinToast() : _isInitialized(false) -{ - DllImporter::initialize(); +WinToast::WinToast() : _isInitialized(false) { + DllImporter::initialize(); } void WinToast::setAppName(_In_ const std::wstring& appName) { - _appName = appName; + _appName = appName; } std::wstring WinToast::appName() const { - return _appName; + return _appName; } std::wstring WinToast::appUserModelId() const { - return _aumi; + return _aumi; } void WinToast::setAppUserModelId(_In_ const std::wstring& aumi) { - _aumi = aumi; + _aumi = aumi; } bool WinToast::isCompatible() { - return !((DllImporter::SetCurrentProcessExplicitAppUserModelID == nullptr) - || (DllImporter::PropVariantToString == nullptr) - || (DllImporter::RoGetActivationFactory == nullptr) - || (DllImporter::WindowsCreateStringReference == nullptr) - || (DllImporter::WindowsDeleteString == nullptr)); + return !((DllImporter::SetCurrentProcessExplicitAppUserModelID == nullptr) || + (DllImporter::PropVariantToString == nullptr) || + (DllImporter::RoGetActivationFactory == nullptr) || + (DllImporter::WindowsCreateStringReference == nullptr) || + (DllImporter::WindowsDeleteString == nullptr)); } -std::wstring WinToast::configureAUMI(_In_ const std::wstring &company, - _In_ const std::wstring &name, - _In_ const std::wstring &surname, - _In_ const std::wstring &versionInfo) -{ - std::wstring aumi = company; - aumi += L"." + name; - aumi += L"." + surname; - aumi += L"." + versionInfo; +std::wstring WinToast::configureAUMI(_In_ const std::wstring& company, + _In_ const std::wstring& name, + _In_ const std::wstring& surname, + _In_ const std::wstring& versionInfo) { + std::wstring aumi = company; + aumi += L"." + name; + aumi += L"." + surname; + aumi += L"." + versionInfo; - return aumi; + return aumi; } bool WinToast::initialize() { - if (_aumi.empty() || _appName.empty()) { - return _isInitialized = false; - } + if (_aumi.empty() || _appName.empty()) { + return _isInitialized = false; + } - if (!isCompatible()) { - return _isInitialized = false; - } + if (!isCompatible()) { + return _isInitialized = false; + } - if (FAILED(DllImporter::SetCurrentProcessExplicitAppUserModelID(_aumi.c_str()))) { - return _isInitialized = false; - } + if (FAILED(DllImporter::SetCurrentProcessExplicitAppUserModelID( + _aumi.c_str()))) { + return _isInitialized = false; + } + HRESULT hr = validateShellLink(); + if (FAILED(hr)) { + hr = createShellLink(); + } - HRESULT hr = validateShellLink(); - if (FAILED(hr)) { - hr = createShellLink(); - } - + if (SUCCEEDED(hr)) { + hr = DllImporter::Wrap_GetActivationFactory( + WinToastStringWrapper( + RuntimeClass_Windows_UI_Notifications_ToastNotificationManager) + .Get(), + &_notificationManager); if (SUCCEEDED(hr)) { - hr = DllImporter::Wrap_GetActivationFactory(WinToastStringWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(), &_notificationManager); + hr = notificationManager()->CreateToastNotifierWithId( + WinToastStringWrapper(_aumi).Get(), &_notifier); + if (SUCCEEDED(hr)) { + hr = DllImporter::Wrap_GetActivationFactory( + WinToastStringWrapper( + RuntimeClass_Windows_UI_Notifications_ToastNotification) + .Get(), + &_notificationFactory); + } + } + } + + return _isInitialized = SUCCEEDED(hr); +} + +HRESULT WinToast::validateShellLink() { + WCHAR _path[MAX_PATH]; + Util::defaultShellLinkPath(_appName, _path); + // Check if the file exist + DWORD attr = GetFileAttributes(_path); + if (attr >= 0xFFFFFFF) { + std::wcout << "Error, shell link not found. Try to create a new one in: " + << _path << std::endl; + return E_FAIL; + } + + // Let's load the file as shell link to validate. + // - Create a shell link + // - Create a persistant file + // - Load the path as data for the persistant file + // - Read the property AUMI and validate with the current + // - Review if AUMI is equal. + ComPtr shellLink; + HRESULT hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(&shellLink)); + if (SUCCEEDED(hr)) { + ComPtr persistFile; + hr = shellLink.As(&persistFile); + if (SUCCEEDED(hr)) { + hr = persistFile->Load(_path, STGM_READWRITE); + if (SUCCEEDED(hr)) { + ComPtr propertyStore; + hr = shellLink.As(&propertyStore); if (SUCCEEDED(hr)) { - hr = notificationManager()->CreateToastNotifierWithId(WinToastStringWrapper(_aumi).Get(), &_notifier); + PROPVARIANT appIdPropVar; + hr = propertyStore->GetValue(PKEY_AppUserModel_ID, &appIdPropVar); + if (SUCCEEDED(hr)) { + WCHAR AUMI[MAX_PATH]; + hr = DllImporter::PropVariantToString(appIdPropVar, AUMI, MAX_PATH); if (SUCCEEDED(hr)) { - hr = DllImporter::Wrap_GetActivationFactory(WinToastStringWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotification).Get(), &_notificationFactory); + hr = (_aumi == AUMI) ? S_OK : E_FAIL; + } else { // AUMI Changed for the same app, let's update the current + // value! =) + PropVariantClear(&appIdPropVar); + hr = InitPropVariantFromString(_aumi.c_str(), &appIdPropVar); + if (SUCCEEDED(hr)) { + hr = + propertyStore->SetValue(PKEY_AppUserModel_ID, appIdPropVar); + if (SUCCEEDED(hr)) { + hr = propertyStore->Commit(); + if (SUCCEEDED(hr) && SUCCEEDED(persistFile->IsDirty())) { + hr = persistFile->Save(_path, TRUE); + } + } + } } + PropVariantClear(&appIdPropVar); + } } + } } - - return _isInitialized = SUCCEEDED(hr); + } + return hr; } -HRESULT WinToast::validateShellLink() { - - WCHAR _path[MAX_PATH]; - Util::defaultShellLinkPath(_appName, _path); - // Check if the file exist - DWORD attr = GetFileAttributes(_path); - if (attr >= 0xFFFFFFF) { - std::wcout << "Error, shell link not found. Try to create a new one in: " << _path << std::endl; - return E_FAIL; - } - - // Let's load the file as shell link to validate. - // - Create a shell link - // - Create a persistant file - // - Load the path as data for the persistant file - // - Read the property AUMI and validate with the current - // - Review if AUMI is equal. - ComPtr shellLink; - HRESULT hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink)); +HRESULT WinToast::createShellLink() { + WCHAR exePath[MAX_PATH]; + WCHAR slPath[MAX_PATH]; + Util::defaultShellLinkPath(_appName, slPath); + Util::defaultExecutablePath(exePath); + ComPtr shellLink; + HRESULT hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(&shellLink)); + if (SUCCEEDED(hr)) { + hr = shellLink->SetPath(exePath); if (SUCCEEDED(hr)) { - ComPtr persistFile; - hr = shellLink.As(&persistFile); + hr = shellLink->SetArguments(L""); + if (SUCCEEDED(hr)) { + hr = shellLink->SetWorkingDirectory(exePath); if (SUCCEEDED(hr)) { - hr = persistFile->Load(_path, STGM_READWRITE); + ComPtr propertyStore; + hr = shellLink.As(&propertyStore); + if (SUCCEEDED(hr)) { + PROPVARIANT appIdPropVar; + hr = InitPropVariantFromString(_aumi.c_str(), &appIdPropVar); if (SUCCEEDED(hr)) { - ComPtr propertyStore; - hr = shellLink.As(&propertyStore); + hr = propertyStore->SetValue(PKEY_AppUserModel_ID, appIdPropVar); + if (SUCCEEDED(hr)) { + hr = propertyStore->Commit(); if (SUCCEEDED(hr)) { - PROPVARIANT appIdPropVar; - hr = propertyStore->GetValue(PKEY_AppUserModel_ID, &appIdPropVar); - if (SUCCEEDED(hr)) { - WCHAR AUMI[MAX_PATH]; - hr = DllImporter::PropVariantToString(appIdPropVar, AUMI, MAX_PATH); - if (SUCCEEDED(hr)) { - hr = (_aumi == AUMI) ? S_OK : E_FAIL; - } else { // AUMI Changed for the same app, let's update the current value! =) - PropVariantClear(&appIdPropVar); - hr = InitPropVariantFromString(_aumi.c_str(), &appIdPropVar); - if (SUCCEEDED(hr)) { - hr = propertyStore->SetValue(PKEY_AppUserModel_ID, appIdPropVar); - if (SUCCEEDED(hr)) { - hr = propertyStore->Commit(); - if (SUCCEEDED(hr) && SUCCEEDED(persistFile->IsDirty())) { - hr = persistFile->Save(_path, TRUE); - } - } - } - } - PropVariantClear(&appIdPropVar); - } + ComPtr persistFile; + hr = shellLink.As(&persistFile); + if (SUCCEEDED(hr)) { + hr = persistFile->Save(slPath, TRUE); + } } + } } + PropVariantClear(&appIdPropVar); + } } + } } - return hr; + } + CoTaskMemFree(exePath); + CoTaskMemFree(slPath); + return hr; } +bool WinToast::showToast(_In_ const WinToastTemplate& toast, + _In_ WinToastHandler* handler) { + if (!isInitialized()) { + return _isInitialized; + } - -HRESULT WinToast::createShellLink() { - WCHAR exePath[MAX_PATH]; - WCHAR slPath[MAX_PATH]; - Util::defaultShellLinkPath(_appName, slPath); - Util::defaultExecutablePath(exePath); - ComPtr shellLink; - HRESULT hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink)); - if (SUCCEEDED(hr)) { - hr = shellLink->SetPath(exePath); + HRESULT hr = _notificationManager->GetTemplateContent( + ToastTemplateType(toast.type()), &_xmlDocument); + if (SUCCEEDED(hr)) { + const int fieldsCount = toast.textFieldsCount(); + for (int i = 0; i < fieldsCount && SUCCEEDED(hr); i++) { + hr = setTextField(toast.textField(WinToastTemplate::TextField(i)), i); + } + if (makeSilent(toast.isSilent())) { + if (SUCCEEDED(hr)) { if (SUCCEEDED(hr)) { - hr = shellLink->SetArguments(L""); + hr = toast.hasImage() ? setImageField(toast.imagePath()) : hr; + if (SUCCEEDED(hr)) { + hr = _notificationFactory->CreateToastNotification(xmlDocument(), + &_notification); if (SUCCEEDED(hr)) { - hr = shellLink->SetWorkingDirectory(exePath); - if (SUCCEEDED(hr)) { - ComPtr propertyStore; - hr = shellLink.As(&propertyStore); - if (SUCCEEDED(hr)) { - PROPVARIANT appIdPropVar; - hr = InitPropVariantFromString(_aumi.c_str(), &appIdPropVar); - if (SUCCEEDED(hr)) { - hr = propertyStore->SetValue(PKEY_AppUserModel_ID, appIdPropVar); - if (SUCCEEDED(hr)) { - hr = propertyStore->Commit(); - if (SUCCEEDED(hr)) { - ComPtr persistFile; - hr = shellLink.As(&persistFile); - if (SUCCEEDED(hr)) { - hr = persistFile->Save(slPath, TRUE); - } - } - } - } - PropVariantClear(&appIdPropVar); - } - } + hr = Util::setEventHandlers(notification(), handler); + if (SUCCEEDED(hr)) { + hr = _notifier->Show(notification()); + } } + } } + } + } else { + return false; } - CoTaskMemFree(exePath); - CoTaskMemFree(slPath); - return hr; + } + + return SUCCEEDED(hr); } - - -bool WinToast::showToast(_In_ const WinToastTemplate& toast, _In_ WinToastHandler* handler) { - if (!isInitialized()) { - return _isInitialized; - } - - HRESULT hr = _notificationManager->GetTemplateContent(ToastTemplateType(toast.type()), &_xmlDocument); - if (SUCCEEDED(hr)) { - const int fieldsCount = toast.textFieldsCount(); - for (int i = 0; i < fieldsCount && SUCCEEDED(hr); i++) { - hr = setTextField(toast.textField(WinToastTemplate::TextField(i)), i); - } - if (makeSilent(toast.isSilent())) { - if (SUCCEEDED(hr)) { - if (SUCCEEDED(hr)) { - hr = toast.hasImage() ? setImageField(toast.imagePath()) : hr; - if (SUCCEEDED(hr)) { - hr = _notificationFactory->CreateToastNotification(xmlDocument(), &_notification); - if (SUCCEEDED(hr)) { - hr = Util::setEventHandlers(notification(), handler); - if (SUCCEEDED(hr)) { - hr = _notifier->Show(notification()); - } - } - } - } - } - } else { - return false; - } - } - - return SUCCEEDED(hr); -} - - HRESULT WinToast::setTextField(_In_ const std::wstring& text, _In_ int pos) { - ComPtr nodeList; - HRESULT hr = _xmlDocument->GetElementsByTagName(WinToastStringWrapper(L"text").Get(), &nodeList); + ComPtr nodeList; + HRESULT hr = _xmlDocument->GetElementsByTagName( + WinToastStringWrapper(L"text").Get(), &nodeList); + if (SUCCEEDED(hr)) { + ComPtr node; + hr = nodeList->Item(pos, &node); if (SUCCEEDED(hr)) { - ComPtr node; - hr = nodeList->Item(pos, &node); - if (SUCCEEDED(hr)) { - hr = Util::setNodeStringValue(text, node.Get(), xmlDocument()); - } + hr = Util::setNodeStringValue(text, node.Get(), xmlDocument()); } - return hr; + } + return hr; } bool WinToast::makeSilent(bool is_silent) { - if (!is_silent) return true; - ScopedHString tag(L"toast"); - if (!tag.success()) - return false; + if (!is_silent) + return true; + ScopedHString tag(L"toast"); + if (!tag.success()) + return false; - ComPtr node_list; - if (FAILED(xmlDocument()->GetElementsByTagName(tag, &node_list))) - return false; + ComPtr node_list; + if (FAILED(xmlDocument()->GetElementsByTagName(tag, &node_list))) + return false; - ComPtr root; - if (FAILED(node_list->Item(0, &root))) - return false; + ComPtr root; + if (FAILED(node_list->Item(0, &root))) + return false; - ComPtr audio_element; - ScopedHString audio_str(L"audio"); - if (FAILED(xmlDocument()->CreateElement(audio_str, &audio_element))) - return false; + ComPtr audio_element; + ScopedHString audio_str(L"audio"); + if (FAILED(xmlDocument()->CreateElement(audio_str, &audio_element))) + return false; - ComPtr audio_node_tmp; - if (FAILED(audio_element.As(&audio_node_tmp))) - return false; + ComPtr audio_node_tmp; + if (FAILED(audio_element.As(&audio_node_tmp))) + return false; - // Append audio node to toast xml - ComPtr audio_node; - if (FAILED(root->AppendChild(audio_node_tmp.Get(), &audio_node))) - return false; + // Append audio node to toast xml + ComPtr audio_node; + if (FAILED(root->AppendChild(audio_node_tmp.Get(), &audio_node))) + return false; - // Create silent attribute - ComPtr attributes; - if (FAILED(audio_node->get_Attributes(&attributes))) - return false; + // Create silent attribute + ComPtr attributes; + if (FAILED(audio_node->get_Attributes(&attributes))) + return false; - ComPtr silent_attribute; - ScopedHString silent_str(L"silent"); - if (FAILED(xmlDocument()->CreateAttribute(silent_str, &silent_attribute))) - return false; + ComPtr silent_attribute; + ScopedHString silent_str(L"silent"); + if (FAILED(xmlDocument()->CreateAttribute(silent_str, &silent_attribute))) + return false; - ComPtr silent_attribute_node; - if (FAILED(silent_attribute.As(&silent_attribute_node))) - return false; + ComPtr silent_attribute_node; + if (FAILED(silent_attribute.As(&silent_attribute_node))) + return false; - // Set silent attribute to true - ScopedHString silent_value(L"true"); - if (!silent_value.success()) - return false; + // Set silent attribute to true + ScopedHString silent_value(L"true"); + if (!silent_value.success()) + return false; - ComPtr silent_text; - if (FAILED(xmlDocument()->CreateTextNode(silent_value, &silent_text))) - return false; + ComPtr silent_text; + if (FAILED(xmlDocument()->CreateTextNode(silent_value, &silent_text))) + return false; - ComPtr silent_node; - if (FAILED(silent_text.As(&silent_node))) - return false; + ComPtr silent_node; + if (FAILED(silent_text.As(&silent_node))) + return false; - ComPtr child_node; - if (FAILED( - silent_attribute_node->AppendChild(silent_node.Get(), &child_node))) - return false; + ComPtr child_node; + if (FAILED( + silent_attribute_node->AppendChild(silent_node.Get(), &child_node))) + return false; - ComPtr silent_attribute_pnode; - return SUCCEEDED(attributes.Get()->SetNamedItem(silent_attribute_node.Get(), - &silent_attribute_pnode)); + ComPtr silent_attribute_pnode; + return SUCCEEDED(attributes.Get()->SetNamedItem(silent_attribute_node.Get(), + &silent_attribute_pnode)); } - -HRESULT WinToast::setImageField(_In_ const std::wstring& path) { - wchar_t imagePath[MAX_PATH] = L"file:///"; - HRESULT hr = StringCchCat(imagePath, MAX_PATH, path.c_str()); +HRESULT WinToast::setImageField(_In_ const std::wstring& path) { + wchar_t imagePath[MAX_PATH] = L"file:///"; + HRESULT hr = StringCchCat(imagePath, MAX_PATH, path.c_str()); + if (SUCCEEDED(hr)) { + ComPtr nodeList; + hr = _xmlDocument->GetElementsByTagName( + WinToastStringWrapper(L"image").Get(), &nodeList); if (SUCCEEDED(hr)) { - ComPtr nodeList; - hr = _xmlDocument->GetElementsByTagName(WinToastStringWrapper(L"image").Get(), &nodeList); + ComPtr node; + hr = nodeList->Item(0, &node); + if (SUCCEEDED(hr)) { + ComPtr attributes; + hr = node->get_Attributes(&attributes); if (SUCCEEDED(hr)) { - ComPtr node; - hr = nodeList->Item(0, &node); - if (SUCCEEDED(hr)) { - ComPtr attributes; - hr = node->get_Attributes(&attributes); - if (SUCCEEDED(hr)) { - ComPtr editedNode; - hr = attributes->GetNamedItem(WinToastStringWrapper(L"src").Get(), &editedNode); - if (SUCCEEDED(hr)) { - Util::setNodeStringValue(imagePath, editedNode.Get(), xmlDocument()); - } - } - } + ComPtr editedNode; + hr = attributes->GetNamedItem(WinToastStringWrapper(L"src").Get(), + &editedNode); + if (SUCCEEDED(hr)) { + Util::setNodeStringValue(imagePath, editedNode.Get(), + xmlDocument()); + } } + } } - return hr; + } + return hr; } -WinToastTemplate::WinToastTemplate(const WinToastTemplateType& type) : - _type(type) -{ - initComponentsFromType(); +WinToastTemplate::WinToastTemplate(const WinToastTemplateType& type) + : _type(type) { + initComponentsFromType(); } - -WinToastTemplate::~WinToastTemplate() -{ - _textFields.clear(); +WinToastTemplate::~WinToastTemplate() { + _textFields.clear(); } -void WinToastTemplate::setTextField(_In_ const std::wstring& txt, _In_ const WinToastTemplate::TextField& pos) { - _textFields[pos] = txt; +void WinToastTemplate::setTextField( + _In_ const std::wstring& txt, + _In_ const WinToastTemplate::TextField& pos) { + _textFields[pos] = txt; } void WinToastTemplate::setImagePath(_In_ const std::wstring& imgPath) { - if (!_hasImage) - return; - _imagePath = imgPath; + if (!_hasImage) + return; + _imagePath = imgPath; } void WinToastTemplate::setSilent(bool is_silent) { - _silent = is_silent; + _silent = is_silent; } -int WinToastTemplate::TextFieldsCount[WinToastTemplateTypeCount] = { 1, 2, 2, 3, 1, 2, 2, 3}; +int WinToastTemplate::TextFieldsCount[WinToastTemplateTypeCount] = {1, 2, 2, 3, + 1, 2, 2, 3}; void WinToastTemplate::initComponentsFromType() { - _hasImage = _type < ToastTemplateType_ToastText01; - _textFields = std::vector(TextFieldsCount[_type], L""); + _hasImage = _type < ToastTemplateType_ToastText01; + _textFields = std::vector(TextFieldsCount[_type], L""); } -WinToastStringWrapper::WinToastStringWrapper(_In_reads_(length) PCWSTR stringRef, _In_ UINT32 length) throw() { - HRESULT hr = DllImporter::WindowsCreateStringReference(stringRef, length, &_header, &_hstring); - if (!SUCCEEDED(hr)) { - RaiseException(static_cast(STATUS_INVALID_PARAMETER), EXCEPTION_NONCONTINUABLE, 0, nullptr); - } +WinToastStringWrapper::WinToastStringWrapper(_In_reads_(length) + PCWSTR stringRef, + _In_ UINT32 length) throw() { + HRESULT hr = DllImporter::WindowsCreateStringReference(stringRef, length, + &_header, &_hstring); + if (!SUCCEEDED(hr)) { + RaiseException(static_cast(STATUS_INVALID_PARAMETER), + EXCEPTION_NONCONTINUABLE, 0, nullptr); + } } - -WinToastStringWrapper::WinToastStringWrapper(const std::wstring &stringRef) -{ - HRESULT hr = DllImporter::WindowsCreateStringReference(stringRef.c_str(), static_cast(stringRef.length()), &_header, &_hstring); - if (FAILED(hr)) { - RaiseException(static_cast(STATUS_INVALID_PARAMETER), EXCEPTION_NONCONTINUABLE, 0, nullptr); - } +WinToastStringWrapper::WinToastStringWrapper(const std::wstring& stringRef) { + HRESULT hr = DllImporter::WindowsCreateStringReference( + stringRef.c_str(), static_cast(stringRef.length()), &_header, + &_hstring); + if (FAILED(hr)) { + RaiseException(static_cast(STATUS_INVALID_PARAMETER), + EXCEPTION_NONCONTINUABLE, 0, nullptr); + } } WinToastStringWrapper::~WinToastStringWrapper() { - DllImporter::WindowsDeleteString(_hstring); + DllImporter::WindowsDeleteString(_hstring); } HSTRING WinToastStringWrapper::Get() const { - return _hstring; -} \ No newline at end of file + return _hstring; +} diff --git a/atom/browser/ui/win/toast_lib.h b/atom/browser/ui/win/toast_lib.h index c7fd0f81d626..8b9907767bfa 100644 --- a/atom/browser/ui/win/toast_lib.h +++ b/atom/browser/ui/win/toast_lib.h @@ -9,8 +9,8 @@ // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, @@ -20,149 +20,179 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#ifndef WINTOASTLIB_H -#define WINTOASTLIB_H -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -using namespace Microsoft::WRL; -using namespace ABI::Windows::Data::Xml::Dom; -using namespace ABI::Windows::Foundation; -using namespace ABI::Windows::UI::Notifications; -using namespace Windows::Foundation; +#ifndef ATOM_BROWSER_UI_WIN_TOAST_LIB_H_ +#define ATOM_BROWSER_UI_WIN_TOAST_LIB_H_ -#define DEFAULT_SHELL_LINKS_PATH L"\\Microsoft\\Windows\\Start Menu\\Programs\\" -#define DEFAULT_LINK_FORMAT L".lnk" +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using Microsoft::WRL::ComPtr; +using Microsoft::WRL::Details::ComPtrRef; +using Microsoft::WRL::Callback; +using Microsoft::WRL::Implements; +using Microsoft::WRL::RuntimeClassFlags; +using Microsoft::WRL::ClassicCom; +using ABI::Windows::Data::Xml::Dom::IXmlAttribute; +using ABI::Windows::Data::Xml::Dom::IXmlDocument; +using ABI::Windows::Data::Xml::Dom::IXmlElement; +using ABI::Windows::Data::Xml::Dom::IXmlNamedNodeMap; +using ABI::Windows::Data::Xml::Dom::IXmlNode; +using ABI::Windows::Data::Xml::Dom::IXmlNodeList; +using ABI::Windows::Data::Xml::Dom::IXmlText; +using ABI::Windows::UI::Notifications::ToastDismissalReason; +using ABI::Windows::UI::Notifications::ToastTemplateType; +using ABI::Windows::UI::Notifications::IToastNotificationManagerStatics; +using ABI::Windows::UI::Notifications::IToastNotifier; +using ABI::Windows::UI::Notifications::IToastNotificationFactory; +using ABI::Windows::UI::Notifications::IToastNotification; +using ABI::Windows::UI::Notifications::ToastNotification; +using ABI::Windows::UI::Notifications::ToastDismissedEventArgs; +using ABI::Windows::UI::Notifications::IToastDismissedEventArgs; +using ABI::Windows::UI::Notifications::ToastFailedEventArgs; +using ABI::Windows::UI::Notifications::IToastFailedEventArgs; +using ABI::Windows::UI::Notifications::ToastTemplateType_ToastText01; +using ABI::Windows::Foundation::ITypedEventHandler; + +#define DEFAULT_SHELL_LINKS_PATH L"\\Microsoft\\Windows\\Start Menu\\Programs\\" +#define DEFAULT_LINK_FORMAT L".lnk" namespace WinToastLib { - class WinToastStringWrapper { - public: - WinToastStringWrapper(_In_reads_(length) PCWSTR stringRef, _In_ UINT32 length) throw(); - WinToastStringWrapper(_In_ const std::wstring &stringRef) throw(); - ~WinToastStringWrapper(); - HSTRING Get() const throw(); - private: - HSTRING _hstring; - HSTRING_HEADER _header; +class WinToastStringWrapper { + public: + WinToastStringWrapper(_In_reads_(length) PCWSTR stringRef, + _In_ UINT32 length) throw(); + explicit WinToastStringWrapper(_In_ const std::wstring& stringRef) throw(); + ~WinToastStringWrapper(); + HSTRING Get() const throw(); - }; + private: + HSTRING _hstring; + HSTRING_HEADER _header; +}; +class WinToastHandler { + public: + enum WinToastDismissalReason { + UserCanceled = ToastDismissalReason::ToastDismissalReason_UserCanceled, + ApplicationHidden = + ToastDismissalReason::ToastDismissalReason_ApplicationHidden, + TimedOut = ToastDismissalReason::ToastDismissalReason_TimedOut + }; - class WinToastHandler { - public: - enum WinToastDismissalReason { - UserCanceled = ToastDismissalReason::ToastDismissalReason_UserCanceled, - ApplicationHidden = ToastDismissalReason::ToastDismissalReason_ApplicationHidden, - TimedOut = ToastDismissalReason::ToastDismissalReason_TimedOut - }; + virtual void toastActivated() {} + virtual void toastDismissed(WinToastDismissalReason state) {} + virtual void toastFailed() {} +}; - virtual void toastActivated() {}; - virtual void toastDismissed(WinToastDismissalReason state) {}; - virtual void toastFailed() {}; - }; +class WinToastTemplate { + public: + enum TextField { FirstLine = 0, SecondLine, ThirdLine, LineCount }; - class WinToastTemplate - { - public: - enum TextField { - FirstLine = 0, - SecondLine, - ThirdLine, - LineCount - }; + enum WinToastTemplateType { + ImageWithOneLine = ToastTemplateType::ToastTemplateType_ToastImageAndText01, + ImageWithTwoLines = + ToastTemplateType::ToastTemplateType_ToastImageAndText02, + ImageWithThreeLines = + ToastTemplateType::ToastTemplateType_ToastImageAndText03, + ImageWithFourLines = + ToastTemplateType::ToastTemplateType_ToastImageAndText04, + TextOneLine = ToastTemplateType::ToastTemplateType_ToastText01, + TextTwoLines = ToastTemplateType::ToastTemplateType_ToastText02, + TextThreeLines = ToastTemplateType::ToastTemplateType_ToastText03, + TextFourLines = ToastTemplateType::ToastTemplateType_ToastText04, + WinToastTemplateTypeCount + }; - enum WinToastTemplateType { - ImageWithOneLine = ToastTemplateType::ToastTemplateType_ToastImageAndText01, - ImageWithTwoLines = ToastTemplateType::ToastTemplateType_ToastImageAndText02, - ImageWithThreeLines = ToastTemplateType::ToastTemplateType_ToastImageAndText03, - ImageWithFourLines = ToastTemplateType::ToastTemplateType_ToastImageAndText04, - TextOneLine = ToastTemplateType::ToastTemplateType_ToastText01, - TextTwoLines = ToastTemplateType::ToastTemplateType_ToastText02, - TextThreeLines = ToastTemplateType::ToastTemplateType_ToastText03, - TextFourLines = ToastTemplateType::ToastTemplateType_ToastText04, - WinToastTemplateTypeCount - }; + explicit WinToastTemplate( + _In_ const WinToastTemplateType& type = ImageWithTwoLines); + ~WinToastTemplate(); - WinToastTemplate(_In_ const WinToastTemplateType& type = ImageWithTwoLines); - ~WinToastTemplate(); + int textFieldsCount() const { return _textFields.size(); } + bool hasImage() const { return _hasImage; } + std::vector textFields() const { return _textFields; } + std::wstring textField(_In_ const TextField& pos) const { + return _textFields[pos]; + } + std::wstring imagePath() const { return _imagePath; } + WinToastTemplateType type() const { return _type; } + void setTextField(_In_ const std::wstring& txt, _In_ const TextField& pos); + void setImagePath(_In_ const std::wstring& imgPath); + void setSilent(bool is_silent); + bool isSilent() const { return _silent; } - int textFieldsCount() const { return _textFields.size(); } - bool hasImage() const { return _hasImage; } - std::vector textFields() const { return _textFields; } - std::wstring textField(_In_ const TextField& pos) const { return _textFields[pos]; } - std::wstring imagePath() const { return _imagePath; } - WinToastTemplateType type() const { return _type; } - void setTextField(_In_ const std::wstring& txt, _In_ const TextField& pos); - void setImagePath(_In_ const std::wstring& imgPath); - void setSilent(bool is_silent); - bool isSilent() const { return _silent; } - private: - static int TextFieldsCount[WinToastTemplateTypeCount]; - bool _hasImage; - bool _silent = false; - std::vector _textFields; - std::wstring _imagePath; - WinToastTemplateType _type; - void initComponentsFromType(); - }; + private: + static int TextFieldsCount[WinToastTemplateTypeCount]; + bool _hasImage; + bool _silent = false; + std::vector _textFields; + std::wstring _imagePath; + WinToastTemplateType _type; + void initComponentsFromType(); +}; - class WinToast { - public: - static WinToast* instance(); - static bool isCompatible(); - static std::wstring configureAUMI(_In_ const std::wstring& company, - _In_ const std::wstring& name, - _In_ const std::wstring& surname, - _In_ const std::wstring& versionInfo - ); - bool initialize(); - bool isInitialized() const { return _isInitialized; } - bool showToast(_In_ const WinToastTemplate& toast, _In_ WinToastHandler* handler); - std::wstring appName() const; - std::wstring appUserModelId() const; - void setAppUserModelId(_In_ const std::wstring& appName); - void setAppName(_In_ const std::wstring& appName); - private: - bool _isInitialized; - std::wstring _appName; - std::wstring _aumi; - ComPtr _xmlDocument; - ComPtr _notificationManager; - ComPtr _notifier; - ComPtr _notificationFactory; - ComPtr _notification; - static WinToast* _instance; +class WinToast { + public: + static WinToast* instance(); + static bool isCompatible(); + static std::wstring configureAUMI(_In_ const std::wstring& company, + _In_ const std::wstring& name, + _In_ const std::wstring& surname, + _In_ const std::wstring& versionInfo); + bool initialize(); + bool isInitialized() const { return _isInitialized; } + bool showToast(_In_ const WinToastTemplate& toast, + _In_ WinToastHandler* handler); + std::wstring appName() const; + std::wstring appUserModelId() const; + void setAppUserModelId(_In_ const std::wstring& appName); + void setAppName(_In_ const std::wstring& appName); - WinToast(void); - IXmlDocument* xmlDocument() const { return _xmlDocument.Get(); } - IToastNotifier* notifier() const { return _notifier.Get(); } - IToastNotificationFactory* notificationFactory() const { return _notificationFactory.Get(); } - IToastNotificationManagerStatics* notificationManager() const { return _notificationManager.Get(); } - IToastNotification* notification() const { return _notification.Get(); } + private: + bool _isInitialized; + std::wstring _appName; + std::wstring _aumi; + ComPtr _xmlDocument; + ComPtr _notificationManager; + ComPtr _notifier; + ComPtr _notificationFactory; + ComPtr _notification; + static WinToast* _instance; - HRESULT validateShellLink(); - HRESULT createShellLink(); - HRESULT setImageField(_In_ const std::wstring& path); - HRESULT setTextField(_In_ const std::wstring& text, _In_ int pos); - bool makeSilent(bool is_silent); - }; + WinToast(void); + IXmlDocument* xmlDocument() const { return _xmlDocument.Get(); } + IToastNotifier* notifier() const { return _notifier.Get(); } + IToastNotificationFactory* notificationFactory() const { + return _notificationFactory.Get(); + } + IToastNotificationManagerStatics* notificationManager() const { + return _notificationManager.Get(); + } + IToastNotification* notification() const { return _notification.Get(); } + HRESULT validateShellLink(); + HRESULT createShellLink(); + HRESULT setImageField(_In_ const std::wstring& path); + HRESULT setTextField(_In_ const std::wstring& text, _In_ int pos); + bool makeSilent(bool is_silent); +}; +} // namespace WinToastLib - -} - -#endif // WINTOASTLIB_H \ No newline at end of file +#endif // ATOM_BROWSER_UI_WIN_TOAST_LIB_H_ diff --git a/default_app/default_app.js b/default_app/default_app.js index d002a8d24aee..afff67669ee5 100644 --- a/default_app/default_app.js +++ b/default_app/default_app.js @@ -32,14 +32,14 @@ exports.load = (appUrl) => { title: 'Hello World', body: 'This is the long and complicated body for this notification that just goes on and on and on and never really seems to stop', silent: true, - icon: '/Users/samuel/Downloads/ninja.png', + icon: path.resolve('C:\\Users\\Samuel\\Downloads\\icon.png'), hasReply: true, replyPlaceholder: 'Type Here!!' - }); - n.on('show', () => console.log('showed')); - n.on('click', () => console.info('clicked!!')); - n.on('reply', (e, reply) => console.log('Replied:', reply)); + }) + n.on('show', () => console.log('showed')) + n.on('click', () => console.info('clicked!!')) + n.on('reply', (e, reply) => console.log('Replied:', reply)) - n.show(); + n.show() }) } From da31615de5758ee6c22bead5a73d93367a236919 Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Mon, 24 Apr 2017 17:30:42 +1000 Subject: [PATCH 07/22] Basic linux impl --- .../api/atom_api_notification_linux.cc | 129 ++++++++++++++++++ filenames.gypi | 1 + 2 files changed, 130 insertions(+) create mode 100644 atom/browser/api/atom_api_notification_linux.cc diff --git a/atom/browser/api/atom_api_notification_linux.cc b/atom/browser/api/atom_api_notification_linux.cc new file mode 100644 index 000000000000..203a66ecf607 --- /dev/null +++ b/atom/browser/api/atom_api_notification_linux.cc @@ -0,0 +1,129 @@ +// Copyright (c) 2014 GitHub, Inc. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "atom/browser/api/atom_api_notification.h" + +#include + +#include "atom/browser/browser.h" +#include "browser/linux/libnotify_loader.h" +#include "browser/linux/libnotify_notification.h" +#include "chrome/browser/ui/libgtkui/skia_utils_gtk.h" +#include "third_party/skia/include/core/SkBitmap.h" + +namespace atom { + +namespace api { + +bool initialized_ = false; +bool available_ = false; + +LibNotifyLoader libnotify_loader_; + +bool HasCapability(const std::string& capability) { + bool result = false; + GList* capabilities = libnotify_loader_.notify_get_server_caps(); + + if (g_list_find_custom(capabilities, capability.c_str(), + (GCompareFunc)g_strcmp0) != NULL) + result = true; + + g_list_free_full(capabilities, g_free); + + return result; +} + +bool NotifierSupportsActions() { + if (getenv("ELECTRON_USE_UBUNTU_NOTIFIER")) + return false; + + static bool notify_has_result = false; + static bool notify_result = false; + + if (notify_has_result) + return notify_result; + + notify_result = HasCapability("actions"); + return notify_result; +} + +void log_and_clear_error(GError* error, const char* context) { + LOG(ERROR) << context + << ": domain=" << error->domain + << " code=" << error->code + << " message=\"" << error->message << '"'; + g_error_free(error); +} + +void Notification::Show() { + if (!available_) return; + + NotifyNotification* notification_ = libnotify_loader_.notify_notification_new( + base::UTF16ToUTF8(title_).c_str(), + base::UTF16ToUTF8(body_).c_str(), + nullptr); + + GQuark id = g_quark_from_string(std::to_string(id_).c_str()); + g_object_set(G_OBJECT(notification_), "id", id, NULL); + + // NB: On Unity and on any other DE using Notify-OSD, adding a notification + // action will cause the notification to display as a modal dialog box. + // Can't make this work, need linux help :D + // if (NotifierSupportsActions()) { + // libnotify_loader_.notify_notification_add_action( + // notification_, "default", "View", OnClickedCallback, this, + // nullptr); + // } + + if (has_icon_) { + SkBitmap image = *(icon_.ToSkBitmap()); + GdkPixbuf* pixbuf = libgtkui::GdkPixbufFromSkBitmap(image); + libnotify_loader_.notify_notification_set_image_from_pixbuf( + notification_, pixbuf); + libnotify_loader_.notify_notification_set_timeout( + notification_, NOTIFY_EXPIRES_DEFAULT); + g_object_unref(pixbuf); + } + + // Always try to append notifications. + // Unique tags can be used to prevent this. + if (HasCapability("append")) { + libnotify_loader_.notify_notification_set_hint_string( + notification_, "append", "true"); + } else if (HasCapability("x-canonical-append")) { + libnotify_loader_.notify_notification_set_hint_string( + notification_, "x-canonical-append", "true"); + } + + GError* error = nullptr; + libnotify_loader_.notify_notification_show(notification_, &error); + if (error) { + log_and_clear_error(error, "notify_notification_show"); + return; + } + + OnShown(); +} + +void Notification::OnInitialProps() { + if (!initialized_) { + initialized_ = true; + if (!libnotify_loader_.Load("libnotify.so.4") && // most common one + !libnotify_loader_.Load("libnotify.so.5") && + !libnotify_loader_.Load("libnotify.so.1") && + !libnotify_loader_.Load("libnotify.so")) { + return; + } + Browser* browser = Browser::Get(); + if (!libnotify_loader_.notify_is_initted() && + !libnotify_loader_.notify_init(browser->GetName().c_str())) { + return; + } + available_ = true; + } +} + +void Notification::NotifyPropsUpdated() {} +} // namespace api +} // namespace atom diff --git a/filenames.gypi b/filenames.gypi index fb67697965a9..100eb2ee503b 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -132,6 +132,7 @@ 'atom/browser/api/atom_api_notification.h', 'atom/browser/api/atom_api_notification_mac.mm', 'atom/browser/api/atom_api_notification_win.cc', + 'atom/browser/api/atom_api_notification_linux.cc', 'atom/browser/api/atom_api_power_monitor.cc', 'atom/browser/api/atom_api_power_monitor.h', 'atom/browser/api/atom_api_power_save_blocker.cc', From e7569ed4993450e2d7dc1215051467454f548be9 Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Sat, 20 May 2017 19:25:59 +1000 Subject: [PATCH 08/22] Remove shell link validation, users should do this themselves --- atom/browser/ui/win/toast_lib.cc | 147 +++---------------------------- atom/browser/ui/win/toast_lib.h | 6 -- 2 files changed, 12 insertions(+), 141 deletions(-) diff --git a/atom/browser/ui/win/toast_lib.cc b/atom/browser/ui/win/toast_lib.cc index 1141a3d73a0e..83f185f5bb26 100644 --- a/atom/browser/ui/win/toast_lib.cc +++ b/atom/browser/ui/win/toast_lib.cc @@ -245,18 +245,6 @@ bool WinToast::isCompatible() { (DllImporter::WindowsDeleteString == nullptr)); } -std::wstring WinToast::configureAUMI(_In_ const std::wstring& company, - _In_ const std::wstring& name, - _In_ const std::wstring& surname, - _In_ const std::wstring& versionInfo) { - std::wstring aumi = company; - aumi += L"." + name; - aumi += L"." + surname; - aumi += L"." + versionInfo; - - return aumi; -} - bool WinToast::initialize() { if (_aumi.empty() || _appName.empty()) { return _isInitialized = false; @@ -271,137 +259,26 @@ bool WinToast::initialize() { return _isInitialized = false; } - HRESULT hr = validateShellLink(); - if (FAILED(hr)) { - hr = createShellLink(); - } - + HRESULT hr = DllImporter::Wrap_GetActivationFactory( + WinToastStringWrapper( + RuntimeClass_Windows_UI_Notifications_ToastNotificationManager) + .Get(), + &_notificationManager); if (SUCCEEDED(hr)) { - hr = DllImporter::Wrap_GetActivationFactory( - WinToastStringWrapper( - RuntimeClass_Windows_UI_Notifications_ToastNotificationManager) - .Get(), - &_notificationManager); + hr = notificationManager()->CreateToastNotifierWithId( + WinToastStringWrapper(_aumi).Get(), &_notifier); if (SUCCEEDED(hr)) { - hr = notificationManager()->CreateToastNotifierWithId( - WinToastStringWrapper(_aumi).Get(), &_notifier); - if (SUCCEEDED(hr)) { - hr = DllImporter::Wrap_GetActivationFactory( - WinToastStringWrapper( - RuntimeClass_Windows_UI_Notifications_ToastNotification) - .Get(), - &_notificationFactory); - } + hr = DllImporter::Wrap_GetActivationFactory( + WinToastStringWrapper( + RuntimeClass_Windows_UI_Notifications_ToastNotification) + .Get(), + &_notificationFactory); } } return _isInitialized = SUCCEEDED(hr); } -HRESULT WinToast::validateShellLink() { - WCHAR _path[MAX_PATH]; - Util::defaultShellLinkPath(_appName, _path); - // Check if the file exist - DWORD attr = GetFileAttributes(_path); - if (attr >= 0xFFFFFFF) { - std::wcout << "Error, shell link not found. Try to create a new one in: " - << _path << std::endl; - return E_FAIL; - } - - // Let's load the file as shell link to validate. - // - Create a shell link - // - Create a persistant file - // - Load the path as data for the persistant file - // - Read the property AUMI and validate with the current - // - Review if AUMI is equal. - ComPtr shellLink; - HRESULT hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, - IID_PPV_ARGS(&shellLink)); - if (SUCCEEDED(hr)) { - ComPtr persistFile; - hr = shellLink.As(&persistFile); - if (SUCCEEDED(hr)) { - hr = persistFile->Load(_path, STGM_READWRITE); - if (SUCCEEDED(hr)) { - ComPtr propertyStore; - hr = shellLink.As(&propertyStore); - if (SUCCEEDED(hr)) { - PROPVARIANT appIdPropVar; - hr = propertyStore->GetValue(PKEY_AppUserModel_ID, &appIdPropVar); - if (SUCCEEDED(hr)) { - WCHAR AUMI[MAX_PATH]; - hr = DllImporter::PropVariantToString(appIdPropVar, AUMI, MAX_PATH); - if (SUCCEEDED(hr)) { - hr = (_aumi == AUMI) ? S_OK : E_FAIL; - } else { // AUMI Changed for the same app, let's update the current - // value! =) - PropVariantClear(&appIdPropVar); - hr = InitPropVariantFromString(_aumi.c_str(), &appIdPropVar); - if (SUCCEEDED(hr)) { - hr = - propertyStore->SetValue(PKEY_AppUserModel_ID, appIdPropVar); - if (SUCCEEDED(hr)) { - hr = propertyStore->Commit(); - if (SUCCEEDED(hr) && SUCCEEDED(persistFile->IsDirty())) { - hr = persistFile->Save(_path, TRUE); - } - } - } - } - PropVariantClear(&appIdPropVar); - } - } - } - } - } - return hr; -} - -HRESULT WinToast::createShellLink() { - WCHAR exePath[MAX_PATH]; - WCHAR slPath[MAX_PATH]; - Util::defaultShellLinkPath(_appName, slPath); - Util::defaultExecutablePath(exePath); - ComPtr shellLink; - HRESULT hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, - IID_PPV_ARGS(&shellLink)); - if (SUCCEEDED(hr)) { - hr = shellLink->SetPath(exePath); - if (SUCCEEDED(hr)) { - hr = shellLink->SetArguments(L""); - if (SUCCEEDED(hr)) { - hr = shellLink->SetWorkingDirectory(exePath); - if (SUCCEEDED(hr)) { - ComPtr propertyStore; - hr = shellLink.As(&propertyStore); - if (SUCCEEDED(hr)) { - PROPVARIANT appIdPropVar; - hr = InitPropVariantFromString(_aumi.c_str(), &appIdPropVar); - if (SUCCEEDED(hr)) { - hr = propertyStore->SetValue(PKEY_AppUserModel_ID, appIdPropVar); - if (SUCCEEDED(hr)) { - hr = propertyStore->Commit(); - if (SUCCEEDED(hr)) { - ComPtr persistFile; - hr = shellLink.As(&persistFile); - if (SUCCEEDED(hr)) { - hr = persistFile->Save(slPath, TRUE); - } - } - } - } - PropVariantClear(&appIdPropVar); - } - } - } - } - } - CoTaskMemFree(exePath); - CoTaskMemFree(slPath); - return hr; -} - bool WinToast::showToast(_In_ const WinToastTemplate& toast, _In_ WinToastHandler* handler) { if (!isInitialized()) { diff --git a/atom/browser/ui/win/toast_lib.h b/atom/browser/ui/win/toast_lib.h index 8b9907767bfa..10e3e5995bb1 100644 --- a/atom/browser/ui/win/toast_lib.h +++ b/atom/browser/ui/win/toast_lib.h @@ -152,10 +152,6 @@ class WinToast { public: static WinToast* instance(); static bool isCompatible(); - static std::wstring configureAUMI(_In_ const std::wstring& company, - _In_ const std::wstring& name, - _In_ const std::wstring& surname, - _In_ const std::wstring& versionInfo); bool initialize(); bool isInitialized() const { return _isInitialized; } bool showToast(_In_ const WinToastTemplate& toast, @@ -187,8 +183,6 @@ class WinToast { } IToastNotification* notification() const { return _notification.Get(); } - HRESULT validateShellLink(); - HRESULT createShellLink(); HRESULT setImageField(_In_ const std::wstring& path); HRESULT setTextField(_In_ const std::wstring& text, _In_ int pos); bool makeSilent(bool is_silent); From 8815da314ffde125963695198bc11229789ab440 Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Sat, 20 May 2017 21:25:26 +1000 Subject: [PATCH 09/22] Add Notification API docs --- docs/api/notification.md | 85 ++++++++++++++++++++++++++++++++++ docs/tutorial/notifications.md | 4 +- 2 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 docs/api/notification.md diff --git a/docs/api/notification.md b/docs/api/notification.md new file mode 100644 index 000000000000..18945a884fe1 --- /dev/null +++ b/docs/api/notification.md @@ -0,0 +1,85 @@ +# Notification + +> Create OS desktop notifications + +Process: [Main](../glossary.md#main-process) + +## Using in the renderer process + +If you want to use Notifications in a renderer process you should use the [HTML5 Notification API](../tutorial/notifications.md) + +## Class: Notification + +> Create OS desktop notifications + +Process: [Main](../glossary.md#main-process) + +`Notification` is an +[EventEmitter](http://nodejs.org/api/events.html#events_class_events_eventemitter). + +It creates a new `Notification` with native properties as set by the `options`. + +### `new Notification([options])` + +* `options` Object + * `title` String - A title for the notification, which will be shown at the top of the notification window when it is shown + * `body` String - The body text of the notification, which will be displayed below the title + * `silent` Boolean - (Optional) Whether or not to emit an OS notification noise when showing the notification + * `icon` [NativeImage](native-image.md) - (Optional) An icon to use in the notification + * `hasReply` Boolean - (Optional) Whether or not to add an inline reply option to the notification. _macOS_ + * `replyPlaceholder` String - (Optional) The placeholder to write in the inline reply input field. _macOS_ + + +### Instance Events + +Objects created with `new Notification` emit the following events: + +**Note:** Some events are only available on specific operating systems and are +labeled as such. + +#### Event: 'click' + +Returns: + +* `event` Event + +Emitted when the document changed its title, calling `event.preventDefault()` +will prevent the native window's title from changing. + +#### Event: 'close' + +Returns: + +* `event` Event + +Emitted when the notification is shown to the user, note this could be fired +multiple times as a notification can be shown multiple times through the +`show()` method. + +#### Event: 'click' + +Returns: + +* `event` Event + +Emitted when the notification is clicked by the user. + +#### Event: 'reply' _macOS_ + +Returns: + +* `event` Event +* `reply` String - The string the user entered into the inline reply field + +Emitted when the user clicks the "Reply" button on a notification with `hasReply: true`. + +### Instance Methods + +Objects created with `new Notification` have the following instance methods: + +#### `notification.show()` + +Immediately shows the notification to the user, please note this means unlike the +HTML5 Notification implementation, simply instantiating a `new Notification` does +not immediately show it to the user, you need to call this method before the OS +will display it. diff --git a/docs/tutorial/notifications.md b/docs/tutorial/notifications.md index 34c64ad9bb4e..2ded823f9178 100644 --- a/docs/tutorial/notifications.md +++ b/docs/tutorial/notifications.md @@ -5,7 +5,9 @@ to the user. Electron conveniently allows developers to send notifications with the [HTML5 Notification API](https://notifications.spec.whatwg.org/), using the currently running operating system's native notification APIs to display it. -**Note:** Since this is an HTML5 API it is only available in the renderer process. +**Note:** Since this is an HTML5 API it is only available in the renderer process. If +you want to show Notifications in the main process please check out the +[Notification](../api/notification.md) module. ```javascript let myNotification = new Notification('Title', { From f5de57dbdbf92054c6e675a0707abfe07b7af363 Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Sat, 20 May 2017 21:25:56 +1000 Subject: [PATCH 10/22] Mark notification API as experimental --- docs/api/notification.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api/notification.md b/docs/api/notification.md index 18945a884fe1..abab31df3c9c 100644 --- a/docs/api/notification.md +++ b/docs/api/notification.md @@ -19,7 +19,7 @@ Process: [Main](../glossary.md#main-process) It creates a new `Notification` with native properties as set by the `options`. -### `new Notification([options])` +### `new Notification([options])` _Experimental_ * `options` Object * `title` String - A title for the notification, which will be shown at the top of the notification window when it is shown From d1fc832ca01b955eeebd645da90c7010578e6d1d Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Tue, 23 May 2017 02:17:59 +1000 Subject: [PATCH 11/22] Update for the brightray move --- atom/browser/api/atom_api_notification_linux.cc | 4 ++-- atom/browser/api/atom_api_notification_win.cc | 8 ++++---- atom/browser/ui/notification_delegate_adapter.cc | 2 +- atom/browser/ui/notification_delegate_adapter.h | 2 +- atom/browser/ui/win/toast_lib.cc | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/atom/browser/api/atom_api_notification_linux.cc b/atom/browser/api/atom_api_notification_linux.cc index 203a66ecf607..109328b47f81 100644 --- a/atom/browser/api/atom_api_notification_linux.cc +++ b/atom/browser/api/atom_api_notification_linux.cc @@ -7,8 +7,8 @@ #include #include "atom/browser/browser.h" -#include "browser/linux/libnotify_loader.h" -#include "browser/linux/libnotify_notification.h" +#include "brightray/browser/linux/libnotify_loader.h" +#include "brightray/browser/linux/libnotify_notification.h" #include "chrome/browser/ui/libgtkui/skia_utils_gtk.h" #include "third_party/skia/include/core/SkBitmap.h" diff --git a/atom/browser/api/atom_api_notification_win.cc b/atom/browser/api/atom_api_notification_win.cc index c740fa9a9e22..ce0e90b938dc 100644 --- a/atom/browser/api/atom_api_notification_win.cc +++ b/atom/browser/api/atom_api_notification_win.cc @@ -11,10 +11,10 @@ #include "base/files/file_util.h" #include "base/md5.h" #include "base/strings/utf_string_conversions.h" -#include "browser/notification.h" -#include "browser/notification_presenter.h" -#include "browser/win/notification_presenter_win.h" -#include "browser/win/notification_presenter_win7.h" +#include "brightray/browser/notification.h" +#include "brightray/browser/notification_presenter.h" +#include "brightray/browser/win/notification_presenter_win.h" +#include "brightray/browser/win/notification_presenter_win7.h" #include "common/string_conversion.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/gfx/codec/png_codec.h" diff --git a/atom/browser/ui/notification_delegate_adapter.cc b/atom/browser/ui/notification_delegate_adapter.cc index d141be43ea64..d44557daa932 100644 --- a/atom/browser/ui/notification_delegate_adapter.cc +++ b/atom/browser/ui/notification_delegate_adapter.cc @@ -5,7 +5,7 @@ #include "atom/browser/ui/notification_delegate_adapter.h" #include "atom/browser/api/atom_api_notification.h" -#include "browser/notification_delegate.h" +#include "brightray/browser/notification_delegate.h" namespace atom { diff --git a/atom/browser/ui/notification_delegate_adapter.h b/atom/browser/ui/notification_delegate_adapter.h index 89e06e3ad1eb..087a207f80d3 100644 --- a/atom/browser/ui/notification_delegate_adapter.h +++ b/atom/browser/ui/notification_delegate_adapter.h @@ -2,7 +2,7 @@ // Use of this source code is governed by the MIT license that can be // found in the LICENSE file. -#include "browser/notification_delegate.h" +#include "brightray/browser/notification_delegate.h" #include "atom/browser/api/atom_api_notification.h" diff --git a/atom/browser/ui/win/toast_lib.cc b/atom/browser/ui/win/toast_lib.cc index 83f185f5bb26..5002a28f7dc0 100644 --- a/atom/browser/ui/win/toast_lib.cc +++ b/atom/browser/ui/win/toast_lib.cc @@ -24,7 +24,7 @@ #include -#include "browser/win/scoped_hstring.h" +#include "brightray/browser/win/scoped_hstring.h" #pragma comment(lib, "shlwapi") #pragma comment(lib, "user32") From 193c5618158fc4a87d760aa149a4541836097589 Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Tue, 23 May 2017 02:19:03 +1000 Subject: [PATCH 12/22] Remove sample notification code --- default_app/default_app.js | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/default_app/default_app.js b/default_app/default_app.js index afff67669ee5..2a3ce3a85ec1 100644 --- a/default_app/default_app.js +++ b/default_app/default_app.js @@ -1,4 +1,4 @@ -const {app, BrowserWindow, Notification} = require('electron') +const {app, BrowserWindow} = require('electron') const path = require('path') let mainWindow = null @@ -27,19 +27,5 @@ exports.load = (appUrl) => { mainWindow = new BrowserWindow(options) mainWindow.loadURL(appUrl) mainWindow.focus() - - const n = new Notification({ - title: 'Hello World', - body: 'This is the long and complicated body for this notification that just goes on and on and on and never really seems to stop', - silent: true, - icon: path.resolve('C:\\Users\\Samuel\\Downloads\\icon.png'), - hasReply: true, - replyPlaceholder: 'Type Here!!' - }) - n.on('show', () => console.log('showed')) - n.on('click', () => console.info('clicked!!')) - n.on('reply', (e, reply) => console.log('Replied:', reply)) - - n.show() }) } From 058bdfbced2e9c8676f87f3461273203c52528de Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Mon, 29 May 2017 20:02:33 +1000 Subject: [PATCH 13/22] Use NotificationPresenter - macOS --- atom/browser/api/atom_api_notification_mac.mm | 93 ++++++------------- .../ui/notification_delegate_adapter.cc | 3 + .../ui/notification_delegate_adapter.h | 1 + .../browser/linux/libnotify_notification.cc | 4 +- .../browser/linux/libnotify_notification.h | 4 +- brightray/browser/mac/cocoa_notification.h | 5 +- brightray/browser/mac/cocoa_notification.mm | 13 ++- .../mac/notification_center_delegate.mm | 10 +- brightray/browser/notification.h | 4 +- brightray/browser/notification_delegate.h | 4 + .../browser/notification_delegate_adapter.cc | 4 + .../browser/platform_notification_service.cc | 3 +- brightray/browser/win/win32_notification.cc | 3 +- brightray/browser/win/win32_notification.h | 3 +- .../browser/win/windows_toast_notification.cc | 4 +- .../browser/win/windows_toast_notification.h | 4 +- default_app/default_app.js | 11 ++- 17 files changed, 93 insertions(+), 80 deletions(-) diff --git a/atom/browser/api/atom_api_notification_mac.mm b/atom/browser/api/atom_api_notification_mac.mm index c56955da2e02..fed369c15bef 100644 --- a/atom/browser/api/atom_api_notification_mac.mm +++ b/atom/browser/api/atom_api_notification_mac.mm @@ -7,89 +7,48 @@ #import #include "atom/browser/browser.h" +#include "atom/browser/ui/notification_delegate_adapter.h" #include "base/mac/mac_util.h" #include "base/mac/scoped_nsobject.h" #include "base/strings/sys_string_conversions.h" - -std::map> native_notifications_; - -@interface AtomNotificationCenter : NSObject { -} -@end - -@implementation AtomNotificationCenter -- (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center - shouldPresentNotification:(NSUserNotification *)notification { - return YES; - } - -- (void)userNotificationCenter:(NSUserNotificationCenter *)center - didActivateNotification:(NSUserNotification *)notification { - int n_id = [[notification.userInfo objectForKey:@"id"] intValue]; - if (atom::api::Notification::HasID(n_id)) { - auto atomNotification = atom::api::Notification::FromID(n_id); - - if (notification.activationType == NSUserNotificationActivationTypeReplied){ - atomNotification->OnReplied([notification.response.string UTF8String]); - } else { - atomNotification->OnClicked(); - } - } - } -@end +#include "brightray/browser/notification.h" +#include "brightray/browser/notification_presenter.h" +#include "brightray/browser/mac/notification_presenter_mac.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "ui/gfx/codec/png_codec.h" +#include "url/gurl.h" namespace atom { namespace api { -AtomNotificationCenter* del = [[AtomNotificationCenter alloc] init]; -bool set_del_ = false; +brightray::NotificationPresenterMac* presenter; void Notification::Show() { - base::scoped_nsobject notification_ = native_notifications_[id_]; - [NSUserNotificationCenter.defaultUserNotificationCenter - deliverNotification:notification_]; - OnShown(); -} - -void Notification::OnInitialProps() { - if (!set_del_) { - [[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:del]; - set_del_ = true; + SkBitmap image = *(new SkBitmap); + if (has_icon_) { + image = *(icon_.ToSkBitmap()); } - base::scoped_nsobject notification_; - notification_.reset([[NSUserNotification alloc] init]); + std::unique_ptr adapter( + new AtomNotificationDelegateAdapter(this)); + auto notif = presenter->CreateNotification(adapter.get()); + if (notif) { + ignore_result(adapter.release()); // it will release itself automatically. + GURL nullUrl = *(new GURL); + notif->Show(title_, body_, "", nullUrl, image, silent_, has_reply_, reply_placeholder_); + } +} - native_notifications_[id_] = notification_; - - NotifyPropsUpdated(); +bool initialized_ = false; +void Notification::OnInitialProps() { + if (!initialized_) { + presenter = new brightray::NotificationPresenterMac; + initialized_ = true; + } } void Notification::NotifyPropsUpdated() { - base::scoped_nsobject notification_ = native_notifications_[id_]; - - [notification_ setTitle:base::SysUTF16ToNSString(title_)]; - [notification_ setInformativeText:base::SysUTF16ToNSString(body_)]; - - NSDictionary * userInfo = [NSMutableDictionary dictionary]; - [userInfo setValue:[NSNumber numberWithInt:id_] forKey:@"id"]; - [notification_ setUserInfo:userInfo]; - - if ([notification_ respondsToSelector:@selector(setContentImage:)] && has_icon_) { - [notification_ setContentImage:icon_.AsNSImage()]; - } - - if (has_reply_) { - [notification_ setResponsePlaceholder:base::SysUTF16ToNSString(reply_placeholder_)]; - [notification_ setHasReplyButton:true]; - } - - if (silent_) { - [notification_ setSoundName:nil]; - } else { - [notification_ setSoundName:NSUserNotificationDefaultSoundName]; - } } } // namespace api diff --git a/atom/browser/ui/notification_delegate_adapter.cc b/atom/browser/ui/notification_delegate_adapter.cc index d44557daa932..b91a1a97d56a 100644 --- a/atom/browser/ui/notification_delegate_adapter.cc +++ b/atom/browser/ui/notification_delegate_adapter.cc @@ -20,6 +20,9 @@ void AtomNotificationDelegateAdapter::NotificationClosed() {} void AtomNotificationDelegateAdapter::NotificationClick() { observer_->OnClicked(); } +void AtomNotificationDelegateAdapter::NotificationReplied(std::string reply) { + observer_->OnReplied(reply); +} void AtomNotificationDelegateAdapter::NotificationDestroyed() {} void AtomNotificationDelegateAdapter::NotificationFailed() {} diff --git a/atom/browser/ui/notification_delegate_adapter.h b/atom/browser/ui/notification_delegate_adapter.h index 087a207f80d3..e0c56e3fa822 100644 --- a/atom/browser/ui/notification_delegate_adapter.h +++ b/atom/browser/ui/notification_delegate_adapter.h @@ -21,6 +21,7 @@ class AtomNotificationDelegateAdapter : public brightray::NotificationDelegate { void NotificationClick(); void NotificationDestroyed(); void NotificationFailed(); + void NotificationReplied(std::string reply); }; } // namespace atom diff --git a/brightray/browser/linux/libnotify_notification.cc b/brightray/browser/linux/libnotify_notification.cc index 9706fbd3d692..27120068bae6 100644 --- a/brightray/browser/linux/libnotify_notification.cc +++ b/brightray/browser/linux/libnotify_notification.cc @@ -88,7 +88,9 @@ void LibnotifyNotification::Show(const base::string16& title, const std::string& tag, const GURL& icon_url, const SkBitmap& icon, - const bool silent) { + const bool silent, + const bool has_reply, + const base::string16 reply_placeholder) { notification_ = libnotify_loader_.notify_notification_new( base::UTF16ToUTF8(title).c_str(), base::UTF16ToUTF8(body).c_str(), diff --git a/brightray/browser/linux/libnotify_notification.h b/brightray/browser/linux/libnotify_notification.h index b6163ae9b56f..861ddce5fa63 100644 --- a/brightray/browser/linux/libnotify_notification.h +++ b/brightray/browser/linux/libnotify_notification.h @@ -27,7 +27,9 @@ class LibnotifyNotification : public Notification { const std::string& tag, const GURL& icon_url, const SkBitmap& icon, - const bool silent) override; + const bool silent, + const bool has_reply, + const base::string16 reply_placeholder) override; void Dismiss() override; private: diff --git a/brightray/browser/mac/cocoa_notification.h b/brightray/browser/mac/cocoa_notification.h index e95b490a70ad..c269dd26020a 100644 --- a/brightray/browser/mac/cocoa_notification.h +++ b/brightray/browser/mac/cocoa_notification.h @@ -26,10 +26,13 @@ class CocoaNotification : public Notification { const std::string& tag, const GURL& icon_url, const SkBitmap& icon, - const bool silent) override; + const bool silent, + const bool hasReply, + const base::string16 replyPlaceholder) override; void Dismiss() override; void NotificationDisplayed(); + void NotificationReplied(std::string reply); NSUserNotification* notification() const { return notification_; } diff --git a/brightray/browser/mac/cocoa_notification.mm b/brightray/browser/mac/cocoa_notification.mm index 354dc8d484b8..010f9ba42769 100644 --- a/brightray/browser/mac/cocoa_notification.mm +++ b/brightray/browser/mac/cocoa_notification.mm @@ -28,7 +28,9 @@ void CocoaNotification::Show(const base::string16& title, const std::string& tag, const GURL& icon_url, const SkBitmap& icon, - const bool silent) { + const bool silent, + const bool has_reply, + const base::string16 reply_placeholder) { notification_.reset([[NSUserNotification alloc] init]); [notification_ setTitle:base::SysUTF16ToNSString(title)]; [notification_ setInformativeText:base::SysUTF16ToNSString(body)]; @@ -46,6 +48,11 @@ void CocoaNotification::Show(const base::string16& title, [notification_ setSoundName:NSUserNotificationDefaultSoundName]; } + if (has_reply) { + [notification_ setResponsePlaceholder:base::SysUTF16ToNSString(reply_placeholder)]; + [notification_ setHasReplyButton:true]; + } + [NSUserNotificationCenter.defaultUserNotificationCenter deliverNotification:notification_]; } @@ -61,4 +68,8 @@ void CocoaNotification::NotificationDisplayed() { delegate()->NotificationDisplayed(); } +void CocoaNotification::NotificationReplied(const std::string reply) { + delegate()->NotificationReplied(reply); +} + } // namespace brightray diff --git a/brightray/browser/mac/notification_center_delegate.mm b/brightray/browser/mac/notification_center_delegate.mm index 42632cd8a9d7..0f21ec88b032 100644 --- a/brightray/browser/mac/notification_center_delegate.mm +++ b/brightray/browser/mac/notification_center_delegate.mm @@ -21,15 +21,21 @@ - (void)userNotificationCenter:(NSUserNotificationCenter*)center didDeliverNotification:(NSUserNotification*)notif { auto notification = presenter_->GetNotification(notif); - if (notification) + if (notification) notification->NotificationDisplayed(); } - (void)userNotificationCenter:(NSUserNotificationCenter*)center didActivateNotification:(NSUserNotification *)notif { auto notification = presenter_->GetNotification(notif); - if (notification) + if (notification) { notification->NotificationClicked(); + if (notif.activationType == NSUserNotificationActivationTypeReplied){ + notification->NotificationReplied([notif.response.string UTF8String]); + } else { + notification->NotificationClicked(); + } + } } - (BOOL)userNotificationCenter:(NSUserNotificationCenter*)center diff --git a/brightray/browser/notification.h b/brightray/browser/notification.h index e59cab7a0cd4..f575753fea73 100644 --- a/brightray/browser/notification.h +++ b/brightray/browser/notification.h @@ -26,7 +26,9 @@ class Notification { const std::string& tag, const GURL& icon_url, const SkBitmap& icon, - const bool silent) = 0; + const bool silent, + const bool hasReply, + const base::string16 replyPlaceholder) = 0; // Closes the notification, this instance will be destroyed after the // notification gets closed. virtual void Dismiss() = 0; diff --git a/brightray/browser/notification_delegate.h b/brightray/browser/notification_delegate.h index c036b83bcfe0..848af0122d63 100644 --- a/brightray/browser/notification_delegate.h +++ b/brightray/browser/notification_delegate.h @@ -6,6 +6,7 @@ #define BRIGHTRAY_BROWSER_NOTIFICATION_DELEGATE_H_ #include "content/public/browser/desktop_notification_delegate.h" +#include namespace brightray { @@ -16,6 +17,9 @@ class NotificationDelegate : public content::DesktopNotificationDelegate { // Failed to send the notification. virtual void NotificationFailed() {} + + // Notification was replied to + virtual void NotificationReplied(std::string reply) {} }; } // namespace brightray diff --git a/brightray/browser/notification_delegate_adapter.cc b/brightray/browser/notification_delegate_adapter.cc index 60405f9ef8d4..4b515d0e88b2 100644 --- a/brightray/browser/notification_delegate_adapter.cc +++ b/brightray/browser/notification_delegate_adapter.cc @@ -30,4 +30,8 @@ void NotificationDelegateAdapter::NotificationClick() { delegate_->NotificationClick(); } +// void NotificationDelegateAdapter::NotificationReplied(std::string reply) { +// delegate_->NotificationReplied(reply); +// } + } // namespace brightray diff --git a/brightray/browser/platform_notification_service.cc b/brightray/browser/platform_notification_service.cc index 1c88a7461d5f..99ab54080c22 100644 --- a/brightray/browser/platform_notification_service.cc +++ b/brightray/browser/platform_notification_service.cc @@ -4,6 +4,7 @@ #include "brightray/browser/platform_notification_service.h" +#include "base/strings/utf_string_conversions.h" #include "brightray/browser/browser_client.h" #include "brightray/browser/notification.h" #include "brightray/browser/notification_delegate_adapter.h" @@ -30,7 +31,7 @@ void OnWebNotificationAllowed(base::WeakPtr notification, return; if (allowed) notification->Show(data.title, data.body, data.tag, data.icon, icon, - audio_muted ? true : data.silent); + audio_muted ? true : data.silent, false, base::UTF8ToUTF16("")); else notification->Destroy(); } diff --git a/brightray/browser/win/win32_notification.cc b/brightray/browser/win/win32_notification.cc index 21e3b6ce8fa6..9aed1de05112 100644 --- a/brightray/browser/win/win32_notification.cc +++ b/brightray/browser/win/win32_notification.cc @@ -12,7 +12,8 @@ namespace brightray { void Win32Notification::Show( const base::string16& title, const base::string16& msg, const std::string& tag, const GURL& icon_url, - const SkBitmap& icon, const bool silent) { + const SkBitmap& icon, const bool silent, + const bool has_reply, const base::string16 reply_placeholder) { auto presenter = static_cast(this->presenter()); if (!presenter) return; diff --git a/brightray/browser/win/win32_notification.h b/brightray/browser/win/win32_notification.h index ce0ba3a0925d..2136551f5678 100644 --- a/brightray/browser/win/win32_notification.h +++ b/brightray/browser/win/win32_notification.h @@ -12,7 +12,8 @@ class Win32Notification : public brightray::Notification { } void Show(const base::string16& title, const base::string16& msg, const std::string& tag, const GURL& icon_url, - const SkBitmap& icon, const bool silent) override; + const SkBitmap& icon, const bool silent, + const bool has_reply, const base::string16 reply_placeholder) override; void Dismiss() override; const DesktopNotificationController::Notification& GetRef() const { diff --git a/brightray/browser/win/windows_toast_notification.cc b/brightray/browser/win/windows_toast_notification.cc index 5b5b7764ebd9..2744958c426d 100644 --- a/brightray/browser/win/windows_toast_notification.cc +++ b/brightray/browser/win/windows_toast_notification.cc @@ -89,7 +89,9 @@ void WindowsToastNotification::Show(const base::string16& title, const std::string& tag, const GURL& icon_url, const SkBitmap& icon, - const bool silent) { + const bool silent, + const bool hasReply, + const base::string16 replyPlaceholder) { auto presenter_win = static_cast(presenter()); std::wstring icon_path = presenter_win->SaveIconToFilesystem(icon, icon_url); diff --git a/brightray/browser/win/windows_toast_notification.h b/brightray/browser/win/windows_toast_notification.h index b36a5cc3797d..62303054f633 100644 --- a/brightray/browser/win/windows_toast_notification.h +++ b/brightray/browser/win/windows_toast_notification.h @@ -55,7 +55,9 @@ class WindowsToastNotification : public Notification { const std::string& tag, const GURL& icon_url, const SkBitmap& icon, - const bool silent) override; + const bool silent, + const bool has_reply, + const base::string16 reply_placeholder) override; void Dismiss() override; private: diff --git a/default_app/default_app.js b/default_app/default_app.js index 2a3ce3a85ec1..e1ef319091e3 100644 --- a/default_app/default_app.js +++ b/default_app/default_app.js @@ -1,4 +1,4 @@ -const {app, BrowserWindow} = require('electron') +const {app, BrowserWindow, Notification} = require('electron') const path = require('path') let mainWindow = null @@ -27,5 +27,14 @@ exports.load = (appUrl) => { mainWindow = new BrowserWindow(options) mainWindow.loadURL(appUrl) mainWindow.focus() + + const n = new Notification({ + title: 'Foo', + body: 'Bar', + hasReply: true, + replyPlaceholder: 'foo' + }); + n.on('reply', (...args) => console.log(args)); + n.show(); }) } From c741b584a1532afb208afdad263516c49a9074e1 Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Mon, 29 May 2017 21:18:18 +1000 Subject: [PATCH 14/22] Remove OS specific implementations --- atom/browser/api/atom_api_notification.cc | 33 ++ atom/browser/api/atom_api_notification.h | 3 + .../api/atom_api_notification_linux.cc | 129 ----- atom/browser/api/atom_api_notification_mac.mm | 56 -- atom/browser/api/atom_api_notification_win.cc | 111 ---- atom/browser/ui/win/toast_handler.cc | 29 -- atom/browser/ui/win/toast_handler.h | 26 - atom/browser/ui/win/toast_lib.cc | 485 ------------------ atom/browser/ui/win/toast_lib.h | 192 ------- default_app/default_app.js | 7 +- filenames.gypi | 7 - 11 files changed, 40 insertions(+), 1038 deletions(-) delete mode 100644 atom/browser/api/atom_api_notification_linux.cc delete mode 100644 atom/browser/api/atom_api_notification_mac.mm delete mode 100644 atom/browser/api/atom_api_notification_win.cc delete mode 100644 atom/browser/ui/win/toast_handler.cc delete mode 100644 atom/browser/ui/win/toast_handler.h delete mode 100644 atom/browser/ui/win/toast_lib.cc delete mode 100644 atom/browser/ui/win/toast_lib.h diff --git a/atom/browser/api/atom_api_notification.cc b/atom/browser/api/atom_api_notification.cc index 502184c2eb9f..608474fdc739 100644 --- a/atom/browser/api/atom_api_notification.cc +++ b/atom/browser/api/atom_api_notification.cc @@ -9,15 +9,21 @@ #include "atom/browser/api/atom_api_menu.h" #include "atom/browser/browser.h" +#include "atom/browser/ui/notification_delegate_adapter.h" #include "atom/common/api/atom_api_native_image.h" #include "atom/common/native_mate_converters/gfx_converter.h" #include "atom/common/native_mate_converters/image_converter.h" #include "atom/common/native_mate_converters/string16_converter.h" #include "atom/common/node_includes.h" +#include "base/strings/utf_string_conversions.h" #include "base/threading/thread_task_runner_handle.h" +#include "common/string_conversion.h" #include "native_mate/constructor.h" #include "native_mate/dictionary.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "ui/gfx/codec/png_codec.h" #include "ui/gfx/image/image.h" +#include "url/gurl.h" namespace atom { @@ -121,6 +127,33 @@ void Notification::OnShown() { Emit("show"); } +void Notification::NotifyPropsUpdated() {} + +// Showing notifications +void Notification::Show() { + SkBitmap image = *(new SkBitmap); + if (has_icon_) { + image = *(icon_.ToSkBitmap()); + } + + std::unique_ptr adapter( + new AtomNotificationDelegateAdapter(this)); + auto notif = presenter_->CreateNotification(adapter.get()); + if (notif) { + ignore_result(adapter.release()); // it will release itself automatically. + GURL nullUrl = *(new GURL); + notif->Show(title_, body_, "", nullUrl, image, silent_, has_reply_, reply_placeholder_); + } +} + +bool initialized_ = false; +void Notification::OnInitialProps() { + if (!initialized_) { + presenter_ = brightray::NotificationPresenter::Create(); + initialized_ = true; + } +} + // static void Notification::BuildPrototype(v8::Isolate* isolate, v8::Local prototype) { diff --git a/atom/browser/api/atom_api_notification.h b/atom/browser/api/atom_api_notification.h index 7882c145f757..513a9a67fd73 100644 --- a/atom/browser/api/atom_api_notification.h +++ b/atom/browser/api/atom_api_notification.h @@ -12,6 +12,8 @@ #include "atom/browser/api/trackable_object.h" #include "atom/browser/ui/notification_observer.h" #include "base/strings/utf_string_conversions.h" +#include "brightray/browser/notification.h" +#include "brightray/browser/notification_presenter.h" #include "native_mate/handle.h" #include "ui/gfx/image/image.h" @@ -69,6 +71,7 @@ class Notification : public mate::TrackableObject, bool silent_ = false; base::string16 reply_placeholder_ = base::UTF8ToUTF16(""); bool has_reply_ = false; + brightray::NotificationPresenter* presenter_; int id_; diff --git a/atom/browser/api/atom_api_notification_linux.cc b/atom/browser/api/atom_api_notification_linux.cc deleted file mode 100644 index 109328b47f81..000000000000 --- a/atom/browser/api/atom_api_notification_linux.cc +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright (c) 2014 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#include "atom/browser/api/atom_api_notification.h" - -#include - -#include "atom/browser/browser.h" -#include "brightray/browser/linux/libnotify_loader.h" -#include "brightray/browser/linux/libnotify_notification.h" -#include "chrome/browser/ui/libgtkui/skia_utils_gtk.h" -#include "third_party/skia/include/core/SkBitmap.h" - -namespace atom { - -namespace api { - -bool initialized_ = false; -bool available_ = false; - -LibNotifyLoader libnotify_loader_; - -bool HasCapability(const std::string& capability) { - bool result = false; - GList* capabilities = libnotify_loader_.notify_get_server_caps(); - - if (g_list_find_custom(capabilities, capability.c_str(), - (GCompareFunc)g_strcmp0) != NULL) - result = true; - - g_list_free_full(capabilities, g_free); - - return result; -} - -bool NotifierSupportsActions() { - if (getenv("ELECTRON_USE_UBUNTU_NOTIFIER")) - return false; - - static bool notify_has_result = false; - static bool notify_result = false; - - if (notify_has_result) - return notify_result; - - notify_result = HasCapability("actions"); - return notify_result; -} - -void log_and_clear_error(GError* error, const char* context) { - LOG(ERROR) << context - << ": domain=" << error->domain - << " code=" << error->code - << " message=\"" << error->message << '"'; - g_error_free(error); -} - -void Notification::Show() { - if (!available_) return; - - NotifyNotification* notification_ = libnotify_loader_.notify_notification_new( - base::UTF16ToUTF8(title_).c_str(), - base::UTF16ToUTF8(body_).c_str(), - nullptr); - - GQuark id = g_quark_from_string(std::to_string(id_).c_str()); - g_object_set(G_OBJECT(notification_), "id", id, NULL); - - // NB: On Unity and on any other DE using Notify-OSD, adding a notification - // action will cause the notification to display as a modal dialog box. - // Can't make this work, need linux help :D - // if (NotifierSupportsActions()) { - // libnotify_loader_.notify_notification_add_action( - // notification_, "default", "View", OnClickedCallback, this, - // nullptr); - // } - - if (has_icon_) { - SkBitmap image = *(icon_.ToSkBitmap()); - GdkPixbuf* pixbuf = libgtkui::GdkPixbufFromSkBitmap(image); - libnotify_loader_.notify_notification_set_image_from_pixbuf( - notification_, pixbuf); - libnotify_loader_.notify_notification_set_timeout( - notification_, NOTIFY_EXPIRES_DEFAULT); - g_object_unref(pixbuf); - } - - // Always try to append notifications. - // Unique tags can be used to prevent this. - if (HasCapability("append")) { - libnotify_loader_.notify_notification_set_hint_string( - notification_, "append", "true"); - } else if (HasCapability("x-canonical-append")) { - libnotify_loader_.notify_notification_set_hint_string( - notification_, "x-canonical-append", "true"); - } - - GError* error = nullptr; - libnotify_loader_.notify_notification_show(notification_, &error); - if (error) { - log_and_clear_error(error, "notify_notification_show"); - return; - } - - OnShown(); -} - -void Notification::OnInitialProps() { - if (!initialized_) { - initialized_ = true; - if (!libnotify_loader_.Load("libnotify.so.4") && // most common one - !libnotify_loader_.Load("libnotify.so.5") && - !libnotify_loader_.Load("libnotify.so.1") && - !libnotify_loader_.Load("libnotify.so")) { - return; - } - Browser* browser = Browser::Get(); - if (!libnotify_loader_.notify_is_initted() && - !libnotify_loader_.notify_init(browser->GetName().c_str())) { - return; - } - available_ = true; - } -} - -void Notification::NotifyPropsUpdated() {} -} // namespace api -} // namespace atom diff --git a/atom/browser/api/atom_api_notification_mac.mm b/atom/browser/api/atom_api_notification_mac.mm deleted file mode 100644 index fed369c15bef..000000000000 --- a/atom/browser/api/atom_api_notification_mac.mm +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) 2014 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#include "atom/browser/api/atom_api_notification.h" - -#import - -#include "atom/browser/browser.h" -#include "atom/browser/ui/notification_delegate_adapter.h" -#include "base/mac/mac_util.h" -#include "base/mac/scoped_nsobject.h" -#include "base/strings/sys_string_conversions.h" -#include "brightray/browser/notification.h" -#include "brightray/browser/notification_presenter.h" -#include "brightray/browser/mac/notification_presenter_mac.h" -#include "third_party/skia/include/core/SkBitmap.h" -#include "ui/gfx/codec/png_codec.h" -#include "url/gurl.h" - -namespace atom { - -namespace api { - -brightray::NotificationPresenterMac* presenter; - -void Notification::Show() { - SkBitmap image = *(new SkBitmap); - if (has_icon_) { - image = *(icon_.ToSkBitmap()); - } - - std::unique_ptr adapter( - new AtomNotificationDelegateAdapter(this)); - auto notif = presenter->CreateNotification(adapter.get()); - if (notif) { - ignore_result(adapter.release()); // it will release itself automatically. - GURL nullUrl = *(new GURL); - notif->Show(title_, body_, "", nullUrl, image, silent_, has_reply_, reply_placeholder_); - } -} - -bool initialized_ = false; -void Notification::OnInitialProps() { - if (!initialized_) { - presenter = new brightray::NotificationPresenterMac; - initialized_ = true; - } -} - -void Notification::NotifyPropsUpdated() { -} - -} // namespace api - -} // namespace atom \ No newline at end of file diff --git a/atom/browser/api/atom_api_notification_win.cc b/atom/browser/api/atom_api_notification_win.cc deleted file mode 100644 index ce0e90b938dc..000000000000 --- a/atom/browser/api/atom_api_notification_win.cc +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) 2014 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#include "atom/browser/api/atom_api_notification.h" - -#include "atom/browser/browser.h" -#include "atom/browser/ui/notification_delegate_adapter.h" -#include "atom/browser/ui/win/toast_handler.h" -#include "atom/browser/ui/win/toast_lib.h" -#include "base/files/file_util.h" -#include "base/md5.h" -#include "base/strings/utf_string_conversions.h" -#include "brightray/browser/notification.h" -#include "brightray/browser/notification_presenter.h" -#include "brightray/browser/win/notification_presenter_win.h" -#include "brightray/browser/win/notification_presenter_win7.h" -#include "common/string_conversion.h" -#include "third_party/skia/include/core/SkBitmap.h" -#include "ui/gfx/codec/png_codec.h" -#include "url/gurl.h" - -namespace atom { - -namespace api { - -bool can_toast_ = true; -bool initialized_ = false; -brightray::NotificationPresenterWin7* presenter; - -base::ScopedTempDir temp_dir_; - -bool SaveIconToPath(const SkBitmap& bitmap, const base::FilePath& path) { - std::vector png_data; - if (!gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, false, &png_data)) - return false; - - char* data = reinterpret_cast(&png_data[0]); - int size = static_cast(png_data.size()); - return base::WriteFile(path, data, size) == size; -} - -void Notification::Show() { - SkBitmap image = *(new SkBitmap); - if (has_icon_) { - image = *(icon_.ToSkBitmap()); - } - - if (can_toast_) { - atom::AtomToastHandler* handler = new atom::AtomToastHandler(this); - WinToastLib::WinToastTemplate::WinToastTemplateType toastType = - WinToastLib::WinToastTemplate::TextOneLine; - if (!has_icon_) { - if (body_ != L"") { - toastType = WinToastLib::WinToastTemplate::TextTwoLines; - } else { - toastType = WinToastLib::WinToastTemplate::TextOneLine; - } - } else { - if (body_ != L"") { - toastType = WinToastLib::WinToastTemplate::ImageWithTwoLines; - } else { - toastType = WinToastLib::WinToastTemplate::ImageWithOneLine; - } - } - WinToastLib::WinToastTemplate toast = - WinToastLib::WinToastTemplate(toastType); - - std::string filename = - base::MD5String(base::UTF16ToUTF8(icon_path_)) + ".png"; - base::FilePath savePath = - temp_dir_.GetPath().Append(base::UTF8ToUTF16(filename)); - if (has_icon_ && SaveIconToPath(image, savePath)) { - toast.setImagePath(savePath.value()); - } - toast.setTextField(title_, - WinToastLib::WinToastTemplate::TextField::FirstLine); - toast.setTextField(body_, - WinToastLib::WinToastTemplate::TextField::SecondLine); - toast.setSilent(silent_); - - WinToastLib::WinToast::instance()->showToast(toast, handler); - - OnShown(); - } else { - AtomNotificationDelegateAdapter* adapter = - new AtomNotificationDelegateAdapter(this); - auto notif = presenter->CreateNotification(adapter); - GURL nullUrl = *(new GURL); - notif->Show(title_, body_, "", nullUrl, image, silent_); - } -} - -void Notification::OnInitialProps() { - if (!initialized_) { - Browser* browser = Browser::Get(); - WinToastLib::WinToast::instance()->setAppName( - base::UTF8ToUTF16(browser->GetName())); - WinToastLib::WinToast::instance()->setAppUserModelId( - browser->GetAppUserModelID()); - can_toast_ = WinToastLib::WinToast::instance()->initialize(); - temp_dir_.CreateUniqueTempDir(); - } - if (!can_toast_) { - presenter = new brightray::NotificationPresenterWin7; - } -} - -void Notification::NotifyPropsUpdated() {} -} // namespace api -} // namespace atom diff --git a/atom/browser/ui/win/toast_handler.cc b/atom/browser/ui/win/toast_handler.cc deleted file mode 100644 index 6ce01ef37136..000000000000 --- a/atom/browser/ui/win/toast_handler.cc +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) 2014 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#include "atom/browser/ui/win/toast_handler.h" - -#include "atom/browser/ui/win/toast_lib.h" -#include "atom/browser/api/atom_api_notification.h" - -namespace atom { - -AtomToastHandler::AtomToastHandler(atom::api::Notification* target) { - observer_ = target; -} - -void AtomToastHandler::toastActivated() { - observer_->OnClicked(); -} - -void AtomToastHandler::toastDismissed( - WinToastLib::WinToastHandler::WinToastDismissalReason state) { - // observer_->OnDismissed(); -} - -void AtomToastHandler::toastFailed() { - // observer_->OnErrored(); -} - -} // namespace atom diff --git a/atom/browser/ui/win/toast_handler.h b/atom/browser/ui/win/toast_handler.h deleted file mode 100644 index 70b8ad604890..000000000000 --- a/atom/browser/ui/win/toast_handler.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2014 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#include "atom/browser/ui/win/toast_lib.h" -#include "atom/browser/api/atom_api_notification.h" - -#ifndef ATOM_BROWSER_UI_WIN_TOAST_HANDLER_H_ -#define ATOM_BROWSER_UI_WIN_TOAST_HANDLER_H_ - -namespace atom { - -class AtomToastHandler : public WinToastLib::WinToastHandler { - public: - atom::api::Notification* observer_; - explicit AtomToastHandler(atom::api::Notification* target); - - void toastActivated() override; - void toastDismissed( - WinToastLib::WinToastHandler::WinToastDismissalReason state); - void toastFailed(); -}; - -} // namespace atom - -#endif // ATOM_BROWSER_UI_WIN_TOAST_HANDLER_H_ diff --git a/atom/browser/ui/win/toast_lib.cc b/atom/browser/ui/win/toast_lib.cc deleted file mode 100644 index 5002a28f7dc0..000000000000 --- a/atom/browser/ui/win/toast_lib.cc +++ /dev/null @@ -1,485 +0,0 @@ -// MIT License - -// Copyright (c) 2016 Mohammed Boujemaoui Boulaghmoudi - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include "atom/browser/ui/win/toast_lib.h" - -#include - -#include "brightray/browser/win/scoped_hstring.h" - -#pragma comment(lib, "shlwapi") -#pragma comment(lib, "user32") - -using WinToastLib::WinToast; -using WinToastLib::WinToastHandler; -using WinToastLib::WinToastStringWrapper; -using WinToastLib::WinToastTemplate; - -namespace DllImporter { - -// Function load a function from library -template -HRESULT loadFunctionFromLibrary(HINSTANCE library, - LPCSTR name, - Function& func) { // NOLINT - if (!library) - return false; - - func = reinterpret_cast(GetProcAddress(library, name)); - return (func != nullptr) ? S_OK : E_FAIL; -} - -typedef HRESULT(FAR STDAPICALLTYPE* f_SetCurrentProcessExplicitAppUserModelID)( - __in PCWSTR AppID); -typedef HRESULT(FAR STDAPICALLTYPE* f_PropVariantToString)( - _In_ REFPROPVARIANT propvar, - _Out_writes_(cch) PWSTR psz, - _In_ UINT cch); -typedef HRESULT(FAR STDAPICALLTYPE* f_RoGetActivationFactory)( - _In_ HSTRING activatableClassId, - _In_ REFIID iid, - _COM_Outptr_ void** factory); -typedef HRESULT(FAR STDAPICALLTYPE* f_WindowsCreateStringReference)( - _In_reads_opt_(length + 1) PCWSTR sourceString, - UINT32 length, - _Out_ HSTRING_HEADER* hstringHeader, - _Outptr_result_maybenull_ _Result_nullonfailure_ HSTRING* string); -typedef HRESULT(FAR STDAPICALLTYPE* f_WindowsDeleteString)( - _In_opt_ HSTRING string); - -f_SetCurrentProcessExplicitAppUserModelID - SetCurrentProcessExplicitAppUserModelID; -f_PropVariantToString PropVariantToString; -f_RoGetActivationFactory RoGetActivationFactory; -f_WindowsCreateStringReference WindowsCreateStringReference; -f_WindowsDeleteString WindowsDeleteString; - -template -_Check_return_ __inline HRESULT _1_GetActivationFactory( - _In_ HSTRING activatableClassId, - _COM_Outptr_ T** factory) { - return RoGetActivationFactory(activatableClassId, IID_INS_ARGS(factory)); -} - -template -inline HRESULT Wrap_GetActivationFactory( - _In_ HSTRING activatableClassId, - _Inout_ ComPtrRef factory) throw() { - return _1_GetActivationFactory(activatableClassId, - factory.ReleaseAndGetAddressOf()); -} - -inline HRESULT initialize() { - HINSTANCE LibShell32 = LoadLibrary(L"SHELL32.DLL"); - HRESULT hr = loadFunctionFromLibrary( - LibShell32, "SetCurrentProcessExplicitAppUserModelID", - SetCurrentProcessExplicitAppUserModelID); - if (SUCCEEDED(hr)) { - HINSTANCE LibPropSys = LoadLibrary(L"PROPSYS.DLL"); - hr = loadFunctionFromLibrary(LibPropSys, "PropVariantToString", - PropVariantToString); - if (SUCCEEDED(hr)) { - HINSTANCE LibComBase = LoadLibrary(L"COMBASE.DLL"); - return SUCCEEDED(loadFunctionFromLibrary(LibComBase, - "RoGetActivationFactory", - RoGetActivationFactory)) && - SUCCEEDED(loadFunctionFromLibrary(LibComBase, - "WindowsCreateStringReference", - WindowsCreateStringReference)) && - SUCCEEDED(loadFunctionFromLibrary( - LibComBase, "WindowsDeleteString", WindowsDeleteString)); - } - } - return hr; -} - -} // namespace DllImporter - -namespace Util { -inline HRESULT defaultExecutablePath(_In_ WCHAR* path, - _In_ DWORD nSize = MAX_PATH) { - DWORD written = - GetModuleFileNameEx(GetCurrentProcess(), nullptr, path, nSize); - return (written > 0) ? S_OK : E_FAIL; -} - -inline HRESULT defaultShellLinksDirectory(_In_ WCHAR* path, - _In_ DWORD nSize = MAX_PATH) { - DWORD written = GetEnvironmentVariable(L"APPDATA", path, nSize); - HRESULT hr = written > 0 ? S_OK : E_INVALIDARG; - if (SUCCEEDED(hr)) { - errno_t result = wcscat_s(path, nSize, DEFAULT_SHELL_LINKS_PATH); - hr = (result == 0) ? S_OK : E_INVALIDARG; - } - return hr; -} - -inline HRESULT defaultShellLinkPath(const std::wstring& appname, - _In_ WCHAR* path, - _In_ DWORD nSize = MAX_PATH) { - HRESULT hr = defaultShellLinksDirectory(path, nSize); - if (SUCCEEDED(hr)) { - const std::wstring appLink(appname + DEFAULT_LINK_FORMAT); - errno_t result = wcscat_s(path, nSize, appLink.c_str()); - hr = (result == 0) ? S_OK : E_INVALIDARG; - } - return hr; -} - -inline HRESULT setNodeStringValue(const std::wstring& string, - IXmlNode* node, - IXmlDocument* xml) { - ComPtr textNode; - HRESULT hr = - xml->CreateTextNode(WinToastStringWrapper(string).Get(), &textNode); - if (SUCCEEDED(hr)) { - ComPtr stringNode; - hr = textNode.As(&stringNode); - if (SUCCEEDED(hr)) { - ComPtr appendedChild; - hr = node->AppendChild(stringNode.Get(), &appendedChild); - } - } - return hr; -} - -inline HRESULT setEventHandlers(_In_ IToastNotification* notification, - _In_ WinToastHandler* eventHandler) { - EventRegistrationToken activatedToken, dismissedToken, failedToken; - HRESULT hr = notification->add_Activated( - Callback< - Implements, - ITypedEventHandler>>( - [eventHandler](IToastNotification*, IInspectable*) { - eventHandler->toastActivated(); - return S_OK; - }) - .Get(), - &activatedToken); - - if (SUCCEEDED(hr)) { - hr = notification->add_Dismissed( - Callback, - ITypedEventHandler>>( - [eventHandler](IToastNotification*, IToastDismissedEventArgs* e) { - ToastDismissalReason reason; - if (SUCCEEDED(e->get_Reason(&reason))) { - eventHandler->toastDismissed( - static_cast( - reason)); - } - return S_OK; - }) - .Get(), - &dismissedToken); - if (SUCCEEDED(hr)) { - hr = notification->add_Failed( - Callback, - ITypedEventHandler>>( - [eventHandler](IToastNotification*, IToastFailedEventArgs*) { - eventHandler->toastFailed(); - return S_OK; - }) - .Get(), - &failedToken); - } - } - return hr; -} - -} // namespace Util - -WinToast* WinToast::_instance = nullptr; -WinToast* WinToast::instance() { - if (_instance == nullptr) { - _instance = new WinToast(); - } - return _instance; -} - -WinToast::WinToast() : _isInitialized(false) { - DllImporter::initialize(); -} - -void WinToast::setAppName(_In_ const std::wstring& appName) { - _appName = appName; -} - -std::wstring WinToast::appName() const { - return _appName; -} - -std::wstring WinToast::appUserModelId() const { - return _aumi; -} -void WinToast::setAppUserModelId(_In_ const std::wstring& aumi) { - _aumi = aumi; -} - -bool WinToast::isCompatible() { - return !((DllImporter::SetCurrentProcessExplicitAppUserModelID == nullptr) || - (DllImporter::PropVariantToString == nullptr) || - (DllImporter::RoGetActivationFactory == nullptr) || - (DllImporter::WindowsCreateStringReference == nullptr) || - (DllImporter::WindowsDeleteString == nullptr)); -} - -bool WinToast::initialize() { - if (_aumi.empty() || _appName.empty()) { - return _isInitialized = false; - } - - if (!isCompatible()) { - return _isInitialized = false; - } - - if (FAILED(DllImporter::SetCurrentProcessExplicitAppUserModelID( - _aumi.c_str()))) { - return _isInitialized = false; - } - - HRESULT hr = DllImporter::Wrap_GetActivationFactory( - WinToastStringWrapper( - RuntimeClass_Windows_UI_Notifications_ToastNotificationManager) - .Get(), - &_notificationManager); - if (SUCCEEDED(hr)) { - hr = notificationManager()->CreateToastNotifierWithId( - WinToastStringWrapper(_aumi).Get(), &_notifier); - if (SUCCEEDED(hr)) { - hr = DllImporter::Wrap_GetActivationFactory( - WinToastStringWrapper( - RuntimeClass_Windows_UI_Notifications_ToastNotification) - .Get(), - &_notificationFactory); - } - } - - return _isInitialized = SUCCEEDED(hr); -} - -bool WinToast::showToast(_In_ const WinToastTemplate& toast, - _In_ WinToastHandler* handler) { - if (!isInitialized()) { - return _isInitialized; - } - - HRESULT hr = _notificationManager->GetTemplateContent( - ToastTemplateType(toast.type()), &_xmlDocument); - if (SUCCEEDED(hr)) { - const int fieldsCount = toast.textFieldsCount(); - for (int i = 0; i < fieldsCount && SUCCEEDED(hr); i++) { - hr = setTextField(toast.textField(WinToastTemplate::TextField(i)), i); - } - if (makeSilent(toast.isSilent())) { - if (SUCCEEDED(hr)) { - if (SUCCEEDED(hr)) { - hr = toast.hasImage() ? setImageField(toast.imagePath()) : hr; - if (SUCCEEDED(hr)) { - hr = _notificationFactory->CreateToastNotification(xmlDocument(), - &_notification); - if (SUCCEEDED(hr)) { - hr = Util::setEventHandlers(notification(), handler); - if (SUCCEEDED(hr)) { - hr = _notifier->Show(notification()); - } - } - } - } - } - } else { - return false; - } - } - - return SUCCEEDED(hr); -} - -HRESULT WinToast::setTextField(_In_ const std::wstring& text, _In_ int pos) { - ComPtr nodeList; - HRESULT hr = _xmlDocument->GetElementsByTagName( - WinToastStringWrapper(L"text").Get(), &nodeList); - if (SUCCEEDED(hr)) { - ComPtr node; - hr = nodeList->Item(pos, &node); - if (SUCCEEDED(hr)) { - hr = Util::setNodeStringValue(text, node.Get(), xmlDocument()); - } - } - return hr; -} - -bool WinToast::makeSilent(bool is_silent) { - if (!is_silent) - return true; - ScopedHString tag(L"toast"); - if (!tag.success()) - return false; - - ComPtr node_list; - if (FAILED(xmlDocument()->GetElementsByTagName(tag, &node_list))) - return false; - - ComPtr root; - if (FAILED(node_list->Item(0, &root))) - return false; - - ComPtr audio_element; - ScopedHString audio_str(L"audio"); - if (FAILED(xmlDocument()->CreateElement(audio_str, &audio_element))) - return false; - - ComPtr audio_node_tmp; - if (FAILED(audio_element.As(&audio_node_tmp))) - return false; - - // Append audio node to toast xml - ComPtr audio_node; - if (FAILED(root->AppendChild(audio_node_tmp.Get(), &audio_node))) - return false; - - // Create silent attribute - ComPtr attributes; - if (FAILED(audio_node->get_Attributes(&attributes))) - return false; - - ComPtr silent_attribute; - ScopedHString silent_str(L"silent"); - if (FAILED(xmlDocument()->CreateAttribute(silent_str, &silent_attribute))) - return false; - - ComPtr silent_attribute_node; - if (FAILED(silent_attribute.As(&silent_attribute_node))) - return false; - - // Set silent attribute to true - ScopedHString silent_value(L"true"); - if (!silent_value.success()) - return false; - - ComPtr silent_text; - if (FAILED(xmlDocument()->CreateTextNode(silent_value, &silent_text))) - return false; - - ComPtr silent_node; - if (FAILED(silent_text.As(&silent_node))) - return false; - - ComPtr child_node; - if (FAILED( - silent_attribute_node->AppendChild(silent_node.Get(), &child_node))) - return false; - - ComPtr silent_attribute_pnode; - return SUCCEEDED(attributes.Get()->SetNamedItem(silent_attribute_node.Get(), - &silent_attribute_pnode)); -} - -HRESULT WinToast::setImageField(_In_ const std::wstring& path) { - wchar_t imagePath[MAX_PATH] = L"file:///"; - HRESULT hr = StringCchCat(imagePath, MAX_PATH, path.c_str()); - if (SUCCEEDED(hr)) { - ComPtr nodeList; - hr = _xmlDocument->GetElementsByTagName( - WinToastStringWrapper(L"image").Get(), &nodeList); - if (SUCCEEDED(hr)) { - ComPtr node; - hr = nodeList->Item(0, &node); - if (SUCCEEDED(hr)) { - ComPtr attributes; - hr = node->get_Attributes(&attributes); - if (SUCCEEDED(hr)) { - ComPtr editedNode; - hr = attributes->GetNamedItem(WinToastStringWrapper(L"src").Get(), - &editedNode); - if (SUCCEEDED(hr)) { - Util::setNodeStringValue(imagePath, editedNode.Get(), - xmlDocument()); - } - } - } - } - } - return hr; -} - -WinToastTemplate::WinToastTemplate(const WinToastTemplateType& type) - : _type(type) { - initComponentsFromType(); -} - -WinToastTemplate::~WinToastTemplate() { - _textFields.clear(); -} - -void WinToastTemplate::setTextField( - _In_ const std::wstring& txt, - _In_ const WinToastTemplate::TextField& pos) { - _textFields[pos] = txt; -} -void WinToastTemplate::setImagePath(_In_ const std::wstring& imgPath) { - if (!_hasImage) - return; - _imagePath = imgPath; -} - -void WinToastTemplate::setSilent(bool is_silent) { - _silent = is_silent; -} - -int WinToastTemplate::TextFieldsCount[WinToastTemplateTypeCount] = {1, 2, 2, 3, - 1, 2, 2, 3}; -void WinToastTemplate::initComponentsFromType() { - _hasImage = _type < ToastTemplateType_ToastText01; - _textFields = std::vector(TextFieldsCount[_type], L""); -} - -WinToastStringWrapper::WinToastStringWrapper(_In_reads_(length) - PCWSTR stringRef, - _In_ UINT32 length) throw() { - HRESULT hr = DllImporter::WindowsCreateStringReference(stringRef, length, - &_header, &_hstring); - if (!SUCCEEDED(hr)) { - RaiseException(static_cast(STATUS_INVALID_PARAMETER), - EXCEPTION_NONCONTINUABLE, 0, nullptr); - } -} - -WinToastStringWrapper::WinToastStringWrapper(const std::wstring& stringRef) { - HRESULT hr = DllImporter::WindowsCreateStringReference( - stringRef.c_str(), static_cast(stringRef.length()), &_header, - &_hstring); - if (FAILED(hr)) { - RaiseException(static_cast(STATUS_INVALID_PARAMETER), - EXCEPTION_NONCONTINUABLE, 0, nullptr); - } -} - -WinToastStringWrapper::~WinToastStringWrapper() { - DllImporter::WindowsDeleteString(_hstring); -} - -HSTRING WinToastStringWrapper::Get() const { - return _hstring; -} diff --git a/atom/browser/ui/win/toast_lib.h b/atom/browser/ui/win/toast_lib.h deleted file mode 100644 index 10e3e5995bb1..000000000000 --- a/atom/browser/ui/win/toast_lib.h +++ /dev/null @@ -1,192 +0,0 @@ -// MIT License - -// Copyright (c) 2016 Mohammed Boujemaoui Boulaghmoudi - -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: - -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. - -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#ifndef ATOM_BROWSER_UI_WIN_TOAST_LIB_H_ -#define ATOM_BROWSER_UI_WIN_TOAST_LIB_H_ - -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -using Microsoft::WRL::ComPtr; -using Microsoft::WRL::Details::ComPtrRef; -using Microsoft::WRL::Callback; -using Microsoft::WRL::Implements; -using Microsoft::WRL::RuntimeClassFlags; -using Microsoft::WRL::ClassicCom; -using ABI::Windows::Data::Xml::Dom::IXmlAttribute; -using ABI::Windows::Data::Xml::Dom::IXmlDocument; -using ABI::Windows::Data::Xml::Dom::IXmlElement; -using ABI::Windows::Data::Xml::Dom::IXmlNamedNodeMap; -using ABI::Windows::Data::Xml::Dom::IXmlNode; -using ABI::Windows::Data::Xml::Dom::IXmlNodeList; -using ABI::Windows::Data::Xml::Dom::IXmlText; -using ABI::Windows::UI::Notifications::ToastDismissalReason; -using ABI::Windows::UI::Notifications::ToastTemplateType; -using ABI::Windows::UI::Notifications::IToastNotificationManagerStatics; -using ABI::Windows::UI::Notifications::IToastNotifier; -using ABI::Windows::UI::Notifications::IToastNotificationFactory; -using ABI::Windows::UI::Notifications::IToastNotification; -using ABI::Windows::UI::Notifications::ToastNotification; -using ABI::Windows::UI::Notifications::ToastDismissedEventArgs; -using ABI::Windows::UI::Notifications::IToastDismissedEventArgs; -using ABI::Windows::UI::Notifications::ToastFailedEventArgs; -using ABI::Windows::UI::Notifications::IToastFailedEventArgs; -using ABI::Windows::UI::Notifications::ToastTemplateType_ToastText01; -using ABI::Windows::Foundation::ITypedEventHandler; - -#define DEFAULT_SHELL_LINKS_PATH L"\\Microsoft\\Windows\\Start Menu\\Programs\\" -#define DEFAULT_LINK_FORMAT L".lnk" - -namespace WinToastLib { -class WinToastStringWrapper { - public: - WinToastStringWrapper(_In_reads_(length) PCWSTR stringRef, - _In_ UINT32 length) throw(); - explicit WinToastStringWrapper(_In_ const std::wstring& stringRef) throw(); - ~WinToastStringWrapper(); - HSTRING Get() const throw(); - - private: - HSTRING _hstring; - HSTRING_HEADER _header; -}; - -class WinToastHandler { - public: - enum WinToastDismissalReason { - UserCanceled = ToastDismissalReason::ToastDismissalReason_UserCanceled, - ApplicationHidden = - ToastDismissalReason::ToastDismissalReason_ApplicationHidden, - TimedOut = ToastDismissalReason::ToastDismissalReason_TimedOut - }; - - virtual void toastActivated() {} - virtual void toastDismissed(WinToastDismissalReason state) {} - virtual void toastFailed() {} -}; - -class WinToastTemplate { - public: - enum TextField { FirstLine = 0, SecondLine, ThirdLine, LineCount }; - - enum WinToastTemplateType { - ImageWithOneLine = ToastTemplateType::ToastTemplateType_ToastImageAndText01, - ImageWithTwoLines = - ToastTemplateType::ToastTemplateType_ToastImageAndText02, - ImageWithThreeLines = - ToastTemplateType::ToastTemplateType_ToastImageAndText03, - ImageWithFourLines = - ToastTemplateType::ToastTemplateType_ToastImageAndText04, - TextOneLine = ToastTemplateType::ToastTemplateType_ToastText01, - TextTwoLines = ToastTemplateType::ToastTemplateType_ToastText02, - TextThreeLines = ToastTemplateType::ToastTemplateType_ToastText03, - TextFourLines = ToastTemplateType::ToastTemplateType_ToastText04, - WinToastTemplateTypeCount - }; - - explicit WinToastTemplate( - _In_ const WinToastTemplateType& type = ImageWithTwoLines); - ~WinToastTemplate(); - - int textFieldsCount() const { return _textFields.size(); } - bool hasImage() const { return _hasImage; } - std::vector textFields() const { return _textFields; } - std::wstring textField(_In_ const TextField& pos) const { - return _textFields[pos]; - } - std::wstring imagePath() const { return _imagePath; } - WinToastTemplateType type() const { return _type; } - void setTextField(_In_ const std::wstring& txt, _In_ const TextField& pos); - void setImagePath(_In_ const std::wstring& imgPath); - void setSilent(bool is_silent); - bool isSilent() const { return _silent; } - - private: - static int TextFieldsCount[WinToastTemplateTypeCount]; - bool _hasImage; - bool _silent = false; - std::vector _textFields; - std::wstring _imagePath; - WinToastTemplateType _type; - void initComponentsFromType(); -}; - -class WinToast { - public: - static WinToast* instance(); - static bool isCompatible(); - bool initialize(); - bool isInitialized() const { return _isInitialized; } - bool showToast(_In_ const WinToastTemplate& toast, - _In_ WinToastHandler* handler); - std::wstring appName() const; - std::wstring appUserModelId() const; - void setAppUserModelId(_In_ const std::wstring& appName); - void setAppName(_In_ const std::wstring& appName); - - private: - bool _isInitialized; - std::wstring _appName; - std::wstring _aumi; - ComPtr _xmlDocument; - ComPtr _notificationManager; - ComPtr _notifier; - ComPtr _notificationFactory; - ComPtr _notification; - static WinToast* _instance; - - WinToast(void); - IXmlDocument* xmlDocument() const { return _xmlDocument.Get(); } - IToastNotifier* notifier() const { return _notifier.Get(); } - IToastNotificationFactory* notificationFactory() const { - return _notificationFactory.Get(); - } - IToastNotificationManagerStatics* notificationManager() const { - return _notificationManager.Get(); - } - IToastNotification* notification() const { return _notification.Get(); } - - HRESULT setImageField(_In_ const std::wstring& path); - HRESULT setTextField(_In_ const std::wstring& text, _In_ int pos); - bool makeSilent(bool is_silent); -}; -} // namespace WinToastLib - -#endif // ATOM_BROWSER_UI_WIN_TOAST_LIB_H_ diff --git a/default_app/default_app.js b/default_app/default_app.js index e1ef319091e3..e926471965c1 100644 --- a/default_app/default_app.js +++ b/default_app/default_app.js @@ -33,8 +33,9 @@ exports.load = (appUrl) => { body: 'Bar', hasReply: true, replyPlaceholder: 'foo' - }); - n.on('reply', (...args) => console.log(args)); - n.show(); + }) + n.on('reply', (...args) => console.log('reply', ...args)) + n.on('click', (...args) => console.log('click', ...args)) + n.show() }) } diff --git a/filenames.gypi b/filenames.gypi index 100eb2ee503b..a56a02c8832b 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -130,9 +130,6 @@ 'atom/browser/api/atom_api_net.h', 'atom/browser/api/atom_api_notification.cc', 'atom/browser/api/atom_api_notification.h', - 'atom/browser/api/atom_api_notification_mac.mm', - 'atom/browser/api/atom_api_notification_win.cc', - 'atom/browser/api/atom_api_notification_linux.cc', 'atom/browser/api/atom_api_power_monitor.cc', 'atom/browser/api/atom_api_power_monitor.h', 'atom/browser/api/atom_api_power_save_blocker.cc', @@ -361,10 +358,6 @@ 'atom/browser/ui/win/notify_icon.h', 'atom/browser/ui/win/taskbar_host.cc', 'atom/browser/ui/win/taskbar_host.h', - 'atom/browser/ui/win/toast_handler.cc', - 'atom/browser/ui/win/toast_handler.h', - 'atom/browser/ui/win/toast_lib.cc', - 'atom/browser/ui/win/toast_lib.h', 'atom/browser/ui/x/event_disabler.cc', 'atom/browser/ui/x/event_disabler.h', 'atom/browser/ui/x/window_state_watcher.cc', From 9aa6b83f86739a1f22f31e5702b338b27d3ac244 Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Mon, 29 May 2017 21:22:44 +1000 Subject: [PATCH 15/22] Fix compilation and click events on macOS --- atom/browser/api/atom_api_notification.cc | 1 - brightray/browser/mac/notification_center_delegate.mm | 1 - 2 files changed, 2 deletions(-) diff --git a/atom/browser/api/atom_api_notification.cc b/atom/browser/api/atom_api_notification.cc index 608474fdc739..1ed3a291c277 100644 --- a/atom/browser/api/atom_api_notification.cc +++ b/atom/browser/api/atom_api_notification.cc @@ -17,7 +17,6 @@ #include "atom/common/node_includes.h" #include "base/strings/utf_string_conversions.h" #include "base/threading/thread_task_runner_handle.h" -#include "common/string_conversion.h" #include "native_mate/constructor.h" #include "native_mate/dictionary.h" #include "third_party/skia/include/core/SkBitmap.h" diff --git a/brightray/browser/mac/notification_center_delegate.mm b/brightray/browser/mac/notification_center_delegate.mm index 0f21ec88b032..ff2b1a498db7 100644 --- a/brightray/browser/mac/notification_center_delegate.mm +++ b/brightray/browser/mac/notification_center_delegate.mm @@ -29,7 +29,6 @@ didActivateNotification:(NSUserNotification *)notif { auto notification = presenter_->GetNotification(notif); if (notification) { - notification->NotificationClicked(); if (notif.activationType == NSUserNotificationActivationTypeReplied){ notification->NotificationReplied([notif.response.string UTF8String]); } else { From 6cdfb43e4e28e1cb1348322b7e8c449ecc36cdc5 Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Mon, 29 May 2017 21:33:43 +1000 Subject: [PATCH 16/22] Emit a close event --- atom/browser/api/atom_api_notification.cc | 4 ++++ atom/browser/api/atom_api_notification.h | 1 + .../ui/notification_delegate_adapter.cc | 4 +++- atom/browser/ui/notification_observer.h | 1 + docs/api/notification.md | 22 ++++++++++--------- 5 files changed, 21 insertions(+), 11 deletions(-) diff --git a/atom/browser/api/atom_api_notification.cc b/atom/browser/api/atom_api_notification.cc index 1ed3a291c277..f5b1fc4ce33f 100644 --- a/atom/browser/api/atom_api_notification.cc +++ b/atom/browser/api/atom_api_notification.cc @@ -126,6 +126,10 @@ void Notification::OnShown() { Emit("show"); } +void Notification::OnClosed() { + Emit("close"); +} + void Notification::NotifyPropsUpdated() {} // Showing notifications diff --git a/atom/browser/api/atom_api_notification.h b/atom/browser/api/atom_api_notification.h index 513a9a67fd73..5309745a308e 100644 --- a/atom/browser/api/atom_api_notification.h +++ b/atom/browser/api/atom_api_notification.h @@ -35,6 +35,7 @@ class Notification : public mate::TrackableObject, void OnClicked() override; void OnReplied(std::string reply) override; void OnShown() override; + void OnClosed() override; protected: Notification(v8::Isolate* isolate, diff --git a/atom/browser/ui/notification_delegate_adapter.cc b/atom/browser/ui/notification_delegate_adapter.cc index b91a1a97d56a..0ac9ef111573 100644 --- a/atom/browser/ui/notification_delegate_adapter.cc +++ b/atom/browser/ui/notification_delegate_adapter.cc @@ -23,7 +23,9 @@ void AtomNotificationDelegateAdapter::NotificationClick() { void AtomNotificationDelegateAdapter::NotificationReplied(std::string reply) { observer_->OnReplied(reply); } -void AtomNotificationDelegateAdapter::NotificationDestroyed() {} +void AtomNotificationDelegateAdapter::NotificationDestroyed() { + observer_->OnClosed(); +} void AtomNotificationDelegateAdapter::NotificationFailed() {} } // namespace atom diff --git a/atom/browser/ui/notification_observer.h b/atom/browser/ui/notification_observer.h index 5b1ba053ec50..7b4e9acd1732 100644 --- a/atom/browser/ui/notification_observer.h +++ b/atom/browser/ui/notification_observer.h @@ -14,6 +14,7 @@ class NotifictionObserver { virtual void OnClicked() {} virtual void OnReplied(std::string reply) {} virtual void OnShown() {} + virtual void OnClosed() {} protected: virtual ~NotifictionObserver() {} diff --git a/docs/api/notification.md b/docs/api/notification.md index abab31df3c9c..cc8cd7c165eb 100644 --- a/docs/api/notification.md +++ b/docs/api/notification.md @@ -37,16 +37,7 @@ Objects created with `new Notification` emit the following events: **Note:** Some events are only available on specific operating systems and are labeled as such. -#### Event: 'click' - -Returns: - -* `event` Event - -Emitted when the document changed its title, calling `event.preventDefault()` -will prevent the native window's title from changing. - -#### Event: 'close' +#### Event: 'show' Returns: @@ -64,6 +55,17 @@ Returns: Emitted when the notification is clicked by the user. +#### Event: 'close' + +Returns: + +* `event` Event + +Emitted when the notification is closed by manual intervention from the user. + +This event is not guarunteed to be emitted in all cases where the notification +is closed. + #### Event: 'reply' _macOS_ Returns: From 5048425e6ed984e490ecfe9086390715168707d1 Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Tue, 30 May 2017 19:06:51 +1000 Subject: [PATCH 17/22] Update implementation as per feedback --- atom/browser/api/atom_api_notification.cc | 63 ++++++------------- atom/browser/api/atom_api_notification.h | 34 +++++----- .../ui/notification_delegate_adapter.cc | 31 --------- .../ui/notification_delegate_adapter.h | 29 --------- atom/browser/ui/notification_observer.h | 25 -------- brightray/browser/browser_client.cc | 2 +- .../browser/linux/libnotify_notification.cc | 9 +-- .../browser/linux/libnotify_notification.h | 6 +- brightray/browser/mac/cocoa_notification.h | 4 +- brightray/browser/mac/cocoa_notification.mm | 12 ++-- .../mac/notification_center_delegate.mm | 2 +- brightray/browser/notification.cc | 16 +++-- brightray/browser/notification.h | 7 ++- brightray/browser/notification_delegate.h | 2 +- .../browser/notification_delegate_adapter.cc | 4 -- .../browser/win/notification_presenter_win.cc | 2 +- brightray/browser/win/win32_notification.cc | 4 +- brightray/browser/win/win32_notification.h | 4 +- .../browser/win/windows_toast_notification.cc | 11 ++-- .../browser/win/windows_toast_notification.h | 6 +- docs/api/notification.md | 2 +- filenames.gypi | 3 - 22 files changed, 85 insertions(+), 193 deletions(-) delete mode 100644 atom/browser/ui/notification_delegate_adapter.cc delete mode 100644 atom/browser/ui/notification_delegate_adapter.h delete mode 100644 atom/browser/ui/notification_observer.h diff --git a/atom/browser/api/atom_api_notification.cc b/atom/browser/api/atom_api_notification.cc index f5b1fc4ce33f..c6fd06b43e9b 100644 --- a/atom/browser/api/atom_api_notification.cc +++ b/atom/browser/api/atom_api_notification.cc @@ -9,7 +9,6 @@ #include "atom/browser/api/atom_api_menu.h" #include "atom/browser/browser.h" -#include "atom/browser/ui/notification_delegate_adapter.h" #include "atom/common/api/atom_api_native_image.h" #include "atom/common/native_mate_converters/gfx_converter.h" #include "atom/common/native_mate_converters/image_converter.h" @@ -17,6 +16,7 @@ #include "atom/common/node_includes.h" #include "base/strings/utf_string_conversions.h" #include "base/threading/thread_task_runner_handle.h" +#include "brightray/browser/browser_client.h" #include "native_mate/constructor.h" #include "native_mate/dictionary.h" #include "third_party/skia/include/core/SkBitmap.h" @@ -28,9 +28,6 @@ namespace atom { namespace api { -int id_counter = 1; -std::map notifications_; - Notification::Notification(v8::Isolate* isolate, v8::Local wrapper, mate::Arguments* args) { @@ -47,13 +44,13 @@ Notification::Notification(v8::Isolate* isolate, opts.Get("silent", &silent_); opts.Get("replyPlaceholder", &reply_placeholder_); opts.Get("hasReply", &has_reply_); - id_ = id_counter++; } - notifications_[id_] = this; OnInitialProps(); } -Notification::~Notification() {} +Notification::~Notification() { + notification_->set_delegate(nullptr); +} // static mate::WrappableBase* Notification::New(mate::Arguments* args) { @@ -64,18 +61,7 @@ mate::WrappableBase* Notification::New(mate::Arguments* args) { return new Notification(args->isolate(), args->GetThis(), args); } -bool Notification::HasID(int id) { - return notifications_.find(id) != notifications_.end(); -} - -Notification* Notification::FromID(int id) { - return notifications_[id]; -} - // Getters -int Notification::GetID() { - return id_; -} base::string16 Notification::GetTitle() { return title_; } @@ -93,66 +79,54 @@ bool Notification::GetHasReply() { } // Setters -void Notification::SetTitle(base::string16 new_title) { +void Notification::SetTitle(const base::string16& new_title) { title_ = new_title; - NotifyPropsUpdated(); } -void Notification::SetBody(base::string16 new_body) { +void Notification::SetBody(const base::string16& new_body) { body_ = new_body; - NotifyPropsUpdated(); } void Notification::SetSilent(bool new_silent) { silent_ = new_silent; - NotifyPropsUpdated(); } -void Notification::SetReplyPlaceholder(base::string16 new_reply_placeholder) { +void Notification::SetReplyPlaceholder(const base::string16& new_reply_placeholder) { reply_placeholder_ = new_reply_placeholder; - NotifyPropsUpdated(); } void Notification::SetHasReply(bool new_has_reply) { has_reply_ = new_has_reply; - NotifyPropsUpdated(); } -void Notification::OnClicked() { +void Notification::NotificationClick() { Emit("click"); } -void Notification::OnReplied(std::string reply) { +void Notification::NotificationReplied(const std::string reply) { Emit("reply", reply); } -void Notification::OnShown() { +void Notification::NotificationDisplayed() { Emit("show"); } -void Notification::OnClosed() { +void Notification::NotificationDestroyed() { Emit("close"); } -void Notification::NotifyPropsUpdated() {} +void Notification::NotificationClosed() {} // Showing notifications void Notification::Show() { - SkBitmap image = *(new SkBitmap); - if (has_icon_) { - image = *(icon_.ToSkBitmap()); - } - - std::unique_ptr adapter( - new AtomNotificationDelegateAdapter(this)); - auto notif = presenter_->CreateNotification(adapter.get()); - if (notif) { - ignore_result(adapter.release()); // it will release itself automatically. - GURL nullUrl = *(new GURL); - notif->Show(title_, body_, "", nullUrl, image, silent_, has_reply_, reply_placeholder_); + if (presenter_) { + notification_ = presenter_->CreateNotification(this); + if (notification_) { + notification_->Show(title_, body_, "", GURL(), icon_.AsBitmap(), silent_, has_reply_, reply_placeholder_); + } } } bool initialized_ = false; void Notification::OnInitialProps() { if (!initialized_) { - presenter_ = brightray::NotificationPresenter::Create(); + presenter_ = brightray::BrowserClient::Get()->GetNotificationPresenter(); initialized_ = true; } } @@ -164,7 +138,6 @@ void Notification::BuildPrototype(v8::Isolate* isolate, mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate()) .MakeDestroyable() .SetMethod("show", &Notification::Show) - .SetProperty("id", &Notification::GetID) .SetProperty("title", &Notification::GetTitle, &Notification::SetTitle) .SetProperty("body", &Notification::GetBody, &Notification::SetBody) .SetProperty("silent", &Notification::GetSilent, &Notification::SetSilent) diff --git a/atom/browser/api/atom_api_notification.h b/atom/browser/api/atom_api_notification.h index 5309745a308e..fd0b9a2c9409 100644 --- a/atom/browser/api/atom_api_notification.h +++ b/atom/browser/api/atom_api_notification.h @@ -10,9 +10,9 @@ #include #include "atom/browser/api/trackable_object.h" -#include "atom/browser/ui/notification_observer.h" #include "base/strings/utf_string_conversions.h" #include "brightray/browser/notification.h" +#include "brightray/browser/notification_delegate.h" #include "brightray/browser/notification_presenter.h" #include "native_mate/handle.h" #include "ui/gfx/image/image.h" @@ -22,7 +22,7 @@ namespace atom { namespace api { class Notification : public mate::TrackableObject, - public NotifictionObserver { + public brightray::NotificationDelegate { public: static mate::WrappableBase* New(mate::Arguments* args); static bool HasID(int id); @@ -31,11 +31,12 @@ class Notification : public mate::TrackableObject, static void BuildPrototype(v8::Isolate* isolate, v8::Local prototype); - // NotificationObserver: - void OnClicked() override; - void OnReplied(std::string reply) override; - void OnShown() override; - void OnClosed() override; + // NotificationDelegate: + void NotificationClick() override; + void NotificationReplied(const std::string reply) override; + void NotificationDisplayed() override; + void NotificationDestroyed() override; + void NotificationClosed() override; protected: Notification(v8::Isolate* isolate, @@ -47,7 +48,6 @@ class Notification : public mate::TrackableObject, void Show(); // Prop Getters - int GetID(); base::string16 GetTitle(); base::string16 GetBody(); bool GetSilent(); @@ -55,26 +55,24 @@ class Notification : public mate::TrackableObject, bool GetHasReply(); // Prop Setters - void SetTitle(base::string16 new_title); - void SetBody(base::string16 new_body); + void SetTitle(const base::string16& new_title); + void SetBody(const base::string16& new_body); void SetSilent(bool new_silent); - void SetReplyPlaceholder(base::string16 new_reply_placeholder); + void SetReplyPlaceholder(const base::string16& new_reply_placeholder); void SetHasReply(bool new_has_reply); - void NotifyPropsUpdated(); - private: - base::string16 title_ = base::UTF8ToUTF16(""); - base::string16 body_ = base::UTF8ToUTF16(""); + base::string16 title_; + base::string16 body_; gfx::Image icon_; - base::string16 icon_path_ = base::UTF8ToUTF16(""); + base::string16 icon_path_; bool has_icon_ = false; bool silent_ = false; - base::string16 reply_placeholder_ = base::UTF8ToUTF16(""); + base::string16 reply_placeholder_; bool has_reply_ = false; brightray::NotificationPresenter* presenter_; - int id_; + base::WeakPtr notification_; DISALLOW_COPY_AND_ASSIGN(Notification); }; diff --git a/atom/browser/ui/notification_delegate_adapter.cc b/atom/browser/ui/notification_delegate_adapter.cc deleted file mode 100644 index 0ac9ef111573..000000000000 --- a/atom/browser/ui/notification_delegate_adapter.cc +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) 2014 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#include "atom/browser/ui/notification_delegate_adapter.h" - -#include "atom/browser/api/atom_api_notification.h" -#include "brightray/browser/notification_delegate.h" - -namespace atom { - -AtomNotificationDelegateAdapter::AtomNotificationDelegateAdapter( - atom::api::Notification* target) { - observer_ = target; -} -void AtomNotificationDelegateAdapter::NotificationDisplayed() { - observer_->OnShown(); -} -void AtomNotificationDelegateAdapter::NotificationClosed() {} -void AtomNotificationDelegateAdapter::NotificationClick() { - observer_->OnClicked(); -} -void AtomNotificationDelegateAdapter::NotificationReplied(std::string reply) { - observer_->OnReplied(reply); -} -void AtomNotificationDelegateAdapter::NotificationDestroyed() { - observer_->OnClosed(); -} -void AtomNotificationDelegateAdapter::NotificationFailed() {} - -} // namespace atom diff --git a/atom/browser/ui/notification_delegate_adapter.h b/atom/browser/ui/notification_delegate_adapter.h deleted file mode 100644 index e0c56e3fa822..000000000000 --- a/atom/browser/ui/notification_delegate_adapter.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) 2014 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#include "brightray/browser/notification_delegate.h" - -#include "atom/browser/api/atom_api_notification.h" - -#ifndef ATOM_BROWSER_UI_NOTIFICATION_DELEGATE_ADAPTER_H_ -#define ATOM_BROWSER_UI_NOTIFICATION_DELEGATE_ADAPTER_H_ - -namespace atom { - -class AtomNotificationDelegateAdapter : public brightray::NotificationDelegate { - public: - atom::api::Notification* observer_; - explicit AtomNotificationDelegateAdapter(atom::api::Notification* target); - - void NotificationDisplayed(); - void NotificationClosed(); - void NotificationClick(); - void NotificationDestroyed(); - void NotificationFailed(); - void NotificationReplied(std::string reply); -}; - -} // namespace atom - -#endif // ATOM_BROWSER_UI_NOTIFICATION_DELEGATE_ADAPTER_H_ diff --git a/atom/browser/ui/notification_observer.h b/atom/browser/ui/notification_observer.h deleted file mode 100644 index 7b4e9acd1732..000000000000 --- a/atom/browser/ui/notification_observer.h +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) 2014 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#ifndef ATOM_BROWSER_UI_NOTIFICATION_OBSERVER_H_ -#define ATOM_BROWSER_UI_NOTIFICATION_OBSERVER_H_ - -#include - -namespace atom { - -class NotifictionObserver { - public: - virtual void OnClicked() {} - virtual void OnReplied(std::string reply) {} - virtual void OnShown() {} - virtual void OnClosed() {} - - protected: - virtual ~NotifictionObserver() {} -}; - -} // namespace atom - -#endif // ATOM_BROWSER_UI_NOTIFICATION_OBSERVER_H_ diff --git a/brightray/browser/browser_client.cc b/brightray/browser/browser_client.cc index 5aa3208dafcd..26445aabdedf 100644 --- a/brightray/browser/browser_client.cc +++ b/brightray/browser/browser_client.cc @@ -36,7 +36,7 @@ BrowserClient::~BrowserClient() { NotificationPresenter* BrowserClient::GetNotificationPresenter() { if (!notification_presenter_) { - // Create a new presenter if on OS X, Linux, or Windows 8+ + // Create a new presenter if on OS X, Linux, or Windows 7+ notification_presenter_.reset(NotificationPresenter::Create()); } return notification_presenter_.get(); diff --git a/brightray/browser/linux/libnotify_notification.cc b/brightray/browser/linux/libnotify_notification.cc index 27120068bae6..ff3e61110101 100644 --- a/brightray/browser/linux/libnotify_notification.cc +++ b/brightray/browser/linux/libnotify_notification.cc @@ -88,9 +88,9 @@ void LibnotifyNotification::Show(const base::string16& title, const std::string& tag, const GURL& icon_url, const SkBitmap& icon, - const bool silent, - const bool has_reply, - const base::string16 reply_placeholder) { + bool silent, + bool has_reply, + const base::string16& reply_placeholder) { notification_ = libnotify_loader_.notify_notification_new( base::UTF16ToUTF8(title).c_str(), base::UTF16ToUTF8(body).c_str(), @@ -139,7 +139,8 @@ void LibnotifyNotification::Show(const base::string16& title, return; } - delegate()->NotificationDisplayed(); + if (delegate()) + delegate()->NotificationDisplayed(); } void LibnotifyNotification::Dismiss() { diff --git a/brightray/browser/linux/libnotify_notification.h b/brightray/browser/linux/libnotify_notification.h index 861ddce5fa63..dbc34f27ae44 100644 --- a/brightray/browser/linux/libnotify_notification.h +++ b/brightray/browser/linux/libnotify_notification.h @@ -27,9 +27,9 @@ class LibnotifyNotification : public Notification { const std::string& tag, const GURL& icon_url, const SkBitmap& icon, - const bool silent, - const bool has_reply, - const base::string16 reply_placeholder) override; + bool silent, + bool has_reply, + const base::string16& reply_placeholder) override; void Dismiss() override; private: diff --git a/brightray/browser/mac/cocoa_notification.h b/brightray/browser/mac/cocoa_notification.h index c269dd26020a..4e17a67f834f 100644 --- a/brightray/browser/mac/cocoa_notification.h +++ b/brightray/browser/mac/cocoa_notification.h @@ -26,13 +26,13 @@ class CocoaNotification : public Notification { const std::string& tag, const GURL& icon_url, const SkBitmap& icon, - const bool silent, + bool silent, const bool hasReply, const base::string16 replyPlaceholder) override; void Dismiss() override; void NotificationDisplayed(); - void NotificationReplied(std::string reply); + void NotificationReplied(const std::string reply); NSUserNotification* notification() const { return notification_; } diff --git a/brightray/browser/mac/cocoa_notification.mm b/brightray/browser/mac/cocoa_notification.mm index 010f9ba42769..40ac97c66af2 100644 --- a/brightray/browser/mac/cocoa_notification.mm +++ b/brightray/browser/mac/cocoa_notification.mm @@ -28,9 +28,9 @@ void CocoaNotification::Show(const base::string16& title, const std::string& tag, const GURL& icon_url, const SkBitmap& icon, - const bool silent, - const bool has_reply, - const base::string16 reply_placeholder) { + bool silent, + bool has_reply, + const base::string16& reply_placeholder) { notification_.reset([[NSUserNotification alloc] init]); [notification_ setTitle:base::SysUTF16ToNSString(title)]; [notification_ setInformativeText:base::SysUTF16ToNSString(body)]; @@ -65,11 +65,13 @@ void CocoaNotification::Dismiss() { } void CocoaNotification::NotificationDisplayed() { - delegate()->NotificationDisplayed(); + if (delegate()) + delegate()->NotificationDisplayed(); } void CocoaNotification::NotificationReplied(const std::string reply) { - delegate()->NotificationReplied(reply); + if (delegate()) + delegate()->NotificationReplied(reply); } } // namespace brightray diff --git a/brightray/browser/mac/notification_center_delegate.mm b/brightray/browser/mac/notification_center_delegate.mm index ff2b1a498db7..e0b4dfa0e8fd 100644 --- a/brightray/browser/mac/notification_center_delegate.mm +++ b/brightray/browser/mac/notification_center_delegate.mm @@ -21,7 +21,7 @@ - (void)userNotificationCenter:(NSUserNotificationCenter*)center didDeliverNotification:(NSUserNotification*)notif { auto notification = presenter_->GetNotification(notif); - if (notification) + if (notification) notification->NotificationDisplayed(); } diff --git a/brightray/browser/notification.cc b/brightray/browser/notification.cc index 8f7bef979478..4976a91c03fe 100644 --- a/brightray/browser/notification.cc +++ b/brightray/browser/notification.cc @@ -17,21 +17,29 @@ Notification::Notification(NotificationDelegate* delegate, } Notification::~Notification() { - delegate()->NotificationDestroyed(); + if (delegate()) + delegate()->NotificationDestroyed(); +} + +void Notification::set_delegate(NotificationDelegate* delegate) { + delegate_ = delegate; } void Notification::NotificationClicked() { - delegate()->NotificationClick(); + if (delegate()) + delegate()->NotificationClick(); Destroy(); } void Notification::NotificationDismissed() { - delegate()->NotificationClosed(); + if (delegate()) + delegate()->NotificationClosed(); Destroy(); } void Notification::NotificationFailed() { - delegate()->NotificationFailed(); + if (delegate()) + delegate()->NotificationFailed(); Destroy(); } diff --git a/brightray/browser/notification.h b/brightray/browser/notification.h index f575753fea73..1b15855dd5a5 100644 --- a/brightray/browser/notification.h +++ b/brightray/browser/notification.h @@ -26,9 +26,9 @@ class Notification { const std::string& tag, const GURL& icon_url, const SkBitmap& icon, - const bool silent, - const bool hasReply, - const base::string16 replyPlaceholder) = 0; + bool silent, + bool hasReply, + const base::string16& reply_placeholder) = 0; // Closes the notification, this instance will be destroyed after the // notification gets closed. virtual void Dismiss() = 0; @@ -54,6 +54,7 @@ class Notification { public: virtual ~Notification(); + void set_delegate(NotificationDelegate* delegate); private: NotificationDelegate* delegate_; diff --git a/brightray/browser/notification_delegate.h b/brightray/browser/notification_delegate.h index 848af0122d63..33c3534111f8 100644 --- a/brightray/browser/notification_delegate.h +++ b/brightray/browser/notification_delegate.h @@ -19,7 +19,7 @@ class NotificationDelegate : public content::DesktopNotificationDelegate { virtual void NotificationFailed() {} // Notification was replied to - virtual void NotificationReplied(std::string reply) {} + virtual void NotificationReplied(const std::string reply) {} }; } // namespace brightray diff --git a/brightray/browser/notification_delegate_adapter.cc b/brightray/browser/notification_delegate_adapter.cc index 4b515d0e88b2..60405f9ef8d4 100644 --- a/brightray/browser/notification_delegate_adapter.cc +++ b/brightray/browser/notification_delegate_adapter.cc @@ -30,8 +30,4 @@ void NotificationDelegateAdapter::NotificationClick() { delegate_->NotificationClick(); } -// void NotificationDelegateAdapter::NotificationReplied(std::string reply) { -// delegate_->NotificationReplied(reply); -// } - } // namespace brightray diff --git a/brightray/browser/win/notification_presenter_win.cc b/brightray/browser/win/notification_presenter_win.cc index a14a60aaa904..8d44cbdad0f5 100644 --- a/brightray/browser/win/notification_presenter_win.cc +++ b/brightray/browser/win/notification_presenter_win.cc @@ -16,7 +16,7 @@ #include "brightray/browser/win/notification_presenter_win7.h" #include "brightray/browser/win/windows_toast_notification.h" #include "content/public/browser/desktop_notification_delegate.h" -#include "content/public/common/platform_notification_data.h" +#include "content/public/common/platform_notification_data.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/gfx/codec/png_codec.h" diff --git a/brightray/browser/win/win32_notification.cc b/brightray/browser/win/win32_notification.cc index 9aed1de05112..ea3b35ab2c4f 100644 --- a/brightray/browser/win/win32_notification.cc +++ b/brightray/browser/win/win32_notification.cc @@ -12,8 +12,8 @@ namespace brightray { void Win32Notification::Show( const base::string16& title, const base::string16& msg, const std::string& tag, const GURL& icon_url, - const SkBitmap& icon, const bool silent, - const bool has_reply, const base::string16 reply_placeholder) { + const SkBitmap& icon, bool silent, + bool has_reply, const base::string16& reply_placeholder) { auto presenter = static_cast(this->presenter()); if (!presenter) return; diff --git a/brightray/browser/win/win32_notification.h b/brightray/browser/win/win32_notification.h index 2136551f5678..3ec6dfb17e56 100644 --- a/brightray/browser/win/win32_notification.h +++ b/brightray/browser/win/win32_notification.h @@ -12,8 +12,8 @@ class Win32Notification : public brightray::Notification { } void Show(const base::string16& title, const base::string16& msg, const std::string& tag, const GURL& icon_url, - const SkBitmap& icon, const bool silent, - const bool has_reply, const base::string16 reply_placeholder) override; + const SkBitmap& icon, bool silent, + bool has_reply, const base::string16& reply_placeholder) override; void Dismiss() override; const DesktopNotificationController::Notification& GetRef() const { diff --git a/brightray/browser/win/windows_toast_notification.cc b/brightray/browser/win/windows_toast_notification.cc index 2744958c426d..335b2d924fdc 100644 --- a/brightray/browser/win/windows_toast_notification.cc +++ b/brightray/browser/win/windows_toast_notification.cc @@ -89,9 +89,9 @@ void WindowsToastNotification::Show(const base::string16& title, const std::string& tag, const GURL& icon_url, const SkBitmap& icon, - const bool silent, - const bool hasReply, - const base::string16 replyPlaceholder) { + bool silent, + bool has_reply, + const base::string16& reply_placeholder) { auto presenter_win = static_cast(presenter()); std::wstring icon_path = presenter_win->SaveIconToFilesystem(icon, icon_url); @@ -133,7 +133,8 @@ void WindowsToastNotification::Show(const base::string16& title, return; } - delegate()->NotificationDisplayed(); + if (delegate()) + delegate()->NotificationDisplayed(); } void WindowsToastNotification::Dismiss() { @@ -146,7 +147,7 @@ bool WindowsToastNotification::GetToastXml( const std::wstring& title, const std::wstring& msg, const std::wstring& icon_path, - const bool silent, + bool silent, IXmlDocument** toast_xml) { ABI::Windows::UI::Notifications::ToastTemplateType template_type; if (title.empty() || msg.empty()) { diff --git a/brightray/browser/win/windows_toast_notification.h b/brightray/browser/win/windows_toast_notification.h index 62303054f633..0b96f43c34f9 100644 --- a/brightray/browser/win/windows_toast_notification.h +++ b/brightray/browser/win/windows_toast_notification.h @@ -55,9 +55,9 @@ class WindowsToastNotification : public Notification { const std::string& tag, const GURL& icon_url, const SkBitmap& icon, - const bool silent, - const bool has_reply, - const base::string16 reply_placeholder) override; + bool silent, + bool has_reply, + const base::string16& reply_placeholder) override; void Dismiss() override; private: diff --git a/docs/api/notification.md b/docs/api/notification.md index cc8cd7c165eb..5821232447e0 100644 --- a/docs/api/notification.md +++ b/docs/api/notification.md @@ -6,7 +6,7 @@ Process: [Main](../glossary.md#main-process) ## Using in the renderer process -If you want to use Notifications in a renderer process you should use the [HTML5 Notification API](../tutorial/notifications.md) +If you want to show Notifications from a renderer process you should use the [HTML5 Notification API](../tutorial/notifications.md) ## Class: Notification diff --git a/filenames.gypi b/filenames.gypi index a56a02c8832b..ada68f12309d 100644 --- a/filenames.gypi +++ b/filenames.gypi @@ -313,9 +313,6 @@ 'atom/browser/ui/message_box_gtk.cc', 'atom/browser/ui/message_box_mac.mm', 'atom/browser/ui/message_box_win.cc', - 'atom/browser/ui/notification_delegate_adapter.h', - 'atom/browser/ui/notification_delegate_adapter.cc', - 'atom/browser/ui/notification_observer.h', 'atom/browser/ui/tray_icon.cc', 'atom/browser/ui/tray_icon.h', 'atom/browser/ui/tray_icon_gtk.cc', From 686b1388b1370e010a6a4dd06376f350a39a6922 Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Tue, 30 May 2017 19:12:36 +1000 Subject: [PATCH 18/22] Fix linting issues --- atom/browser/api/atom_api_notification.cc | 7 ++++--- brightray/browser/notification_delegate.h | 3 ++- brightray/browser/platform_notification_service.cc | 3 ++- brightray/browser/win/notification_presenter_win.cc | 2 +- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/atom/browser/api/atom_api_notification.cc b/atom/browser/api/atom_api_notification.cc index c6fd06b43e9b..ed4c4b06c099 100644 --- a/atom/browser/api/atom_api_notification.cc +++ b/atom/browser/api/atom_api_notification.cc @@ -88,8 +88,8 @@ void Notification::SetBody(const base::string16& new_body) { void Notification::SetSilent(bool new_silent) { silent_ = new_silent; } -void Notification::SetReplyPlaceholder(const base::string16& new_reply_placeholder) { - reply_placeholder_ = new_reply_placeholder; +void Notification::SetReplyPlaceholder(const base::string16& new_placeholder) { + reply_placeholder_ = new_placeholder; } void Notification::SetHasReply(bool new_has_reply) { has_reply_ = new_has_reply; @@ -118,7 +118,8 @@ void Notification::Show() { if (presenter_) { notification_ = presenter_->CreateNotification(this); if (notification_) { - notification_->Show(title_, body_, "", GURL(), icon_.AsBitmap(), silent_, has_reply_, reply_placeholder_); + notification_->Show(title_, body_, "", GURL(), icon_.AsBitmap(), silent_, + has_reply_, reply_placeholder_); } } } diff --git a/brightray/browser/notification_delegate.h b/brightray/browser/notification_delegate.h index 33c3534111f8..16eda723a5b4 100644 --- a/brightray/browser/notification_delegate.h +++ b/brightray/browser/notification_delegate.h @@ -5,9 +5,10 @@ #ifndef BRIGHTRAY_BROWSER_NOTIFICATION_DELEGATE_H_ #define BRIGHTRAY_BROWSER_NOTIFICATION_DELEGATE_H_ -#include "content/public/browser/desktop_notification_delegate.h" #include +#include "content/public/browser/desktop_notification_delegate.h" + namespace brightray { class NotificationDelegate : public content::DesktopNotificationDelegate { diff --git a/brightray/browser/platform_notification_service.cc b/brightray/browser/platform_notification_service.cc index 99ab54080c22..964fa67f5218 100644 --- a/brightray/browser/platform_notification_service.cc +++ b/brightray/browser/platform_notification_service.cc @@ -31,7 +31,8 @@ void OnWebNotificationAllowed(base::WeakPtr notification, return; if (allowed) notification->Show(data.title, data.body, data.tag, data.icon, icon, - audio_muted ? true : data.silent, false, base::UTF8ToUTF16("")); + audio_muted ? true : data.silent, false, + base::UTF8ToUTF16("")); else notification->Destroy(); } diff --git a/brightray/browser/win/notification_presenter_win.cc b/brightray/browser/win/notification_presenter_win.cc index 8d44cbdad0f5..a14a60aaa904 100644 --- a/brightray/browser/win/notification_presenter_win.cc +++ b/brightray/browser/win/notification_presenter_win.cc @@ -16,7 +16,7 @@ #include "brightray/browser/win/notification_presenter_win7.h" #include "brightray/browser/win/windows_toast_notification.h" #include "content/public/browser/desktop_notification_delegate.h" -#include "content/public/common/platform_notification_data.h" +#include "content/public/common/platform_notification_data.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/gfx/codec/png_codec.h" From 3938373ecb5a43f68214eaa0882ec44356e37069 Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Tue, 30 May 2017 20:27:24 +1000 Subject: [PATCH 19/22] Fix linting errors and add isSupported --- atom/browser/api/atom_api_notification.cc | 6 ++++++ atom/browser/api/atom_api_notification.h | 3 +-- brightray/browser/mac/cocoa_notification.h | 4 ++-- brightray/browser/notification.h | 2 +- docs/api/notification.md | 8 ++++++++ lib/browser/api/notification.js | 4 +++- 6 files changed, 21 insertions(+), 6 deletions(-) diff --git a/atom/browser/api/atom_api_notification.cc b/atom/browser/api/atom_api_notification.cc index ed4c4b06c099..58b3847e472d 100644 --- a/atom/browser/api/atom_api_notification.cc +++ b/atom/browser/api/atom_api_notification.cc @@ -132,6 +132,10 @@ void Notification::OnInitialProps() { } } +bool Notification::IsSupported() { + return !!brightray::BrowserClient::Get()->GetNotificationPresenter(); +} + // static void Notification::BuildPrototype(v8::Isolate* isolate, v8::Local prototype) { @@ -166,6 +170,8 @@ void Initialize(v8::Local exports, mate::Dictionary dict(isolate, exports); dict.Set("Notification", Notification::GetConstructor(isolate)->GetFunction()); + + dict.SetMethod("isSupported", &Notification::IsSupported); } } // namespace diff --git a/atom/browser/api/atom_api_notification.h b/atom/browser/api/atom_api_notification.h index fd0b9a2c9409..1b6a4fedbbb9 100644 --- a/atom/browser/api/atom_api_notification.h +++ b/atom/browser/api/atom_api_notification.h @@ -25,8 +25,7 @@ class Notification : public mate::TrackableObject, public brightray::NotificationDelegate { public: static mate::WrappableBase* New(mate::Arguments* args); - static bool HasID(int id); - static Notification* FromID(int id); + static bool IsSupported(); static void BuildPrototype(v8::Isolate* isolate, v8::Local prototype); diff --git a/brightray/browser/mac/cocoa_notification.h b/brightray/browser/mac/cocoa_notification.h index 4e17a67f834f..07de1c0875b6 100644 --- a/brightray/browser/mac/cocoa_notification.h +++ b/brightray/browser/mac/cocoa_notification.h @@ -27,8 +27,8 @@ class CocoaNotification : public Notification { const GURL& icon_url, const SkBitmap& icon, bool silent, - const bool hasReply, - const base::string16 replyPlaceholder) override; + const bool has_reply, + const base::string16 reply_placeholder) override; void Dismiss() override; void NotificationDisplayed(); diff --git a/brightray/browser/notification.h b/brightray/browser/notification.h index 1b15855dd5a5..054adb5bea3d 100644 --- a/brightray/browser/notification.h +++ b/brightray/browser/notification.h @@ -27,7 +27,7 @@ class Notification { const GURL& icon_url, const SkBitmap& icon, bool silent, - bool hasReply, + bool has_reply, const base::string16& reply_placeholder) = 0; // Closes the notification, this instance will be destroyed after the // notification gets closed. diff --git a/docs/api/notification.md b/docs/api/notification.md index 5821232447e0..01bb12a34a97 100644 --- a/docs/api/notification.md +++ b/docs/api/notification.md @@ -19,6 +19,14 @@ Process: [Main](../glossary.md#main-process) It creates a new `Notification` with native properties as set by the `options`. +### Static Methods + +The `Notification` class has the following static methods: + +#### `BrowserWindow.isSupported()` + +Returns `Boolean` - Whether or not desktop notifications are supported on the current system + ### `new Notification([options])` _Experimental_ * `options` Object diff --git a/lib/browser/api/notification.js b/lib/browser/api/notification.js index 953f7d8d4987..d9885ae2aa2e 100644 --- a/lib/browser/api/notification.js +++ b/lib/browser/api/notification.js @@ -1,6 +1,8 @@ const {EventEmitter} = require('events') -const {Notification} = process.atomBinding('notification') +const {Notification, isSupported} = process.atomBinding('notification') Object.setPrototypeOf(Notification.prototype, EventEmitter.prototype) +Notification.isSupported = isSupported + module.exports = Notification From 227a2bd5dc57ee93bf4b4ea9e3a2c8837e629f51 Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Tue, 30 May 2017 20:29:27 +1000 Subject: [PATCH 20/22] Fix reference typed reply placeholder --- brightray/browser/mac/cocoa_notification.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/brightray/browser/mac/cocoa_notification.h b/brightray/browser/mac/cocoa_notification.h index 07de1c0875b6..a6411d924fab 100644 --- a/brightray/browser/mac/cocoa_notification.h +++ b/brightray/browser/mac/cocoa_notification.h @@ -28,7 +28,7 @@ class CocoaNotification : public Notification { const SkBitmap& icon, bool silent, const bool has_reply, - const base::string16 reply_placeholder) override; + const base::string16& reply_placeholder) override; void Dismiss() override; void NotificationDisplayed(); From 5fa2831756b705f9121498a56786c2e165965cc2 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Wed, 31 May 2017 16:17:29 +0900 Subject: [PATCH 21/22] Coding style fixes --- atom/browser/api/atom_api_notification.cc | 35 +++++++++------------ atom/browser/api/atom_api_notification.h | 4 +-- brightray/browser/mac/cocoa_notification.h | 2 +- brightray/browser/mac/cocoa_notification.mm | 2 +- brightray/browser/notification.cc | 4 --- brightray/browser/notification.h | 7 ++--- brightray/browser/notification_delegate.h | 2 +- 7 files changed, 23 insertions(+), 33 deletions(-) diff --git a/atom/browser/api/atom_api_notification.cc b/atom/browser/api/atom_api_notification.cc index 58b3847e472d..791d574774ca 100644 --- a/atom/browser/api/atom_api_notification.cc +++ b/atom/browser/api/atom_api_notification.cc @@ -4,24 +4,16 @@ #include "atom/browser/api/atom_api_notification.h" -#include -#include - #include "atom/browser/api/atom_api_menu.h" #include "atom/browser/browser.h" -#include "atom/common/api/atom_api_native_image.h" #include "atom/common/native_mate_converters/gfx_converter.h" #include "atom/common/native_mate_converters/image_converter.h" #include "atom/common/native_mate_converters/string16_converter.h" #include "atom/common/node_includes.h" #include "base/strings/utf_string_conversions.h" -#include "base/threading/thread_task_runner_handle.h" #include "brightray/browser/browser_client.h" #include "native_mate/constructor.h" #include "native_mate/dictionary.h" -#include "third_party/skia/include/core/SkBitmap.h" -#include "ui/gfx/codec/png_codec.h" -#include "ui/gfx/image/image.h" #include "url/gurl.h" namespace atom { @@ -33,6 +25,8 @@ Notification::Notification(v8::Isolate* isolate, mate::Arguments* args) { InitWith(isolate, wrapper); + presenter_ = brightray::BrowserClient::Get()->GetNotificationPresenter(); + mate::Dictionary opts; if (args->GetNext(&opts)) { opts.Get("title", &title_); @@ -45,11 +39,11 @@ Notification::Notification(v8::Isolate* isolate, opts.Get("replyPlaceholder", &reply_placeholder_); opts.Get("hasReply", &has_reply_); } - OnInitialProps(); } Notification::~Notification() { - notification_->set_delegate(nullptr); + if (notification_) + notification_->set_delegate(nullptr); } // static @@ -65,15 +59,19 @@ mate::WrappableBase* Notification::New(mate::Arguments* args) { base::string16 Notification::GetTitle() { return title_; } + base::string16 Notification::GetBody() { return body_; } + bool Notification::GetSilent() { return silent_; } + base::string16 Notification::GetReplyPlaceholder() { return reply_placeholder_; } + bool Notification::GetHasReply() { return has_reply_; } @@ -82,15 +80,19 @@ bool Notification::GetHasReply() { void Notification::SetTitle(const base::string16& new_title) { title_ = new_title; } + void Notification::SetBody(const base::string16& new_body) { body_ = new_body; } + void Notification::SetSilent(bool new_silent) { silent_ = new_silent; } + void Notification::SetReplyPlaceholder(const base::string16& new_placeholder) { reply_placeholder_ = new_placeholder; } + void Notification::SetHasReply(bool new_has_reply) { has_reply_ = new_has_reply; } @@ -99,7 +101,7 @@ void Notification::NotificationClick() { Emit("click"); } -void Notification::NotificationReplied(const std::string reply) { +void Notification::NotificationReplied(const std::string& reply) { Emit("reply", reply); } @@ -111,7 +113,8 @@ void Notification::NotificationDestroyed() { Emit("close"); } -void Notification::NotificationClosed() {} +void Notification::NotificationClosed() { +} // Showing notifications void Notification::Show() { @@ -124,14 +127,6 @@ void Notification::Show() { } } -bool initialized_ = false; -void Notification::OnInitialProps() { - if (!initialized_) { - presenter_ = brightray::BrowserClient::Get()->GetNotificationPresenter(); - initialized_ = true; - } -} - bool Notification::IsSupported() { return !!brightray::BrowserClient::Get()->GetNotificationPresenter(); } diff --git a/atom/browser/api/atom_api_notification.h b/atom/browser/api/atom_api_notification.h index 1b6a4fedbbb9..9e1c47eeb74e 100644 --- a/atom/browser/api/atom_api_notification.h +++ b/atom/browser/api/atom_api_notification.h @@ -32,7 +32,7 @@ class Notification : public mate::TrackableObject, // NotificationDelegate: void NotificationClick() override; - void NotificationReplied(const std::string reply) override; + void NotificationReplied(const std::string& reply) override; void NotificationDisplayed() override; void NotificationDestroyed() override; void NotificationClosed() override; @@ -43,7 +43,6 @@ class Notification : public mate::TrackableObject, mate::Arguments* args); ~Notification() override; - void OnInitialProps(); void Show(); // Prop Getters @@ -69,6 +68,7 @@ class Notification : public mate::TrackableObject, bool silent_ = false; base::string16 reply_placeholder_; bool has_reply_ = false; + brightray::NotificationPresenter* presenter_; base::WeakPtr notification_; diff --git a/brightray/browser/mac/cocoa_notification.h b/brightray/browser/mac/cocoa_notification.h index a6411d924fab..4e043dd7d8fc 100644 --- a/brightray/browser/mac/cocoa_notification.h +++ b/brightray/browser/mac/cocoa_notification.h @@ -32,7 +32,7 @@ class CocoaNotification : public Notification { void Dismiss() override; void NotificationDisplayed(); - void NotificationReplied(const std::string reply); + void NotificationReplied(const std::string& reply); NSUserNotification* notification() const { return notification_; } diff --git a/brightray/browser/mac/cocoa_notification.mm b/brightray/browser/mac/cocoa_notification.mm index 40ac97c66af2..5a16d40d1a7a 100644 --- a/brightray/browser/mac/cocoa_notification.mm +++ b/brightray/browser/mac/cocoa_notification.mm @@ -69,7 +69,7 @@ void CocoaNotification::NotificationDisplayed() { delegate()->NotificationDisplayed(); } -void CocoaNotification::NotificationReplied(const std::string reply) { +void CocoaNotification::NotificationReplied(const std::string& reply) { if (delegate()) delegate()->NotificationReplied(reply); } diff --git a/brightray/browser/notification.cc b/brightray/browser/notification.cc index 4976a91c03fe..2db8e5ce4fb9 100644 --- a/brightray/browser/notification.cc +++ b/brightray/browser/notification.cc @@ -21,10 +21,6 @@ Notification::~Notification() { delegate()->NotificationDestroyed(); } -void Notification::set_delegate(NotificationDelegate* delegate) { - delegate_ = delegate; -} - void Notification::NotificationClicked() { if (delegate()) delegate()->NotificationClick(); diff --git a/brightray/browser/notification.h b/brightray/browser/notification.h index 054adb5bea3d..f5e00a23a9b7 100644 --- a/brightray/browser/notification.h +++ b/brightray/browser/notification.h @@ -20,6 +20,8 @@ class NotificationPresenter; class Notification { public: + virtual ~Notification(); + // Shows the notification. virtual void Show(const base::string16& title, const base::string16& msg, @@ -45,6 +47,7 @@ class Notification { return weak_factory_.GetWeakPtr(); } + void set_delegate(NotificationDelegate* delegate) { delegate_ = delegate; } NotificationDelegate* delegate() const { return delegate_; } NotificationPresenter* presenter() const { return presenter_; } @@ -52,10 +55,6 @@ class Notification { Notification(NotificationDelegate* delegate, NotificationPresenter* presenter); - public: - virtual ~Notification(); - void set_delegate(NotificationDelegate* delegate); - private: NotificationDelegate* delegate_; NotificationPresenter* presenter_; diff --git a/brightray/browser/notification_delegate.h b/brightray/browser/notification_delegate.h index 16eda723a5b4..8493fdc3a5aa 100644 --- a/brightray/browser/notification_delegate.h +++ b/brightray/browser/notification_delegate.h @@ -20,7 +20,7 @@ class NotificationDelegate : public content::DesktopNotificationDelegate { virtual void NotificationFailed() {} // Notification was replied to - virtual void NotificationReplied(const std::string reply) {} + virtual void NotificationReplied(const std::string& reply) {} }; } // namespace brightray From 9d4588a5d05e62562dda30625616e7f3bee3edc3 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Wed, 31 May 2017 16:39:53 +0900 Subject: [PATCH 22/22] Remove test code --- default_app/default_app.js | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/default_app/default_app.js b/default_app/default_app.js index e926471965c1..2a3ce3a85ec1 100644 --- a/default_app/default_app.js +++ b/default_app/default_app.js @@ -1,4 +1,4 @@ -const {app, BrowserWindow, Notification} = require('electron') +const {app, BrowserWindow} = require('electron') const path = require('path') let mainWindow = null @@ -27,15 +27,5 @@ exports.load = (appUrl) => { mainWindow = new BrowserWindow(options) mainWindow.loadURL(appUrl) mainWindow.focus() - - const n = new Notification({ - title: 'Foo', - body: 'Bar', - hasReply: true, - replyPlaceholder: 'foo' - }) - n.on('reply', (...args) => console.log('reply', ...args)) - n.on('click', (...args) => console.log('click', ...args)) - n.show() }) }