[telepathy-qt4/master] TextChannel: Added support for receiving chat state notifications.

Andre Moreira Magalhaes (andrunko) andre.magalhaes at collabora.co.uk
Mon Nov 2 07:55:04 PST 2009


---
 TelepathyQt4/text-channel.cpp |  123 ++++++++++++++++++++++++++++++++++++++++-
 TelepathyQt4/text-channel.h   |    7 ++
 2 files changed, 129 insertions(+), 1 deletions(-)

diff --git a/TelepathyQt4/text-channel.cpp b/TelepathyQt4/text-channel.cpp
index 135af23..05aa624 100644
--- a/TelepathyQt4/text-channel.cpp
+++ b/TelepathyQt4/text-channel.cpp
@@ -102,11 +102,13 @@ struct TELEPATHY_QT4_NO_EXPORT TextChannel::Private
     static void introspectMessageQueue(Private *self);
     static void introspectMessageCapabilities(Private *self);
     static void introspectMessageSentSignal(Private *self);
+    static void enableChatStateNotifications(Private *self);
 
     void updateInitialMessages();
     void updateCapabilities();
 
     void processMessageQueue();
+    void processChatStateQueue();
 
     void contactLost(uint handle);
     void contactFound(ContactPtr contact);
@@ -146,6 +148,19 @@ struct TELEPATHY_QT4_NO_EXPORT TextChannel::Private
     QList<MessageEvent *> incompleteMessages;
     QSet<uint> awaitingContacts;
     QHash<QDBusPendingCallWatcher *, UIntList> acknowledgeBatches;
+
+    // FeatureChatState
+    struct ChatStateEvent
+    {
+        ChatStateEvent(uint contactHandle, uint state)
+            : contactHandle(contactHandle), state(state)
+        { }
+
+        ContactPtr contact;
+        uint contactHandle;
+        uint state;
+    };
+    QList<ChatStateEvent *> chatStateQueue;
 };
 
 TextChannel::Private::Private(TextChannel *parent)
@@ -183,6 +198,14 @@ TextChannel::Private::Private(TextChannel *parent)
         this);
     introspectables[FeatureMessageSentSignal] = introspectableMessageSentSignal;
 
+    ReadinessHelper::Introspectable introspectableChatState(
+        QSet<uint>() << 0,                                                      // makesSenseForStatuses
+        Features() << Channel::FeatureCore,                                     // dependsOnFeatures (core)
+        QStringList(TELEPATHY_INTERFACE_CHANNEL_INTERFACE_CHAT_STATE),          // dependsOnInterfaces
+        (ReadinessHelper::IntrospectFunc) &Private::enableChatStateNotifications,
+        this);
+    introspectables[FeatureChatState] = introspectableChatState;
+
     readinessHelper->addIntrospectables(introspectables);
 }
 
@@ -191,6 +214,10 @@ TextChannel::Private::~Private()
     foreach (MessageEvent *e, incompleteMessages) {
         delete e;
     }
+
+    foreach (ChatStateEvent *e, chatStateQueue) {
+        delete e;
+    }
 }
 
 void TextChannel::Private::introspectMessageQueue(
@@ -286,6 +313,20 @@ void TextChannel::Private::introspectMessageSentSignal(
     self->readinessHelper->setIntrospectCompleted(FeatureMessageSentSignal, true);
 }
 
+void TextChannel::Private::enableChatStateNotifications(
+        TextChannel::Private *self)
+{
+    TextChannel *parent = self->parent;
+    Client::ChannelInterfaceChatStateInterface *chatStateInterface =
+        parent->chatStateInterface();
+
+    parent->connect(chatStateInterface,
+            SIGNAL(ChatStateChanged(uint, uint)),
+            SLOT(onChatStateChanged(uint, uint)));
+
+    self->readinessHelper->setIntrospectCompleted(FeatureChatState, true);
+}
+
 void TextChannel::Private::updateInitialMessages()
 {
     if (!readinessHelper->requestedFeatures().contains(FeatureMessageQueue) ||
@@ -399,6 +440,44 @@ void TextChannel::Private::processMessageQueue()
     awaitingContacts |= contactsRequired;
 }
 
+void TextChannel::Private::processChatStateQueue()
+{
+    while (!chatStateQueue.isEmpty()) {
+        const ChatStateEvent *e = chatStateQueue.first();
+        debug() << "ChatStateEvent:" << e;
+
+        if (e->contact.isNull()) {
+            // the chat state Contact object wasn't retrieved yet, but needs
+            // one. We'll have to stop processing here, and come back to it
+            // when we have more Contact objects
+            break;
+        }
+
+        // if we reach here, the Contact object is ready
+        emit parent->chatStateChanged(e->contact, (ChannelChatState) e->state);
+
+        debug() << "Dropping first event";
+        delete chatStateQueue.takeFirst();
+    }
+
+    // What Contact objects do we need in order to proceed, ignoring those
+    // for which we've already sent a request?
+    QSet<uint> contactsRequired;
+    foreach (const ChatStateEvent *e, chatStateQueue) {
+        if (!e->contact &&
+            !awaitingContacts.contains(e->contactHandle)) {
+            contactsRequired << e->contactHandle;
+        }
+    }
+
+    parent->connect(parent->connection()->contactManager()->contactsForHandles(
+                contactsRequired.toList()),
+            SIGNAL(finished(Tp::PendingOperation *)),
+            SLOT(onContactsFinished(Tp::PendingOperation *)));
+
+    awaitingContacts |= contactsRequired;
+}
+
 void TextChannel::Private::contactLost(uint handle)
 {
     // we're not going to get a Contact object for this handle, so mark the
@@ -409,6 +488,15 @@ void TextChannel::Private::contactLost(uint handle)
             e->message.clearSenderHandle();
         }
     }
+
+    // there is no point in sending chat state notifications for unknown
+    // contacts, removing chat state events from queue that refer to this handle
+    foreach (ChatStateEvent *e, chatStateQueue) {
+        if (e->contactHandle == handle) {
+            chatStateQueue.removeOne(e);
+            delete e;
+        }
+    }
 }
 
 void TextChannel::Private::contactFound(ContactPtr contact)
