From fbc28148a0f1dcfb28aac680dd1b963b44b234a5 Mon Sep 17 00:00:00 2001 From: Tobias Fella Date: Mon, 2 Nov 2020 16:12:33 +0000 Subject: [PATCH] Cleanup cmake, metadata --- CMakeLists.txt | 261 +--- linux/org.kde.neochat.appdata.xml | 134 -- linux/org.kde.neochat.desktop | 39 - org.kde.neochat.appdata.xml | 21 + org.kde.neochat.desktop | 9 + src/CMakeLists.txt | 25 + src/controller.h | 1 - src/main.cpp | 2 +- .../managerlinux.cpp => manager.cpp} | 0 src/{notifications => }/manager.h | 0 src/notifications/managermac.mm | 39 - src/notifications/managerwin.cpp | 93 -- src/notifications/wintoastlib.cpp | 1091 ----------------- src/notifications/wintoastlib.h | 156 --- 14 files changed, 77 insertions(+), 1794 deletions(-) delete mode 100644 linux/org.kde.neochat.appdata.xml delete mode 100644 linux/org.kde.neochat.desktop create mode 100644 org.kde.neochat.appdata.xml create mode 100644 org.kde.neochat.desktop create mode 100644 src/CMakeLists.txt rename src/{notifications/managerlinux.cpp => manager.cpp} (100%) rename src/{notifications => }/manager.h (100%) delete mode 100644 src/notifications/managermac.mm delete mode 100644 src/notifications/managerwin.cpp delete mode 100644 src/notifications/wintoastlib.cpp delete mode 100644 src/notifications/wintoastlib.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 74236107d..6ebde19c4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,263 +1,44 @@ -CMAKE_MINIMUM_REQUIRED(VERSION 3.1) +cmake_minimum_required(VERSION 3.1) -project(NeoChat VERSION 0.0.90) +project(neoChat VERSION 0.0.90) -set(REQUIRED_KF5_VERSION "5.57.0") -find_package(ECM ${REQUIRED_KF5_VERSION} REQUIRED NO_MODULE) +set(KF5_MIN_VERSION "5.75.0") +set(QT_MIN_VERSION "5.15.0") -set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake ${ECM_MODULE_PATH}) +find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE) -include(KDEClangFormat) -set(IDENTIFIER "org.kde.neochat") -set(COPYRIGHT "Copyright © 2018-2019 bhat@encom.eu.org, 2020 KDE Community") - -if(UNIX AND NOT APPLE) - set(LINUX 1) -endif(UNIX AND NOT APPLE) - -include(CheckCXXCompilerFlag) -if (NOT WIN32) - include(GNUInstallDirs) - include(cmake/ECMInstallIcons.cmake) -endif(NOT WIN32) - -# Find includes in corresponding build directories -set(CMAKE_INCLUDE_CURRENT_DIR ON) -# Instruct CMake to run moc automatically when needed. -set(CMAKE_AUTOMOC ON) - -# Set a default build type if none was specified -if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) - message(STATUS "Setting build type to 'Debug' as none was specified") - set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build" FORCE) - # Set the possible values of build type for cmake-gui - set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" - "MinSizeRel" "RelWithDebInfo") -endif() +set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake) set(CMAKE_CXX_STANDARD 17) -# Setup command line parameters for the compiler and linker -foreach (FLAG "" all pedantic extra no-unused-parameter) - CHECK_CXX_COMPILER_FLAG("-W${FLAG}" WARN_${FLAG}_SUPPORTED) - if ( WARN_${FLAG}_SUPPORTED AND NOT CMAKE_CXX_FLAGS MATCHES "(^| )-W?${FLAG}($| )") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W${FLAG}") - endif () -endforeach () - -# Find the libraries -find_package(Qt5 5.12 REQUIRED Widgets Network Quick Qml Gui Svg Multimedia QuickControls2) -if(LINUX) - find_package(Qt5DBus REQUIRED) -endif(LINUX) -if (APPLE) - find_package(Qt5MacExtras REQUIRED) -endif(APPLE) -# Qt5_Prefix is only used to show Qt path in message() -# Qt5_BinDir is where all the binary tools for Qt are -if (QT_QMAKE_EXECUTABLE) - get_filename_component(Qt5_BinDir "${QT_QMAKE_EXECUTABLE}" DIRECTORY) - get_filename_component(Qt5_Prefix "${Qt5_BinDir}/.." ABSOLUTE) -else() - get_filename_component(Qt5_BinDir "${Qt5_DIR}/../../../bin" ABSOLUTE) - get_filename_component(Qt5_Prefix "${Qt5_DIR}/../../../.." ABSOLUTE) -endif() - -# prevent error "You must build your code with position independent code if Qt was built with.. -if (Qt5_POSITION_INDEPENDENT_CODE) - SET(CMAKE_POSITION_INDEPENDENT_CODE ON) -endif() - -set(QML_IMPORT_PATH ${CMAKE_SOURCE_DIR}/qml ${CMAKE_SOURCE_DIR}/imports CACHE string "" FORCE) - -if(WIN32) - enable_language(RC) - include(CMakeDetermineRCCompiler) -endif() - -if ((NOT DEFINED USE_INTREE_LIBQMC OR USE_INTREE_LIBQMC) - AND EXISTS ${PROJECT_SOURCE_DIR}/include/libQuotient/lib/util.h) - add_subdirectory(include/libQuotient EXCLUDE_FROM_ALL) - include_directories(include/libQuotient) - if (NOT DEFINED USE_INTREE_LIBQMC) - set (USE_INTREE_LIBQMC 1) - endif () -endif () -if (NOT USE_INTREE_LIBQMC) - find_package(Quotient 0.6 REQUIRED) - if (NOT Quotient_FOUND) - message( WARNING "libQuotient not found; configuration will most likely fail.") - endif () -endif () +include(FeatureSummary) +include(ECMSetupVersion) +include(KDEInstallDirs) +include(KDEClangFormat) +include(KDECMakeSettings) +include(KDECompilerSettings NO_POLICY_SCOPE) +find_package(Qt5 ${QT_MIN_VERSION} REQUIRED NO_MODULE COMPONENTS Widgets Core Quick Gui QuickControls2 Multimedia) find_package(KF5 ${REQUIRED_KF5_VERSION} REQUIRED Kirigami2 ItemModels I18n ) -find_package(Qt5Keychain REQUIRED) +find_package(Quotient 0.7 REQUIRED) +find_package(Qt5Keychain REQUIRED) find_package(cmark REQUIRED) -message( STATUS ) -message( STATUS "=============================================================================" ) -message( STATUS " Spectral Build Information" ) -message( STATUS "=============================================================================" ) -if (CMAKE_BUILD_TYPE) - message( STATUS "Build type: ${CMAKE_BUILD_TYPE}") -endif(CMAKE_BUILD_TYPE) -message( STATUS "Spectral install prefix: ${CMAKE_INSTALL_PREFIX}" ) -# Get Git info if possible -find_package(Git) -if(GIT_FOUND) - execute_process(COMMAND - "${GIT_EXECUTABLE}" rev-parse -q HEAD - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - OUTPUT_VARIABLE GIT_SHA1 - OUTPUT_STRIP_TRAILING_WHITESPACE) - message( STATUS "Git SHA1: ${GIT_SHA1}") -endif() -message( STATUS "Using compiler: ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}" ) -message( STATUS "Using Qt ${Qt5_VERSION} at ${Qt5_Prefix}" ) -if (USE_INTREE_LIBQMC) - message( STATUS "Using in-tree libQuotient") - if (GIT_FOUND) - execute_process(COMMAND - "${GIT_EXECUTABLE}" rev-parse -q HEAD - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/libQuotient - OUTPUT_VARIABLE LIB_GIT_SHA1 - OUTPUT_STRIP_TRAILING_WHITESPACE) - message( STATUS " Library git SHA1: ${LIB_GIT_SHA1}") - endif (GIT_FOUND) -else () - message( STATUS "Using libQuotient ${Quotient_VERSION} at ${Quotient_DIR}") -endif () -message( STATUS "=============================================================================" ) -message( STATUS ) +install(PROGRAMS org.kde.neochat.desktop DESTINATION ${KDE_INSTALL_APPDIR}) +install(FILES org.kde.neochat.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR}) +#install(FILES neochat.svg DESTINATION ${KDE_INSTALL_FULL_ICONDIR}/hicolor/scalable/apps) -# Set up source files -set(spectral_SRCS - src/notifications/manager.h - src/accountlistmodel.h - src/controller.h - src/emojimodel.h - src/imageclipboard.h - src/matriximageprovider.h - src/messageeventmodel.h - src/roomlistmodel.h - src/spectralroom.h - src/spectraluser.h - src/trayicon.h - src/userlistmodel.h - src/publicroomlistmodel.h - src/userdirectorylistmodel.h - src/utils.h - src/accountlistmodel.cpp - src/controller.cpp - src/emojimodel.cpp - src/imageclipboard.cpp - src/matriximageprovider.cpp - src/messageeventmodel.cpp - src/roomlistmodel.cpp - src/spectralroom.cpp - src/spectraluser.cpp - src/trayicon.cpp - src/userlistmodel.cpp - src/publicroomlistmodel.cpp - src/userdirectorylistmodel.cpp - src/utils.cpp - src/main.cpp -) +add_subdirectory(src) -if (APPLE) - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -framework Foundation -framework Cocoa") - set(spectral_SRCS ${spectral_SRCS} src/notifications/managermac.mm) -elseif (WIN32) - set(spectral_SRCS ${spectral_SRCS} src/notifications/managerwin.cpp src/notifications/wintoastlib.cpp) -else () - set(spectral_SRCS ${spectral_SRCS} src/notifications/managerlinux.cpp) -endif () +feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) -set(spectral_QRC - res.qrc -) - -QT5_ADD_RESOURCES(spectral_QRC_SRC ${spectral_QRC}) -set_property(SOURCE qrc_resources.cpp PROPERTY SKIP_AUTOMOC ON) - -if(WIN32) - set(spectral_WINRC spectral_win32.rc) - set_property(SOURCE spectral_win32.rc APPEND PROPERTY - OBJECT_DEPENDS ${PROJECT_SOURCE_DIR}/icons/icon.ico - ) -endif() - -if(APPLE) - set(MACOSX_BUNDLE_GUI_IDENTIFIER ${IDENTIFIER}) - set(MACOSX_BUNDLE_BUNDLE_NAME ${PROJECT_NAME}) - - set(MACOSX_BUNDLE_COPYRIGHT ${COPYRIGHT}) - - set(MACOSX_BUNDLE_SHORT_VERSION_STRING ${spectral_VERSION}) - set(MACOSX_BUNDLE_BUNDLE_VERSION ${spectral_VERSION}) - - set(ICON_NAME "icon.icns") - set(${PROJECT_NAME}_MAC_ICON "${PROJECT_SOURCE_DIR}/icons/${ICON_NAME}") - set(MACOSX_BUNDLE_ICON_FILE ${ICON_NAME}) - set_property(SOURCE "${${PROJECT_NAME}_MAC_ICON}" PROPERTY - MACOSX_PACKAGE_LOCATION Resources) -endif(APPLE) - -# Windows, this is a GUI executable; OSX, make a bundle -add_executable(${PROJECT_NAME} WIN32 MACOSX_BUNDLE - ${spectral_SRCS} ${spectral_QRC_SRC} - ${spectral_WINRC} ${${PROJECT_NAME}_MAC_ICON}) - -target_link_libraries(${PROJECT_NAME} - Qt5::Widgets Qt5::Quick Qt5::Qml Qt5::Gui Qt5::Network Qt5::Svg Qt5::QuickControls2 - KF5::Kirigami2 - Quotient - cmark::cmark - ${QTKEYCHAIN_LIBRARIES} - ) -target_compile_definitions(${PROJECT_NAME} PRIVATE - GIT_SHA1="${GIT_SHA1}" LIB_GIT_SHA1="${LIB_GIT_SHA1}") - -if (APPLE) - target_link_libraries(${PROJECT_NAME} Qt5::MacExtras) -elseif(LINUX) - target_link_libraries(${PROJECT_NAME} Qt5::DBus) -endif() - -# macOS specific config for bundling -set_property(TARGET ${PROJECT_NAME} PROPERTY - MACOSX_BUNDLE_INFO_PLIST "${PROJECT_SOURCE_DIR}/macOS/Info.plist.in") - -# Installation - -if (NOT CMAKE_INSTALL_BINDIR) - set(CMAKE_INSTALL_BINDIR ".") -endif() - -install(TARGETS ${PROJECT_NAME} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR}) -if(LINUX) - install(FILES linux/${IDENTIFIER}.desktop - DESTINATION ${CMAKE_INSTALL_DATADIR}/applications - ) - install(FILES linux/${IDENTIFIER}.appdata.xml - DESTINATION ${CMAKE_INSTALL_DATADIR}/metainfo - ) - file(GLOB spectral_icons icons/hicolor/*-apps-org.kde.neochat.png) - ecm_install_icons(ICONS ${spectral_icons} - DESTINATION ${CMAKE_INSTALL_DATADIR}/icons - ) -endif(LINUX) - -# add clang-format target for all our real source files -file(GLOB_RECURSE ALL_CLANG_FORMAT_SOURCE_FILES *.cpp *.h) +file(GLOB_RECURSE ALL_CLANG_FORMAT_SOURCE_FILES src/*.cpp src/*.h) kde_clang_format(${ALL_CLANG_FORMAT_SOURCE_FILES}) diff --git a/linux/org.kde.neochat.appdata.xml b/linux/org.kde.neochat.appdata.xml deleted file mode 100644 index 7cc75811a..000000000 --- a/linux/org.kde.neochat.appdata.xml +++ /dev/null @@ -1,134 +0,0 @@ - - - org.eu.encom.spectral - - spectral - - Spectral - Spectral - Spectral - Spectral - Spectral - Spectral - Spectral - Spectral - Spectral - Spectral - xxSpectralxx - IM client for the Matrix protocol - Client de MI per al protocol Matrix - Client de MI per al protocol Matrix - Cliente de MI para el protocolo Matrix - Client « IM » pour le protocole « Matrix » - IM-client voor het Matrix-protocol - Lynmeldings­klient for Matrix-protokollen - Cliente de MI para o protocolo Matrix - Direktmeddelandeklient för protokollet Matrix - Клієнт служби миттєвого обміну повідомленнями для протоколу Matrix - xxIM client for the Matrix protocolxx - -

