diff --git a/interfaces/conversationmessage.cpp b/interfaces/conversationmessage.cpp index 8cb40f8a..8fada475 100644 --- a/interfaces/conversationmessage.cpp +++ b/interfaces/conversationmessage.cpp @@ -1,133 +1,131 @@ /** * Copyright 2018 Simon Redman * * This program 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 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program 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 this program. If not, see . */ #include "conversationmessage.h" #include #include ConversationMessage::ConversationMessage(const QVariantMap& args, QObject* parent) : QObject(parent), m_body(args["body"].toString()), m_address(args["address"].toString()), m_date(args["date"].toLongLong()), m_type(args["type"].toInt()), m_read(args["read"].toInt()), m_threadID(args["thread_id"].toInt()), m_uID(args["_id"].toInt()) - { +{ } ConversationMessage::ConversationMessage (const QString& body, const QString& address, const qint64& date, const qint32& type, const qint32& read, const qint32& threadID, const qint32& uID, QObject* parent) : QObject(parent) , m_body(body) , m_address(address) , m_date(date) , m_type(type) , m_read(read) , m_threadID(threadID) , m_uID(uID) { - } ConversationMessage::ConversationMessage(const ConversationMessage& other, QObject* parent) : QObject(parent) , m_body(other.m_body) , m_address(other.m_address) , m_date(other.m_date) , m_type(other.m_type) , m_read(other.m_read) , m_threadID(other.m_threadID) , m_uID(other.m_uID) { - } ConversationMessage::~ConversationMessage() { } ConversationMessage& ConversationMessage::operator=(const ConversationMessage& other) { this->m_body = other.m_body; this->m_address = other.m_address; this->m_date = other.m_date; this->m_type = other.m_type; this->m_read = other.m_read; this->m_threadID = other.m_threadID; this->m_uID = other.m_uID; return *this; } QVariantMap ConversationMessage::toVariant() const { return { {"body", m_body}, {"address", m_address}, {"date", m_date}, {"type", m_type}, {"read", m_read}, {"thread_id", m_threadID}, {"_id", m_uID}, }; } QDBusArgument &operator<<(QDBusArgument &argument, const ConversationMessage &message) { argument.beginStructure(); argument << message.body() << message.address() << message.date() << message.type() << message.read() << message.threadID() << message.uID(); argument.endStructure(); return argument; } const QDBusArgument &operator>>(const QDBusArgument &argument, ConversationMessage &message) { QString body; QString address; qint64 date; qint32 type; qint32 read; qint32 threadID; qint32 uID; argument.beginStructure(); argument >> body; argument >> address; argument >> date; argument >> type; argument >> read; argument >> threadID; argument >> uID; argument.endStructure(); message = ConversationMessage(body, address, date, type, read, threadID, uID); return argument; } void ConversationMessage::registerDbusType() { qDBusRegisterMetaType(); qRegisterMetaType(); } diff --git a/interfaces/conversationmessage.h b/interfaces/conversationmessage.h index 70b36250..aad56608 100644 --- a/interfaces/conversationmessage.h +++ b/interfaces/conversationmessage.h @@ -1,123 +1,122 @@ /** * Copyright 2018 Simon Redman * * This program 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 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program 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 this program. If not, see . */ #ifndef PLUGINS_TELEPHONY_CONVERSATIONMESSAGE_H_ #define PLUGINS_TELEPHONY_CONVERSATIONMESSAGE_H_ #include #include #include #include #include "interfaces/kdeconnectinterfaces_export.h" class KDECONNECTINTERFACES_EXPORT ConversationMessage : public QObject { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.kdeconnect.device.telephony.messages") Q_PROPERTY(QString body READ body) Q_PROPERTY(QString address READ address) Q_PROPERTY(qint64 date READ date) Q_PROPERTY(qint32 type READ type) Q_PROPERTY(qint32 read READ read) Q_PROPERTY(qint32 threadID READ threadID) Q_PROPERTY(qint32 uID READ uID) public: // TYPE field values from Android - enum Types - { + enum Types { MessageTypeAll = 0, MessageTypeInbox = 1, MessageTypeSent = 2, MessageTypeDraft = 3, MessageTypeOutbox = 4, MessageTypeFailed = 5, MessageTypeQueued = 6, }; Q_ENUM(Types); /** * Build a new message from a keyword argument dictionary * * @param args mapping of field names to values as might be contained in a network packet containing a message */ ConversationMessage(const QVariantMap& args = QVariantMap(), QObject* parent = Q_NULLPTR); ConversationMessage(const QString& body, const QString& address, const qint64& date, const qint32& type, const qint32& read, const qint32& threadID, const qint32& uID, QObject* parent = Q_NULLPTR); ConversationMessage(const ConversationMessage& other, QObject* parent = Q_NULLPTR); ~ConversationMessage(); ConversationMessage& operator=(const ConversationMessage& other); static void registerDbusType(); QString body() const { return m_body; } QString address() const { return m_address; } qint64 date() const { return m_date; } qint32 type() const { return m_type; } qint32 read() const { return m_read; } qint32 threadID() const { return m_threadID; } qint32 uID() const { return m_uID; } QVariantMap toVariant() const; protected: /** * Body of the message */ QString m_body; /** * Remote-side address of the message. Most likely a phone number, but may be an email address */ QString m_address; /** * Date stamp (Unix epoch millis) associated with the message */ qint64 m_date; /** * Type of the message. See the message.type enum */ qint32 m_type; /** * Whether we have a read report for this message */ qint32 m_read; /** * Tag which binds individual messages into a thread */ qint32 m_threadID; /** * Value which uniquely identifies a message */ qint32 m_uID; }; Q_DECLARE_METATYPE(ConversationMessage); #endif /* PLUGINS_TELEPHONY_CONVERSATIONMESSAGE_H_ */ diff --git a/plugins/sms/conversationsdbusinterface.cpp b/plugins/sms/conversationsdbusinterface.cpp index 3ed907cf..f8937d3b 100644 --- a/plugins/sms/conversationsdbusinterface.cpp +++ b/plugins/sms/conversationsdbusinterface.cpp @@ -1,134 +1,127 @@ /** * Copyright 2018 Simon Redman * * This program 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 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program 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 this program. If not, see . */ #include "conversationsdbusinterface.h" #include "interfaces/dbusinterfaces.h" #include "interfaces/conversationmessage.h" #include #include #include Q_LOGGING_CATEGORY(KDECONNECT_CONVERSATIONS, "kdeconnect.conversations") ConversationsDbusInterface::ConversationsDbusInterface(KdeConnectPlugin* plugin) : QDBusAbstractAdaptor(const_cast(plugin->device())) , m_device(plugin->device()) , m_plugin(plugin) , m_lastId(0) , m_smsInterface(m_device->id()) { ConversationMessage::registerDbusType(); } ConversationsDbusInterface::~ConversationsDbusInterface() { } QStringList ConversationsDbusInterface::activeConversations() { return m_conversations.keys(); } void ConversationsDbusInterface::requestConversation(const QString& conversationID, int start, int end) { const auto messagesList = m_conversations[conversationID].values(); - if (messagesList.isEmpty()) - { + if (messagesList.isEmpty()) { // Since there are no messages in the conversation, it's likely that it is a junk ID, but go ahead anyway qCWarning(KDECONNECT_CONVERSATIONS) << "Got a conversationID for a conversation with no messages!" << conversationID; } m_smsInterface.requestConversation(conversationID); // Messages are sorted in ascending order of keys, meaning the front of the list has the oldest // messages (smallest timestamp number) // Therefore, return the end of the list first (most recent messages) int i = start; - for(auto it = messagesList.crbegin(); it != messagesList.crend(); ++it) - { + for(auto it = messagesList.crbegin(); it != messagesList.crend(); ++it) { Q_EMIT conversationMessageReceived(it->toVariant(), i); i++; - if (i >= end) - { + if (i >= end) { break; } } } void ConversationsDbusInterface::addMessage(const ConversationMessage &message) { const QString& threadId = QString::number(message.threadID()); - if (m_known_messages[threadId].contains(message.uID())) - { + if (m_known_messages[threadId].contains(message.uID())) { // This message has already been processed. Don't do anything. return; } // Store the Message in the list corresponding to its thread bool newConversation = !m_conversations.contains(threadId); m_conversations[threadId].insert(message.date(), message); m_known_messages[threadId].insert(message.uID()); // Tell the world about what just happened - if (newConversation) - { + if (newConversation) { Q_EMIT conversationCreated(threadId); - } else - { + } else { Q_EMIT conversationUpdated(message.toVariant()); } } void ConversationsDbusInterface::removeMessage(const QString& internalId) { // TODO: Delete the specified message from our internal structures } void ConversationsDbusInterface::replyToConversation(const QString& conversationID, const QString& message) { const auto messagesList = m_conversations[conversationID]; - if (messagesList.isEmpty()) - { + if (messagesList.isEmpty()) { // Since there are no messages in the conversation, we can't do anything sensible qCWarning(KDECONNECT_CONVERSATIONS) << "Got a conversationID for a conversation with no messages!"; return; } // Caution: // This method assumes that the address of any message (in this case, whichever one pops out // with .first()) will be the same. This works fine for single-target SMS but might break down // for group MMS, etc. const QString& address = messagesList.first().address(); m_smsInterface.sendSms(address, message); } void ConversationsDbusInterface::requestAllConversationThreads() { // Prepare the list of conversations by requesting the first in every thread m_smsInterface.requestAllConversations(); } QString ConversationsDbusInterface::newId() { return QString::number(++m_lastId); } diff --git a/plugins/sms/conversationsdbusinterface.h b/plugins/sms/conversationsdbusinterface.h index fbde4f3e..65c1bb95 100644 --- a/plugins/sms/conversationsdbusinterface.h +++ b/plugins/sms/conversationsdbusinterface.h @@ -1,103 +1,104 @@ /** * Copyright 2013 Albert Vaca + * Copyright 2018 Simon Redman * * This program 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 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program 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 this program. If not, see . */ #ifndef CONVERSATIONSDBUSINTERFACE_H #define CONVERSATIONSDBUSINTERFACE_H #include #include #include #include #include #include #include #include #include "interfaces/conversationmessage.h" #include "interfaces/dbusinterfaces.h" class KdeConnectPlugin; class Device; Q_DECLARE_LOGGING_CATEGORY(KDECONNECT_CONVERSATIONS) class ConversationsDbusInterface : public QDBusAbstractAdaptor { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.kdeconnect.device.conversations") public: explicit ConversationsDbusInterface(KdeConnectPlugin* plugin); ~ConversationsDbusInterface() override; void addMessage(const ConversationMessage &message); void removeMessage(const QString& internalId); public Q_SLOTS: /** * Return a list of the threadID for all valid conversations */ QStringList activeConversations(); void requestConversation(const QString &conversationID, int start, int end); /** * Send a new message to this conversation */ void replyToConversation(const QString& conversationID, const QString& message); /** * Send the request to the Telephony plugin to update the list of conversation threads */ void requestAllConversationThreads(); Q_SIGNALS: Q_SCRIPTABLE void conversationCreated(const QString& threadID); Q_SCRIPTABLE void conversationRemoved(const QString& threadID); Q_SCRIPTABLE void conversationUpdated(const QVariantMap& msg) const; Q_SCRIPTABLE void conversationMessageReceived(const QVariantMap& msg, int pos) const; private /*methods*/: QString newId(); //Generates successive identifitiers to use as public ids private /*attributes*/: const Device* m_device; KdeConnectPlugin* m_plugin; /** * Mapping of threadID to the messages which make up that thread * * The messages are stored as a QMap of the timestamp to the actual message object so that * we can use .values() to get a sorted list of messages from least- to most-recent */ QHash> m_conversations; /** * Mapping of threadID to the set of uIDs known in the corresponding conversation */ QHash> m_known_messages; int m_lastId; SmsDbusInterface m_smsInterface; }; #endif // CONVERSATIONSDBUSINTERFACE_H diff --git a/plugins/sms/smsplugin.cpp b/plugins/sms/smsplugin.cpp index 26a51f6e..92c67ae6 100644 --- a/plugins/sms/smsplugin.cpp +++ b/plugins/sms/smsplugin.cpp @@ -1,119 +1,119 @@ /** * Copyright 2013 Albert Vaca + * Copyright 2018 Simon Redman * * This program 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 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program 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 this program. If not, see . */ #include "smsplugin.h" #include #include #include #include #include #include #include #include "sendreplydialog.h" K_PLUGIN_FACTORY_WITH_JSON( KdeConnectPluginFactory, "kdeconnect_sms.json", registerPlugin< SmsPlugin >(); ) Q_LOGGING_CATEGORY(KDECONNECT_PLUGIN_SMS, "kdeconnect.plugin.sms") SmsPlugin::SmsPlugin(QObject* parent, const QVariantList& args) : KdeConnectPlugin(parent, args) , m_telepathyInterface(QStringLiteral("org.freedesktop.Telepathy.ConnectionManager.kdeconnect"), QStringLiteral("/kdeconnect")) , m_conversationInterface(new ConversationsDbusInterface(this)) { } SmsPlugin::~SmsPlugin() { } bool SmsPlugin::receivePacket(const NetworkPacket& np) { if (np.type() == PACKET_TYPE_SMS_MESSAGES) { return handleBatchMessages(np); } return true; } void SmsPlugin::sendSms(const QString& phoneNumber, const QString& messageBody) { NetworkPacket np(PACKET_TYPE_SMS_REQUEST, { {"sendSms", true}, {"phoneNumber", phoneNumber}, {"messageBody", messageBody} }); qDebug(KDECONNECT_PLUGIN_SMS) << "Dispatching SMS send request to remote"; sendPacket(np); } void SmsPlugin::requestAllConversations() { NetworkPacket np(PACKET_TYPE_SMS_REQUEST_CONVERSATIONS); sendPacket(np); } void SmsPlugin::requestConversation (const QString& conversationID) const { NetworkPacket np(PACKET_TYPE_SMS_REQUEST_CONVERSATION); np.set("threadID", conversationID.toInt()); sendPacket(np); } void SmsPlugin::forwardToTelepathy(const ConversationMessage& message) { // If we don't have a valid Telepathy interface, bail out if (!(m_telepathyInterface.isValid())) return; qCDebug(KDECONNECT_PLUGIN_SMS) << "Passing a text message to the telepathy interface"; connect(&m_telepathyInterface, SIGNAL(messageReceived(QString,QString)), SLOT(sendSms(QString,QString)), Qt::UniqueConnection); const QString messageBody = message.body(); const QString contactName; // TODO: When telepathy support is improved, look up the contact with KPeople const QString phoneNumber = message.address(); m_telepathyInterface.call(QDBus::NoBlock, QStringLiteral("sendMessage"), phoneNumber, contactName, messageBody); } bool SmsPlugin::handleBatchMessages(const NetworkPacket& np) { const auto messages = np.get("messages"); - for (const QVariant& body : messages) - { + for (const QVariant& body : messages) { ConversationMessage message(body.toMap()); forwardToTelepathy(message); m_conversationInterface->addMessage(message); } return true; } QString SmsPlugin::dbusPath() const { return "/modules/kdeconnect/devices/" + device()->id() + "/sms"; } #include "smsplugin.moc" diff --git a/plugins/sms/smsplugin.h b/plugins/sms/smsplugin.h index 61ed1f79..276b5feb 100644 --- a/plugins/sms/smsplugin.h +++ b/plugins/sms/smsplugin.h @@ -1,132 +1,132 @@ /** * Copyright 2013 Albert Vaca + * Copyright 2018 Simon Redman * * This program 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 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program 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 this program. If not, see . */ #ifndef SMSPLUGIN_H #define SMSPLUGIN_H #include #include #include "conversationsdbusinterface.h" #include "interfaces/conversationmessage.h" #include "sendreplydialog.h" /** * Packet used to indicate a batch of messages has been pushed from the remote device * * The body should contain the key "messages" mapping to an array of messages * * For example: * { "messages" : [ * { "event" : "sms", * "messageBody" : "Hello", * "phoneNumber" : "2021234567", * "messageDate" : "1518846484880", * "messageType" : "2", * "threadID" : "132" * }, * { ... }, * ... * ] * } */ #define PACKET_TYPE_SMS_MESSAGES QStringLiteral("kdeconnect.sms.messages") /** * Packet sent to request a message be sent * * This will almost certainly need to be replaced or augmented to support MMS, * but be sure the Android side remains compatible with old desktop apps! * * The body should look like so: * { "sendSms": true, * "phoneNumber": "542904563213", * "messageBody": "Hi mom!" * } */ #define PACKET_TYPE_SMS_REQUEST QStringLiteral("kdeconnect.sms.request") /** * Packet sent to request the most-recent message in each conversations on the device * * The request packet shall contain no body */ #define PACKET_TYPE_SMS_REQUEST_CONVERSATIONS QStringLiteral("kdeconnect.sms.request_conversations") /** * Packet sent to request all the messages in a particular conversation * * The body should contain the key "threadID" mapping to the threadID being requested * For example: * { "threadID": 203 } */ #define PACKET_TYPE_SMS_REQUEST_CONVERSATION QStringLiteral("kdeconnect.sms.request_conversation") Q_DECLARE_LOGGING_CATEGORY(KDECONNECT_PLUGIN_SMS) class Q_DECL_EXPORT SmsPlugin : public KdeConnectPlugin { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.kdeconnect.device.sms") public: explicit SmsPlugin(QObject* parent, const QVariantList& args); ~SmsPlugin() override; bool receivePacket(const NetworkPacket& np) override; void connected() override {} QString dbusPath() const override; public Q_SLOTS: Q_SCRIPTABLE void sendSms(const QString& phoneNumber, const QString& messageBody); /** * Send a request to the remote for all of its conversations */ Q_SCRIPTABLE void requestAllConversations(); /** * Send a request to the remote for a particular conversation */ Q_SCRIPTABLE void requestConversation(const QString& conversationID) const; private: /** * Send to the telepathy plugin if it is available */ void forwardToTelepathy(const ConversationMessage& message); /** * Handle a packet which contains many messages, such as PACKET_TYPE_TELEPHONY_MESSAGE */ bool handleBatchMessages(const NetworkPacket& np); - QDBusInterface m_telepathyInterface; ConversationsDbusInterface* m_conversationInterface; }; #endif diff --git a/plugins/telephony/telephonyplugin.cpp b/plugins/telephony/telephonyplugin.cpp index 79dddd20..fae9468e 100644 --- a/plugins/telephony/telephonyplugin.cpp +++ b/plugins/telephony/telephonyplugin.cpp @@ -1,135 +1,133 @@ /** * Copyright 2013 Albert Vaca * Copyright 2018 Simon Redman * * This program 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 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program 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 this program. If not, see . */ #include "telephonyplugin.h" #include #include #include #include #include K_PLUGIN_FACTORY_WITH_JSON( KdeConnectPluginFactory, "kdeconnect_telephony.json", registerPlugin< TelephonyPlugin >(); ) Q_LOGGING_CATEGORY(KDECONNECT_PLUGIN_TELEPHONY, "kdeconnect.plugin.telephony") TelephonyPlugin::TelephonyPlugin(QObject* parent, const QVariantList& args) : KdeConnectPlugin(parent, args) { } TelephonyPlugin::~TelephonyPlugin() { } KNotification* TelephonyPlugin::createNotification(const NetworkPacket& np) { const QString event = np.get(QStringLiteral("event")); const QString phoneNumber = np.get(QStringLiteral("phoneNumber"), i18n("unknown number")); const QString contactName = np.get(QStringLiteral("contactName"), phoneNumber); const QByteArray phoneThumbnail = QByteArray::fromBase64(np.get(QStringLiteral("phoneThumbnail"), "")); const QString messageBody = np.get(QStringLiteral("messageBody"),{}); QString content, type, icon; KNotification::NotificationFlags flags = KNotification::CloseOnTimeout; const QString title = device()->name(); if (event == QLatin1String("ringing")) { type = QStringLiteral("callReceived"); icon = QStringLiteral("call-start"); content = i18n("Incoming call from %1", contactName); } else if (event == QLatin1String("missedCall")) { type = QStringLiteral("missedCall"); icon = QStringLiteral("call-start"); content = i18n("Missed call from %1", contactName); flags |= KNotification::Persistent; //Note that in Unity this generates a message box! } else if (event == QLatin1String("talking")) { return nullptr; } else { #ifndef NDEBUG return nullptr; #else type = QStringLiteral("callReceived"); icon = QStringLiteral("phone"); content = i18n("Unknown telephony event: %1", event); #endif } qCDebug(KDECONNECT_PLUGIN_TELEPHONY) << "Creating notification with type:" << type; KNotification* notification = new KNotification(type, flags, this); if (!phoneThumbnail.isEmpty()) { QPixmap photo; photo.loadFromData(phoneThumbnail, "JPEG"); notification->setPixmap(photo); } else { notification->setIconName(icon); } notification->setComponentName(QStringLiteral("kdeconnect")); notification->setTitle(title); notification->setText(content); if (event == QLatin1String("ringing")) { notification->setActions( QStringList(i18n("Mute Call")) ); connect(notification, &KNotification::action1Activated, this, &TelephonyPlugin::sendMutePacket); } return notification; } bool TelephonyPlugin::receivePacket(const NetworkPacket& np) { if (np.get(QStringLiteral("isCancel"))) { //TODO: Clear the old notification return true; } const QString& event = np.get(QStringLiteral("event"), QStringLiteral("unknown")); // Handle old-style packets - if (np.type() == PACKET_TYPE_TELEPHONY) - { - if (event == QLatin1String("sms")) - { + if (np.type() == PACKET_TYPE_TELEPHONY) { + if (event == QLatin1String("sms")) { return false; } KNotification* n = createNotification(np); if (n != nullptr) n->sendEvent(); return true; } return true; } void TelephonyPlugin::sendMutePacket() { NetworkPacket packet(PACKET_TYPE_TELEPHONY_REQUEST_MUTE, {{"action", "mute"}}); sendPacket(packet); } QString TelephonyPlugin::dbusPath() const { return "/modules/kdeconnect/devices/" + device()->id() + "/telephony"; } #include "telephonyplugin.moc" diff --git a/smsapp/conversationlistmodel.cpp b/smsapp/conversationlistmodel.cpp index ea8c9ebe..1bbbcc54 100644 --- a/smsapp/conversationlistmodel.cpp +++ b/smsapp/conversationlistmodel.cpp @@ -1,174 +1,169 @@ /* * This file is part of KDE Telepathy Chat * * Copyright (C) 2018 Aleix Pol Gonzalez + * Copyright (C) 2018 Simon Redman * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "conversationlistmodel.h" #include #include "interfaces/conversationmessage.h" Q_LOGGING_CATEGORY(KDECONNECT_SMS_CONVERSATIONS_LIST_MODEL, "kdeconnect.sms.conversations_list") ConversationListModel::ConversationListModel(QObject* parent) : QStandardItemModel(parent) , m_conversationsInterface(nullptr) { qCCritical(KDECONNECT_SMS_CONVERSATIONS_LIST_MODEL) << "Constructing" << this; auto roles = roleNames(); roles.insert(FromMeRole, "fromMe"); roles.insert(AddressRole, "address"); roles.insert(PersonUriRole, "personUri"); roles.insert(ConversationIdRole, "conversationId"); roles.insert(DateRole, "date"); setItemRoleNames(roles); ConversationMessage::registerDbusType(); } ConversationListModel::~ConversationListModel() { } void ConversationListModel::setDeviceId(const QString& deviceId) { qCCritical(KDECONNECT_SMS_CONVERSATIONS_LIST_MODEL) << "setDeviceId" << deviceId << "of" << this; if (deviceId == m_deviceId) return; if (m_conversationsInterface) { disconnect(m_conversationsInterface, SIGNAL(conversationCreated(QString)), this, SLOT(handleCreatedConversation(QString))); delete m_conversationsInterface; } m_deviceId = deviceId; m_conversationsInterface = new DeviceConversationsDbusInterface(deviceId, this); connect(m_conversationsInterface, SIGNAL(conversationCreated(QString)), this, SLOT(handleCreatedConversation(QString))); connect(m_conversationsInterface, SIGNAL(conversationMessageReceived(QVariantMap, int)), this, SLOT(createRowFromMessage(QVariantMap, int))); prepareConversationsList(); m_conversationsInterface->requestAllConversationThreads(); } void ConversationListModel::prepareConversationsList() { QDBusPendingReply validThreadIDsReply = m_conversationsInterface->activeConversations(); setWhenAvailable(validThreadIDsReply, [this](const QStringList& convs) { clear(); for (const QString& conversationId : convs) { handleCreatedConversation(conversationId); } }, this); } void ConversationListModel::handleCreatedConversation(const QString& conversationId) { m_conversationsInterface->requestConversation(conversationId, 0, 1); } void ConversationListModel::printDBusError(const QDBusError& error) { qCWarning(KDECONNECT_SMS_CONVERSATIONS_LIST_MODEL) << error; } QStandardItem * ConversationListModel::conversationForThreadId(qint32 threadId) { for(int i=0, c=rowCount(); idata(ConversationIdRole) == threadId) return it; } return nullptr; } void ConversationListModel::createRowFromMessage(const QVariantMap& msg, int row) { if (row != 0) return; const ConversationMessage message(msg); - if (message.type() == -1) - { + if (message.type() == -1) { // The Android side currently hacks in -1 if something weird comes up // TODO: Remove this hack when MMS support is implemented return; } bool toadd = false; QStandardItem* item = conversationForThreadId(message.threadID()); if (!item) { toadd = true; item = new QStandardItem(); QScopedPointer personData(lookupPersonByAddress(message.address())); - if (personData) - { + if (personData) { item->setText(personData->name()); item->setIcon(QIcon(personData->photo())); item->setData(personData->personUri(), PersonUriRole); - } - else - { + } else { item->setData(QString(), PersonUriRole); item->setText(message.address()); } item->setData(message.threadID(), ConversationIdRole); } item->setData(message.address(), AddressRole); item->setData(message.type() == ConversationMessage::MessageTypeSent, FromMeRole); item->setData(message.body(), Qt::ToolTipRole); item->setData(message.date(), DateRole); if (toadd) appendRow(item); } KPeople::PersonData* ConversationListModel::lookupPersonByAddress(const QString& address) { int rowIndex = 0; - for (rowIndex = 0; rowIndex < m_people.rowCount(); rowIndex++) - { + for (rowIndex = 0; rowIndex < m_people.rowCount(); rowIndex++) { const QString& uri = m_people.get(rowIndex, KPeople::PersonsModel::PersonUriRole).toString(); KPeople::PersonData* person = new KPeople::PersonData(uri); const QString& email = person->email(); const QString& phoneNumber = canonicalizePhoneNumber(person->contactCustomProperty("phoneNumber").toString()); - if (address == email || canonicalizePhoneNumber(address) == phoneNumber) - { + if (address == email || canonicalizePhoneNumber(address) == phoneNumber) { qCDebug(KDECONNECT_SMS_CONVERSATIONS_LIST_MODEL) << "Matched" << address << "to" << person->name(); return person; } delete person; } return nullptr; } QString ConversationListModel::canonicalizePhoneNumber(const QString& phoneNumber) { QString toReturn(phoneNumber); toReturn = toReturn.remove(' '); toReturn = toReturn.remove('-'); toReturn = toReturn.remove('('); toReturn = toReturn.remove(')'); toReturn = toReturn.remove('+'); return toReturn; } diff --git a/smsapp/conversationlistmodel.h b/smsapp/conversationlistmodel.h index e75fd169..21bceede 100644 --- a/smsapp/conversationlistmodel.h +++ b/smsapp/conversationlistmodel.h @@ -1,116 +1,117 @@ /* * This file is part of KDE Telepathy Chat * * Copyright (C) 2018 Aleix Pol Gonzalez + * Copyright (C) 2018 Simon Redman * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef CONVERSATIONLISTMODEL_H #define CONVERSATIONLISTMODEL_H #include #include #include #include #include #include "interfaces/conversationmessage.h" #include "interfaces/dbusinterfaces.h" Q_DECLARE_LOGGING_CATEGORY(KDECONNECT_SMS_CONVERSATIONS_LIST_MODEL) class OurSortFilterProxyModel : public QSortFilterProxyModel, public QQmlParserStatus { Q_OBJECT Q_INTERFACES(QQmlParserStatus) Q_PROPERTY(Qt::SortOrder sortOrder READ sortOrder WRITE setSortOrder) public: Qt::SortOrder sortOrder() const { return m_sortOrder; } void setSortOrder(Qt::SortOrder sortOrder) { if (m_sortOrder != sortOrder) { m_sortOrder = sortOrder; sortNow(); } } void classBegin() override {} void componentComplete() override { m_completed = true; sortNow(); } private: void sortNow() { if (m_completed && dynamicSortFilter()) sort(0, m_sortOrder); } bool m_completed = false; Qt::SortOrder m_sortOrder = Qt::AscendingOrder; }; class ConversationListModel : public QStandardItemModel { Q_OBJECT Q_PROPERTY(QString deviceId READ deviceId WRITE setDeviceId) public: ConversationListModel(QObject* parent = nullptr); ~ConversationListModel(); enum Roles { FromMeRole = Qt::UserRole, PersonUriRole, AddressRole, ConversationIdRole, DateRole, }; Q_ENUM(Roles) QString deviceId() const { return m_deviceId; } void setDeviceId(const QString &/*deviceId*/); public Q_SLOTS: void handleCreatedConversation(const QString& conversationId); void createRowFromMessage(const QVariantMap& message, int row); void printDBusError(const QDBusError& error); private: /** * Get all conversations currently known by the conversationsInterface, if any */ void prepareConversationsList(); /** * Get the data for a particular person given their contact address */ KPeople::PersonData* lookupPersonByAddress(const QString& address); /** * Simplify a phone number to a known form */ QString canonicalizePhoneNumber(const QString& phoneNumber); QStandardItem* conversationForThreadId(qint32 threadId); DeviceConversationsDbusInterface* m_conversationsInterface; QString m_deviceId; KPeople::PersonsModel m_people; }; #endif // CONVERSATIONLISTMODEL_H diff --git a/smsapp/conversationmodel.h b/smsapp/conversationmodel.h index 0de265c4..80805c30 100644 --- a/smsapp/conversationmodel.h +++ b/smsapp/conversationmodel.h @@ -1,67 +1,68 @@ /* * This file is part of KDE Telepathy Chat * * Copyright (C) 2018 Aleix Pol Gonzalez + * Copyright (C) 2018 Simon Redman * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef CONVERSATIONMODEL_H #define CONVERSATIONMODEL_H #include #include #include "interfaces/dbusinterfaces.h" Q_DECLARE_LOGGING_CATEGORY(KDECONNECT_SMS_CONVERSATION_MODEL) class ConversationModel : public QStandardItemModel { Q_OBJECT Q_PROPERTY(QString threadId READ threadId WRITE setThreadId) Q_PROPERTY(QString deviceId READ deviceId WRITE setDeviceId) public: ConversationModel(QObject* parent = nullptr); ~ConversationModel(); enum Roles { FromMeRole = Qt::UserRole, DateRole, }; Q_ENUMS(Roles) QString threadId() const; void setThreadId(const QString &threadId); QString deviceId() const { return m_deviceId; } void setDeviceId(const QString &/*deviceId*/); Q_INVOKABLE void sendReplyToConversation(const QString& message); private Q_SLOTS: void createRowFromMessage(const QVariantMap &msg, int pos); void handleConversationUpdate(const QVariantMap &msg); private: DeviceConversationsDbusInterface* m_conversationsInterface; QString m_deviceId; QString m_threadId; }; #endif // CONVERSATIONMODEL_H diff --git a/smsapp/main.cpp b/smsapp/main.cpp index 4ed669be..00a7cc0d 100644 --- a/smsapp/main.cpp +++ b/smsapp/main.cpp @@ -1,62 +1,65 @@ /* * This file is part of KDE Telepathy Chat * - * Copyright (C) 2015 Aleix Pol Gonzalez + * Copyright (C) 2018 Aleix Pol Gonzalez + * Copyright (C) 2018 Simon Redman * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "conversationmodel.h" #include "conversationlistmodel.h" #include "kdeconnect-version.h" #include #include #include #include #include #include #include #include #include int main(int argc, char *argv[]) { QApplication app(argc, argv); KAboutData aboutData("org.kde.kdeconnect.sms", i18n("SMS Instant Messaging"), QStringLiteral(KDECONNECT_VERSION_STRING), i18n("KDE Connect SMS"), KAboutLicense::GPL, i18n("(c) 2018, Aleix Pol Gonzalez")); aboutData.addAuthor(i18n("Aleix Pol Gonzalez"), {}, "aleixpol@kde.org"); + aboutData.addAuthor(i18n("Nicolas Fella"), {}, "nicolas.fella@gmx.de"); + aboutData.addAuthor(i18n("Simon Redman"), {}, "simon@ergotech.com"); KAboutData::setApplicationData(aboutData); { QCommandLineParser parser; aboutData.setupCommandLine(&parser); parser.addVersionOption(); parser.addHelpOption(); parser.process(app); aboutData.processCommandLine(&parser); } KDBusService service(KDBusService::Unique); qmlRegisterType("org.kde.kdeconnect.sms", 1, 0, "QSortFilterProxyModel"); qmlRegisterType("org.kde.kdeconnect.sms", 1, 0, "ConversationModel"); qmlRegisterType("org.kde.kdeconnect.sms", 1, 0, "ConversationListModel"); QQmlApplicationEngine engine; engine.rootContext()->setContextObject(new KLocalizedContext(&engine)); engine.load(QUrl("qrc:/qml/main.qml")); return app.exec(); }