From 498bff9e5ab5f8faa93614b6047ebe5504fe644a Mon Sep 17 00:00:00 2001 From: Jason Poon Date: Wed, 4 Nov 2015 10:13:52 -0800 Subject: [PATCH] Windows Toasts: Enable Basic Notifications To test this puppy out, build and open up the Chromium Dev Tools. Then, run `new Notification("Test title", {body: "Test body"});` --- brightray/brightray.gyp | 10 + .../platform_notification_service_impl.cc | 2 - .../browser/win/notification_presenter_win.cc | 75 +++ .../browser/win/notification_presenter_win.h | 38 ++ .../browser/win/windows_toast_notification.cc | 593 ++++++++++++++++++ .../browser/win/windows_toast_notification.h | 99 +++ brightray/filenames.gypi | 4 + 7 files changed, 819 insertions(+), 2 deletions(-) create mode 100644 brightray/browser/win/notification_presenter_win.cc create mode 100644 brightray/browser/win/notification_presenter_win.h create mode 100644 brightray/browser/win/windows_toast_notification.cc create mode 100644 brightray/browser/win/windows_toast_notification.h diff --git a/brightray/brightray.gyp b/brightray/brightray.gyp index 4176e7ddbf..697a4c962e 100644 --- a/brightray/brightray.gyp +++ b/brightray/brightray.gyp @@ -161,6 +161,14 @@ ] }], # OS=="mac" ['OS=="win"', { + 'msvs_settings': { + 'VCLinkerTool': { + 'AdditionalDependencies': [ + 'runtimeobject.lib', + 'windowsapp.lib' + ], + }, + }, 'conditions': [ ['libchromiumcontent_component', { # sandbox, base_static, devtools_discovery, devtools_http_handler, @@ -195,6 +203,8 @@ 'msvs_settings': { 'VCLinkerTool': { 'AdditionalDependencies': [ + 'Shlwapi.lib', + 'Crypt32.lib', 'advapi32.lib', 'dbghelp.lib', 'delayimp.lib', diff --git a/brightray/browser/platform_notification_service_impl.cc b/brightray/browser/platform_notification_service_impl.cc index d2b0f679a6..db8572ee89 100644 --- a/brightray/browser/platform_notification_service_impl.cc +++ b/brightray/browser/platform_notification_service_impl.cc @@ -20,10 +20,8 @@ PlatformNotificationServiceImpl::PlatformNotificationServiceImpl() {} PlatformNotificationServiceImpl::~PlatformNotificationServiceImpl() {} NotificationPresenter* PlatformNotificationServiceImpl::notification_presenter() { -#if defined(OS_MACOSX) || defined(OS_LINUX) if (!notification_presenter_) notification_presenter_.reset(NotificationPresenter::Create()); -#endif return notification_presenter_.get(); } diff --git a/brightray/browser/win/notification_presenter_win.cc b/brightray/browser/win/notification_presenter_win.cc new file mode 100644 index 0000000000..98cc7d0418 --- /dev/null +++ b/brightray/browser/win/notification_presenter_win.cc @@ -0,0 +1,75 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright (c) 2013 Patrick Reynolds . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#include "browser/win/notification_presenter_win.h" + +#include "base/bind.h" +#include "base/logging.h" +#include "base/files/file_enumerator.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "content/public/browser/desktop_notification_delegate.h" +#include "content/public/common/platform_notification_data.h" +#include "common/application_info.h" +#include +#include +#include +#include "third_party/skia/include/core/SkBitmap.h" +#include "base/win/windows_version.h" + +// Windows Header +#define WIN32_LEAN_AND_MEAN + +#include +#include +#include +#include +#include +#include "windows_toast_notification.h" + +#pragma comment(lib, "runtimeobject.lib") +#pragma comment(lib, "Crypt32.lib") + +using namespace WinToasts; +using namespace Microsoft::WRL; +using namespace ABI::Windows::UI::Notifications; +using namespace ABI::Windows::Data::Xml::Dom; +using namespace ABI::Windows::Foundation; + +namespace brightray { + +// static +NotificationPresenter* NotificationPresenter::Create() { + return new NotificationPresenterWin; +} + +NotificationPresenterWin::NotificationPresenterWin() { +} + +NotificationPresenterWin::~NotificationPresenterWin() { +} + +void NotificationPresenterWin::ShowNotification( + const content::PlatformNotificationData& data, + const SkBitmap& icon, + scoped_ptr delegate_ptr, + base::Closure* cancel_callback) { + + std::wstring title = data.title; + std::wstring body = data.body; + std::string appName = GetApplicationName(); + char* img = NULL; + + WinToasts::WindowsToastNotification* wtn = new WinToasts::WindowsToastNotification(appName.c_str()); + wtn->ShowNotification(title.c_str(), body.c_str(), img); +} + +void NotificationPresenterWin::CancelNotification() { +} + +void NotificationPresenterWin::DeleteNotification() { +} + +} // namespace brightray \ No newline at end of file diff --git a/brightray/browser/win/notification_presenter_win.h b/brightray/browser/win/notification_presenter_win.h new file mode 100644 index 0000000000..0205422f92 --- /dev/null +++ b/brightray/browser/win/notification_presenter_win.h @@ -0,0 +1,38 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright (c) 2013 Patrick Reynolds . All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE-CHROMIUM file. + +#ifndef BRIGHTRAY_BROWSER_NOTIFICATION_PRESENTER_WIN_H_ +#define BRIGHTRAY_BROWSER_NOTIFICATION_PRESENTER_WIN_H_ + +#include "base/compiler_specific.h" +#include "browser/notification_presenter.h" + +#include + +namespace brightray { + +class NotificationPresenterWin : public NotificationPresenter { + public: + NotificationPresenterWin(); + ~NotificationPresenterWin(); + + // NotificationPresenter: + void ShowNotification( + const content::PlatformNotificationData&, + const SkBitmap& icon, + scoped_ptr delegate, + base::Closure* cancel_callback) override; + + void RemoveNotification(); + + private: + + void CancelNotification(); + void DeleteNotification(); +}; + +} // namespace brightray + +#endif diff --git a/brightray/browser/win/windows_toast_notification.cc b/brightray/browser/win/windows_toast_notification.cc new file mode 100644 index 0000000000..7a90ac3d3f --- /dev/null +++ b/brightray/browser/win/windows_toast_notification.cc @@ -0,0 +1,593 @@ +// Copyright (c) 2015 Felix Rieseberg . All rights reserved. +// Copyright (c) 2015 Ryan McShane and Brandon Smith +// Thanks to both of those folks mentioned above who first thought up a bunch of this code +// and released it as MIT to the world. + +#include "windows_toast_notification.h" +#include +#include +#include +#include +#include +#include + +using namespace WinToasts; +using namespace Windows::Foundation; + +#define BREAK_IF_BAD(hr) if(!SUCCEEDED(hr)) break; +#define SHORTCUT_FORMAT "\\Microsoft\\Windows\\Start Menu\\Programs\\%s.lnk" + +char WindowsToastNotification::s_appName[MAX_PATH] = {}; +char WindowsToastNotification::s_tempDirPath[MAX_PATH] = {}; + +namespace WinToasts { + +WindowsToastNotification::WindowsToastNotification(const char* appName) +{ + sprintf_s(s_appName, ARRAYSIZE(s_appName), "%s", appName); + m_eventHandler = NULL; + m_tempFilePath = NULL; +} + +WindowsToastNotification::~WindowsToastNotification() +{ + if (m_eventHandler){ + delete m_eventHandler; + } + + if (m_tempFilePath){ + DeleteTempImageFile(); + delete m_tempFilePath; + } +} + +/*static*/ void WindowsToastNotification::InitSystemProps(char* appName, char* tempDirPath) +{ + + sprintf_s(s_tempDirPath, ARRAYSIZE(s_tempDirPath), "%s", tempDirPath); +} + +void WindowsToastNotification::ShowNotification(const WCHAR* title, const WCHAR* msg, const char* img) +{ + // Init using WRL + Windows::Foundation::Initialize(RO_INIT_MULTITHREADED); + + HSTRING toastNotifMgrStr = NULL; + HSTRING appId = NULL; + HSTRING toastNotifStr = NULL; + + // Create a shortcut in the user's app data folder + // (requirement for Windows 8 to show notifications - Windows 10 is fine without it) + // TODO: This shoudl be cleaned up + // HRESULT hr = EnsureShortcut(); + + do{ + HRESULT hr = CreateHString(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager, &toastNotifMgrStr); + BREAK_IF_BAD(hr); + + ComPtr toastMgr; + hr = Windows::Foundation::GetActivationFactory(toastNotifMgrStr, &toastMgr); + BREAK_IF_BAD(hr); + + ComPtr toastXml; + hr = GetToastXml(toastMgr.Get(), title, msg, img, &toastXml); + BREAK_IF_BAD(hr); + + WCHAR wAppName[MAX_PATH]; + swprintf(wAppName, ARRAYSIZE(wAppName), L"%S", s_appName); + hr = CreateHString(wAppName, &appId); + BREAK_IF_BAD(hr); + + ComPtr notifier; + toastMgr->CreateToastNotifierWithId(appId, ¬ifier); + BREAK_IF_BAD(hr); + + hr = CreateHString(RuntimeClass_Windows_UI_Notifications_ToastNotification, &toastNotifStr); + BREAK_IF_BAD(hr); + + ComPtr toastFactory; + hr = Windows::Foundation::GetActivationFactory(toastNotifStr, &toastFactory); + BREAK_IF_BAD(hr); + + ComPtr toast; + hr = toastFactory->CreateToastNotification(toastXml.Get(), &toast); + BREAK_IF_BAD(hr); + + hr = SetupCallbacks(toast.Get()); + BREAK_IF_BAD(hr); + + hr = notifier->Show(toast.Get()); + BREAK_IF_BAD(hr); + + //m_cbFn.Reset(Isolate::GetCurrent(), cbFn); + + //todo: + //cbPtr->NotificationDisplayed(); + } while (FALSE); + + if (toastNotifMgrStr != NULL){ + WindowsDeleteString(toastNotifMgrStr); + } + + if (appId != NULL){ + WindowsDeleteString(appId); + } + + if (toastNotifStr != NULL){ + WindowsDeleteString(toastNotifStr); + } + + // if (!SUCCEEDED(hr)){ + // delete this; + // } +} + + +void WindowsToastNotification::NotificationClicked() +{ + // Local fn = Local::New(Isolate::GetCurrent(), m_cbFn); + // Handle args[1]; + // fn->Call(Isolate::GetCurrent()->GetCurrentContext()->Global(), 0, args); + // delete this; +} + + +void WindowsToastNotification::NotificationDismissed() +{ + delete this; +} + + +HRESULT WindowsToastNotification::GetToastXml(IToastNotificationManagerStatics* toastMgr, const WCHAR* title, const WCHAR* msg, const char* img, IXmlDocument** toastXml) +{ + HRESULT hr; + ToastTemplateType templateType; + if (title == NULL || msg == NULL){ + // Single line toast + templateType = img == NULL ? ToastTemplateType_ToastText01 : ToastTemplateType_ToastImageAndText01; + hr = toastMgr->GetTemplateContent(templateType, toastXml); + if (SUCCEEDED(hr)) { + const WCHAR* text = title != NULL ? title : msg; + hr = SetXmlText(*toastXml, text); + } + } else { + // Title and body toast + templateType = img == NULL ? ToastTemplateType_ToastText02 : ToastTemplateType_ToastImageAndText02; + hr = toastMgr->GetTemplateContent(templateType, toastXml); + if (SUCCEEDED(hr)) { + hr = SetXmlText(*toastXml, title, msg); + } + } + + if (img != NULL && SUCCEEDED(hr)){ + // Toast has image + hr = CreateTempImageFile(img); + if (SUCCEEDED(hr)){ + hr = SetXmlImage(*toastXml); + } + + // Don't stop a notification from showing just because an image couldn't be displayed. By default the app icon will be shown. + hr = S_OK; + } + + return hr; +} + + +HRESULT WindowsToastNotification::SetXmlText(IXmlDocument* doc, const WCHAR* text) +{ + HSTRING tag = NULL; + + ComPtr nodeList; + HRESULT hr = GetTextNodeList(&tag, doc, &nodeList, 1); + do{ + BREAK_IF_BAD(hr); + + ComPtr node; + hr = nodeList->Item(0, &node); + BREAK_IF_BAD(hr); + + hr = AppendTextToXml(doc, node.Get(), text); + } while (FALSE); + + if (tag != NULL){ + WindowsDeleteString(tag); + } + + return hr; +} + + +HRESULT WindowsToastNotification::SetXmlText(IXmlDocument* doc, const WCHAR* title, const WCHAR* body) +{ + HSTRING tag = NULL; + ComPtr nodeList; + HRESULT hr = GetTextNodeList(&tag, doc, &nodeList, 2); + do{ + BREAK_IF_BAD(hr); + + ComPtr node; + hr = nodeList->Item(0, &node); + BREAK_IF_BAD(hr); + + hr = AppendTextToXml(doc, node.Get(), title); + BREAK_IF_BAD(hr); + + hr = nodeList->Item(1, &node); + BREAK_IF_BAD(hr); + + hr = AppendTextToXml(doc, node.Get(), body); + } while (FALSE); + + if (tag != NULL){ + WindowsDeleteString(tag); + } + + return hr; +} + + +HRESULT WindowsToastNotification::SetXmlImage(IXmlDocument* doc) +{ + HSTRING tag = NULL; + HSTRING src = NULL; + HSTRING imgPath = NULL; + HRESULT hr = CreateHString(L"image", &tag); + do{ + BREAK_IF_BAD(hr); + + ComPtr nodeList; + hr = doc->GetElementsByTagName(tag, &nodeList); + BREAK_IF_BAD(hr); + + ComPtr imageNode; + hr = nodeList->Item(0, &imageNode); + BREAK_IF_BAD(hr); + + ComPtr attrs; + hr = imageNode->get_Attributes(&attrs); + BREAK_IF_BAD(hr); + + hr = CreateHString(L"src", &src); + BREAK_IF_BAD(hr); + + ComPtr srcAttr; + hr = attrs->GetNamedItem(src, &srcAttr); + BREAK_IF_BAD(hr); + + WCHAR xmlPath[MAX_PATH]; + swprintf(xmlPath, ARRAYSIZE(xmlPath), L"file:///%S", m_tempFilePath); + hr = CreateHString(xmlPath, &imgPath); + BREAK_IF_BAD(hr); + + ComPtr srcText; + hr = doc->CreateTextNode(imgPath, &srcText); + BREAK_IF_BAD(hr); + + ComPtr srcNode; + hr = srcText.As(&srcNode); + BREAK_IF_BAD(hr); + + ComPtr childNode; + hr = srcAttr->AppendChild(srcNode.Get(), &childNode); + } while (FALSE); + + if (tag != NULL){ + WindowsDeleteString(tag); + } + if (src != NULL){ + WindowsDeleteString(src); + } + if (imgPath != NULL){ + WindowsDeleteString(imgPath); + } + + return hr; +} + + +HRESULT WindowsToastNotification::GetTextNodeList(HSTRING* tag, IXmlDocument* doc, IXmlNodeList** nodeList, UINT32 reqLength) +{ + HRESULT hr = CreateHString(L"text", tag); + do{ + BREAK_IF_BAD(hr); + + hr = doc->GetElementsByTagName(*tag, nodeList); + BREAK_IF_BAD(hr); + + UINT32 nodeLength; + hr = (*nodeList)->get_Length(&nodeLength); + BREAK_IF_BAD(hr); + + if (nodeLength < reqLength) { + hr = E_INVALIDARG; + } + } while (FALSE); + + if (!SUCCEEDED(hr)){ + // Allow the caller to delete this string on success + WindowsDeleteString(*tag); + } + + return hr; +} + + +HRESULT WindowsToastNotification::AppendTextToXml(IXmlDocument* doc, IXmlNode* node, const WCHAR* text) +{ + HSTRING str = NULL; + HRESULT hr = CreateHString(text, &str); + do{ + BREAK_IF_BAD(hr); + + ComPtr xmlText; + hr = doc->CreateTextNode(str, &xmlText); + BREAK_IF_BAD(hr); + + ComPtr textNode; + hr = xmlText.As(&textNode); + BREAK_IF_BAD(hr); + + ComPtr appendNode; + hr = node->AppendChild(textNode.Get(), &appendNode); + } while (FALSE); + + if (str != NULL){ + WindowsDeleteString(str); + } + + return hr; +} + + +HRESULT WindowsToastNotification::SetupCallbacks(IToastNotification* toast) +{ + EventRegistrationToken activatedToken, dismissedToken; + m_eventHandler = new ToastEventHandler(this); + ComPtr eventHandler(m_eventHandler); + HRESULT hr = toast->add_Activated(eventHandler.Get(), &activatedToken); + if (SUCCEEDED(hr)) { + hr = toast->add_Dismissed(eventHandler.Get(), &dismissedToken); + } + + return hr; +} + + +HRESULT WindowsToastNotification::CreateHString(const WCHAR* source, HSTRING* dest) +{ + if (source == NULL || dest == NULL) { + return E_INVALIDARG; + } + + HRESULT hr = WindowsCreateString(source, wcslen(source), dest); + return hr; +} + + +HRESULT WindowsToastNotification::CreateTempImageFile(const char* base64) +{ + BYTE* buff = NULL; + HRESULT hr = EnsureTempDir(); + do{ + BREAK_IF_BAD(hr); + + DWORD buffLen; + hr = ConvertBase64DataToImage(base64, &buff, &buffLen); + BREAK_IF_BAD(hr); + + hr = WriteToFile(buff, buffLen); + } while (FALSE); + + if (buff != NULL){ + delete buff; + } + + return hr; +} + + +HRESULT WindowsToastNotification::ConvertBase64DataToImage(const char* base64, BYTE** buff, DWORD* buffLen) +{ + HRESULT hr = E_FAIL; + *buffLen = 0; + DWORD reqSize; + if (CryptStringToBinary((LPCWSTR)base64, 0, CRYPT_STRING_BASE64, NULL, &reqSize, NULL, NULL)){ + *buff = new BYTE[reqSize]; + if (CryptStringToBinary((LPCWSTR)base64, 0, CRYPT_STRING_BASE64, *buff, &reqSize, NULL, NULL)){ + *buffLen = reqSize; + hr = S_OK; + } + } + + return hr; +} + + +HRESULT WindowsToastNotification::WriteToFile(BYTE* buff, DWORD buffLen) +{ + HRESULT hr = E_FAIL; + + GUID guid; + hr = CoCreateGuid(&guid); + if (SUCCEEDED(hr)){ + WCHAR randomName[MAX_PATH]; + StringFromGUID2(guid, randomName, ARRAYSIZE(randomName)); + + m_tempFilePath = new char[MAX_PATH]; + sprintf(m_tempFilePath, "%s\\%S", s_tempDirPath, randomName); + HANDLE file = CreateFile((LPCWSTR)m_tempFilePath, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); + if (file != INVALID_HANDLE_VALUE){ + DWORD bytesWritten; + if (WriteFile(file, buff, buffLen, &bytesWritten, NULL)){ + hr = S_OK; + } + else{ + DeleteTempImageFile(); + } + + CloseHandle(file); + } + } + + if (!SUCCEEDED(hr) && m_tempFilePath){ + delete m_tempFilePath; + m_tempFilePath = NULL; + } + + + return hr; +} + + +HRESULT WindowsToastNotification::EnsureTempDir() +{ + // Check for the Dir already existing or easy creation + HRESULT hr = E_FAIL; + if (CreateDirectory((LPCWSTR)s_tempDirPath, NULL) || GetLastError() == ERROR_ALREADY_EXISTS){ + hr = S_OK; + } else { + // NOTE: This makes an assumption that the path is a FULL LENGTH PATH. Therefore the first folder starts at the + // 4th character. ie. "c:\path\to\temp\dir" + + // It's possible that multiple directories need to be created. + for (int i = 3; i < ARRAYSIZE(s_tempDirPath); i++) { + char c = s_tempDirPath[i]; + if (c == '\\'){ + //Substring until this char + char* temp = new char[i + 1]; + strncpy(temp, s_tempDirPath, i); + temp[i] = 0; + if (!CreateDirectory((LPCWSTR)temp, NULL) && GetLastError() != ERROR_ALREADY_EXISTS){ + delete temp; + return hr; + } + + delete temp; + } + } + + //Try to create the full path one more time. This will take care of paths that don't end with a slash + if (CreateDirectory((LPCWSTR)s_tempDirPath, NULL) || GetLastError() == ERROR_ALREADY_EXISTS){ + hr = S_OK; + } + } + + return hr; +} + + +HRESULT WindowsToastNotification::DeleteTempImageFile() +{ + if (DeleteFile((LPCWSTR)m_tempFilePath)){ + return S_OK; + } + + return E_FAIL; +} + + +HRESULT WindowsToastNotification::EnsureShortcut() +{ + HRESULT hr = E_FAIL; + char shortcut[MAX_PATH]; + DWORD charsWritten = GetEnvironmentVariable(L"APPDATA", (LPWSTR)shortcut, MAX_PATH); + + if (charsWritten > 0) { + char shortcutCat[MAX_PATH]; + sprintf(shortcutCat, SHORTCUT_FORMAT, s_appName); + errno_t concatErr = strcat_s(shortcut, ARRAYSIZE(shortcut), shortcutCat); + if (concatErr == 0) { + DWORD attr = GetFileAttributes((LPCWSTR)shortcut); + bool exists = attr < 0xFFFFFFF; + if (exists){ + hr = S_OK; + } + else{ + WCHAR path[MAX_PATH]; + mbstowcs(path, shortcut, ARRAYSIZE(path)); + hr = CreateShortcut(path); + } + } + } + + return hr; +} + + +HRESULT WindowsToastNotification::CreateShortcut(WCHAR* path) +{ + HRESULT hr = E_FAIL; + char exePath[MAX_PATH]; + DWORD charsWritten = GetModuleFileNameEx(GetCurrentProcess(), nullptr, (LPWSTR)exePath, ARRAYSIZE(exePath)); + if (charsWritten > 0) { + PROPVARIANT propVariant; + ComPtr shellLink; + hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink)); + do{ + BREAK_IF_BAD(hr); + + hr = shellLink->SetPath((LPCWSTR)exePath); + BREAK_IF_BAD(hr); + + ComPtr propStore; + hr = shellLink.As(&propStore); + BREAK_IF_BAD(hr); + + WCHAR wAppName[MAX_PATH]; + swprintf(wAppName, ARRAYSIZE(wAppName), L"%S", s_appName); + hr = InitPropVariantFromString(wAppName, &propVariant); + BREAK_IF_BAD(hr); + + hr = propStore->SetValue(PKEY_AppUserModel_ID, propVariant); + BREAK_IF_BAD(hr); + + hr = propStore->Commit(); + BREAK_IF_BAD(hr); + + ComPtr persistFile; + hr = shellLink.As(&persistFile); + BREAK_IF_BAD(hr); + + hr = persistFile->Save(path, TRUE); + } while (FALSE); + + PropVariantClear(&propVariant); + } + + return hr; +} + +ToastEventHandler::ToastEventHandler(WindowsToastNotification* notification) +{ + m_ref = 1; + m_notification = notification; +} + + +ToastEventHandler::~ToastEventHandler() +{ + // Empty +} + + +IFACEMETHODIMP ToastEventHandler::Invoke(IToastNotification* sender, IInspectable* args) +{ + //Notification "activated" (clicked) + if (m_notification != NULL){ + m_notification->NotificationClicked(); + } + return S_OK; +} + + +IFACEMETHODIMP ToastEventHandler::Invoke(IToastNotification* sender, IToastDismissedEventArgs* e) +{ + //Notification dismissed + if (m_notification != NULL){ + m_notification->NotificationDismissed(); + } + return S_OK; +} + +} //namespace \ No newline at end of file diff --git a/brightray/browser/win/windows_toast_notification.h b/brightray/browser/win/windows_toast_notification.h new file mode 100644 index 0000000000..ff745a8aac --- /dev/null +++ b/brightray/browser/win/windows_toast_notification.h @@ -0,0 +1,99 @@ +#ifndef __WINDOWS_TOAST_NOTIFICATION_H__ +#define __WINDOWS_TOAST_NOTIFICATION_H__ + +#define WIN32_LEAN_AND_MEAN + +#include +#include +#include +#include + +using namespace Microsoft::WRL; +using namespace ABI::Windows::UI::Notifications; +using namespace ABI::Windows::Foundation; +using namespace ABI::Windows::Data::Xml::Dom; + +namespace WinToasts{ + + typedef ITypedEventHandler DesktopToastActivatedEventHandler; + typedef ITypedEventHandler DesktopToastDismissedEventHandler; + + class ToastEventHandler; + + class WindowsToastNotification + { + private: + static char s_appName[MAX_PATH]; + static char s_tempDirPath[MAX_PATH]; + + char* m_tempFilePath; + ToastEventHandler* m_eventHandler; + + HRESULT GetToastXml(IToastNotificationManagerStatics* toastMgr, const WCHAR* title, const WCHAR* msg, const char* img, IXmlDocument** toastXml); + HRESULT SetXmlText(IXmlDocument* doc, const WCHAR* text); + HRESULT SetXmlText(IXmlDocument* doc, const WCHAR* title, const WCHAR* body); + HRESULT SetXmlImage(IXmlDocument* doc); + HRESULT GetTextNodeList(HSTRING* tag, IXmlDocument* doc, IXmlNodeList** nodeList, UINT32 reqLength); + HRESULT AppendTextToXml(IXmlDocument* doc, IXmlNode* node, const WCHAR* text); + HRESULT SetupCallbacks(IToastNotification* toast); + HRESULT CreateHString(const WCHAR* source, HSTRING* dest); + HRESULT CreateTempImageFile(const char* base64); + HRESULT ConvertBase64DataToImage(const char* base64, BYTE** buff, DWORD* buffLen); + HRESULT WriteToFile(BYTE* buff, DWORD buffLen); + HRESULT EnsureTempDir(); + HRESULT DeleteTempImageFile(); + HRESULT EnsureShortcut(); + HRESULT CreateShortcut(WCHAR* path); + + public: + WindowsToastNotification(const char* appName); + ~WindowsToastNotification(); + void ShowNotification(const WCHAR* title, const WCHAR* msg, const char* img); + void NotificationClicked(); + void NotificationDismissed(); + + static void InitSystemProps(char* appName, char* tempDirPath); + }; + + + class ToastEventHandler : + public Implements < DesktopToastActivatedEventHandler, DesktopToastDismissedEventHandler > + { + private: + ULONG m_ref; + WindowsToastNotification* m_notification; + + public: + ToastEventHandler(WindowsToastNotification* notification); + ~ToastEventHandler(); + IFACEMETHODIMP Invoke(IToastNotification* sender, IInspectable* args); + IFACEMETHODIMP Invoke(IToastNotification* sender, IToastDismissedEventArgs* e); + IFACEMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&m_ref); } + + IFACEMETHODIMP_(ULONG) Release() { + ULONG l = InterlockedDecrement(&m_ref); + if (l == 0) delete this; + return l; + } + + IFACEMETHODIMP QueryInterface(_In_ REFIID riid, _COM_Outptr_ void **ppv) { + if (IsEqualIID(riid, IID_IUnknown)) + *ppv = static_cast(static_cast(this)); + else if (IsEqualIID(riid, __uuidof(DesktopToastActivatedEventHandler))) + *ppv = static_cast(this); + else if (IsEqualIID(riid, __uuidof(DesktopToastDismissedEventHandler))) + *ppv = static_cast(this); + else *ppv = nullptr; + + if (*ppv) { + reinterpret_cast(*ppv)->AddRef(); + return S_OK; + } + + return E_NOINTERFACE; + } + }; + +} // namespace + +#endif //__WINDOWS_TOAST_NOTIFICATION_H__ \ No newline at end of file diff --git a/brightray/filenames.gypi b/brightray/filenames.gypi index bbde28694d..84dc4291ed 100644 --- a/brightray/filenames.gypi +++ b/brightray/filenames.gypi @@ -63,6 +63,10 @@ 'browser/platform_notification_service_impl.h', 'browser/linux/notification_presenter_linux.h', 'browser/linux/notification_presenter_linux.cc', + 'browser/win/notification_presenter_win.h', + 'browser/win/notification_presenter_win.cc', + 'browser/win/windows_toast_notification.h', + 'browser/win/windows_toast_notification.cc', 'browser/special_storage_policy.cc', 'browser/special_storage_policy.h', 'browser/url_request_context_getter.cc',