- Spectral is a glossy, cross-platform client for Matrix, the decentralized communication protocol for instant messaging. -

-

L'Spectral és un client multiplataforma brillant per al Matrix, el protocol descentralitzat de comunicacions per a missatgeria instantània.

-

L'Spectral és un client multiplataforma brillant per al Matrix, el protocol descentralitzat de comunicacions per a missatgeria instantània.

-

Spectral es un brillante cliente multiplataforma para Matrix, el protocolo de comunicación descentralizado de mensajería instantánea.

-

Spectral est un client multi-plate-forme et brillant pour Matrix, le protocole décentralisé de communications pour la messagerie instantanée.

-

Spectral is een glossy, cross-platform client voor Matrix, het gedecentraliseerde communicatieprotocol voor instant messaging.

-

Spectral er ein lekker multiplattform-klient for Matrix, den desentraliserte lynmeldings­protokollen.

-

O Spectral é um cliente bonito e multi-plataforma para a Matrix, o protocolo de comunicações descentralizado de mensagens instantâneas.

-

Spectral är en glansig klient för Matrix, det decentraliserade kommunikationsprotokollet för direktmeddelanden, för flera plattformar.

-

Spectral — блискучий клієнт Matrix для багатьох платформ. Matrix є децентралізованим протоколом обміну даними для миттєвого обміну повідомленнями.

-

xxSpectral is a glossy, cross-platform client for Matrix, the decentralized communication protocol for instant messaging.xx

-
- https://gitlab.com/spectral-im/spectral/ - https://gitlab.com/spectral-im/spectral/issues/ - https://spectral.im/docs/ - - Matrix - Internet - - Black Hat - Black Hat - Black Hat - Black Hat - Hacker éthique - Black Hat - Black Hat - Chapéu Preto - Svart hatt - Black Hat - xxBlack Hatxx - CC0-1.0 - GPL-3.0 - - - Overview - Vista general - Vista general - Resumen - Aperçu - Overzicht - Oversikt - Introdução - Översikt - Огляд - xxOverviewxx - https://gitlab.com/spectral-im/spectral/raw/master/screenshots/1.png - - - Room Timeline - Línia de temps de les sales - Línia de temps de les sales - Cronología de la sala - Frise chronologique pour Room - Tijdlijn van room - Rom-tidslinje - Cronologia da Sala - Tidslinje för rum - Шкала часу кімнати - xxRoom Timelinexx - https://gitlab.com/spectral-im/spectral/raw/master/screenshots/2.png - - - Room Config - Configuració de les sales - Configuració de les sales - Configuración de la sala - Configuration de « Room » - Configuratie van room - Romoppsett - Configuração da Sala - Inställning av rum - Налаштовування кімнати - xxRoom Configxx - https://gitlab.com/spectral-im/spectral/raw/master/screenshots/3.png - - - Settings - Arranjament - Arranjament - Preferencias - Configuration - Instellingen - Innstillingar - Configuração - Inställningar - Параметри - xxSettingsxx - https://gitlab.com/spectral-im/spectral/raw/master/screenshots/4.png - - - - intense - intense - - - - - - - - - -
diff --git a/linux/org.kde.neochat.desktop b/linux/org.kde.neochat.desktop deleted file mode 100644 index 719ce374f..000000000 --- a/linux/org.kde.neochat.desktop +++ /dev/null @@ -1,39 +0,0 @@ -[Desktop Entry] -Name=Spectral -Name[ca]=Spectral -Name[ca@valencia]=Spectral -Name[es]=Spectral -Name[fr]=Spectral -Name[nl]=Spectral -Name[nn]=Spectral -Name[pt]=Spectral -Name[sv]=Spectral -Name[uk]=Spectral -Name[x-test]=xxSpectralxx -GenericName=Matrix Client -GenericName[ca]=Client del Matrix -GenericName[ca@valencia]=Client del Matrix -GenericName[es]=Cliente para Matrix -GenericName[fr]=Client « Matrix » -GenericName[nl]=Matrix-client -GenericName[nn]=Matrix-klient -GenericName[pt]=Cliente de Matrix -GenericName[sv]=Matrix-klient -GenericName[uk]=Клієнт Matrix -GenericName[x-test]=xxMatrix Clientxx -Comment=IM client for the Matrix protocol -Comment[ca]=Client de MI per al protocol Matrix -Comment[ca@valencia]=Client de MI per al protocol Matrix -Comment[es]=Cliente de MI para el protocolo Matrix -Comment[fr]=Client « IM » pour le protocole « Matrix » -Comment[nl]=IM-client voor het Matrix-protocol -Comment[nn]=Lynmeldings­klient for Matrix-protokollen -Comment[pt]=Cliente de MI para o protocolo Matrix -Comment[sv]=Direktmeddelandeklient för protokollet Matrix -Comment[uk]=Клієнт служби миттєвого обміну повідомленнями для протоколу Matrix -Comment[x-test]=xxIM client for the Matrix protocolxx -Exec=spectral -Terminal=false -Icon=org.eu.encom.spectral -Type=Application -Categories=Network;InstantMessaging; diff --git a/org.kde.neochat.appdata.xml b/org.kde.neochat.appdata.xml new file mode 100644 index 000000000..4cfacf34a --- /dev/null +++ b/org.kde.neochat.appdata.xml @@ -0,0 +1,21 @@ + + + org.kde.neochat + + neochat + + Neochat + IM client for the Matrix protocol + +

