File indexing completed on 2024-04-28 04:57:11

0001 /**
0002  * SPDX-FileCopyrightText: 2023 Fushan Wen <qydwhotmail@gmail.com>
0003  *
0004  * SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "windowsnotificationslistener.h"
0008 
0009 #include <QImage>
0010 
0011 #include <core/kdeconnectplugin.h>
0012 
0013 #include <winrt/Windows.ApplicationModel.h>
0014 #include <winrt/Windows.Foundation.Collections.h>
0015 #include <winrt/Windows.Storage.Streams.h>
0016 
0017 #include "plugin_sendnotifications_debug.h"
0018 
0019 using namespace winrt;
0020 using namespace Windows::ApplicationModel;
0021 using namespace Windows::Foundation;
0022 using namespace Windows::Foundation::Collections;
0023 using namespace Windows::UI::Notifications;
0024 using namespace Windows::UI::Notifications::Management;
0025 using namespace Windows::Storage::Streams;
0026 
0027 WindowsNotificationsListener::WindowsNotificationsListener(KdeConnectPlugin *aPlugin)
0028     : NotificationsListener(aPlugin)
0029 {
0030     setupWindowsUserNotificationListener();
0031 }
0032 
0033 WindowsNotificationsListener::~WindowsNotificationsListener()
0034 {
0035     UserNotificationListener::Current().NotificationChanged(m_notificationEventToken);
0036 }
0037 
0038 void WindowsNotificationsListener::setupWindowsUserNotificationListener()
0039 {
0040     // Register the notification listener
0041     const UserNotificationListenerAccessStatus accessStatus = UserNotificationListener::Current().RequestAccessAsync().get();
0042     if (accessStatus != UserNotificationListenerAccessStatus::Allowed) {
0043         return;
0044     }
0045 
0046     // Register the event handler for notifications
0047     m_notificationEventToken = UserNotificationListener::Current().NotificationChanged({this, &WindowsNotificationsListener::onNotificationChanged});
0048 }
0049 
0050 void WindowsNotificationsListener::onNotificationChanged(const UserNotificationListener &sender, const UserNotificationChangedEventArgs &args)
0051 {
0052     // Get the notification from the event arguments
0053     const UserNotificationChangedKind changeKind = args.ChangeKind();
0054     if (changeKind == UserNotificationChangedKind::Removed) {
0055         return;
0056     }
0057 
0058     const UserNotification userNotification = sender.GetNotification(args.UserNotificationId());
0059     if (!userNotification) {
0060         return;
0061     }
0062 
0063     const AppDisplayInfo appDisplayInfo = userNotification.AppInfo().DisplayInfo();
0064     const std::wstring_view appDisplayName = appDisplayInfo.DisplayName();
0065     const QString appName = QString::fromWCharArray(appDisplayName.data(), appDisplayName.size());
0066     if (!checkApplicationName(appName)) {
0067         return;
0068     }
0069 
0070     const winrt::Windows::UI::Notifications::Notification notification = userNotification.Notification();
0071     const NotificationBinding toastBinding = notification.Visual().GetBinding(KnownNotificationBindings::ToastGeneric());
0072     if (!toastBinding) {
0073         return;
0074     }
0075     const auto textElements = toastBinding.GetTextElements();
0076     if (textElements.Size() == 0) {
0077         return;
0078     }
0079 
0080     std::wstring ticker = static_cast<std::wstring>(textElements.GetAt(0).Text());
0081     if (m_plugin->config()->getBool(QStringLiteral("generalIncludeBody"), true)) {
0082         for (unsigned i = 1; i < textElements.Size(); ++i) {
0083             ticker += L"\n" + textElements.GetAt(i).Text();
0084         }
0085     }
0086 
0087     const QString content = QString::fromStdWString(ticker);
0088     if (checkIsInBlacklist(appName, content)) {
0089         return;
0090     }
0091 
0092     static unsigned id = 0;
0093     if (id == std::numeric_limits<unsigned>::max()) {
0094         id = 0;
0095     }
0096     NetworkPacket np(PACKET_TYPE_NOTIFICATION,
0097                      {{QStringLiteral("id"), id++},
0098                       {QStringLiteral("appName"), appName},
0099                       {QStringLiteral("ticker"), content},
0100                       {QStringLiteral("isClearable"), true}}); // A Windows notification is always clearable
0101 
0102     if (m_plugin->config()->getBool(QStringLiteral("generalSynchronizeIcons"), true)) {
0103         const RandomAccessStreamReference appLogoStreamReference = appDisplayInfo.GetLogo(Size(64, 64));
0104         if (appLogoStreamReference) { // Can be false when Package.appxmanifest doesn't contain icons
0105             // Read the logo stream into a buffer
0106             IRandomAccessStreamWithContentType logoStream = appLogoStreamReference.OpenReadAsync().get(); // TODO Port to coroutine
0107             DataReader reader(logoStream);
0108             reader.LoadAsync(static_cast<uint32_t>(logoStream.Size())).get();
0109             std::vector<unsigned char> bufferArray;
0110             bufferArray.resize(reader.UnconsumedBufferLength());
0111             if (!bufferArray.empty()) {
0112                 reader.ReadBytes({bufferArray.data(), bufferArray.data() + bufferArray.size()});
0113 
0114                 QImage image;
0115                 if (image.loadFromData(bufferArray.data(), bufferArray.size())) {
0116                     // Write the logo buffer to the QIODevice
0117                     QSharedPointer<QIODevice> iconSource = iconFromQImage(image);
0118                     if (iconSource) {
0119                         np.setPayload(iconSource, iconSource->size());
0120                     }
0121                 }
0122             }
0123         }
0124     }
0125 
0126     m_plugin->sendPacket(np);
0127 }
0128 
0129 #include "moc_windowsnotificationslistener.cpp"