From 1d20eabbc32274b491b4c2bedf73d19933d97bfd Mon Sep 17 00:00:00 2001
From: Kevin Smith <git@kismith.co.uk>
Date: Wed, 10 Feb 2010 15:29:44 +0000
Subject: ChatStateNotifier and tests, ready for XEP-85 support.


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..d053cb5
--- /dev/null
+++ b/Swiften/Chat/ChatStateMessageSender.cpp
@@ -0,0 +1,5 @@
+#include "Swiften/Chat/ChatStateMessageSender.h"
+
+namespace Swift {
+
+}
diff --git a/Swiften/Chat/ChatStateMessageSender.h b/Swiften/Chat/ChatStateMessageSender.h
new file mode 100644
index 0000000..d27973d
--- /dev/null
+++ b/Swiften/Chat/ChatStateMessageSender.h
@@ -0,0 +1,7 @@
+#pragma once
+
+namespace Swift {
+	class ChatStateMessageSender {
+		
+	};
+}
diff --git a/Swiften/Chat/ChatStateNotifier.cpp b/Swiften/Chat/ChatStateNotifier.cpp
new file mode 100644
index 0000000..432f708
--- /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(Composing);
+	}
+}
+
+void ChatStateNotifier::userSentMessage() {
+	userIsTyping_ = false;
+}
+
+void ChatStateNotifier::userCancelledNewMessage() {
+	if (userIsTyping_) {
+		userIsTyping_ = false;
+		onChatStateChanged(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..90228b7
--- /dev/null
+++ b/Swiften/Chat/ChatStateNotifier.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include <boost/signals.hpp>
+#include <boost/shared_ptr.hpp>
+
+namespace Swift {
+	class ChatStateNotifier {
+		public:
+			enum ChatState {Active, Composing, Paused, Inactive, Gone};
+			ChatStateNotifier();
+			void setContactHas85Caps(bool hasCaps);
+			void setUserIsTyping();
+			void userSentMessage();
+			void userCancelledNewMessage();
+			void receivedMessageFromContact(bool hasActiveElement);
+			bool contactShouldReceiveStates();
+
+			boost::signal<void (ChatState)> 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..553d2f4
--- /dev/null
+++ b/Swiften/Chat/ChatStateTracker.cpp
@@ -0,0 +1,5 @@
+#include "Swiften/Chat/ChatStateTracker.h"
+
+namespace Swift {
+
+}
diff --git a/Swiften/Chat/ChatStateTracker.h b/Swiften/Chat/ChatStateTracker.h
new file mode 100644
index 0000000..005c479
--- /dev/null
+++ b/Swiften/Chat/ChatStateTracker.h
@@ -0,0 +1,7 @@
+#pragma once
+
+namespace Swift {
+	class ChatStateTracker {
+		
+	};
+}
diff --git a/Swiften/Chat/UnitTest/ChatStateNotifierTest.cpp b/Swiften/Chat/UnitTest/ChatStateNotifierTest.cpp
new file mode 100644
index 0000000..bacfc3a
--- /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;
+	ChatStateNotifier::ChatState currentState;
+
+private:
+	void handleChatStateChanged(ChatStateNotifier::ChatState newState) {
+		switch (newState) {
+			case ChatStateNotifier::Composing:
+				composingCallCount++;
+				break;
+			case ChatStateNotifier::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(ChatStateNotifier::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);
diff --git a/Swiften/SConscript b/Swiften/SConscript
index f8e98de..e31818c 100644
--- a/Swiften/SConscript
+++ b/Swiften/SConscript
@@ -17,6 +17,9 @@ sources = [
 		"Avatars/AvatarFileStorage.cpp",
 		"Avatars/AvatarManager.cpp",
 		"Avatars/AvatarStorage.cpp",
+		"Chat/ChatStateTracker.cpp",
+		"Chat/ChatStateNotifier.cpp",
+		"Chat/ChatStateMessageSender.cpp",
 		"Client/Client.cpp",
 		"Client/ClientSession.cpp",
 		"Compress/ZLibCodecompressor.cpp",
@@ -119,6 +122,8 @@ env.Append(UNITTEST_SOURCES = [
 		File("Base/UnitTest/IDGeneratorTest.cpp"),
 		File("Base/UnitTest/StringTest.cpp"),
 		File("Base/UnitTest/ByteArrayTest.cpp"),
+		File("Chat/UnitTest/ChatStateNotifierTest.cpp"),
+#		File("Chat/UnitTest/ChatStateTrackerTest.cpp"),
 		File("Client/UnitTest/ClientSessionTest.cpp"),
 		File("Compress/UnitTest/ZLibCompressorTest.cpp"),
 		File("Compress/UnitTest/ZLibDecompressorTest.cpp"),
-- 
cgit v0.10.2-6-g49f6