File indexing completed on 2024-12-01 04:33:08

0001 /**
0002  * SPDX-FileCopyrightText: 2013 Albert Vaca <albertvaka@gmail.com>
0003  * SPDX-FileCopyrightText: 2018 Simon Redman <simon@ergotech.com>
0004  *
0005  * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0006  */
0007 
0008 #pragma once
0009 
0010 #include <QDBusAbstractAdaptor>
0011 #include <QDir>
0012 #include <QHash>
0013 #include <QList>
0014 #include <QMap>
0015 #include <QPointer>
0016 #include <QString>
0017 #include <QStringList>
0018 
0019 #include "interfaces/conversationmessage.h"
0020 #include "interfaces/dbusinterfaces.h"
0021 
0022 class KdeConnectPlugin;
0023 class Device;
0024 
0025 Q_DECLARE_LOGGING_CATEGORY(KDECONNECT_CONVERSATIONS)
0026 
0027 // There is some amount of overhead and delay to making a request, so make sure to request at least a few
0028 #define MIN_NUMBER_TO_REQUEST 25
0029 // Some low-water mark after which we want to fill the cache
0030 #define CACHE_LOW_WATER_MARK_PERCENT 10
0031 
0032 class ConversationsDbusInterface : public QDBusAbstractAdaptor
0033 {
0034     Q_OBJECT
0035     Q_CLASSINFO("D-Bus Interface", "org.kde.kdeconnect.device.conversations")
0036 
0037 public:
0038     explicit ConversationsDbusInterface(KdeConnectPlugin *plugin);
0039     ~ConversationsDbusInterface() override;
0040 
0041     void addMessages(const QList<ConversationMessage> &messages);
0042     void removeMessage(const QString &internalId);
0043 
0044     /**
0045      * Return a shallow copy of the requested conversation
0046      */
0047     QList<ConversationMessage> getConversation(const qint64 &conversationID) const;
0048 
0049     /**
0050      * Get some new messages for the requested conversation from the remote device
0051      * Requests a quantity of new messages equal to the current number of messages in the conversation
0052      */
0053     void updateConversation(const qint64 &conversationID);
0054 
0055     /**
0056      * Gets the path of the succesfully downloaded attachment file and send
0057      * update to the conversationModel
0058      */
0059     void attachmentDownloaded(const QString &filePath, const QString &fileName);
0060 
0061 public Q_SLOTS:
0062     /**
0063      * Return a list of the first message in every conversation
0064      *
0065      * Note that the return value is a list of QVariants, which in turn have a value of
0066      * QVariantMap created from each message
0067      */
0068     QVariantList activeConversations();
0069 
0070     /**
0071      * Request the specified range of the specified conversation
0072      *
0073      * Emits conversationUpdated for every message in the requested range
0074      *
0075      * If the conversation does not have enough messages to fill the request,
0076      * this method may return fewer messages
0077      */
0078     void requestConversation(const qint64 &conversationID, int start, int end);
0079 
0080     /**
0081      * Send a new message to this conversation
0082      */
0083     void replyToConversation(const qint64 &conversationID, const QString &message, const QVariantList &attachmentUrls);
0084 
0085     /**
0086      * Send a new message to the contact having no previous coversation with
0087      */
0088     void sendWithoutConversation(const QVariantList &addressList, const QString &message, const QVariantList &attachmentUrls);
0089 
0090     /**
0091      * Send the request to the Telephony plugin to update the list of conversation threads
0092      */
0093     void requestAllConversationThreads();
0094 
0095     /**
0096      * Send the request to SMS plugin to fetch original attachment file path
0097      */
0098     void requestAttachmentFile(const qint64 &partID, const QString &uniqueIdentifier);
0099 
0100 Q_SIGNALS:
0101 
0102     /**
0103      * This signal is never emitted, but if it's not here then qdbuscpp2xml in Qt6
0104      * will not generate all the signals that use QDBusVariant in the output XML
0105      */
0106     Q_SCRIPTABLE void veryHackyWorkaround(const QVariant &);
0107 
0108     /**
0109      * Emitted whenever a conversation with no cached messages is added, either because the cache
0110      * is being populated or because a new conversation has been created
0111      */
0112     Q_SCRIPTABLE void conversationCreated(const QDBusVariant &msg);
0113 
0114     /**
0115      * Emitted whenever a conversation is being deleted
0116      */
0117     Q_SCRIPTABLE void conversationRemoved(const qint64 &conversationID);
0118 
0119     /**
0120      * Emitted whenever a message is added to a conversation and it is the newest message in the
0121      * conversation
0122      */
0123     Q_SCRIPTABLE void conversationUpdated(const QDBusVariant &msg);
0124 
0125     /**
0126      * Emitted whenever we have handled a response from the phone indicating the total number of
0127      * (locally-known) messages in the given conversation
0128      */
0129     Q_SCRIPTABLE void conversationLoaded(qint64 conversationID, quint64 messageCount);
0130 
0131     /**
0132      * Emitted whenever we have succesfully download a requested attachment file from the phone
0133      */
0134     Q_SCRIPTABLE void attachmentReceived(QString filePath, QString fileName);
0135 
0136 private /*methods*/:
0137     QString newId(); // Generates successive identifiers to use as public ids
0138 
0139 private /*attributes*/:
0140     const QString m_device;
0141 
0142     /**
0143      * Mapping of threadID to the messages which make up that thread
0144      *
0145      * The messages are stored as a QMap of the timestamp to the actual message object so that
0146      * we can use .values() to get a sorted list of messages from least- to most-recent
0147      */
0148     QHash<qint64, QMap<qint64, ConversationMessage>> m_conversations;
0149 
0150     /**
0151      * Mapping of threadID to the set of uIDs known in the corresponding conversation
0152      */
0153     QHash<qint64, QSet<qint32>> m_known_messages;
0154 
0155     /*
0156      * Keep a map of all interfaces ever constructed
0157      * Because of how Qt's Dbus is designed, we are unable to immediately delete the interface once
0158      * the device has disconnected. We save the list of existing interfaces and delete them only after
0159      * we have replaced them (in ConversationsDbusInterface's constructor)
0160      * See the comment in ~NotificationsPlugin() for more information
0161      */
0162     static QMap<QString, ConversationsDbusInterface *> liveConversationInterfaces;
0163 
0164     int m_lastId;
0165 
0166     SmsDbusInterface m_smsInterface;
0167 
0168     QSet<qint64> conversationsWaitingForMessages;
0169     QMutex waitingForMessagesLock;
0170     QWaitCondition waitingForMessages;
0171 };