summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin Smith <git@kismith.co.uk>2010-09-18 20:58:48 (GMT)
committerKevin Smith <git@kismith.co.uk>2010-10-01 11:38:56 (GMT)
commit5a91a3ef54c00a6d4d960725f2ff84b5e0c43cab (patch)
treede73910e121edc91f6eec2a9af863b7be8d352e3
parent5166d2def025a4fb1e3c4a723d90dc82669b36ee (diff)
downloadswift-contrib-5a91a3ef54c00a6d4d960725f2ff84b5e0c43cab.zip
swift-contrib-5a91a3ef54c00a6d4d960725f2ff84b5e0c43cab.tar.bz2
Use caps for enabling chat state notifications.
Resolves: #93
-rw-r--r--Swift/Controllers/Chat/ChatController.cpp19
-rw-r--r--Swift/Controllers/Chat/ChatController.h6
-rw-r--r--Swift/Controllers/Chat/ChatsManager.cpp4
-rw-r--r--Swift/Controllers/Chat/ChatsManager.h4
-rw-r--r--Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp14
-rw-r--r--Swift/Controllers/MainController.cpp4
-rw-r--r--Swiften/Chat/ChatStateNotifier.cpp21
-rw-r--r--Swiften/Chat/ChatStateNotifier.h2
-rw-r--r--Swiften/Chat/UnitTest/ChatStateNotifierTest.cpp6
-rw-r--r--Swiften/Elements/ChatState.h2
10 files changed, 62 insertions, 20 deletions
diff --git a/Swift/Controllers/Chat/ChatController.cpp b/Swift/Controllers/Chat/ChatController.cpp
index 7fbf677..90ca7f8 100644
--- a/Swift/Controllers/Chat/ChatController.cpp
+++ b/Swift/Controllers/Chat/ChatController.cpp
@@ -7,12 +7,14 @@
#include "Swift/Controllers/Chat/ChatController.h"
#include <boost/bind.hpp>
+#include <stdio.h>
#include "Swiften/Avatars/AvatarManager.h"
#include "Swiften/Chat/ChatStateNotifier.h"
#include "Swiften/Chat/ChatStateMessageSender.h"
#include "Swiften/Chat/ChatStateTracker.h"
#include "Swiften/Client/StanzaChannel.h"
+#include "Swiften/Disco/EntityCapsManager.h"
#include "Swift/Controllers/UIInterfaces/ChatWindow.h"
#include "Swift/Controllers/UIInterfaces/ChatWindowFactory.h"
#include "Swift/Controllers/NickResolver.h"
@@ -23,11 +25,14 @@ namespace Swift {
/**
* The controller does not gain ownership of the stanzaChannel, nor the factory.
*/
-ChatController::ChatController(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &contact, NickResolver* nickResolver, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool isInMUC, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory)
+ChatController::ChatController(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &contact, NickResolver* nickResolver, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool isInMUC, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory, EntityCapsManager* entityCapsManager)
: ChatControllerBase(self, stanzaChannel, iqRouter, chatWindowFactory, contact, presenceOracle, avatarManager, useDelayForLatency, eventStream, eventController, timerFactory) {
isInMUC_ = isInMUC;
lastWasPresence_ = false;
+ entityCapsManager_ = entityCapsManager;
chatStateNotifier_ = new ChatStateNotifier();
+ entityCapsManager_->onCapsChanged.connect(boost::bind(&ChatController::handleCapsChanged, this, _1));
+ handleCapsChanged(toJID_);
chatStateMessageSender_ = new ChatStateMessageSender(chatStateNotifier_, stanzaChannel, contact);
chatStateTracker_ = new ChatStateTracker();
nickResolver_ = nickResolver;
@@ -62,9 +67,19 @@ ChatController::~ChatController() {
delete chatStateTracker_;
}
+void ChatController::handleCapsChanged(const JID& jid) {
+ if (jid == toJID_) {
+ DiscoInfo::ref caps = entityCapsManager_->getCaps(toJID_);
+ bool hasCSN = caps && caps->hasFeature(ChatState::getFeatureNamespace());
+ chatStateNotifier_->setContactHas85Caps(hasCSN);
+ }
+}
+
void ChatController::setToJID(const JID& jid) {
+ chatStateNotifier_->contactJIDHasChanged();
chatStateMessageSender_->setContact(jid);
ChatControllerBase::setToJID(jid);
+ handleCapsChanged(jid);
}
bool ChatController::isIncomingMessageFromMe(boost::shared_ptr<Message>) {
@@ -77,7 +92,7 @@ void ChatController::preHandleIncomingMessage(boost::shared_ptr<MessageEvent> me
JID from = message->getFrom();
if (!from.equals(toJID_, JID::WithResource)) {
if (toJID_.equals(from, JID::WithoutResource) && toJID_.isBare()){
- toJID_ = from;
+ setToJID(from);
}
}
chatStateNotifier_->receivedMessageFromContact(message->getPayload<ChatState>());
diff --git a/Swift/Controllers/Chat/ChatController.h b/Swift/Controllers/Chat/ChatController.h
index 6cb1443..1e530ac 100644
--- a/Swift/Controllers/Chat/ChatController.h
+++ b/Swift/Controllers/Chat/ChatController.h
@@ -15,9 +15,10 @@ namespace Swift {
class ChatStateMessageSender;
class ChatStateTracker;
class NickResolver;
+ class EntityCapsManager;
class ChatController : public ChatControllerBase {
public:
- ChatController(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &contact, NickResolver* nickResolver, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool isInMUC, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory);
+ ChatController(const JID& self, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, const JID &contact, NickResolver* nickResolver, PresenceOracle* presenceOracle, AvatarManager* avatarManager, bool isInMUC, bool useDelayForLatency, UIEventStream* eventStream, EventController* eventController, TimerFactory* timerFactory, EntityCapsManager* entityCapsManager);
virtual ~ChatController();
virtual void setToJID(const JID& jid);
virtual void setEnabled(bool enabled);
@@ -33,13 +34,14 @@ namespace Swift {
virtual boost::optional<boost::posix_time::ptime> getMessageTimestamp(boost::shared_ptr<Message>) const;
void handleStanzaAcked(boost::shared_ptr<Stanza> stanza);
void dayTicked() {lastWasPresence_ = false;}
+ void handleCapsChanged(const JID& jid);
private:
NickResolver* nickResolver_;
- JID contact_;
ChatStateNotifier* chatStateNotifier_;
ChatStateMessageSender* chatStateMessageSender_;
ChatStateTracker* chatStateTracker_;
+ EntityCapsManager* entityCapsManager_;
bool isInMUC_;
bool lastWasPresence_;
String lastStatusChangeString_;
diff --git a/Swift/Controllers/Chat/ChatsManager.cpp b/Swift/Controllers/Chat/ChatsManager.cpp
index bd4fcb8..8c93120 100644
--- a/Swift/Controllers/Chat/ChatsManager.cpp
+++ b/Swift/Controllers/Chat/ChatsManager.cpp
@@ -26,7 +26,7 @@ namespace Swift {
typedef std::pair<JID, ChatController*> JIDChatControllerPair;
typedef std::pair<JID, MUCController*> JIDMUCControllerPair;
-ChatsManager::ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, boost::shared_ptr<DiscoInfo> serverDiscoInfo, PresenceSender* presenceSender, UIEventStream* uiEventStream, ChatListWindowFactory* chatListWindowFactory, bool useDelayForLatency, TimerFactory* timerFactory, MUCRegistry* mucRegistry) : jid_(jid), useDelayForLatency_(useDelayForLatency), mucRegistry_(mucRegistry) {
+ChatsManager::ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, boost::shared_ptr<DiscoInfo> serverDiscoInfo, PresenceSender* presenceSender, UIEventStream* uiEventStream, ChatListWindowFactory* chatListWindowFactory, bool useDelayForLatency, TimerFactory* timerFactory, MUCRegistry* mucRegistry, EntityCapsManager* entityCapsManager) : jid_(jid), useDelayForLatency_(useDelayForLatency), mucRegistry_(mucRegistry), entityCapsManager_(entityCapsManager) {
timerFactory_ = timerFactory;
eventController_ = eventController;
stanzaChannel_ = stanzaChannel;
@@ -183,7 +183,7 @@ ChatController* ChatsManager::getChatControllerOrFindAnother(const JID &contact)
}
ChatController* ChatsManager::createNewChatController(const JID& contact) {
- ChatController* controller = new ChatController(jid_, stanzaChannel_, iqRouter_, chatWindowFactory_, contact, nickResolver_, presenceOracle_, avatarManager_, mucRegistry_->isMUC(contact.toBare()), useDelayForLatency_, uiEventStream_, eventController_, timerFactory_);
+ ChatController* controller = new ChatController(jid_, stanzaChannel_, iqRouter_, chatWindowFactory_, contact, nickResolver_, presenceOracle_, avatarManager_, mucRegistry_->isMUC(contact.toBare()), useDelayForLatency_, uiEventStream_, eventController_, timerFactory_, entityCapsManager_);
chatControllers_[contact] = controller;
controller->setAvailableServerFeatures(serverDiscoInfo_);
return controller;
diff --git a/Swift/Controllers/Chat/ChatsManager.h b/Swift/Controllers/Chat/ChatsManager.h
index 17a5d94..1e31458 100644
--- a/Swift/Controllers/Chat/ChatsManager.h
+++ b/Swift/Controllers/Chat/ChatsManager.h
@@ -34,10 +34,11 @@ namespace Swift {
class ChatListWindow;
class ChatListWindowFactory;
class TimerFactory;
+ class EntityCapsManager;
class ChatsManager {
public:
- ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, boost::shared_ptr<DiscoInfo> serverDiscoInfo, PresenceSender* presenceSender, UIEventStream* uiEventStream, ChatListWindowFactory* chatListWindowFactory, bool useDelayForLatency, TimerFactory* timerFactory, MUCRegistry* mucRegistry);
+ ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, boost::shared_ptr<DiscoInfo> serverDiscoInfo, PresenceSender* presenceSender, UIEventStream* uiEventStream, ChatListWindowFactory* chatListWindowFactory, bool useDelayForLatency, TimerFactory* timerFactory, MUCRegistry* mucRegistry, EntityCapsManager* entityCapsManager);
virtual ~ChatsManager();
void setAvatarManager(AvatarManager* avatarManager);
void setEnabled(bool enabled);
@@ -76,5 +77,6 @@ namespace Swift {
bool useDelayForLatency_;
TimerFactory* timerFactory_;
MUCRegistry* mucRegistry_;
+ EntityCapsManager* entityCapsManager_;
};
}
diff --git a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
index a17575c..e770e88 100644
--- a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
+++ b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
@@ -14,6 +14,8 @@
#include "Swift/Controllers/UIInterfaces/ChatWindowFactory.h"
#include "Swift/Controllers/UIInterfaces/ChatListWindowFactory.h"
#include "Swiften/Client/Client.h"
+#include "Swiften/Disco/EntityCapsManager.h"
+#include "Swiften/Disco/CapsProvider.h"
#include "Swift/Controllers/Chat/ChatController.h"
#include "Swift/Controllers/XMPPEvents/EventController.h"
#include "Swift/Controllers/Chat/MUCController.h"
@@ -35,6 +37,10 @@
using namespace Swift;
+class DummyCapsProvider : public CapsProvider {
+ DiscoInfo::ref getCaps(const String&) const {return DiscoInfo::ref(new DiscoInfo());}
+};
+
class ChatsManagerTest : public CppUnit::TestFixture
{
CPPUNIT_TEST_SUITE(ChatsManagerTest);
@@ -57,6 +63,7 @@ public:
stanzaChannel_ = new DummyStanzaChannel();
iqChannel_ = new DummyIQChannel();
iqRouter_ = new IQRouter(iqChannel_);
+ capsProvider_ = new DummyCapsProvider();
eventController_ = new EventController();
chatWindowFactory_ = mocks_->InterfaceMock<ChatWindowFactory>();
xmppRoster_ = new XMPPRoster();
@@ -66,9 +73,10 @@ public:
serverDiscoInfo_ = boost::shared_ptr<DiscoInfo>(new DiscoInfo());
presenceSender_ = new PresenceSender(stanzaChannel_);
uiEventStream_ = new UIEventStream();
+ entityCapsManager_ = new EntityCapsManager(capsProvider_, stanzaChannel_);
chatListWindowFactory_ = mocks_->InterfaceMock<ChatListWindowFactory>();
mocks_->ExpectCall(chatListWindowFactory_, ChatListWindowFactory::createWindow).With(uiEventStream_).Return(NULL);
- manager_ = new ChatsManager(jid_, stanzaChannel_, iqRouter_, eventController_, chatWindowFactory_, nickResolver_, presenceOracle_, serverDiscoInfo_, presenceSender_, uiEventStream_, chatListWindowFactory_, true, NULL, mucRegistry_);
+ manager_ = new ChatsManager(jid_, stanzaChannel_, iqRouter_, eventController_, chatWindowFactory_, nickResolver_, presenceOracle_, serverDiscoInfo_, presenceSender_, uiEventStream_, chatListWindowFactory_, true, NULL, mucRegistry_, entityCapsManager_);
avatarManager_ = new NullAvatarManager();
manager_->setAvatarManager(avatarManager_);
@@ -89,6 +97,8 @@ public:
delete iqRouter_;
delete uiEventStream_;
delete xmppRoster_;
+ delete entityCapsManager_;
+ delete capsProvider_;
}
void testFirstOpenWindowIncoming() {
@@ -319,6 +329,8 @@ private:
UIEventStream* uiEventStream_;
ChatListWindowFactory* chatListWindowFactory_;
MUCRegistry* mucRegistry_;
+ EntityCapsManager* entityCapsManager_;
+ CapsProvider* capsProvider_;
};
CPPUNIT_TEST_SUITE_REGISTRATION(ChatsManagerTest);
diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp
index 3cfa2a7..fdfab98 100644
--- a/Swift/Controllers/MainController.cpp
+++ b/Swift/Controllers/MainController.cpp
@@ -44,6 +44,7 @@
#include "Swiften/Base/String.h"
#include "Swiften/Client/Client.h"
#include "Swiften/Presence/PresenceSender.h"
+#include "Swiften/Elements/ChatState.h"
#include "Swiften/Elements/Presence.h"
#include "Swiften/Elements/VCardUpdate.h"
#include "Swiften/Queries/Responders/SoftwareVersionResponder.h"
@@ -257,7 +258,7 @@ void MainController::handleConnected() {
rosterController_->onChangeStatusRequest.connect(boost::bind(&MainController::handleChangeStatusRequest, this, _1, _2));
rosterController_->onSignOutRequest.connect(boost::bind(&MainController::signOut, this));
- chatsManager_ = new ChatsManager(jid_, client_, client_->getIQRouter(), eventController_, chatWindowFactory_, nickResolver_, presenceOracle_, serverDiscoInfo_, presenceSender_, uiEventStream_, chatListWindowFactory_, useDelayForLatency_, &timerFactory_, mucRegistry_);
+ chatsManager_ = new ChatsManager(jid_, client_, client_->getIQRouter(), eventController_, chatWindowFactory_, nickResolver_, presenceOracle_, serverDiscoInfo_, presenceSender_, uiEventStream_, chatListWindowFactory_, useDelayForLatency_, &timerFactory_, mucRegistry_, entityCapsManager_);
client_->onMessageReceived.connect(boost::bind(&ChatsManager::handleIncomingMessage, chatsManager_, _1));
chatsManager_->setAvatarManager(avatarManager_);
@@ -271,6 +272,7 @@ void MainController::handleConnected() {
DiscoInfo discoInfo;
discoInfo.addIdentity(DiscoInfo::Identity(CLIENT_NAME, "client", "pc"));
discoInfo.addFeature("urn:xmpp:sec-label:0");
+ discoInfo.addFeature(ChatState::getFeatureNamespace());
capsInfo_ = boost::shared_ptr<CapsInfo>(new CapsInfo(CapsInfoGenerator(CLIENT_NODE).generateCapsInfo(discoInfo)));
discoResponder_ = new DiscoInfoResponder(client_->getIQRouter());
diff --git a/Swiften/Chat/ChatStateNotifier.cpp b/Swiften/Chat/ChatStateNotifier.cpp
index 5024958..667c244 100644
--- a/Swiften/Chat/ChatStateNotifier.cpp
+++ b/Swiften/Chat/ChatStateNotifier.cpp
@@ -9,18 +9,22 @@
namespace Swift {
ChatStateNotifier::ChatStateNotifier() {
- contactHas85Caps_ = false;
- isInConversation_ = false;
- contactHasSentActive_ = false;
- userIsTyping_ = false;
+ contactJIDHasChanged();
}
void ChatStateNotifier::setContactHas85Caps(bool hasCaps) {
contactHas85Caps_ = hasCaps;
}
+void ChatStateNotifier::contactJIDHasChanged() {
+ contactHasSentActive_ = false;
+ contactHas85Caps_ = false;
+ userIsTyping_ = false;
+}
+
void ChatStateNotifier::setUserIsTyping() {
- if (contactShouldReceiveStates() && !userIsTyping_) {
+ bool should = contactShouldReceiveStates();
+ if (should && !userIsTyping_) {
userIsTyping_ = true;
onChatStateChanged(ChatState::Composing);
}
@@ -38,14 +42,15 @@ void ChatStateNotifier::userCancelledNewMessage() {
}
void ChatStateNotifier::receivedMessageFromContact(bool hasActiveElement) {
- isInConversation_ = true;
contactHasSentActive_ = hasActiveElement;
}
bool ChatStateNotifier::contactShouldReceiveStates() {
/* So, yes, the XEP says to look at caps, but it also says that once you've
- heard from the contact, the active state overrides this.*/
- return contactHasSentActive_ || (contactHas85Caps_ && !isInConversation_);;
+ heard from the contact, the active state overrides this.
+ *HOWEVER* it says that the MUST NOT send csn if you haven't received
+ active is OPTIONAL behaviour for if you haven't got caps.*/
+ return contactHasSentActive_ || contactHas85Caps_ ;
}
}
diff --git a/Swiften/Chat/ChatStateNotifier.h b/Swiften/Chat/ChatStateNotifier.h
index 71febfa..8dcd5cd 100644
--- a/Swiften/Chat/ChatStateNotifier.h
+++ b/Swiften/Chat/ChatStateNotifier.h
@@ -21,11 +21,11 @@ namespace Swift {
void userCancelledNewMessage();
void receivedMessageFromContact(bool hasActiveElement);
bool contactShouldReceiveStates();
+ void contactJIDHasChanged();
boost::signal<void (ChatState::ChatStateType)> onChatStateChanged;
private:
bool contactHas85Caps_;
- bool isInConversation_;
bool contactHasSentActive_;
bool userIsTyping_;
};
diff --git a/Swiften/Chat/UnitTest/ChatStateNotifierTest.cpp b/Swiften/Chat/UnitTest/ChatStateNotifierTest.cpp
index 712ba10..2fa4c26 100644
--- a/Swiften/Chat/UnitTest/ChatStateNotifierTest.cpp
+++ b/Swiften/Chat/UnitTest/ChatStateNotifierTest.cpp
@@ -114,7 +114,11 @@ public:
void testContactShouldReceiveStates_ActiveOverrideOff() {
notifier_->setContactHas85Caps(true);
notifier_->receivedMessageFromContact(false);
- CPPUNIT_ASSERT_EQUAL(false, notifier_->contactShouldReceiveStates());
+ /* I originally read the MUST NOT send after receiving without Active and
+ * thought this should check for false, but I later found it was OPTIONAL
+ * (MAY) behaviour only for if you didn't receive caps.
+ */
+ CPPUNIT_ASSERT_EQUAL(true, notifier_->contactShouldReceiveStates());
}
diff --git a/Swiften/Elements/ChatState.h b/Swiften/Elements/ChatState.h
index 8dcf77c..8a96bf1 100644
--- a/Swiften/Elements/ChatState.h
+++ b/Swiften/Elements/ChatState.h
@@ -19,7 +19,7 @@ namespace Swift {
ChatStateType getChatState() { return state_; }
void setChatState(ChatStateType state) {state_ = state;}
-
+ static String getFeatureNamespace() {return "http://jabber.org/protocol/chatstates";}
private:
ChatStateType state_;
};