summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'Swiften/Chat')
-rw-r--r--Swiften/Chat/ChatStateActionProvider.h7
-rw-r--r--Swiften/Chat/ChatStateMessageSender.cpp22
-rw-r--r--Swiften/Chat/ChatStateMessageSender.h20
-rw-r--r--Swiften/Chat/ChatStateNotifier.cpp45
-rw-r--r--Swiften/Chat/ChatStateNotifier.h26
-rw-r--r--Swiften/Chat/ChatStateTracker.cpp28
-rw-r--r--Swiften/Chat/ChatStateTracker.h21
-rw-r--r--Swiften/Chat/UnitTest/ChatStateNotifierTest.cpp132
8 files changed, 301 insertions, 0 deletions
diff --git a/Swiften/Chat/ChatStateActionProvider.h b/Swiften/Chat/ChatStateActionProvider.h
new file mode 100644
index 0000000..82bed3f
--- /dev/null
+++ b/Swiften/Chat/ChatStateActionProvider.h
@@ -0,0 +1,7 @@
+#pragma once
+
+namespace Swift {
+ class ChatState {
+
+ };
+}
diff --git a/Swiften/Chat/ChatStateMessageSender.cpp b/Swiften/Chat/ChatStateMessageSender.cpp
new file mode 100644
index 0000000..ad1495f
--- /dev/null
+++ b/Swiften/Chat/ChatStateMessageSender.cpp
@@ -0,0 +1,22 @@
+#include "Swiften/Chat/ChatStateMessageSender.h"
+
+#include <boost/bind.hpp>
+
+#include "Swiften/Client/StanzaChannel.h"
+
+namespace Swift {
+
+ChatStateMessageSender::ChatStateMessageSender(ChatStateNotifier* notifier, StanzaChannel* stanzaChannel, const JID& contact) : contact_(contact) {
+ notifier_ = notifier;
+ stanzaChannel_ = stanzaChannel;
+ notifier_->onChatStateChanged.connect(boost::bind(&ChatStateMessageSender::handleChatStateChanged, this, _1));
+}
+
+void ChatStateMessageSender::handleChatStateChanged(ChatState::ChatStateType state) {
+ boost::shared_ptr<Message> message(new Message());
+ message->setTo(contact_);
+ message->addPayload(boost::shared_ptr<Payload>(new ChatState(state)));
+ stanzaChannel_->sendMessage(message);
+}
+
+}
diff --git a/Swiften/Chat/ChatStateMessageSender.h b/Swiften/Chat/ChatStateMessageSender.h
new file mode 100644
index 0000000..aff0791
--- /dev/null
+++ b/Swiften/Chat/ChatStateMessageSender.h
@@ -0,0 +1,20 @@
+#pragma once
+
+#include "Swiften/Chat/ChatStateNotifier.h"
+#include "Swiften/Elements/ChatState.h"
+#include "Swiften/JID/JID.h"
+
+namespace Swift {
+ class StanzaChannel;
+ class ChatStateMessageSender {
+ public:
+ ChatStateMessageSender(ChatStateNotifier* notifier, StanzaChannel* stanzaChannel, const JID& contact);
+ void setContact(const JID& contact) {contact_ = contact;};
+
+ private:
+ void handleChatStateChanged(ChatState::ChatStateType state);
+ ChatStateNotifier* notifier_;
+ StanzaChannel* stanzaChannel_;
+ JID contact_;
+ };
+}
diff --git a/Swiften/Chat/ChatStateNotifier.cpp b/Swiften/Chat/ChatStateNotifier.cpp
new file mode 100644
index 0000000..7c6560f
--- /dev/null
+++ b/Swiften/Chat/ChatStateNotifier.cpp
@@ -0,0 +1,45 @@
+#include "Swiften/Chat/ChatStateNotifier.h"
+
+namespace Swift {
+
+ChatStateNotifier::ChatStateNotifier() {
+ contactHas85Caps_ = false;
+ isInConversation_ = false;
+ contactHasSentActive_ = false;
+ userIsTyping_ = false;
+}
+
+void ChatStateNotifier::setContactHas85Caps(bool hasCaps) {
+ contactHas85Caps_ = hasCaps;
+}
+
+void ChatStateNotifier::setUserIsTyping() {
+ if (contactShouldReceiveStates() && !userIsTyping_) {
+ userIsTyping_ = true;
+ onChatStateChanged(ChatState::Composing);
+ }
+}
+
+void ChatStateNotifier::userSentMessage() {
+ userIsTyping_ = false;
+}
+
+void ChatStateNotifier::userCancelledNewMessage() {
+ if (userIsTyping_) {
+ userIsTyping_ = false;
+ onChatStateChanged(ChatState::Active);
+ }
+}
+
+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_);;
+}
+
+}
diff --git a/Swiften/Chat/ChatStateNotifier.h b/Swiften/Chat/ChatStateNotifier.h
new file mode 100644
index 0000000..0ef4255
--- /dev/null
+++ b/Swiften/Chat/ChatStateNotifier.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#include <boost/signals.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Elements/ChatState.h"
+
+namespace Swift {
+ class ChatStateNotifier {
+ public:
+ ChatStateNotifier();
+ void setContactHas85Caps(bool hasCaps);
+ void setUserIsTyping();
+ void userSentMessage();
+ void userCancelledNewMessage();
+ void receivedMessageFromContact(bool hasActiveElement);
+ bool contactShouldReceiveStates();
+
+ boost::signal<void (ChatState::ChatStateType)> onChatStateChanged;
+ private:
+ bool contactHas85Caps_;
+ bool isInConversation_;
+ bool contactHasSentActive_;
+ bool userIsTyping_;
+ };
+}
diff --git a/Swiften/Chat/ChatStateTracker.cpp b/Swiften/Chat/ChatStateTracker.cpp
new file mode 100644
index 0000000..3076845
--- /dev/null
+++ b/Swiften/Chat/ChatStateTracker.cpp
@@ -0,0 +1,28 @@
+#include "Swiften/Chat/ChatStateTracker.h"
+
+namespace Swift {
+ChatStateTracker::ChatStateTracker() {
+ currentState_ = ChatState::Gone;
+}
+
+void ChatStateTracker::handleMessageReceived(boost::shared_ptr<Message> message) {
+ boost::shared_ptr<ChatState> statePayload = message->getPayload<ChatState>();
+ if (statePayload) {
+ changeState(statePayload->getChatState());;
+ }
+}
+
+void ChatStateTracker::handlePresenceChange(boost::shared_ptr<Presence> newPresence, boost::shared_ptr<Presence>) {
+ if (newPresence->getType() == Presence::Unavailable) {
+ onChatStateChange(ChatState::Gone);
+ }
+}
+
+void ChatStateTracker::changeState(ChatState::ChatStateType state) {
+ if (state != currentState_) {
+ currentState_ = state;
+ onChatStateChange(state);
+ }
+}
+
+}
diff --git a/Swiften/Chat/ChatStateTracker.h b/Swiften/Chat/ChatStateTracker.h
new file mode 100644
index 0000000..e66bbae
--- /dev/null
+++ b/Swiften/Chat/ChatStateTracker.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include <boost/signal.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Elements/Message.h"
+#include "Swiften/Elements/Presence.h"
+#include "Swiften/Elements/ChatState.h"
+
+namespace Swift {
+ class ChatStateTracker {
+ public:
+ ChatStateTracker();
+ void handleMessageReceived(boost::shared_ptr<Message> message);
+ void handlePresenceChange(boost::shared_ptr<Presence> newPresence, boost::shared_ptr<Presence> oldPresence);
+ boost::signal<void (ChatState::ChatStateType)> onChatStateChange;
+ private:
+ void changeState(ChatState::ChatStateType state);
+ ChatState::ChatStateType currentState_;
+ };
+}
diff --git a/Swiften/Chat/UnitTest/ChatStateNotifierTest.cpp b/Swiften/Chat/UnitTest/ChatStateNotifierTest.cpp
new file mode 100644
index 0000000..44ec9d8
--- /dev/null
+++ b/Swiften/Chat/UnitTest/ChatStateNotifierTest.cpp
@@ -0,0 +1,132 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <boost/bind.hpp>
+
+#include "Swiften/Chat/ChatStateNotifier.h"
+
+using namespace Swift;
+
+
+class ChatStateMonitor {
+public:
+ ChatStateMonitor(ChatStateNotifier* notifier) {
+ notifier_ = notifier;
+ composingCallCount = 0;
+ activeCallCount = 0;
+ notifier->onChatStateChanged.connect(boost::bind(&ChatStateMonitor::handleChatStateChanged, this, _1));
+ };
+
+ int composingCallCount;
+ int activeCallCount;
+ ChatState::ChatStateType currentState;
+
+private:
+ void handleChatStateChanged(ChatState::ChatStateType newState) {
+ switch (newState) {
+ case ChatState::Composing:
+ composingCallCount++;
+ break;
+ case ChatState::Active:
+ activeCallCount++;
+ break;
+ default:
+ break;
+ }
+ currentState = newState;
+ };
+
+ ChatStateNotifier* notifier_;
+};
+
+class ChatStateNotifierTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(ChatStateNotifierTest);
+ CPPUNIT_TEST(testStartTypingReply_CapsNotIncluded);
+ CPPUNIT_TEST(testStartTypingReply_CapsIncluded);
+ CPPUNIT_TEST(testCancelledNewMessage);
+ CPPUNIT_TEST(testContinueTypingReply_CapsIncluded);
+ CPPUNIT_TEST(testContactShouldReceiveStates_CapsOnly);
+ CPPUNIT_TEST(testContactShouldReceiveStates_CapsNorActive);
+ CPPUNIT_TEST(testContactShouldReceiveStates_ActiveOverrideOn);
+ CPPUNIT_TEST(testContactShouldReceiveStates_ActiveOverrideOff);
+ CPPUNIT_TEST_SUITE_END();
+
+private:
+ ChatStateNotifier* notifier_;
+ ChatStateMonitor* monitor_;
+
+public:
+ void setUp() {
+ notifier_ = new ChatStateNotifier();
+ monitor_ = new ChatStateMonitor(notifier_);
+ }
+
+ void tearDown() {
+ delete notifier_;
+ delete monitor_;
+ }
+
+ void testStartTypingReply_CapsNotIncluded() {
+ notifier_->setContactHas85Caps(false);
+ notifier_->setUserIsTyping();
+ CPPUNIT_ASSERT_EQUAL(0, monitor_->composingCallCount);
+ }
+
+ void testSendTwoMessages() {
+ notifier_->setContactHas85Caps(true);
+ notifier_->setUserIsTyping();
+ notifier_->userSentMessage();
+ notifier_->setUserIsTyping();
+ notifier_->userSentMessage();
+ CPPUNIT_ASSERT_EQUAL(2, monitor_->composingCallCount);
+ }
+
+ void testCancelledNewMessage() {
+ notifier_->setContactHas85Caps(true);
+ notifier_->setUserIsTyping();
+ notifier_->userCancelledNewMessage();
+ CPPUNIT_ASSERT_EQUAL(1, monitor_->composingCallCount);
+ CPPUNIT_ASSERT_EQUAL(1, monitor_->activeCallCount);
+ CPPUNIT_ASSERT_EQUAL(ChatState::Active, monitor_->currentState);
+ }
+
+
+ void testContactShouldReceiveStates_CapsOnly() {
+ notifier_->setContactHas85Caps(true);
+ CPPUNIT_ASSERT_EQUAL(true, notifier_->contactShouldReceiveStates());
+ }
+
+ void testContactShouldReceiveStates_CapsNorActive() {
+ CPPUNIT_ASSERT_EQUAL(false, notifier_->contactShouldReceiveStates());
+ }
+
+ void testContactShouldReceiveStates_ActiveOverrideOn() {
+ notifier_->setContactHas85Caps(false);
+ notifier_->receivedMessageFromContact(true);
+ CPPUNIT_ASSERT_EQUAL(true, notifier_->contactShouldReceiveStates());
+ }
+
+ void testContactShouldReceiveStates_ActiveOverrideOff() {
+ notifier_->setContactHas85Caps(true);
+ notifier_->receivedMessageFromContact(false);
+ CPPUNIT_ASSERT_EQUAL(false, notifier_->contactShouldReceiveStates());
+ }
+
+
+ void testStartTypingReply_CapsIncluded() {
+ notifier_->setContactHas85Caps(true);
+ notifier_->setUserIsTyping();
+ CPPUNIT_ASSERT_EQUAL(1, monitor_->composingCallCount);
+ }
+
+ void testContinueTypingReply_CapsIncluded() {
+ notifier_->setContactHas85Caps(true);
+ notifier_->setUserIsTyping();
+ notifier_->setUserIsTyping();
+ notifier_->setUserIsTyping();
+ CPPUNIT_ASSERT_EQUAL(1, monitor_->composingCallCount);
+ }
+
+
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(ChatStateNotifierTest);