[Telepathy-commits] [telepathy-qt4/master] TextChannel: implement isReady, becomeReady, hasMessagesInterface

Simon McVittie simon.mcvittie at collabora.co.uk
Wed Feb 18 10:26:16 PST 2009


Also connect to the Sent or MessageSent signal (as appropriate).

Limitations:
- becomeReady() for the MessageQueue feature fetches the necessary
  info but doesn't do anything with it yet
- The MessageCapabilities feature should work, but there are no accessors
- It's unclear when the signals for sent messages start to be guaranteed
---
 TelepathyQt4/Client/text-channel.cpp |  276 ++++++++++++++++++++++++++++++++++
 TelepathyQt4/Client/text-channel.h   |   33 +++-
 2 files changed, 301 insertions(+), 8 deletions(-)

diff --git a/TelepathyQt4/Client/text-channel.cpp b/TelepathyQt4/Client/text-channel.cpp
index 402cd1e..99b625e 100644
--- a/TelepathyQt4/Client/text-channel.cpp
+++ b/TelepathyQt4/Client/text-channel.cpp
@@ -21,6 +21,9 @@
 #include <TelepathyQt4/Client/TextChannel>
 
 #include "TelepathyQt4/Client/_gen/text-channel.moc.hpp"
+
+#include <TelepathyQt4/Client/PendingReadyChannel>
+
 #include "TelepathyQt4/debug-internal.h"
 
 namespace Telepathy
@@ -32,9 +35,27 @@ struct TextChannel::Private
 {
     inline Private();
     inline ~Private();
+
+    TextChannel::Features features;
+    TextChannel::Features desiredFeatures;
+    QList<PendingReadyChannel *> pendingReady;
+    void continueReadying(TextChannel *channel);
+    void failReadying(TextChannel *channel, const QString &error,
+            const QString &message);
+
+    // requires FeatureMessageCapabilities
+    QStringList supportedContentTypes;
+    MessagePartSupportFlags messagePartSupport;
+    DeliveryReportingSupportFlags deliveryReportingSupport;
 };
 
 TextChannel::Private::Private()
+    : features(0),
+      desiredFeatures(0),
+      pendingReady(),
+      supportedContentTypes(),
+      messagePartSupport(0),
+      deliveryReportingSupport(0)
 {
 }
 
@@ -80,5 +101,260 @@ TextChannel::~TextChannel()
     delete mPriv;
 }
 
