electron/brightray/browser/win/windows_toast_notification.cc

307 lines
8.8 KiB
C++
Raw Normal View History

// Copyright (c) 2015 Felix Rieseberg <feriese@microsoft.com> and Jason Poon <jason.poon@microsoft.com>. All rights reserved.
// Copyright (c) 2015 Ryan McShane <rmcshane@bandwidth.com> and Brandon Smith <bsmith@bandwidth.com>
// 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 "browser/win/windows_toast_notification.h"
2015-11-20 05:28:37 +00:00
#include <shlobj.h>
2015-11-10 11:50:38 +00:00
#include "base/strings/utf_string_conversions.h"
#include "browser/win/scoped_hstring.h"
#include "content/public/browser/desktop_notification_delegate.h"
using namespace ABI::Windows::Data::Xml::Dom;
2015-11-10 12:07:12 +00:00
namespace brightray {
2015-11-16 08:47:34 +00:00
2015-11-20 05:28:37 +00:00
namespace {
bool GetAppUserModelId(ScopedHString* app_id) {
PWSTR current_app_id;
if (FAILED(GetCurrentProcessExplicitAppUserModelID(&current_app_id)))
return false;
app_id->Set(current_app_id);
CoTaskMemFree(current_app_id);
return app_id->success();
}
} // namespace
// static
ComPtr<ABI::Windows::UI::Notifications::IToastNotificationManagerStatics>
WindowsToastNotification::toast_manager_;
// static
ComPtr<ABI::Windows::UI::Notifications::IToastNotifier>
WindowsToastNotification::toast_notifier_;
// static
bool WindowsToastNotification::Initialize() {
// Just initialize, don't care if it fails or already initialized.
Windows::Foundation::Initialize(RO_INIT_MULTITHREADED);
2015-11-16 08:47:34 +00:00
2015-11-10 11:50:38 +00:00
ScopedHString toast_manager_str(
RuntimeClass_Windows_UI_Notifications_ToastNotificationManager);
if (!toast_manager_str.success())
return false;
if (FAILED(Windows::Foundation::GetActivationFactory(toast_manager_str,
&toast_manager_)))
return false;
2015-11-10 11:50:38 +00:00
2015-11-20 05:28:37 +00:00
ScopedHString app_id;
if (!GetAppUserModelId(&app_id))
return false;
return SUCCEEDED(
toast_manager_->CreateToastNotifierWithId(app_id, &toast_notifier_));
}
2015-11-10 11:50:38 +00:00
WindowsToastNotification::WindowsToastNotification(
scoped_ptr<content::DesktopNotificationDelegate> delegate)
: delegate_(delegate.Pass()),
weak_factory_(this) {
}
WindowsToastNotification::~WindowsToastNotification() {
}
void WindowsToastNotification::ShowNotification(
2015-11-10 12:07:12 +00:00
const std::wstring& title,
const std::wstring& msg,
2015-11-10 12:23:08 +00:00
std::string icon_path) {
2015-11-10 11:50:38 +00:00
ComPtr<IXmlDocument> toast_xml;
if(FAILED(GetToastXml(toast_manager_.Get(), title, msg, icon_path, &toast_xml)))
return;
2015-11-10 11:50:38 +00:00
ScopedHString toast_str(
RuntimeClass_Windows_UI_Notifications_ToastNotification);
if (!toast_str.success())
return;
2015-11-11 02:04:09 +00:00
ComPtr<ABI::Windows::UI::Notifications::IToastNotificationFactory> toast_factory;
2015-11-10 12:23:08 +00:00
if (FAILED(Windows::Foundation::GetActivationFactory(toast_str,
&toast_factory)))
2015-11-10 11:50:38 +00:00
return;
2015-11-10 12:23:08 +00:00
if (FAILED(toast_factory->CreateToastNotification(toast_xml.Get(),
&toast_notification_)))
2015-11-10 11:50:38 +00:00
return;
2015-11-10 12:23:08 +00:00
if (FAILED(SetupCallbacks(toast_notification_.Get())))
2015-11-10 11:50:38 +00:00
return;
2015-11-10 12:23:08 +00:00
if (FAILED(toast_notifier_->Show(toast_notification_.Get())))
2015-11-10 11:50:38 +00:00
return;
2015-11-10 11:50:38 +00:00
delegate_->NotificationDisplayed();
}
2015-11-10 12:23:08 +00:00
void WindowsToastNotification::DismissNotification() {
toast_notifier_->Hide(toast_notification_.Get());
}
void WindowsToastNotification::NotificationClicked() {
2015-11-10 11:50:38 +00:00
delegate_->NotificationClick();
delete this;
}
void WindowsToastNotification::NotificationDismissed() {
2015-11-10 11:50:38 +00:00
delegate_->NotificationClosed();
delete this;
}
void WindowsToastNotification::NotificationFailed() {
delete this;
}
2015-11-10 11:50:38 +00:00
bool WindowsToastNotification::GetToastXml(
2015-11-11 02:04:09 +00:00
ABI::Windows::UI::Notifications::IToastNotificationManagerStatics* toastManager,
2015-11-10 12:07:12 +00:00
const std::wstring& title,
const std::wstring& msg,
2015-11-10 11:50:38 +00:00
std::string icon_path,
IXmlDocument** toast_xml) {
2015-11-11 02:04:09 +00:00
ABI::Windows::UI::Notifications::ToastTemplateType template_type;
2015-11-10 12:07:12 +00:00
if (title.empty() || msg.empty()) {
2015-11-10 11:50:38 +00:00
// Single line toast.
2015-11-11 02:04:09 +00:00
template_type = icon_path.empty() ? ABI::Windows::UI::Notifications::ToastTemplateType_ToastText01 :
ABI::Windows::UI::Notifications::ToastTemplateType_ToastImageAndText01;
2015-11-10 11:50:38 +00:00
if (FAILED(toast_manager_->GetTemplateContent(template_type, toast_xml)))
return false;
2015-11-10 12:07:12 +00:00
if (!SetXmlText(*toast_xml, title.empty() ? msg : title))
2015-11-10 11:50:38 +00:00
return false;
} else {
2015-11-10 11:50:38 +00:00
// Title and body toast.
2015-11-11 02:04:09 +00:00
template_type = icon_path.empty() ? ABI::Windows::UI::Notifications::ToastTemplateType_ToastText02 :
ABI::Windows::UI::Notifications::ToastTemplateType_ToastImageAndText02;
2015-11-10 11:50:38 +00:00
if (FAILED(toastManager->GetTemplateContent(template_type, toast_xml)))
return false;
if (!SetXmlText(*toast_xml, title, msg))
return false;
}
2015-11-10 11:50:38 +00:00
// Toast has image
if (!icon_path.empty())
return SetXmlImage(*toast_xml, icon_path);
2015-11-10 11:50:38 +00:00
return true;
}
2015-11-10 11:50:38 +00:00
bool WindowsToastNotification::SetXmlText(
2015-11-10 12:07:12 +00:00
IXmlDocument* doc, const std::wstring& text) {
2015-11-10 11:50:38 +00:00
ScopedHString tag;
ComPtr<IXmlNodeList> node_list;
if (!GetTextNodeList(&tag, doc, &node_list, 1))
return false;
2015-11-10 11:50:38 +00:00
ComPtr<IXmlNode> node;
if (FAILED(node_list->Item(0, &node)))
return false;
2015-11-10 11:50:38 +00:00
return AppendTextToXml(doc, node.Get(), text);
}
2015-11-10 11:50:38 +00:00
bool WindowsToastNotification::SetXmlText(
2015-11-10 12:07:12 +00:00
IXmlDocument* doc, const std::wstring& title, const std::wstring& body) {
2015-11-10 11:50:38 +00:00
ScopedHString tag;
ComPtr<IXmlNodeList> node_list;
if (!GetTextNodeList(&tag, doc, &node_list, 2))
return false;
2015-11-10 11:50:38 +00:00
ComPtr<IXmlNode> node;
if (FAILED(node_list->Item(0, &node)))
return false;
2015-11-10 11:50:38 +00:00
if (!AppendTextToXml(doc, node.Get(), title))
return false;
2015-11-10 11:50:38 +00:00
if (FAILED(node_list->Item(1, &node)))
return false;
2015-11-10 11:50:38 +00:00
return AppendTextToXml(doc, node.Get(), body);
}
2015-11-10 11:50:38 +00:00
bool WindowsToastNotification::SetXmlImage(
IXmlDocument* doc, std::string icon_path) {
ScopedHString tag(L"image");
2015-11-10 11:50:38 +00:00
if (!tag.success())
return false;
2015-11-10 11:50:38 +00:00
ComPtr<IXmlNodeList> node_list;
if (FAILED(doc->GetElementsByTagName(tag, &node_list)))
return false;
2015-11-10 11:50:38 +00:00
ComPtr<IXmlNode> image_node;
if (FAILED(node_list->Item(0, &image_node)))
return false;
2015-11-10 11:50:38 +00:00
ComPtr<IXmlNamedNodeMap> attrs;
if (FAILED(image_node->get_Attributes(&attrs)))
return false;
2015-11-10 11:50:38 +00:00
ScopedHString src(L"src");
if (!src.success())
return false;
2015-11-10 11:50:38 +00:00
ComPtr<IXmlNode> src_attr;
if (FAILED(attrs->GetNamedItem(src, &src_attr)))
return false;
2015-11-10 11:50:38 +00:00
ScopedHString img_path(base::UTF8ToUTF16(icon_path).c_str());
if (!img_path.success())
return false;
2015-11-10 11:50:38 +00:00
ComPtr<IXmlText> src_text;
if (FAILED(doc->CreateTextNode(img_path, &src_text)))
return false;
2015-11-10 11:50:38 +00:00
ComPtr<IXmlNode> src_node;
if (FAILED(src_text.As(&src_node)))
return false;
2015-11-10 11:50:38 +00:00
ComPtr<IXmlNode> child_node;
return SUCCEEDED(src_attr->AppendChild(src_node.Get(), &child_node));
}
2015-11-10 11:50:38 +00:00
bool WindowsToastNotification::GetTextNodeList(
ScopedHString* tag,
IXmlDocument* doc,
2015-11-10 11:50:38 +00:00
IXmlNodeList** node_list,
UINT32 req_length) {
tag->Set(L"text");
if (!tag->success())
return false;
2015-11-10 11:50:38 +00:00
if (FAILED(doc->GetElementsByTagName(*tag, node_list)))
return false;
2015-11-10 11:50:38 +00:00
UINT32 node_length;
if (FAILED((*node_list)->get_Length(&node_length)))
return false;
2015-11-10 11:50:38 +00:00
return node_length >= req_length;
}
2015-11-10 11:50:38 +00:00
bool WindowsToastNotification::AppendTextToXml(
2015-11-10 12:07:12 +00:00
IXmlDocument* doc, IXmlNode* node, const std::wstring& text) {
2015-11-10 11:50:38 +00:00
ScopedHString str(text);
if (!str.success())
return false;
2015-11-10 11:50:38 +00:00
ComPtr<IXmlText> xml_text;
if (FAILED(doc->CreateTextNode(str, &xml_text)))
return false;
2015-11-10 11:50:38 +00:00
ComPtr<IXmlNode> text_node;
if (FAILED(xml_text.As(&text_node)))
return false;
2015-11-10 11:50:38 +00:00
ComPtr<IXmlNode> append_node;
return SUCCEEDED(node->AppendChild(text_node.Get(), &append_node));
}
2015-11-11 02:04:09 +00:00
bool WindowsToastNotification::SetupCallbacks(ABI::Windows::UI::Notifications::IToastNotification* toast) {
EventRegistrationToken activatedToken, dismissedToken, failedToken;
2015-11-10 11:50:38 +00:00
event_handler_ = Make<ToastEventHandler>(this);
if (FAILED(toast->add_Activated(event_handler_.Get(), &activatedToken)))
return false;
if (FAILED(toast->add_Dismissed(event_handler_.Get(), &dismissedToken)))
return false;
return SUCCEEDED(toast->add_Failed(event_handler_.Get(), &failedToken));
}
/*
/ Toast Event Handler
*/
2015-11-10 11:50:38 +00:00
ToastEventHandler::ToastEventHandler(WindowsToastNotification* notification)
: notification_(notification) {
}
ToastEventHandler::~ToastEventHandler() {
}
IFACEMETHODIMP ToastEventHandler::Invoke(
2015-11-11 02:04:09 +00:00
ABI::Windows::UI::Notifications::IToastNotification* sender, IInspectable* args) {
2015-11-10 11:50:38 +00:00
notification_->NotificationClicked();
return S_OK;
}
IFACEMETHODIMP ToastEventHandler::Invoke(
2015-11-11 02:04:09 +00:00
ABI::Windows::UI::Notifications::IToastNotification* sender,
ABI::Windows::UI::Notifications::IToastDismissedEventArgs* e) {
2015-11-10 11:50:38 +00:00
notification_->NotificationDismissed();
return S_OK;
}
IFACEMETHODIMP ToastEventHandler::Invoke(
ABI::Windows::UI::Notifications::IToastNotification* sender,
ABI::Windows::UI::Notifications::IToastFailedEventArgs* e) {
notification_->NotificationFailed();
return S_OK;
}
2015-11-10 12:07:12 +00:00
} // namespace brightray