/* Copyright (C) 2020 Sebastian J. Wolf This file is part of Fernschreiber. Fernschreiber is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fernschreiber is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Fernschreiber. If not, see . */ #include "notificationmanager.h" #include #include #include #include #include #include #include #include NotificationManager::NotificationManager(TDLibWrapper *tdLibWrapper, QObject *parent) : QObject(parent) { qDebug() << "[NotificationManager] Initializing..."; this->tdLibWrapper = tdLibWrapper; this->ngfClient = new Ngf::Client(this); connect(this->tdLibWrapper, SIGNAL(activeNotificationsUpdated(QVariantList)), this, SLOT(handleUpdateActiveNotifications(QVariantList))); connect(this->tdLibWrapper, SIGNAL(notificationGroupUpdated(QVariantMap)), this, SLOT(handleUpdateNotificationGroup(QVariantMap))); connect(this->tdLibWrapper, SIGNAL(notificationUpdated(QVariantMap)), this, SLOT(handleUpdateNotification(QVariantMap))); connect(this->tdLibWrapper, SIGNAL(newChatDiscovered(QString, QVariantMap)), this, SLOT(handleChatDiscovered(QString, QVariantMap))); connect(this->ngfClient, SIGNAL(connectionStatus(bool)), this, SLOT(handleNgfConnectionStatus(bool))); connect(this->ngfClient, SIGNAL(eventCompleted(quint32)), this, SLOT(handleNgfEventCompleted(quint32))); connect(this->ngfClient, SIGNAL(eventFailed(quint32)), this, SLOT(handleNgfEventFailed(quint32))); connect(this->ngfClient, SIGNAL(eventPlaying(quint32)), this, SLOT(handleNgfEventPlaying(quint32))); if (this->ngfClient->connect()) { qDebug() << "[NotificationManager] NGF Client successfully initialized..."; } else { qDebug() << "[NotificationManager] Failed to initialize NGF Client..."; } this->controlLedNotification(false); } NotificationManager::~NotificationManager() { qDebug() << "[NotificationManager] Destroying myself..."; } void NotificationManager::handleUpdateActiveNotifications(const QVariantList notificationGroups) { qDebug() << "[NotificationManager] Received active notifications, number of groups:" << notificationGroups.size(); } void NotificationManager::handleUpdateNotificationGroup(const QVariantMap notificationGroupUpdate) { QString notificationGroupId = notificationGroupUpdate.value("notification_group_id").toString(); qDebug() << "[NotificationManager] Received notification group update, group ID:" << notificationGroupId; QVariantMap notificationGroup = this->notificationGroups.value(notificationGroupId).toMap(); QString chatId = notificationGroupUpdate.value("chat_id").toString(); notificationGroup.insert("type", notificationGroupUpdate.value("type")); notificationGroup.insert("chat_id", chatId); notificationGroup.insert("notification_group_id", notificationGroupId); notificationGroup.insert("notification_settings_chat_id", notificationGroupUpdate.value("notification_settings_chat_id")); notificationGroup.insert("is_silent", notificationGroupUpdate.value("is_silent")); notificationGroup.insert("total_count", notificationGroupUpdate.value("total_count")); QVariantMap activeNotifications = notificationGroup.value("notifications").toMap(); QVariantList removedNotificationIds = notificationGroupUpdate.value("removed_notification_ids").toList(); QListIterator removedNotificationsIterator(removedNotificationIds); while (removedNotificationsIterator.hasNext()) { QString removedNotificationId = removedNotificationsIterator.next().toString(); QVariantMap notificationInformation = activeNotifications.value(removedNotificationId).toMap(); if (!notificationInformation.isEmpty()) { this->removeNotification(notificationInformation); activeNotifications.remove(removedNotificationId); } } // If we have deleted notifications, we need to update possibly existing ones if (!removedNotificationIds.isEmpty() && !activeNotifications.isEmpty()) { qDebug() << "[NotificationManager] Some removals happend, but we have " << activeNotifications.size() << "existing notifications."; QVariantMap firstActiveNotification = activeNotifications.first().toMap(); activeNotifications.remove(firstActiveNotification.value("id").toString()); QVariantMap newFirstActiveNotification = this->sendNotification(chatId, firstActiveNotification, activeNotifications); QVariantMap newActiveNotifications; QListIterator activeNotificationsIterator(activeNotifications.values()); while (activeNotificationsIterator.hasNext()) { QVariantMap newActiveNotification = activeNotificationsIterator.next().toMap(); newActiveNotification.insert("replaces_id", newFirstActiveNotification.value("replaces_id")); newActiveNotifications.insert(newActiveNotification.value("id").toString(), newActiveNotification); } newActiveNotifications.insert(newFirstActiveNotification.value("id").toString(), newFirstActiveNotification); activeNotifications = newActiveNotifications; } if (activeNotifications.isEmpty()) { this->controlLedNotification(false); } QVariantList addedNotifications = notificationGroupUpdate.value("added_notifications").toList(); QListIterator addedNotificationIterator(addedNotifications); while (addedNotificationIterator.hasNext()) { QVariantMap addedNotification = addedNotificationIterator.next().toMap(); QVariantMap activeNotification = this->sendNotification(chatId, addedNotification, activeNotifications); activeNotifications.insert(activeNotification.value("id").toString(), activeNotification); } notificationGroup.insert("notifications", activeNotifications); this->notificationGroups.insert(notificationGroupId, notificationGroup); } void NotificationManager::handleUpdateNotification(const QVariantMap updatedNotification) { qDebug() << "[NotificationManager] Received notification update, group ID:" << updatedNotification.value("notification_group_id").toInt(); } void NotificationManager::handleChatDiscovered(const QString &chatId, const QVariantMap &chatInformation) { this->chatListMutex.lock(); qDebug() << "[NotificationManager] Adding chat to internal map " << chatId; this->chatMap.insert(chatId, chatInformation); this->chatListMutex.unlock(); } void NotificationManager::handleNgfConnectionStatus(const bool &connected) { qDebug() << "[NotificationManager] NGF Daemon connection status changed " << connected; } void NotificationManager::handleNgfEventFailed(const quint32 &eventId) { qDebug() << "[NotificationManager] NGF event failed, id: " << eventId; } void NotificationManager::handleNgfEventCompleted(const quint32 &eventId) { qDebug() << "[NotificationManager] NGF event completed, id: " << eventId; } void NotificationManager::handleNgfEventPlaying(const quint32 &eventId) { qDebug() << "[NotificationManager] NGF event playing, id: " << eventId; } void NotificationManager::handleNgfEventPaused(const quint32 &eventId) { qDebug() << "[NotificationManager] NGF event paused, id: " << eventId; } QVariantMap NotificationManager::sendNotification(const QString &chatId, const QVariantMap ¬ificationInformation, const QVariantMap &activeNotifications) { qDebug() << "[NotificationManager] Sending notification" << notificationInformation.value("id").toString(); QVariantMap chatInformation = this->chatMap.value(chatId).toMap(); QString chatType = chatInformation.value("type").toMap().value("@type").toString(); bool addAuthor = false; if (chatType == "chatTypeBasicGroup" || ( chatType == "chatTypeSupergroup" && !chatInformation.value("@type").toMap().value("is_channel").toBool() )) { addAuthor = true; } QVariantMap updatedNotificationInformation = notificationInformation; QUrl appIconUrl = SailfishApp::pathTo("images/fernschreiber-notification.png"); QVariantMap messageMap = notificationInformation.value("type").toMap().value("message").toMap(); Notification nemoNotification; nemoNotification.setAppName("Fernschreiber"); nemoNotification.setAppIcon(appIconUrl.toLocalFile()); nemoNotification.setSummary(chatInformation.value("title").toString()); nemoNotification.setCategory("x-nemo.messaging.im"); nemoNotification.setTimestamp(QDateTime::fromMSecsSinceEpoch(messageMap.value("date").toLongLong() * 1000)); QVariantList remoteActionArguments; remoteActionArguments.append(chatId); remoteActionArguments.append(messageMap.value("id").toString()); nemoNotification.setRemoteAction(Notification::remoteAction("default", "openMessage", "de.ygriega.fernschreiber", "/de/ygriega/fernschreiber", "de.ygriega.fernschreiber", "openMessage", remoteActionArguments)); if (activeNotifications.isEmpty()) { QString notificationBody; if (addAuthor) { QVariantMap authorInformation = tdLibWrapper->getUserInformation(messageMap.value("sender_user_id").toString()); QString firstName = authorInformation.value("first_name").toString(); QString lastName = authorInformation.value("last_name").toString(); QString fullName = firstName + " " + lastName; notificationBody = notificationBody + fullName.trimmed() + ": "; } notificationBody = notificationBody + this->getNotificationText(messageMap.value("content").toMap()); nemoNotification.setBody(notificationBody); } else { nemoNotification.setReplacesId(activeNotifications.first().toMap().value("replaces_id").toUInt()); nemoNotification.setBody(tr("%1 unread messages").arg(activeNotifications.size() + 1)); } nemoNotification.publish(); this->ngfClient->play("chat"); this->controlLedNotification(true); updatedNotificationInformation.insert("replaces_id", nemoNotification.replacesId()); return updatedNotificationInformation; } void NotificationManager::removeNotification(const QVariantMap ¬ificationInformation) { qDebug() << "[NotificationManager] Removing notification" << notificationInformation.value("id").toString(); Notification nemoNotification; nemoNotification.setReplacesId(notificationInformation.value("replaces_id").toUInt()); nemoNotification.close(); } QString NotificationManager::getNotificationText(const QVariantMap ¬ificationContent) { qDebug() << "[NotificationManager] Getting notification text from content" << notificationContent; QString contentType = notificationContent.value("@type").toString(); if (contentType == "messageText") { return notificationContent.value("text").toMap().value("text").toString(); } if (contentType == "messagePhoto") { return tr("sent a picture"); } if (contentType == "messageVideo") { return tr("sent a video"); } if (contentType == "messageAnimation") { return tr("sent an animation"); } if (contentType == "messageVoiceNote") { return tr("sent a voice note"); } if (contentType == "messageDocument") { return tr("sent a document"); } if (contentType == "messageLocation") { return tr("sent a location"); } if (contentType == "messageContactRegistered") { return tr("has registered with Telegram"); } if (contentType == "messageChatJoinByLink") { return tr("joined this chat"); } if (contentType == "messageChatAddMembers") { return tr("was added to this chat"); } if (contentType == "messageChatDeleteMember") { return tr("left this chat"); } return tr("Unsupported message: %1").arg(contentType.mid(7)); } void NotificationManager::controlLedNotification(const bool &enabled) { qDebug() << "[NotificationManager] Controlling notification LED" << enabled; QDBusConnection dbusConnection = QDBusConnection::connectToBus(QDBusConnection::SystemBus, "system"); QDBusInterface dbusInterface("com.nokia.mce", "/com/nokia/mce/request", "com.nokia.mce.request", dbusConnection); if (enabled) { dbusInterface.call("req_led_pattern_activate", "PatternCommunicationIM"); } else { dbusInterface.call("req_led_pattern_deactivate", "PatternCommunicationIM"); } }