+/**
+ * Return whether this channel supports the Telepathy Messages interface.
+ * If it does not, some advanced functionality will be unavailable.
+ *
+ * \return true if the Messages interface is supported
+ */
+bool TextChannel::hasMessagesInterface() const
+{
+    return interfaces().contains(QLatin1String(
+                TELEPATHY_INTERFACE_CHANNEL_INTERFACE_MESSAGES));
+}
+
+/**
+ * Return whether the desired features are ready for use.
+ *
+ * \return true if all the requested features are ready
+ */
+bool TextChannel::isReady(Channel::Features channelFeatures,
+        Features textFeatures) const
+{
+    debug() << "Checking whether ready:" << channelFeatures << textFeatures;
+    debug() << "Am I ready? mPriv->features =" << mPriv->features;
+    return Channel::isReady(channelFeatures) &&
+        ((mPriv->features & textFeatures) == textFeatures);
+}
+
+PendingReadyChannel *TextChannel::becomeReady(
+        Channel::Features channelFeatures,
+        Features textFeatures)
+{
+    PendingReadyChannel *textReady;
+
+    if (!isValid()) {
+        textReady = new PendingReadyChannel(channelFeatures, this);
+        textReady->setFinishedWithError(invalidationReason(),
+                invalidationMessage());
+        return textReady;
+    }
+
+    if (isReady(channelFeatures, textFeatures)) {
+        textReady = new PendingReadyChannel(channelFeatures, this);
+        textReady->setFinished();
+        return textReady;
+    }
+
+    if ((textFeatures & (FeatureMessageQueue | FeatureMessageCapabilities))
+            != textFeatures) {
+        textReady = new PendingReadyChannel(channelFeatures, this);
+        textReady->setFinishedWithError(TELEPATHY_ERROR_INVALID_ARGUMENT,
+                "Invalid features argument");
+        return textReady;
+    }
+
+    PendingReadyChannel *channelReady = Channel::becomeReady(channelFeatures);
+
+    textReady = new PendingReadyChannel(channelFeatures, this);
+
+    connect(channelReady,
+            SIGNAL(finished(Telepathy::Client::PendingOperation *)),
+            this,
+            SLOT(onChannelReady(Telepathy::Client::PendingOperation *)));
+
+    mPriv->pendingReady.append(textReady);
+    mPriv->desiredFeatures |= textFeatures;
+    return textReady;
+}
+
+void TextChannel::onChannelReady(Telepathy::Client::PendingOperation *op)
+{
+    // Handle the error and insanity cases
+    if (op->isError()) {
+        mPriv->failReadying(this, op->errorName(), op->errorMessage());
+    }
+    if (channelType() != QLatin1String(TELEPATHY_INTERFACE_CHANNEL_TYPE_TEXT)) {
+        mPriv->failReadying(this,
+                QLatin1String(TELEPATHY_ERROR_INVALID_ARGUMENT),
+                QLatin1String("TextChannel object with non-Text channel"));
+        return;
+    }
+
+    // Now that the basic Channel stuff is ready, we can know whether we have
+    // the Messages interface.
+    if (hasMessagesInterface()) {
+        connect(messagesInterface(),
+                SIGNAL(MessageSent(const Telepathy::MessagePartList &,
+                        uint, const QString &)),
+                this,
+                SLOT(onMessageSent(const Telepathy::MessagePartList &,
+                        uint, const QString &)));
+    } else {
+        // For plain Text channels, FeatureMessageCapabilities is trivial to
+        // implement - we don't do anything special at all - so we might as
+        // well set it up even if the library user didn't actually care.
+        mPriv->supportedContentTypes =
+            (QStringList(QLatin1String("text/plain")));
+        mPriv->messagePartSupport = 0;
+        mPriv->deliveryReportingSupport = 0;
+        mPriv->features |= FeatureMessageCapabilities;
+
+        connect(textInterface(),
+                SIGNAL(Sent(uint, uint, const QString &)),
+                this,
+                SLOT(onTextSent(uint, uint, const QString &)));
+    }
+
+    mPriv->continueReadying(this);
+}
+
+void TextChannel::Private::failReadying(TextChannel *channel,
+        const QString &error, const QString &message)
+{
+    QList<PendingReadyChannel *> ops = pendingReady;
+    pendingReady.clear();
+
+    foreach (PendingReadyChannel *op, ops) {
+        op->setFinishedWithError(error, message);
+    }
+    channel->invalidate(error, message);
+}
+
+void TextChannel::Private::continueReadying(TextChannel *channel)
+{
+    if ((desiredFeatures & features) == desiredFeatures) {
+        // everything we wanted is ready
+        QList<PendingReadyChannel *> ops = pendingReady;
+        pendingReady.clear();
+        foreach (PendingReadyChannel *op, ops) {
+            op->setFinished();
+        }
+        return;
+    }
+
+    // else there's more work to do yet
+
+    if (channel->hasMessagesInterface()) {
+        // FeatureMessageQueue needs signal connections + Get (but we
+        //  might as well do GetAll and reduce the number of code paths)
+        // FeatureMessageCapabilities needs GetAll
+
+        if (desiredFeatures & TextChannel::FeatureMessageQueue) {
+            channel->connect(channel->messagesInterface(),
+                    SIGNAL(MessageReceived(const Telepathy::MessagePartList &)),
+                    SLOT(onMessageReceived(const Telepathy::MessagePartList &)));
+
+            channel->connect(channel->messagesInterface(),
+                    SIGNAL(PendingMessagesRemoved(const Telepathy::UIntList &)),
+                    SLOT(onPendingMessagesRemoved(const Telepathy::UIntList &)));
+        }
+
+        QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(
+                channel->propertiesInterface()->GetAll(
+                    QLatin1String(TELEPATHY_INTERFACE_CHANNEL_INTERFACE_MESSAGES)),
+                    channel);
+        channel->connect(watcher,
+                SIGNAL(finished(QDBusPendingCallWatcher *)),
+                SLOT(onGetAllMessagesReply(QDBusPendingCallWatcher *)));
+    } else {
+        // FeatureMessageQueue needs signal connections +
+        //  ListPendingMessages
+        // FeatureMessageCapabilities already done
+
+        // this is all we can have right now
+        Q_ASSERT(desiredFeatures == TextChannel::FeatureMessageQueue);
+
+        channel->connect(channel->textInterface(),
+                SIGNAL(Received(uint, uint, uint, uint, uint,
+                        const QString &)),
+                SLOT(onTextReceived(uint, uint, uint, uint, uint,
+                        const QString &)));
+
+        // we present SendError signals as if they were incoming messages,
+        // to be consistent with Messages
+        channel->connect(channel->textInterface(),
+                SIGNAL(SendError(uint, uint, uint, const QString &)),
+                SLOT(onTextSendError(uint, uint, uint, const QString &)));
+
+        QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(
+                channel->textInterface()->ListPendingMessages(false), channel);
+        channel->connect(watcher,
+                SIGNAL(finished(QDBusPendingCallWatcher *)),
+                SLOT(onListPendingMessagesReply(QDBusPendingCallWatcher *)));
+    }
+}
+
+void TextChannel::onMessageSent(const Telepathy::MessagePartList &parts,
+        uint flags,
+        const QString &sentMessageToken)
+{
+}
+
+void TextChannel::onMessageReceived(const Telepathy::MessagePartList &parts)
+{
+}
+
+void TextChannel::onPendingMessagesRemoved(const Telepathy::UIntList &ids)
+{
+}
+
+void TextChannel::onTextSent(uint timestamp, uint type, const QString &text)
+{
+}
+
+void TextChannel::onTextReceived(uint id, uint timestamp, uint sender,
+        uint type, uint flags, const QString &text)
+{
+}
+
+void TextChannel::onTextSendError(uint error, uint timestamp, uint type,
+        const QString &text)
+{
+}
+
+void TextChannel::onGetAllMessagesReply(QDBusPendingCallWatcher *watcher)
+{
+    QDBusPendingReply<QVariantMap> reply = *watcher;
+    QVariantMap props;
+
+    if (!reply.isError()) {
+        debug() << "Properties::GetAll(Channel.Interface.Messages) returned";
+        props = reply.value();
+    } else {
+        warning().nospace() << "Properties::GetAll(Channel.Interface.Messages)"
+            " failed with " << reply.error().name() << ": " <<
+            reply.error().message();
+        // ... and act as though props had been empty
+    }
+
+    if (mPriv->desiredFeatures & FeatureMessageQueue) {
+        // FIXME: actually put the messages in the queue
+
+        mPriv->features |= FeatureMessageQueue;
+    }
+
+    mPriv->supportedContentTypes = qdbus_cast<QStringList>(
+            props["SupportedContentTypes"]);
+    if (mPriv->supportedContentTypes.isEmpty()) {
+        mPriv->supportedContentTypes << QLatin1String("text/plain");
+    }
+    mPriv->messagePartSupport = MessagePartSupportFlags(qdbus_cast<uint>(
+            props["MessagePartSupportFlags"]));
+    mPriv->deliveryReportingSupport = DeliveryReportingSupportFlags(
+            qdbus_cast<uint>(props["DeliveryReportingSupport"]));
+
+    mPriv->features |= FeatureMessageCapabilities;
+    mPriv->continueReadying(this);
+}
+
+void TextChannel::onListPendingMessagesReply(QDBusPendingCallWatcher *watcher)
+{
+    // FIXME: actually put the messages in the queue
+
+    mPriv->features |= FeatureMessageQueue;
+    mPriv->continueReadying(this);
+}
+
 } // Telepathy::Client
 } // Telepathy
