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"