diff options
Diffstat (limited to 'Swiften/Chat')
-rw-r--r-- | Swiften/Chat/ChatStateActionProvider.h | 7 | ||||
-rw-r--r-- | Swiften/Chat/ChatStateMessageSender.cpp | 22 | ||||
-rw-r--r-- | Swiften/Chat/ChatStateMessageSender.h | 20 | ||||
-rw-r--r-- | Swiften/Chat/ChatStateNotifier.cpp | 45 | ||||
-rw-r--r-- | Swiften/Chat/ChatStateNotifier.h | 26 | ||||
-rw-r--r-- | Swiften/Chat/ChatStateTracker.cpp | 28 | ||||
-rw-r--r-- | Swiften/Chat/ChatStateTracker.h | 21 | ||||
-rw-r--r-- | Swiften/Chat/UnitTest/ChatStateNotifierTest.cpp | 132 |
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); |