From 7f28ac65dfb913b97823ee9bc2eb866f73fcdad6 Mon Sep 17 00:00:00 2001 From: Andreas Traczyk Date: Tue, 6 Apr 2021 17:47:48 -0400 Subject: [PATCH] notifications: add avatars to gnu/linux notifications Gitlab: #320 Change-Id: Ib2121257af2704dd2a246b499961657b66ae944d --- CMakeLists.txt | 8 ++++++-- src/calladapter.cpp | 15 +++++++++++++-- src/conversationsadapter.cpp | 15 +++++++++++---- src/systemtray.cpp | 14 ++++++++++++-- src/systemtray.h | 3 ++- src/utils.cpp | 13 +++++++++++-- src/utils.h | 3 ++- 7 files changed, 57 insertions(+), 14 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fd3c0b9b..3e5b4002 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -140,6 +140,8 @@ pkg_check_modules(LIBNOTIFY libnotify>=0.7.6) if(LIBNOTIFY_FOUND) add_definitions(-DUSE_LIBNOTIFY) add_definitions(${LIBNOTIFY_CFLAGS}) + pkg_check_modules(LIBGDKPIXBUF gdk-pixbuf-2.0>=2.40.0) + add_definitions(${LIBGDKPIXBUF_CFLAGS}) endif() if(QT5_VER AND QT5_PATH) @@ -217,7 +219,8 @@ include_directories(${PROJECT_SOURCE_DIR} ${SRC_DIR} ${LRC_SRC_PATH} ${LIBNM_INCLUDE_DIRS} - ${LIBNOTIFY_INCLUDE_DIRS}) + ${LIBNOTIFY_INCLUDE_DIRS} + ${LIBGDKPIXBUF_INCLUDE_DIRS}) add_executable(${PROJECT_NAME} ${SRC_DIR}/main.cpp @@ -239,7 +242,8 @@ target_link_libraries(jami-qt ${qrencode} ${X11} ${LIBNM_LIBRARIES} - ${LIBNOTIFY_LIBRARIES}) + ${LIBNOTIFY_LIBRARIES} + ${LIBGDKPIXBUF_LIBRARIES}) if(ENABLE_TESTS) message("Add Jami tests") diff --git a/src/calladapter.cpp b/src/calladapter.cpp index dc55f0e3..9a3b4495 100644 --- a/src/calladapter.cpp +++ b/src/calladapter.cpp @@ -102,13 +102,19 @@ CallAdapter::onCallStatusChanged(const QString& accountId, const QString& callId if (systemTray_->hideNotification(QString("%1;%2").arg(accountId).arg(convInfo.uid)) && call.startTime.time_since_epoch().count() == 0) { // This was a missed call; show a missed call notification + // TODO: swarmify(avoid using convInfo.participants[0]) + auto contactPhoto = Utils::contactPhoto(lrcInstance_, + convInfo.participants[0], + QSize(50, 50), + accountId); auto& accInfo = lrcInstance_->getAccountInfo(accountId); auto from = accInfo.contactModel->bestNameForContact(convInfo.participants[0]); auto notifId = QString("%1;%2").arg(accountId).arg(convInfo.uid); systemTray_->showNotification(notifId, tr("Missed call"), tr("Missed call from %1").arg(from), - NotificationType::CHAT); + NotificationType::CHAT, + Utils::QImageToByteArray(contactPhoto)); } } #endif @@ -384,11 +390,16 @@ CallAdapter::showNotification(const QString& accountId, const QString& convUid) Q_EMIT lrcInstance_->updateSmartList(); #ifdef Q_OS_LINUX + auto contactPhoto = Utils::contactPhoto(lrcInstance_, + convInfo.participants[0], + QSize(50, 50), + accountId); auto notifId = QString("%1;%2").arg(accountId).arg(convUid); systemTray_->showNotification(notifId, tr("Incoming call"), tr("%1 is calling you").arg(from), - NotificationType::CALL); + NotificationType::CALL, + Utils::QImageToByteArray(contactPhoto)); #else auto onClicked = [this, accountId, convUid = convInfo.uid]() { const auto& convInfo = lrcInstance_->getConversationFromConvUid(convUid, accountId); diff --git a/src/conversationsadapter.cpp b/src/conversationsadapter.cpp index 95ece1e6..079a260d 100644 --- a/src/conversationsadapter.cpp +++ b/src/conversationsadapter.cpp @@ -171,14 +171,19 @@ ConversationsAdapter::onNewUnreadInteraction(const QString& accountId, if (!interaction.authorUri.isEmpty() && (!QApplication::focusWindow() || accountId != lrcInstance_->getCurrAccId() || convUid != lrcInstance_->getCurrentConvUid())) { - auto& accInfo = lrcInstance_->getAccountInfo(accountId); - auto from = accInfo.contactModel->bestNameForContact(interaction.authorUri); + auto& accountInfo = lrcInstance_->getAccountInfo(accountId); + auto from = accountInfo.contactModel->bestNameForContact(interaction.authorUri); #ifdef Q_OS_LINUX + auto contactPhoto = Utils::contactPhoto(lrcInstance_, + interaction.authorUri, + QSize(50, 50), + accountId); auto notifId = QString("%1;%2;%3").arg(accountId).arg(convUid).arg(interactionId); systemTray_->showNotification(notifId, tr("New message"), from + ": " + interaction.body, - NotificationType::CHAT); + NotificationType::CHAT, + Utils::QImageToByteArray(contactPhoto)); #else Q_UNUSED(interactionId) @@ -215,11 +220,13 @@ ConversationsAdapter::onNewTrustRequest(const QString& accountId, const QString& if (!QApplication::focusWindow() || accountId != lrcInstance_->getCurrAccId()) { auto& accInfo = lrcInstance_->getAccountInfo(accountId); auto from = accInfo.contactModel->bestNameForContact(peerUri); + auto contactPhoto = Utils::contactPhoto(lrcInstance_, peerUri, QSize(50, 50), accountId); auto notifId = QString("%1;%2").arg(accountId).arg(peerUri); systemTray_->showNotification(notifId, tr("Trust request"), "New request from " + from, - NotificationType::REQUEST); + NotificationType::REQUEST, + Utils::QImageToByteArray(contactPhoto)); } #endif } diff --git a/src/systemtray.cpp b/src/systemtray.cpp index 33753d52..d54e0147 100644 --- a/src/systemtray.cpp +++ b/src/systemtray.cpp @@ -167,7 +167,8 @@ void SystemTray::showNotification(const QString& id, const QString& title, const QString& body, - NotificationType type) + NotificationType type, + const QByteArray& avatar) { if (!settingsManager_->getValue(Settings::Key::EnableNotifications).toBool()) return; @@ -184,7 +185,16 @@ SystemTray::showNotification(const QString& id, pimpl_->notifications.emplace(id, n); - // TODO: notify_notification_set_image_from_pixbuf <- GdkPixbuf + if (!avatar.isEmpty()) { + GError* error = nullptr; + GdkPixbuf* pixbuf = nullptr; + GInputStream* stream = nullptr; + stream = g_memory_input_stream_new_from_data(avatar.constData(), avatar.size(), NULL); + pixbuf = gdk_pixbuf_new_from_stream(stream, nullptr, &error); + g_input_stream_close(stream, nullptr, nullptr); + g_object_unref(stream); + notify_notification_set_image_from_pixbuf(notification.get(), pixbuf); + } if (type != NotificationType::CHAT) { notify_notification_set_urgency(notification.get(), NOTIFY_URGENCY_CRITICAL); diff --git a/src/systemtray.h b/src/systemtray.h index b648b53b..523bc31a 100644 --- a/src/systemtray.h +++ b/src/systemtray.h @@ -42,7 +42,8 @@ public: void showNotification(const QString& id, const QString& title, const QString& body, - NotificationType type); + NotificationType type, + const QByteArray& avatar = {}); Q_SIGNALS: void openConversationActivated(const QString& accountId, const QString& convUid); diff --git a/src/utils.cpp b/src/utils.cpp index 60947c1c..27d25315 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -68,6 +68,7 @@ Utils::CreateStartupLink(const std::wstring& wstrAppName) return Utils::CreateLink(programPath.c_str(), linkPath.c_str()); #else + Q_UNUSED(wstrAppName) QString desktopPath; /* cmake should set JAMI_INSTALL_PREFIX, otherwise it checks the following dirs * - /usr/ @@ -179,6 +180,7 @@ Utils::DeleteStartupLink(const std::wstring& wstrAppName) DeleteFile(linkPath.c_str()); #else + Q_UNUSED(wstrAppName) QString desktopFile = QStandardPaths::locate(QStandardPaths::ConfigLocation, "autostart/jami-qt.desktop"); if (!desktopFile.isEmpty()) { @@ -205,6 +207,7 @@ Utils::CheckStartupLink(const std::wstring& wstrAppName) linkPath += std::wstring(TEXT("\\") + wstrAppName + TEXT(".lnk")); return PathFileExists(linkPath.c_str()); #else + Q_UNUSED(wstrAppName) return (!QStandardPaths::locate(QStandardPaths::ConfigLocation, "autostart/jami-qt.desktop") .isEmpty()); #endif @@ -222,6 +225,7 @@ Utils::WinGetEnv(const char* name) return 0; } #else + Q_UNUSED(name) return 0; #endif } @@ -321,7 +325,10 @@ Utils::GetISODate() } QImage -Utils::contactPhoto(LRCInstance* instance, const QString& contactUri, const QSize& size) +Utils::contactPhoto(LRCInstance* instance, + const QString& contactUri, + const QSize& size, + const QString& accountId) { QImage photo; @@ -329,7 +336,8 @@ Utils::contactPhoto(LRCInstance* instance, const QString& contactUri, const QSiz /* * Get first contact photo. */ - auto& accountInfo = instance->accountModel().getAccountInfo(instance->getCurrAccId()); + auto& accountInfo = instance->accountModel().getAccountInfo( + accountId.isEmpty() ? instance->getCurrAccId() : accountId); auto contactInfo = accountInfo.contactModel->getContact(contactUri); auto contactPhoto = contactInfo.profileInfo.avatar; auto bestName = accountInfo.contactModel->bestNameForContact(contactUri); @@ -405,6 +413,7 @@ Utils::getRealSize(QScreen* screen) (DEVMODE*) &dmThisScreen); return QSize(dmThisScreen.dmPelsWidth, dmThisScreen.dmPelsHeight); #else + Q_UNUSED(screen) return {}; #endif } diff --git a/src/utils.h b/src/utils.h index b56b888b..d0340a4f 100644 --- a/src/utils.h +++ b/src/utils.h @@ -84,7 +84,8 @@ static const QSize defaultAvatarSize {128, 128}; QImage contactPhotoFromBase64(const QByteArray& data, const QString& type); QImage contactPhoto(LRCInstance* instance, const QString& contactUri, - const QSize& size = defaultAvatarSize); + const QSize& size = defaultAvatarSize, + const QString& accountId = {}); QImage getCirclePhoto(const QImage original, int sizePhoto); QColor getAvatarColor(const QString& canonicalUri); QImage fallbackAvatar(const QString& canonicalUriStr,