diff --git a/TelepathyQt4/Client/text-channel.h b/TelepathyQt4/Client/text-channel.h
index 002f54a..92ee039 100644
--- a/TelepathyQt4/Client/text-channel.h
+++ b/TelepathyQt4/Client/text-channel.h
@@ -33,6 +33,7 @@ namespace Client
 {
 
 class TextChannel;
+class PendingReadyChannel;
 
 class Message
 {
@@ -152,7 +153,7 @@ public:
         // messageQueue when their corresponding contact objects have been
         // created, and that messageReceived will be emitted. If not enabled,
         // messageReceived will not be emitted.
-        FeatureMessageQueue,
+        FeatureMessageQueue = 1,
 
         // FeatureMessageCapabilities guarantees that messagePartSupport,
         // supportedContentTypes and deliveryReportingSupport have been
@@ -161,24 +162,26 @@ public:
         // Implementation detail: FeatureMessageQueue might as
         // well imply this as a side-effect (it doesn't take any more round
         // trips).
-        FeatureMessageCapabilities,
+        FeatureMessageCapabilities = 2,
 
         _Padding = 0xFFFFFFFF
     };
     Q_DECLARE_FLAGS(Features, Feature)
 
-#if 0
-    PendingOperation *becomeReady(Features features = 0,
-            Channel::Features features = 0);
+    bool hasMessagesInterface() const;
+
+    bool isReady(Channel::Features channelFeatures = 0,
+            Features textFeatures = 0) const;
 
+    PendingReadyChannel *becomeReady(Channel::Features channelFeatures = 0,
+            Features textFeatures = 0);
+
+#if 0
     inline bool canInviteContacts() const
     {
         return groupCanAddContacts();
     }
 
-    // if false, some features won't work
-    bool hasMessagesInterface() const;
-
     // requires FeatureMessageCapabilities
     //
     // ["text/plain"] if hasMessagesInterface() is false
@@ -263,6 +266,20 @@ Q_SIGNALS:
     void pendingMessagesRemoved(QList<ReceivedMessage> messages);
 #endif
 
+private Q_SLOTS:
+    void onChannelReady(Telepathy::Client::PendingOperation *);
+
+    void onMessageSent(const Telepathy::MessagePartList &, uint,
+            const QString &);
+    void onMessageReceived(const Telepathy::MessagePartList &);
+    void onPendingMessagesRemoved(const Telepathy::UIntList &);
+    void onGetAllMessagesReply(QDBusPendingCallWatcher *);
+
+    void onTextSent(uint, uint, const QString &);
+    void onTextReceived(uint, uint, uint, uint, uint, const QString &);
+    void onTextSendError(uint, uint, uint, const QString &);
+    void onListPendingMessagesReply(QDBusPendingCallWatcher *);
+
 private:
     struct Private;
     friend struct Private;
-- 
1.5.6.5




More information about the telepathy-commits mailing list