#include "WindowsNotificationBackend.hpp" #include "core/App.hpp" #include "core/call/CallGui.hpp" #include "core/chat/ChatGui.hpp" #include "core/event-filter/LockEventFilter.hpp" #include "tool/Constants.hpp" #include "tool/Utils.hpp" #include "DesktopNotificationManagerCompat.hpp" #include #include #include using namespace Microsoft::WRL; using namespace ABI::Windows::UI::Notifications; using namespace ABI::Windows::Foundation; using namespace Microsoft::WRL::Wrappers; NotificationBackend::NotificationBackend(QObject *parent) : AbstractNotificationBackend(parent) { connect(App::getInstance(), &App::sessionLockedChanged, this, [this] { if (!App::getInstance()->getSessionLocked()) { qDebug() << "Session unlocked, flush pending notifications"; flushPendingNotifications(); } }); } void NotificationBackend::flushPendingNotifications() { for (const auto ¬if : mPendingNotifications) { sendNotification(notif.type, notif.data); } mPendingNotifications.clear(); } void NotificationBackend::sendMessageNotification(QVariantMap data) { IToastNotifier *notifier = nullptr; HRESULT hr = DesktopNotificationManagerCompat::CreateToastNotifier(¬ifier); if (FAILED(hr) || !notifier) { lWarning() << "CreateToastNotifier failed:" << Qt::hex << hr; return; } auto msgTxt = data["message"].toString().toStdWString(); auto remoteAddress = data["remoteAddress"].toString().toStdWString(); auto chatRoomName = data["chatRoomName"].toString().toStdWString(); auto chatRoomAddress = data["chatRoomAddress"].toString().toStdWString(); auto avatarUri = data["avatarUri"].toString().toStdWString(); bool isGroup = data["isGroupChat"].toBool(); ChatGui *chat = data["chat"].value(); std::wstring xml = L"" L" " L" " L" " L" " L" " L" " L" " L" " L" " L" " L" " L" "; ABI::Windows::Data::Xml::Dom::IXmlDocument *doc = nullptr; hr = DesktopNotificationManagerCompat::CreateXmlDocumentFromString(xml.c_str(), &doc); if (FAILED(hr) || !doc) { lWarning() << "CreateXmlDocumentFromString failed:" << Qt::hex << hr; notifier->Release(); return; } IToastNotification *toast = nullptr; hr = DesktopNotificationManagerCompat::CreateToastNotification(doc, &toast); if (FAILED(hr) || !toast) { qWarning() << "CreateToastNotification failed:" << Qt::hex << hr; doc->Release(); notifier->Release(); Utils::showInformationPopup(tr("info_popup_error_title"), tr("info_popup_error_creating_notification"), false); return; } EventRegistrationToken token; toast->add_Activated(Microsoft::WRL::Callback>( [this, chat](IToastNotification *sender, IInspectable *args) -> HRESULT { qInfo() << "Message toast clicked!"; Utils::openChat(chat); return S_OK; }) .Get(), &token); hr = notifier->Show(toast); if (FAILED(hr)) { qWarning() << "Toast Show failed:" << Qt::hex << hr; } toast->Release(); doc->Release(); notifier->Release(); } void NotificationBackend::sendCallNotification(QVariantMap data) { IToastNotifier *notifier = nullptr; HRESULT hr = DesktopNotificationManagerCompat::CreateToastNotifier(¬ifier); if (FAILED(hr) || !notifier) { lWarning() << "CreateToastNotifier failed:" << Qt::hex << hr; return; } auto displayName = data["displayName"].toString().toStdWString(); CallGui *call = data["call"].value(); int timeout = 2; // AbstractNotificationBackend::Notifications[(int)NotificationType::ReceivedCall].getTimeout(); // Incoming call auto callDescription = tr("incoming_call").toStdWString(); QList actions; QString declineIcon = getIconAsPng(Utils::getAppIcon("endCall").toString()); QString acceptIcon = getIconAsPng(Utils::getAppIcon("phone").toString()); actions.append(ToastButton(tr("accept_button"), "accept", acceptIcon)); actions.append(ToastButton(tr("decline_button"), "decline", declineIcon)); std::wstring wActions; if (!actions.isEmpty()) { wActions += L""; for (const auto &action : actions) { std::wstring wLabel = action.label.toStdWString(); std::wstring wArg = action.argument.toStdWString(); std::wstring wIcon = action.icon.toStdWString(); qDebug() << "toast icon action" << wIcon; wActions += L""; } wActions += L""; } std::wstring xml = L"" L" " L" " L" " + displayName + L"" L" " + callDescription + L"" L" " L" " + wActions + L" "; ABI::Windows::Data::Xml::Dom::IXmlDocument *doc = nullptr; hr = DesktopNotificationManagerCompat::CreateXmlDocumentFromString(xml.c_str(), &doc); if (FAILED(hr) || !doc) { lWarning() << "CreateXmlDocumentFromString failed:" << Qt::hex << hr; notifier->Release(); return; } IToastNotification *toast = nullptr; hr = DesktopNotificationManagerCompat::CreateToastNotification(doc, &toast); if (FAILED(hr) || !toast) { qWarning() << "CreateToastNotification failed:" << Qt::hex << hr; doc->Release(); notifier->Release(); Utils::showInformationPopup(tr("info_popup_error_title"), tr("info_popup_error_creating_notification"), false); return; } ComPtr toast2; hr = toast->QueryInterface(IID_PPV_ARGS(&toast2)); if (FAILED(hr)) qWarning() << "QueryInterface failed"; auto callId = call->mCore->getCallId(); qDebug() << "put tag to toast" << callId; hr = toast2->put_Tag(HStringReference(reinterpret_cast(callId.utf16())).Get()); toast2->put_Group(HStringReference(L"linphone").Get()); if (FAILED(hr)) qWarning() << "puting tag on toast failed"; connect(call->mCore.get(), &CallCore::stateChanged, this, [this, call, notifier, toast] { if (call->mCore->getState() == LinphoneEnums::CallState::End || call->mCore->getState() == LinphoneEnums::CallState::Error) { qDebug() << "Call ended or error, remove toast"; auto callId = call->mCore->getCallId(); call->deleteLater(); std::unique_ptr history; DesktopNotificationManagerCompat::get_History(&history); auto hr = history->RemoveGroupedTag(reinterpret_cast(callId.utf16()), L"linphone"); if (FAILED(hr)) { qWarning() << "removing toast failed"; } } }); EventRegistrationToken token; toast->add_Activated(Microsoft::WRL::Callback>( [this, call](IToastNotification *sender, IInspectable *args) -> HRESULT { qInfo() << "Toast clicked!"; // Cast args en IToastActivatedEventArgs Microsoft::WRL::ComPtr activatedArgs; HRESULT hr = args->QueryInterface(IID_PPV_ARGS(&activatedArgs)); if (SUCCEEDED(hr)) { HSTRING argumentsHString; activatedArgs->get_Arguments(&argumentsHString); // Convertir HSTRING en wstring UINT32 length; const wchar_t *rawStr = WindowsGetStringRawBuffer(argumentsHString, &length); std::wstring arguments(rawStr, length); QString arg = QString::fromStdWString(arguments); qInfo() << "Toast activated with args:" << arg; if (arg.compare("accept") == 0) { if (call) { qDebug() << "Accept call"; Utils::openCallsWindow(call); call->mCore->lAccept(false); } } else if (arg.compare("decline") == 0) { if (call) { qDebug() << "Decline call"; call->mCore->lDecline(); } } else if (arg.isEmpty()) { if (call) Utils::openCallsWindow(call); } WindowsDeleteString(argumentsHString); } return S_OK; }) .Get(), &token); hr = notifier->Show(toast); if (FAILED(hr)) { qWarning() << "Toast Show failed:" << Qt::hex << hr; } toast->Release(); doc->Release(); notifier->Release(); } void NotificationBackend::sendNotification(NotificationType type, QVariantMap data) { if (App::getInstance()->getSessionLocked()) { mPendingNotifications.append({type, data}); return; } switch (type) { case NotificationType::ReceivedCall: sendCallNotification(data); break; case NotificationType::ReceivedMessage: sendMessageNotification(data); break; } }