@@ -421,6 +509,12 @@ void TextChannel::Private::contactFound(ContactPtr contact)
             e->message.setSender(contact);
         }
     }
+
+    foreach (ChatStateEvent *e, chatStateQueue) {
+        if (e->contactHandle == handle) {
+            e->contact = contact;
+        }
+    }
 }
 
 /**
@@ -449,10 +543,18 @@ void TextChannel::Private::contactFound(ContactPtr contact)
  * \var Feature TextChannel::FeatureMessageSentSignal
  * The messageSent signal will be emitted when a message is sent
  */
+/**
+ * \var Feature TextChannel::FeatureChatState
+ * Enable this feature in order to receive notifications of remote contacts'
+ * state.
+ * The chatStateChanged() signal will be emitted when a remote contact chat
+ * state changes.
+ */
 
 const Feature TextChannel::FeatureMessageQueue = Feature(TextChannel::staticMetaObject.className(), 0);
 const Feature TextChannel::FeatureMessageCapabilities = Feature(TextChannel::staticMetaObject.className(), 1);
 const Feature TextChannel::FeatureMessageSentSignal = Feature(TextChannel::staticMetaObject.className(), 2);
+const Feature TextChannel::FeatureChatState = Feature(TextChannel::staticMetaObject.className(), 3);
 
 /**
  * \fn void TextChannel::messageSent(const Tp::Message &message,
@@ -495,6 +597,15 @@ const Feature TextChannel::FeatureMessageSentSignal = Feature(TextChannel::stati
  * circumstances in which this happens.
  */
 
+/**
+ * \fn void TextChannel::chatStateChanged(const Tp::ContactPtr &contact,
+ *      ChannelChatState state)
+ *
+ * Emitted when the state of a member of the channel has changed, if the
+ * FeatureChatState feature has been enabled.
+ * This includes local state.
+ */
+
 TextChannelPtr TextChannel::create(const ConnectionPtr &connection,
         const QString &objectPath, const QVariantMap &immutableProperties)
 {
@@ -810,8 +921,11 @@ void TextChannel::onContactsFinished(PendingOperation *op)
             mPriv->contactLost(handle);
         }
     }
-    // all the messages we were asking about should now be ready
+
+    // all contacts for messages and chat state events we were asking about
+    // should now be ready
     mPriv->processMessageQueue();
+    mPriv->processChatStateQueue();
 }
 
 void TextChannel::onMessageReceived(const MessagePartList &parts)
@@ -1016,4 +1130,11 @@ void TextChannel::gotPendingMessages(QDBusPendingCallWatcher *watcher)
     watcher->deleteLater();
 }
 
+void TextChannel::onChatStateChanged(uint contactHandle, uint state)
+{
+    mPriv->chatStateQueue.append(new Private::ChatStateEvent(
+                contactHandle, state));
+    mPriv->processChatStateQueue();
+}
+
 } // Tp
diff --git a/TelepathyQt4/text-channel.h b/TelepathyQt4/text-channel.h
index b1a1d09..9287521 100644
--- a/TelepathyQt4/text-channel.h
+++ b/TelepathyQt4/text-channel.h
@@ -69,6 +69,7 @@ public:
     static const Feature FeatureMessageQueue;
     static const Feature FeatureMessageCapabilities;
     static const Feature FeatureMessageSentSignal;
+    static const Feature FeatureChatState;
 
     static TextChannelPtr create(const ConnectionPtr &connection,
             const QString &objectPath, const QVariantMap &immutableProperties);
@@ -116,6 +117,10 @@ Q_SIGNALS:
     void pendingMessageRemoved(
             const Tp::ReceivedMessage &message);
 
+    // FeatureChatState
+    void chatStateChanged(const Tp::ContactPtr &contact,
+            ChannelChatState state);
+
 protected:
     TextChannel(const ConnectionPtr &connection, const QString &objectPath,
             const QVariantMap &immutableProperties);
@@ -136,6 +141,8 @@ private Q_SLOTS:
     void gotProperties(QDBusPendingCallWatcher *);
     void gotPendingMessages(QDBusPendingCallWatcher *);
 
+    void onChatStateChanged(uint, uint);
+
 private:
     struct Private;
     friend struct Private;
-- 
1.5.6.5




More information about the telepathy-commits mailing list