A client for matrix, the decentralized communication protocol.

+
+ https://kde.org + https://bugs.kde.org + + Matrix + Internet + + The KDE Community + CC0-1.0 + GPL-3.0 +
diff --git a/org.kde.neochat.desktop b/org.kde.neochat.desktop new file mode 100644 index 000000000..08f54b297 --- /dev/null +++ b/org.kde.neochat.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Name=Neochat +GenericName=Matrix Client +Comment=Client for the Matrix protocol +Exec=neochat +Terminal=false +Icon=org.kde.neochat +Type=Application +Categories=Network;InstantMessaging; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 000000000..675e82327 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,25 @@ +set(neochat_SRCS + accountlistmodel.cpp + controller.cpp + emojimodel.cpp + imageclipboard.cpp + matriximageprovider.cpp + messageeventmodel.cpp + roomlistmodel.cpp + spectralroom.cpp + spectraluser.cpp + trayicon.cpp + userlistmodel.cpp + publicroomlistmodel.cpp + userdirectorylistmodel.cpp + utils.cpp + main.cpp + manager.cpp + ../res.qrc +) + +add_executable(neochat ${neochat_SRCS}) + +target_link_libraries(neochat Qt5::Widgets Qt5::Quick Qt5::Qml Qt5::Gui Qt5::Network Qt5::QuickControls2 KF5::Kirigami2 Quotient cmark::cmark ${QTKEYCHAIN_LIBRARIES}) + +install(TARGETS neochat ${KF5_INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/src/controller.h b/src/controller.h index 28652c3e0..469727637 100644 --- a/src/controller.h +++ b/src/controller.h @@ -15,7 +15,6 @@ #include "connection.h" #include "csapi/list_public_rooms.h" -#include "notifications/manager.h" #include "room.h" #include "settings.h" #include "user.h" diff --git a/src/main.cpp b/src/main.cpp index 91e50a3da..7890b61c1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -18,7 +18,7 @@ #include "imageclipboard.h" #include "matriximageprovider.h" #include "messageeventmodel.h" -#include "notifications/manager.h" +#include "manager.h" #include "publicroomlistmodel.h" #include "room.h" #include "roomlistmodel.h" diff --git a/src/notifications/managerlinux.cpp b/src/manager.cpp similarity index 100% rename from src/notifications/managerlinux.cpp rename to src/manager.cpp diff --git a/src/notifications/manager.h b/src/manager.h similarity index 100% rename from src/notifications/manager.h rename to src/manager.h diff --git a/src/notifications/managermac.mm b/src/notifications/managermac.mm deleted file mode 100644 index cf924fca9..000000000 --- a/src/notifications/managermac.mm +++ /dev/null @@ -1,39 +0,0 @@ -#include "manager.h" - -#include -#include -#include - -@interface NSUserNotification (CFIPrivate) -- (void)set_identityImage:(NSImage*)image; -@end - -NotificationsManager::NotificationsManager(QObject* parent) : QObject(parent) {} - -void NotificationsManager::postNotification(const QString& roomId, - const QString& eventId, - const QString& roomName, - const QString& senderName, - const QString& text, - const QImage& icon) { - Q_UNUSED(roomId); - Q_UNUSED(eventId); - Q_UNUSED(icon); - - NSUserNotification* notif = [[NSUserNotification alloc] init]; - - notif.title = roomName.toNSString(); - notif.subtitle = QString("%1 sent a message").arg(senderName).toNSString(); - notif.informativeText = text.toNSString(); - notif.soundName = NSUserNotificationDefaultSoundName; - notif.contentImage = QtMac::toNSImage(QPixmap::fromImage(icon)); - - [[NSUserNotificationCenter defaultUserNotificationCenter] - deliverNotification:notif]; - [notif autorelease]; -} - -// unused -void NotificationsManager::actionInvoked(uint, QString) {} - -void NotificationsManager::notificationClosed(uint, uint) {} diff --git a/src/notifications/managerwin.cpp b/src/notifications/managerwin.cpp deleted file mode 100644 index a7d04f90e..000000000 --- a/src/notifications/managerwin.cpp +++ /dev/null @@ -1,93 +0,0 @@ -#include "manager.h" -#include "wintoastlib.h" - -#include - -using namespace WinToastLib; - -class CustomHandler : public IWinToastHandler -{ -public: - CustomHandler(uint id, NotificationsManager *parent) - : notificationID(id) - , notificationsManager(parent) - { - } - void toastActivated() - { - notificationsManager->actionInvoked(notificationID, ""); - } - void toastActivated(int) - { - notificationsManager->actionInvoked(notificationID, ""); - } - void toastFailed() - { - std::wcout << L"Error showing current toast" << std::endl; - } - void toastDismissed(WinToastDismissalReason) - { - notificationsManager->notificationClosed(notificationID, 0); - } - -private: - uint notificationID; - NotificationsManager *notificationsManager; -}; - -namespace -{ -bool isInitialized = false; -uint count = 0; - -void init() -{ - isInitialized = true; - - WinToast::instance()->setAppName(L"Spectral"); - WinToast::instance()->setAppUserModelId(WinToast::configureAUMI(L"Spectral", L"Spectral")); - if (!WinToast::instance()->initialize()) - std::wcout << "Your system in not compatible with toast notifications\n"; -} -} // namespace - -NotificationsManager::NotificationsManager(QObject *parent) - : QObject(parent) -{ -} - -void NotificationsManager::postNotification(const QString &room_id, const QString &event_id, const QString &room_name, const QString &sender, const QString &text, const QImage &icon) -{ - Q_UNUSED(room_id) - Q_UNUSED(event_id) - Q_UNUSED(icon) - - if (!isInitialized) - init(); - - auto templ = WinToastTemplate(WinToastTemplate::ImageAndText02); - if (room_name != sender) - templ.setTextField(QString("%1 - %2").arg(sender).arg(room_name).toStdWString(), WinToastTemplate::FirstLine); - else - templ.setTextField(QString("%1").arg(sender).toStdWString(), WinToastTemplate::FirstLine); - templ.setTextField(QString("%1").arg(text).toStdWString(), WinToastTemplate::SecondLine); - - count++; - CustomHandler *customHandler = new CustomHandler(count, this); - notificationIds[count] = roomEventId {room_id, event_id}; - - WinToast::instance()->showToast(templ, customHandler); -} - -void NotificationsManager::actionInvoked(uint id, QString action) -{ - if (notificationIds.contains(id)) { - roomEventId idEntry = notificationIds[id]; - emit notificationClicked(idEntry.roomId, idEntry.eventId); - } -} - -void NotificationsManager::notificationClosed(uint id, uint reason) -{ - notificationIds.remove(id); -} diff --git a/src/notifications/wintoastlib.cpp b/src/notifications/wintoastlib.cpp deleted file mode 100644 index 43d152dd4..000000000 --- a/src/notifications/wintoastlib.cpp +++ /dev/null @@ -1,1091 +0,0 @@ -#include "wintoastlib.h" -#include -#include - -#pragma comment(lib, "shlwapi") -#pragma comment(lib, "user32") - -#ifdef NDEBUG -#define DEBUG_MSG(str) \ - do { \ - } while (false) -#else -#define DEBUG_MSG(str) \ - do { \ - std::wcout << str << std::endl; \ - } while (false) -#endif - -// Thanks: https://stackoverflow.com/a/36545162/4297146 - -typedef LONG NTSTATUS, *PNTSTATUS; - -#define STATUS_SUCCESS (0x00000000) - -typedef NTSTATUS(WINAPI *RtlGetVersionPtr)(PRTL_OSVERSIONINFOW); - -RTL_OSVERSIONINFOW GetRealOSVersion() -{ - HMODULE hMod = ::GetModuleHandleW(L"ntdll.dll"); - if (hMod) { - RtlGetVersionPtr fxPtr = (RtlGetVersionPtr)::GetProcAddress(hMod, "RtlGetVersion"); - if (fxPtr != nullptr) { - RTL_OSVERSIONINFOW rovi = {0}; - rovi.dwOSVersionInfoSize = sizeof(rovi); - if (STATUS_SUCCESS == fxPtr(&rovi)) { - return rovi; - } - } - } - RTL_OSVERSIONINFOW rovi = {0}; - return rovi; -} - -// Quickstart: Handling toast activations from Win32 apps in Windows 10 -// https://blogs.msdn.microsoft.com/tiles_and_toasts/2015/10/16/quickstart-handling-toast-activations-from-win32-apps-in-windows-10/ - -using namespace WinToastLib; -namespace DllImporter -{ -// Function load a function from library -template HRESULT loadFunctionFromLibrary(HINSTANCE library, LPCSTR name, Function &func) -{ - if (!library) { - return E_INVALIDARG; - } - func = reinterpret_cast(GetProcAddress(library, name)); - return (func != nullptr) ? S_OK : E_FAIL; -} - -typedef HRESULT(FAR STDAPICALLTYPE *f_SetCurrentProcessExplicitAppUserModelID)(__in PCWSTR AppID); -typedef HRESULT(FAR STDAPICALLTYPE *f_PropVariantToString)(_In_ REFPROPVARIANT propvar, _Out_writes_(cch) PWSTR psz, _In_ UINT cch); -typedef HRESULT(FAR STDAPICALLTYPE *f_RoGetActivationFactory)(_In_ HSTRING activatableClassId, _In_ REFIID iid, _COM_Outptr_ void **factory); -typedef HRESULT(FAR STDAPICALLTYPE *f_WindowsCreateStringReference)(_In_reads_opt_(length + 1) PCWSTR sourceString, UINT32 length, _Out_ HSTRING_HEADER *hstringHeader, _Outptr_result_maybenull_ _Result_nullonfailure_ HSTRING *string); -typedef PCWSTR(FAR STDAPICALLTYPE *f_WindowsGetStringRawBuffer)(_In_ HSTRING string, _Out_ UINT32 *length); -typedef HRESULT(FAR STDAPICALLTYPE *f_WindowsDeleteString)(_In_opt_ HSTRING string); - -static f_SetCurrentProcessExplicitAppUserModelID SetCurrentProcessExplicitAppUserModelID; -static f_PropVariantToString PropVariantToString; -static f_RoGetActivationFactory RoGetActivationFactory; -static f_WindowsCreateStringReference WindowsCreateStringReference; -static f_WindowsGetStringRawBuffer WindowsGetStringRawBuffer; -static f_WindowsDeleteString WindowsDeleteString; - -template _Check_return_ __inline HRESULT _1_GetActivationFactory(_In_ HSTRING activatableClassId, _COM_Outptr_ T **factory) -{ - return RoGetActivationFactory(activatableClassId, IID_INS_ARGS(factory)); -} - -template inline HRESULT Wrap_GetActivationFactory(_In_ HSTRING activatableClassId, _Inout_ Details::ComPtrRef factory) throw() -{ - return _1_GetActivationFactory(activatableClassId, factory.ReleaseAndGetAddressOf()); -} - -inline HRESULT initialize() -{ - HINSTANCE LibShell32 = LoadLibraryW(L"SHELL32.DLL"); - HRESULT hr = loadFunctionFromLibrary(LibShell32, "SetCurrentProcessExplicitAppUserModelID", SetCurrentProcessExplicitAppUserModelID); - if (SUCCEEDED(hr)) { - HINSTANCE LibPropSys = LoadLibraryW(L"PROPSYS.DLL"); - hr = loadFunctionFromLibrary(LibPropSys, "PropVariantToString", PropVariantToString); - if (SUCCEEDED(hr)) { - HINSTANCE LibComBase = LoadLibraryW(L"COMBASE.DLL"); - const bool succeded = SUCCEEDED(loadFunctionFromLibrary(LibComBase, "RoGetActivationFactory", RoGetActivationFactory)) && - SUCCEEDED(loadFunctionFromLibrary(LibComBase, "WindowsCreateStringReference", WindowsCreateStringReference)) && SUCCEEDED(loadFunctionFromLibrary(LibComBase, "WindowsGetStringRawBuffer", WindowsGetStringRawBuffer)) && - SUCCEEDED(loadFunctionFromLibrary(LibComBase, "WindowsDeleteString", WindowsDeleteString)); - return succeded ? S_OK : E_FAIL; - } - } - return hr; -} -} - -class WinToastStringWrapper -{ -public: - WinToastStringWrapper(_In_reads_(length) PCWSTR stringRef, _In_ UINT32 length) throw() - { - HRESULT hr = DllImporter::WindowsCreateStringReference(stringRef, length, &_header, &_hstring); - if (!SUCCEEDED(hr)) { - RaiseException(static_cast(STATUS_INVALID_PARAMETER), EXCEPTION_NONCONTINUABLE, 0, nullptr); - } - } - WinToastStringWrapper(_In_ const std::wstring &stringRef) throw() - { - HRESULT hr = DllImporter::WindowsCreateStringReference(stringRef.c_str(), static_cast(stringRef.length()), &_header, &_hstring); - if (FAILED(hr)) { - RaiseException(static_cast(STATUS_INVALID_PARAMETER), EXCEPTION_NONCONTINUABLE, 0, nullptr); - } - } - ~WinToastStringWrapper() - { - DllImporter::WindowsDeleteString(_hstring); - } - inline HSTRING Get() const throw() - { - return _hstring; - } - -private: - HSTRING _hstring; - HSTRING_HEADER _header; -}; - -class MyDateTime : public IReference -{ -protected: - DateTime _dateTime; - -public: - static INT64 Now() - { - FILETIME now; - GetSystemTimeAsFileTime(&now); - return ((((INT64)now.dwHighDateTime) << 32) | now.dwLowDateTime); - } - - MyDateTime(DateTime dateTime) - : _dateTime(dateTime) - { - } - - MyDateTime(INT64 millisecondsFromNow) - { - _dateTime.UniversalTime = Now() + millisecondsFromNow * 10000; - } - - operator INT64() - { - return _dateTime.UniversalTime; - } - - HRESULT STDMETHODCALLTYPE get_Value(DateTime *dateTime) - { - *dateTime = _dateTime; - return S_OK; - } - - HRESULT STDMETHODCALLTYPE QueryInterface(const IID &riid, void **ppvObject) - { - if (!ppvObject) { - return E_POINTER; - } - if (riid == __uuidof(IUnknown) || riid == __uuidof(IReference)) { - *ppvObject = static_cast(static_cast *>(this)); - return S_OK; - } - return E_NOINTERFACE; - } - - ULONG STDMETHODCALLTYPE Release() - { - return 1; - } - - ULONG STDMETHODCALLTYPE AddRef() - { - return 2; - } - - HRESULT STDMETHODCALLTYPE GetIids(ULONG *, IID **) - { - return E_NOTIMPL; - } - - HRESULT STDMETHODCALLTYPE GetRuntimeClassName(HSTRING *) - { - return E_NOTIMPL; - } - - HRESULT STDMETHODCALLTYPE GetTrustLevel(TrustLevel *) - { - return E_NOTIMPL; - } -}; - -namespace Util -{ -inline HRESULT defaultExecutablePath(_In_ WCHAR *path, _In_ DWORD nSize = MAX_PATH) -{ - DWORD written = GetModuleFileNameExW(GetCurrentProcess(), nullptr, path, nSize); - DEBUG_MSG("Default executable path: " << path); - return (written > 0) ? S_OK : E_FAIL; -} - -inline HRESULT defaultShellLinksDirectory(_In_ WCHAR *path, _In_ DWORD nSize = MAX_PATH) -{ - DWORD written = GetEnvironmentVariableW(L"APPDATA", path, nSize); - HRESULT hr = written > 0 ? S_OK : E_INVALIDARG; - if (SUCCEEDED(hr)) { - errno_t result = wcscat_s(path, nSize, DEFAULT_SHELL_LINKS_PATH); - hr = (result == 0) ? S_OK : E_INVALIDARG; - DEBUG_MSG("Default shell link path: " << path); - } - return hr; -} - -inline HRESULT defaultShellLinkPath(const std::wstring &appname, _In_ WCHAR *path, _In_ DWORD nSize = MAX_PATH) -{ - HRESULT hr = defaultShellLinksDirectory(path, nSize); - if (SUCCEEDED(hr)) { - const std::wstring appLink(appname + DEFAULT_LINK_FORMAT); - errno_t result = wcscat_s(path, nSize, appLink.c_str()); - hr = (result == 0) ? S_OK : E_INVALIDARG; - DEBUG_MSG("Default shell link file path: " << path); - } - return hr; -} - -inline PCWSTR AsString(ComPtr &xmlDocument) -{ - HSTRING xml; - ComPtr ser; - HRESULT hr = xmlDocument.As(&ser); - hr = ser->GetXml(&xml); - if (SUCCEEDED(hr)) - return DllImporter::WindowsGetStringRawBuffer(xml, NULL); - return NULL; -} - -inline PCWSTR AsString(HSTRING hstring) -{ - return DllImporter::WindowsGetStringRawBuffer(hstring, NULL); -} - -inline HRESULT setNodeStringValue(const std::wstring &string, IXmlNode *node, IXmlDocument *xml) -{ - ComPtr textNode; - HRESULT hr = xml->CreateTextNode(WinToastStringWrapper(string).Get(), &textNode); - if (SUCCEEDED(hr)) { - ComPtr stringNode; - hr = textNode.As(&stringNode); - if (SUCCEEDED(hr)) { - ComPtr appendedChild; - hr = node->AppendChild(stringNode.Get(), &appendedChild); - } - } - return hr; -} - -inline HRESULT setEventHandlers(_In_ IToastNotification *notification, _In_ std::shared_ptr eventHandler, _In_ INT64 expirationTime) -{ - EventRegistrationToken activatedToken, dismissedToken, failedToken; - HRESULT hr = notification->add_Activated(Callback, ITypedEventHandler>>([eventHandler](IToastNotification *, IInspectable *inspectable) { - IToastActivatedEventArgs *activatedEventArgs; - HRESULT hr = inspectable->QueryInterface(&activatedEventArgs); - if (SUCCEEDED(hr)) { - HSTRING argumentsHandle; - hr = activatedEventArgs->get_Arguments(&argumentsHandle); - if (SUCCEEDED(hr)) { - PCWSTR arguments = Util::AsString(argumentsHandle); - if (arguments && *arguments) { - eventHandler->toastActivated((int)wcstol(arguments, NULL, 10)); - return S_OK; - } - } - } - eventHandler->toastActivated(); - return S_OK; - }).Get(), - &activatedToken); - - if (SUCCEEDED(hr)) { - hr = notification->add_Dismissed( - Callback, ITypedEventHandler>>([eventHandler, expirationTime](IToastNotification *, IToastDismissedEventArgs *e) { - ToastDismissalReason reason; - if (SUCCEEDED(e->get_Reason(&reason))) { - if (reason == ToastDismissalReason_UserCanceled && expirationTime && MyDateTime::Now() >= expirationTime) - reason = ToastDismissalReason_TimedOut; - eventHandler->toastDismissed(static_cast(reason)); - } - return S_OK; - }).Get(), - &dismissedToken); - if (SUCCEEDED(hr)) { - hr = notification->add_Failed(Callback, ITypedEventHandler>>([eventHandler](IToastNotification *, IToastFailedEventArgs *) { - eventHandler->toastFailed(); - return S_OK; - }).Get(), - &failedToken); - } - } - return hr; -} - -inline HRESULT addAttribute(_In_ IXmlDocument *xml, const std::wstring &name, IXmlNamedNodeMap *attributeMap) -{ - ComPtr srcAttribute; - HRESULT hr = xml->CreateAttribute(WinToastStringWrapper(name).Get(), &srcAttribute); - if (SUCCEEDED(hr)) { - ComPtr node; - hr = srcAttribute.As(&node); - if (SUCCEEDED(hr)) { - ComPtr pNode; - hr = attributeMap->SetNamedItem(node.Get(), &pNode); - } - } - return hr; -} - -inline HRESULT createElement(_In_ IXmlDocument *xml, _In_ const std::wstring &root_node, _In_ const std::wstring &element_name, _In_ const std::vector &attribute_names) -{ - ComPtr rootList; - HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(root_node).Get(), &rootList); - if (SUCCEEDED(hr)) { - ComPtr root; - hr = rootList->Item(0, &root); - if (SUCCEEDED(hr)) { - ComPtr audioElement; - hr = xml->CreateElement(WinToastStringWrapper(element_name).Get(), &audioElement); - if (SUCCEEDED(hr)) { - ComPtr audioNodeTmp; - hr = audioElement.As(&audioNodeTmp); - if (SUCCEEDED(hr)) { - ComPtr audioNode; - hr = root->AppendChild(audioNodeTmp.Get(), &audioNode); - if (SUCCEEDED(hr)) { - ComPtr attributes; - hr = audioNode->get_Attributes(&attributes); - if (SUCCEEDED(hr)) { - for (auto it : attribute_names) { - hr = addAttribute(xml, it, attributes.Get()); - } - } - } - } - } - } - } - return hr; -} -} - -WinToast *WinToast::instance() -{ - static WinToast instance; - return &instance; -} - -WinToast::WinToast() - : _isInitialized(false) - , _hasCoInitialized(false) -{ - if (!isCompatible()) { - DEBUG_MSG(L"Warning: Your system is not compatible with this library "); - } -} - -WinToast::~WinToast() -{ - if (_hasCoInitialized) { - CoUninitialize(); - } -} - -void WinToast::setAppName(_In_ const std::wstring &appName) -{ - _appName = appName; -} - -void WinToast::setAppUserModelId(_In_ const std::wstring &aumi) -{ - _aumi = aumi; - DEBUG_MSG(L"Default App User Model Id: " << _aumi.c_str()); -} - -bool WinToast::isCompatible() -{ - DllImporter::initialize(); - return !((DllImporter::SetCurrentProcessExplicitAppUserModelID == nullptr) || (DllImporter::PropVariantToString == nullptr) || (DllImporter::RoGetActivationFactory == nullptr) || (DllImporter::WindowsCreateStringReference == nullptr) || - (DllImporter::WindowsDeleteString == nullptr)); -} - -bool WinToastLib::WinToast::isSupportingModernFeatures() -{ - RTL_OSVERSIONINFOW tmp = GetRealOSVersion(); - return tmp.dwMajorVersion > 6; -} -std::wstring WinToast::configureAUMI(_In_ const std::wstring &companyName, _In_ const std::wstring &productName, _In_ const std::wstring &subProduct, _In_ const std::wstring &versionInformation) -{ - std::wstring aumi = companyName; - aumi += L"." + productName; - if (subProduct.length() > 0) { - aumi += L"." + subProduct; - if (versionInformation.length() > 0) { - aumi += L"." + versionInformation; - } - } - - if (aumi.length() > SCHAR_MAX) { - DEBUG_MSG("Error: max size allowed for AUMI: 128 characters."); - } - return aumi; -} - -enum WinToast::ShortcutResult WinToast::createShortcut() -{ - if (_aumi.empty() || _appName.empty()) { - DEBUG_MSG(L"Error: App User Model Id or Appname is empty!"); - return SHORTCUT_MISSING_PARAMETERS; - } - - if (!isCompatible()) { - DEBUG_MSG(L"Your OS is not compatible with this library! =("); - return SHORTCUT_INCOMPATIBLE_OS; - } - - if (!_hasCoInitialized) { - HRESULT initHr = CoInitializeEx(NULL, COINIT::COINIT_MULTITHREADED); - if (initHr != RPC_E_CHANGED_MODE) { - if (FAILED(initHr) && initHr != S_FALSE) { - DEBUG_MSG(L"Error on COM library initialization!"); - return SHORTCUT_COM_INIT_FAILURE; - } else { - _hasCoInitialized = true; - } - } - } - - bool wasChanged; - HRESULT hr = validateShellLinkHelper(wasChanged); - if (SUCCEEDED(hr)) - return wasChanged ? SHORTCUT_WAS_CHANGED : SHORTCUT_UNCHANGED; - - hr = createShellLinkHelper(); - return SUCCEEDED(hr) ? SHORTCUT_WAS_CREATED : SHORTCUT_CREATE_FAILED; -} - -bool WinToast::initialize(_Out_ WinToastError *error) -{ - _isInitialized = false; - setError(error, WinToastError::NoError); - - if (!isCompatible()) { - setError(error, WinToastError::SystemNotSupported); - DEBUG_MSG(L"Error: system not supported."); - return false; - } - - if (_aumi.empty() || _appName.empty()) { - setError(error, WinToastError::InvalidParameters); - DEBUG_MSG(L"Error while initializing, did you set up a valid AUMI and App name?"); - return false; - } - - if (createShortcut() < 0) { - setError(error, WinToastError::ShellLinkNotCreated); - DEBUG_MSG(L"Error while attaching the AUMI to the current proccess =("); - return false; - } - - if (FAILED(DllImporter::SetCurrentProcessExplicitAppUserModelID(_aumi.c_str()))) { - setError(error, WinToastError::InvalidAppUserModelID); - DEBUG_MSG(L"Error while attaching the AUMI to the current proccess =("); - return false; - } - - _isInitialized = true; - return _isInitialized; -} - -bool WinToast::isInitialized() const -{ - return _isInitialized; -} - -const std::wstring &WinToast::appName() const -{ - return _appName; -} - -const std::wstring &WinToast::appUserModelId() const -{ - return _aumi; -} - -HRESULT WinToast::validateShellLinkHelper(_Out_ bool &wasChanged) -{ - WCHAR path[MAX_PATH] = {L'\0'}; - Util::defaultShellLinkPath(_appName, path); - // Check if the file exist - DWORD attr = GetFileAttributesW(path); - if (attr >= 0xFFFFFFF) { - DEBUG_MSG("Error, shell link not found. Try to create a new one in: " << path); - return E_FAIL; - } - - // Let's load the file as shell link to validate. - // - Create a shell link - // - Create a persistant file - // - Load the path as data for the persistant file - // - Read the property AUMI and validate with the current - // - Review if AUMI is equal. - ComPtr shellLink; - HRESULT hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink)); - if (SUCCEEDED(hr)) { - ComPtr persistFile; - hr = shellLink.As(&persistFile); - if (SUCCEEDED(hr)) { - hr = persistFile->Load(path, STGM_READWRITE); - if (SUCCEEDED(hr)) { - ComPtr propertyStore; - hr = shellLink.As(&propertyStore); - if (SUCCEEDED(hr)) { - PROPVARIANT appIdPropVar; - hr = propertyStore->GetValue(PKEY_AppUserModel_ID, &appIdPropVar); - if (SUCCEEDED(hr)) { - WCHAR AUMI[MAX_PATH]; - hr = DllImporter::PropVariantToString(appIdPropVar, AUMI, MAX_PATH); - wasChanged = false; - if (FAILED(hr) || _aumi != AUMI) { - // AUMI Changed for the same app, let's update the current value! =) - wasChanged = true; - PropVariantClear(&appIdPropVar); - hr = InitPropVariantFromString(_aumi.c_str(), &appIdPropVar); - if (SUCCEEDED(hr)) { - hr = propertyStore->SetValue(PKEY_AppUserModel_ID, appIdPropVar); - if (SUCCEEDED(hr)) { - hr = propertyStore->Commit(); - if (SUCCEEDED(hr) && SUCCEEDED(persistFile->IsDirty())) { - hr = persistFile->Save(path, TRUE); - } - } - } - } - PropVariantClear(&appIdPropVar); - } - } - } - } - } - return hr; -} - -HRESULT WinToast::createShellLinkHelper() -{ - WCHAR exePath[MAX_PATH] {L'\0'}; - WCHAR slPath[MAX_PATH] {L'\0'}; - Util::defaultShellLinkPath(_appName, slPath); - Util::defaultExecutablePath(exePath); - ComPtr shellLink; - HRESULT hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&shellLink)); - if (SUCCEEDED(hr)) { - hr = shellLink->SetPath(exePath); - if (SUCCEEDED(hr)) { - hr = shellLink->SetArguments(L""); - if (SUCCEEDED(hr)) { - hr = shellLink->SetWorkingDirectory(exePath); - if (SUCCEEDED(hr)) { - ComPtr propertyStore; - hr = shellLink.As(&propertyStore); - if (SUCCEEDED(hr)) { - PROPVARIANT appIdPropVar; - hr = InitPropVariantFromString(_aumi.c_str(), &appIdPropVar); - if (SUCCEEDED(hr)) { - hr = propertyStore->SetValue(PKEY_AppUserModel_ID, appIdPropVar); - if (SUCCEEDED(hr)) { - hr = propertyStore->Commit(); - if (SUCCEEDED(hr)) { - ComPtr persistFile; - hr = shellLink.As(&persistFile); - if (SUCCEEDED(hr)) { - hr = persistFile->Save(slPath, TRUE); - } - } - } - PropVariantClear(&appIdPropVar); - } - } - } - } - } - } - return hr; -} - -INT64 WinToast::showToast(_In_ const WinToastTemplate &toast, _In_ IWinToastHandler *handler, _Out_ WinToastError *error) -{ - setError(error, WinToastError::NoError); - INT64 id = -1; - if (!isInitialized()) { - setError(error, WinToastError::NotInitialized); - DEBUG_MSG("Error when launching the toast. WinToast is not initialized."); - return id; - } - if (!handler) { - setError(error, WinToastError::InvalidHandler); - DEBUG_MSG("Error when launching the toast. Handler cannot be null."); - return id; - } - - ComPtr notificationManager; - HRESULT hr = DllImporter::Wrap_GetActivationFactory(WinToastStringWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(), ¬ificationManager); - if (SUCCEEDED(hr)) { - ComPtr notifier; - hr = notificationManager->CreateToastNotifierWithId(WinToastStringWrapper(_aumi).Get(), ¬ifier); - if (SUCCEEDED(hr)) { - ComPtr notificationFactory; - hr = DllImporter::Wrap_GetActivationFactory(WinToastStringWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotification).Get(), ¬ificationFactory); - if (SUCCEEDED(hr)) { - ComPtr xmlDocument; - HRESULT hr = notificationManager->GetTemplateContent(ToastTemplateType(toast.type()), &xmlDocument); - if (SUCCEEDED(hr)) { - const int fieldsCount = toast.textFieldsCount(); - for (int i = 0; i < fieldsCount && SUCCEEDED(hr); i++) { - hr = setTextFieldHelper(xmlDocument.Get(), toast.textField(WinToastTemplate::TextField(i)), i); - } - - // Modern feature are supported Windows > Windows 10 - if (SUCCEEDED(hr) && isSupportingModernFeatures()) { - // Note that we do this *after* using toast.textFieldsCount() to - // iterate/fill the template's text fields, since we're adding yet another text field. - if (SUCCEEDED(hr) && !toast.attributionText().empty()) { - hr = setAttributionTextFieldHelper(xmlDocument.Get(), toast.attributionText()); - } - - const int actionsCount = toast.actionsCount(); - WCHAR buf[12]; - for (int i = 0; i < actionsCount && SUCCEEDED(hr); i++) { - _snwprintf_s(buf, sizeof(buf) / sizeof(*buf), _TRUNCATE, L"%d", i); - hr = addActionHelper(xmlDocument.Get(), toast.actionLabel(i), buf); - } - - if (SUCCEEDED(hr)) { - hr = (toast.audioPath().empty() && toast.audioOption() == WinToastTemplate::AudioOption::Default) ? hr : setAudioFieldHelper(xmlDocument.Get(), toast.audioPath(), toast.audioOption()); - } - - if (SUCCEEDED(hr) && toast.duration() != WinToastTemplate::Duration::System) { - hr = addDurationHelper(xmlDocument.Get(), (toast.duration() == WinToastTemplate::Duration::Short) ? L"short" : L"long"); - } - - } else { - DEBUG_MSG("Modern features (Actions/Sounds/Attributes) not supported in this os version"); - } - - if (SUCCEEDED(hr)) { - hr = toast.hasImage() ? setImageFieldHelper(xmlDocument.Get(), toast.imagePath()) : hr; - if (SUCCEEDED(hr)) { - ComPtr notification; - hr = notificationFactory->CreateToastNotification(xmlDocument.Get(), ¬ification); - if (SUCCEEDED(hr)) { - INT64 expiration = 0, relativeExpiration = toast.expiration(); - if (relativeExpiration > 0) { - MyDateTime expirationDateTime(relativeExpiration); - expiration = expirationDateTime; - hr = notification->put_ExpirationTime(&expirationDateTime); - } - - if (SUCCEEDED(hr)) { - hr = Util::setEventHandlers(notification.Get(), std::shared_ptr(handler), expiration); - if (FAILED(hr)) { - setError(error, WinToastError::InvalidHandler); - } - } - - if (SUCCEEDED(hr)) { - GUID guid; - hr = CoCreateGuid(&guid); - if (SUCCEEDED(hr)) { - id = guid.Data1; - _buffer[id] = notification; - DEBUG_MSG("xml: " << Util::AsString(xmlDocument)); - hr = notifier->Show(notification.Get()); - if (FAILED(hr)) { - setError(error, WinToastError::NotDisplayed); - } - } - } - } - } - } - } - } - } - } - return FAILED(hr) ? -1 : id; -} - -ComPtr WinToast::notifier(_In_ bool *succeded) const -{ - ComPtr notificationManager; - ComPtr notifier; - HRESULT hr = DllImporter::Wrap_GetActivationFactory(WinToastStringWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(), ¬ificationManager); - if (SUCCEEDED(hr)) { - hr = notificationManager->CreateToastNotifierWithId(WinToastStringWrapper(_aumi).Get(), ¬ifier); - } - *succeded = SUCCEEDED(hr); - return notifier; -} - -bool WinToast::hideToast(_In_ INT64 id) -{ - if (!isInitialized()) { - DEBUG_MSG("Error when hiding the toast. WinToast is not initialized."); - return false; - } - const bool find = _buffer.find(id) != _buffer.end(); - if (find) { - bool succeded = false; - ComPtr notify = notifier(&succeded); - if (succeded) { - notify->Hide(_buffer[id].Get()); - } - _buffer.erase(id); - } - return find; -} - -void WinToast::clear() -{ - bool succeded = false; - ComPtr notify = notifier(&succeded); - if (succeded) { - auto end = _buffer.end(); - for (auto it = _buffer.begin(); it != end; ++it) { - notify->Hide(it->second.Get()); - } - } - _buffer.clear(); -} - -// -// Available as of Windows 10 Anniversary Update -// Ref: https://docs.microsoft.com/en-us/windows/uwp/design/shell/tiles-and-notifications/adaptive-interactive-toasts -// -// NOTE: This will add a new text field, so be aware when iterating over -// the toast's text fields or getting a count of them. -// -HRESULT WinToast::setAttributionTextFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring &text) -{ - Util::createElement(xml, L"binding", L"text", {L"placement"}); - ComPtr nodeList; - HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"text").Get(), &nodeList); - if (SUCCEEDED(hr)) { - UINT32 nodeListLength; - hr = nodeList->get_Length(&nodeListLength); - if (SUCCEEDED(hr)) { - for (UINT32 i = 0; i < nodeListLength; i++) { - ComPtr textNode; - hr = nodeList->Item(i, &textNode); - if (SUCCEEDED(hr)) { - ComPtr attributes; - hr = textNode->get_Attributes(&attributes); - if (SUCCEEDED(hr)) { - ComPtr editedNode; - if (SUCCEEDED(hr)) { - hr = attributes->GetNamedItem(WinToastStringWrapper(L"placement").Get(), &editedNode); - if (FAILED(hr) || !editedNode) { - continue; - } - hr = Util::setNodeStringValue(L"attribution", editedNode.Get(), xml); - if (SUCCEEDED(hr)) { - return setTextFieldHelper(xml, text, i); - } - } - } - } - } - } - } - return hr; -} - -HRESULT WinToast::addDurationHelper(_In_ IXmlDocument *xml, _In_ const std::wstring &duration) -{ - ComPtr nodeList; - HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"toast").Get(), &nodeList); - if (SUCCEEDED(hr)) { - UINT32 length; - hr = nodeList->get_Length(&length); - if (SUCCEEDED(hr)) { - ComPtr toastNode; - hr = nodeList->Item(0, &toastNode); - if (SUCCEEDED(hr)) { - ComPtr toastElement; - hr = toastNode.As(&toastElement); - if (SUCCEEDED(hr)) { - hr = toastElement->SetAttribute(WinToastStringWrapper(L"duration").Get(), WinToastStringWrapper(duration).Get()); - } - } - } - } - return hr; -} - -HRESULT WinToast::setTextFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring &text, _In_ int pos) -{ - ComPtr nodeList; - HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"text").Get(), &nodeList); - if (SUCCEEDED(hr)) { - ComPtr node; - hr = nodeList->Item(pos, &node); - if (SUCCEEDED(hr)) { - hr = Util::setNodeStringValue(text, node.Get(), xml); - } - } - return hr; -} - -HRESULT WinToast::setImageFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring &path) -{ - wchar_t imagePath[MAX_PATH] = L"file:///"; - HRESULT hr = StringCchCatW(imagePath, MAX_PATH, path.c_str()); - if (SUCCEEDED(hr)) { - ComPtr nodeList; - HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"image").Get(), &nodeList); - if (SUCCEEDED(hr)) { - ComPtr node; - hr = nodeList->Item(0, &node); - if (SUCCEEDED(hr)) { - ComPtr attributes; - hr = node->get_Attributes(&attributes); - if (SUCCEEDED(hr)) { - ComPtr editedNode; - hr = attributes->GetNamedItem(WinToastStringWrapper(L"src").Get(), &editedNode); - if (SUCCEEDED(hr)) { - Util::setNodeStringValue(imagePath, editedNode.Get(), xml); - } - } - } - } - } - return hr; -} - -HRESULT WinToast::setAudioFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring &path, _In_opt_ WinToastTemplate::AudioOption option) -{ - std::vector attrs; - if (!path.empty()) - attrs.push_back(L"src"); - if (option == WinToastTemplate::AudioOption::Loop) - attrs.push_back(L"loop"); - if (option == WinToastTemplate::AudioOption::Silent) - attrs.push_back(L"silent"); - Util::createElement(xml, L"toast", L"audio", attrs); - - ComPtr nodeList; - HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"audio").Get(), &nodeList); - if (SUCCEEDED(hr)) { - ComPtr node; - hr = nodeList->Item(0, &node); - if (SUCCEEDED(hr)) { - ComPtr attributes; - hr = node->get_Attributes(&attributes); - if (SUCCEEDED(hr)) { - ComPtr editedNode; - if (!path.empty()) { - if (SUCCEEDED(hr)) { - hr = attributes->GetNamedItem(WinToastStringWrapper(L"src").Get(), &editedNode); - if (SUCCEEDED(hr)) { - hr = Util::setNodeStringValue(path, editedNode.Get(), xml); - } - } - } - - if (SUCCEEDED(hr)) { - switch (option) { - case WinToastTemplate::AudioOption::Loop: - hr = attributes->GetNamedItem(WinToastStringWrapper(L"loop").Get(), &editedNode); - if (SUCCEEDED(hr)) { - hr = Util::setNodeStringValue(L"true", editedNode.Get(), xml); - } - break; - case WinToastTemplate::AudioOption::Silent: - hr = attributes->GetNamedItem(WinToastStringWrapper(L"silent").Get(), &editedNode); - if (SUCCEEDED(hr)) { - hr = Util::setNodeStringValue(L"true", editedNode.Get(), xml); - } - default: - break; - } - } - } - } - } - return hr; -} - -HRESULT WinToast::addActionHelper(_In_ IXmlDocument *xml, _In_ const std::wstring &content, _In_ const std::wstring &arguments) -{ - ComPtr nodeList; - HRESULT hr = xml->GetElementsByTagName(WinToastStringWrapper(L"actions").Get(), &nodeList); - if (SUCCEEDED(hr)) { - UINT32 length; - hr = nodeList->get_Length(&length); - if (SUCCEEDED(hr)) { - ComPtr actionsNode; - if (length > 0) { - hr = nodeList->Item(0, &actionsNode); - } else { - hr = xml->GetElementsByTagName(WinToastStringWrapper(L"toast").Get(), &nodeList); - if (SUCCEEDED(hr)) { - hr = nodeList->get_Length(&length); - if (SUCCEEDED(hr)) { - ComPtr toastNode; - hr = nodeList->Item(0, &toastNode); - if (SUCCEEDED(hr)) { - ComPtr toastElement; - hr = toastNode.As(&toastElement); - if (SUCCEEDED(hr)) - hr = toastElement->SetAttribute(WinToastStringWrapper(L"template").Get(), WinToastStringWrapper(L"ToastGeneric").Get()); - if (SUCCEEDED(hr)) - hr = toastElement->SetAttribute(WinToastStringWrapper(L"duration").Get(), WinToastStringWrapper(L"long").Get()); - if (SUCCEEDED(hr)) { - ComPtr actionsElement; - hr = xml->CreateElement(WinToastStringWrapper(L"actions").Get(), &actionsElement); - if (SUCCEEDED(hr)) { - hr = actionsElement.As(&actionsNode); - if (SUCCEEDED(hr)) { - ComPtr appendedChild; - hr = toastNode->AppendChild(actionsNode.Get(), &appendedChild); - } - } - } - } - } - } - } - if (SUCCEEDED(hr)) { - ComPtr actionElement; - hr = xml->CreateElement(WinToastStringWrapper(L"action").Get(), &actionElement); - if (SUCCEEDED(hr)) - hr = actionElement->SetAttribute(WinToastStringWrapper(L"content").Get(), WinToastStringWrapper(content).Get()); - if (SUCCEEDED(hr)) - hr = actionElement->SetAttribute(WinToastStringWrapper(L"arguments").Get(), WinToastStringWrapper(arguments).Get()); - if (SUCCEEDED(hr)) { - ComPtr actionNode; - hr = actionElement.As(&actionNode); - if (SUCCEEDED(hr)) { - ComPtr appendedChild; - hr = actionsNode->AppendChild(actionNode.Get(), &appendedChild); - } - } - } - } - } - return hr; -} - -void WinToast::setError(_Out_ WinToastError *error, _In_ WinToastError value) -{ - if (error) { - *error = value; - } -} - -WinToastTemplate::WinToastTemplate(_In_ WinToastTemplateType type) - : _type(type) -{ - static const std::size_t TextFieldsCount[] = {1, 2, 2, 3, 1, 2, 2, 3}; - _textFields = std::vector(TextFieldsCount[type], L""); -} - -WinToastTemplate::~WinToastTemplate() -{ - _textFields.clear(); -} - -void WinToastTemplate::setTextField(_In_ const std::wstring &txt, _In_ WinToastTemplate::TextField pos) -{ - _textFields[pos] = txt; -} - -void WinToastTemplate::setImagePath(_In_ const std::wstring &imgPath) -{ - _imagePath = imgPath; -} - -void WinToastTemplate::setAudioPath(_In_ const std::wstring &audioPath) -{ - _audioPath = audioPath; -} - -void WinToastTemplate::setAudioOption(_In_ WinToastTemplate::AudioOption audioOption) -{ - _audioOption = audioOption; -} - -void WinToastTemplate::setDuration(_In_ Duration duration) -{ - _duration = duration; -} - -void WinToastTemplate::setExpiration(_In_ INT64 millisecondsFromNow) -{ - _expiration = millisecondsFromNow; -} - -void WinToastTemplate::setAttributionText(_In_ const std::wstring &attributionText) -{ - _attributionText = attributionText; -} - -void WinToastTemplate::addAction(_In_ const std::wstring &label) -{ - _actions.push_back(label); -} - -std::size_t WinToastTemplate::textFieldsCount() const -{ - return _textFields.size(); -} - -std::size_t WinToastTemplate::actionsCount() const -{ - return _actions.size(); -} - -bool WinToastTemplate::hasImage() const -{ - return _type < WinToastTemplateType::Text01; -} - -const std::vector &WinToastTemplate::textFields() const -{ - return _textFields; -} - -const std::wstring &WinToastTemplate::textField(_In_ TextField pos) const -{ - return _textFields[pos]; -} - -const std::wstring &WinToastTemplate::actionLabel(_In_ int pos) const -{ - return _actions[pos]; -} - -const std::wstring &WinToastTemplate::imagePath() const -{ - return _imagePath; -} - -const std::wstring &WinToastTemplate::audioPath() const -{ - return _audioPath; -} - -const std::wstring &WinToastTemplate::attributionText() const -{ - return _attributionText; -} - -INT64 WinToastTemplate::expiration() const -{ - return _expiration; -} - -WinToastTemplate::WinToastTemplateType WinToastTemplate::type() const -{ - return _type; -} - -WinToastTemplate::AudioOption WinToastTemplate::audioOption() const -{ - return _audioOption; -} - -WinToastTemplate::Duration WinToastTemplate::duration() const -{ - return _duration; -} diff --git a/src/notifications/wintoastlib.h b/src/notifications/wintoastlib.h deleted file mode 100644 index 6457ae49d..000000000 --- a/src/notifications/wintoastlib.h +++ /dev/null @@ -1,156 +0,0 @@ -#ifndef WINTOASTLIB_H -#define WINTOASTLIB_H -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -using namespace Microsoft::WRL; -using namespace ABI::Windows::Data::Xml::Dom; -using namespace ABI::Windows::Foundation; -using namespace ABI::Windows::UI::Notifications; -using namespace Windows::Foundation; - -#define DEFAULT_SHELL_LINKS_PATH L"\\Microsoft\\Windows\\Start Menu\\Programs\\" -#define DEFAULT_LINK_FORMAT L".lnk" -namespace WinToastLib -{ -class IWinToastHandler -{ -public: - enum WinToastDismissalReason { - UserCanceled = ToastDismissalReason::ToastDismissalReason_UserCanceled, - ApplicationHidden = ToastDismissalReason::ToastDismissalReason_ApplicationHidden, - TimedOut = ToastDismissalReason::ToastDismissalReason_TimedOut - }; - virtual ~IWinToastHandler() - { - } - virtual void toastActivated() = 0; - virtual void toastActivated(int actionIndex) = 0; - virtual void toastDismissed(WinToastDismissalReason state) = 0; - virtual void toastFailed() = 0; -}; - -class WinToastTemplate -{ -public: - enum Duration { System, Short, Long }; - enum AudioOption { Default = 0, Silent = 1, Loop = 2 }; - enum TextField { FirstLine = 0, SecondLine, ThirdLine }; - enum WinToastTemplateType { - ImageAndText01 = ToastTemplateType::ToastTemplateType_ToastImageAndText01, - ImageAndText02 = ToastTemplateType::ToastTemplateType_ToastImageAndText02, - ImageAndText03 = ToastTemplateType::ToastTemplateType_ToastImageAndText03, - ImageAndText04 = ToastTemplateType::ToastTemplateType_ToastImageAndText04, - Text01 = ToastTemplateType::ToastTemplateType_ToastText01, - Text02 = ToastTemplateType::ToastTemplateType_ToastText02, - Text03 = ToastTemplateType::ToastTemplateType_ToastText03, - Text04 = ToastTemplateType::ToastTemplateType_ToastText04, - WinToastTemplateTypeCount - }; - - WinToastTemplate(_In_ WinToastTemplateType type = WinToastTemplateType::ImageAndText02); - ~WinToastTemplate(); - - void setTextField(_In_ const std::wstring &txt, _In_ TextField pos); - void setImagePath(_In_ const std::wstring &imgPath); - void setAudioPath(_In_ const std::wstring &audioPath); - void setAttributionText(_In_ const std::wstring &attributionText); - void addAction(_In_ const std::wstring &label); - void setAudioOption(_In_ WinToastTemplate::AudioOption audioOption); - void setDuration(_In_ Duration duration); - void setExpiration(_In_ INT64 millisecondsFromNow); - std::size_t textFieldsCount() const; - std::size_t actionsCount() const; - bool hasImage() const; - const std::vector &textFields() const; - const std::wstring &textField(_In_ TextField pos) const; - const std::wstring &actionLabel(_In_ int pos) const; - const std::wstring &imagePath() const; - const std::wstring &audioPath() const; - const std::wstring &attributionText() const; - INT64 expiration() const; - WinToastTemplateType type() const; - WinToastTemplate::AudioOption audioOption() const; - Duration duration() const; - -private: - std::vector _textFields; - std::vector _actions; - std::wstring _imagePath = L""; - std::wstring _audioPath = L""; - std::wstring _attributionText = L""; - INT64 _expiration = 0; - AudioOption _audioOption = WinToastTemplate::AudioOption::Default; - WinToastTemplateType _type = WinToastTemplateType::Text01; - Duration _duration = Duration::System; -}; - -class WinToast -{ -public: - enum WinToastError { NoError = 0, NotInitialized, SystemNotSupported, ShellLinkNotCreated, InvalidAppUserModelID, InvalidParameters, InvalidHandler, NotDisplayed, UnknownError }; - - enum ShortcutResult { - SHORTCUT_UNCHANGED = 0, - SHORTCUT_WAS_CHANGED = 1, - SHORTCUT_WAS_CREATED = 2, - - SHORTCUT_MISSING_PARAMETERS = -1, - SHORTCUT_INCOMPATIBLE_OS = -2, - SHORTCUT_COM_INIT_FAILURE = -3, - SHORTCUT_CREATE_FAILED = -4 - }; - - WinToast(void); - virtual ~WinToast(); - static WinToast *instance(); - static bool isCompatible(); - static bool isSupportingModernFeatures(); - static std::wstring configureAUMI(_In_ const std::wstring &companyName, _In_ const std::wstring &productName, _In_ const std::wstring &subProduct = std::wstring(), _In_ const std::wstring &versionInformation = std::wstring()); - virtual bool initialize(_Out_ WinToastError *error = nullptr); - virtual bool isInitialized() const; - virtual bool hideToast(_In_ INT64 id); - virtual INT64 showToast(_In_ const WinToastTemplate &toast, _In_ IWinToastHandler *handler, _Out_ WinToastError *error = nullptr); - virtual void clear(); - virtual enum ShortcutResult createShortcut(); - - const std::wstring &appName() const; - const std::wstring &appUserModelId() const; - void setAppUserModelId(_In_ const std::wstring &appName); - void setAppName(_In_ const std::wstring &appName); - -protected: - bool _isInitialized; - bool _hasCoInitialized; - std::wstring _appName; - std::wstring _aumi; - std::map> _buffer; - - HRESULT validateShellLinkHelper(_Out_ bool &wasChanged); - HRESULT createShellLinkHelper(); - HRESULT setImageFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring &path); - HRESULT setAudioFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring &path, _In_opt_ WinToastTemplate::AudioOption option = WinToastTemplate::AudioOption::Default); - HRESULT setTextFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring &text, _In_ int pos); - HRESULT setAttributionTextFieldHelper(_In_ IXmlDocument *xml, _In_ const std::wstring &text); - HRESULT addActionHelper(_In_ IXmlDocument *xml, _In_ const std::wstring &action, _In_ const std::wstring &arguments); - HRESULT addDurationHelper(_In_ IXmlDocument *xml, _In_ const std::wstring &duration); - ComPtr notifier(_In_ bool *succeded) const; - void setError(_Out_ WinToastError *error, _In_ WinToastError value); -}; -} -#endif // WINTOASTLIB_H