summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRemko Tronçon <git@el-tramo.be>2010-09-12 18:29:39 (GMT)
committerRemko Tronçon <git@el-tramo.be>2010-09-13 19:19:52 (GMT)
commitef1052bbdb315aaa1c6254098ea05638d9a25b2f (patch)
tree482277f649f6c0fc00027514ea8986861fe33437
parent3ae8cccfe9c6bfed5dda5f024a5cb046ccfc9793 (diff)
downloadswift-contrib-ef1052bbdb315aaa1c6254098ea05638d9a25b2f.zip
swift-contrib-ef1052bbdb315aaa1c6254098ea05638d9a25b2f.tar.bz2
Added presence notifier.
-rw-r--r--SwifTools/Notifier/GrowlNotifier.cpp18
-rw-r--r--SwifTools/Notifier/LoggingNotifier.h30
-rw-r--r--Swift/Controllers/Chat/ChatController.cpp9
-rw-r--r--Swift/Controllers/Chat/ChatController.h3
-rw-r--r--Swift/Controllers/Chat/ChatsManager.cpp4
-rw-r--r--Swift/Controllers/Chat/ChatsManager.h2
-rw-r--r--Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp10
-rw-r--r--Swift/Controllers/MainController.cpp88
-rw-r--r--Swift/Controllers/MainController.h6
-rw-r--r--Swift/Controllers/NickResolver.cpp2
-rw-r--r--Swift/Controllers/NickResolver.h4
-rw-r--r--Swift/Controllers/PresenceNotifier.cpp138
-rw-r--r--Swift/Controllers/PresenceNotifier.h60
-rw-r--r--Swift/Controllers/RosterController.cpp18
-rw-r--r--Swift/Controllers/RosterController.h8
-rw-r--r--Swift/Controllers/SConscript2
-rw-r--r--Swift/Controllers/UnitTest/NickResolverTest.cpp4
-rw-r--r--Swift/Controllers/UnitTest/PresenceNotifierTest.cpp310
-rw-r--r--Swift/Controllers/UnitTest/RosterControllerTest.cpp12
-rw-r--r--Swift/Controllers/UnitTest/XMPPRosterControllerTest.cpp4
-rw-r--r--Swift/Controllers/XMPPRosterController.cpp2
-rw-r--r--Swift/Controllers/XMPPRosterController.h6
-rw-r--r--Swift/QtUI/SConscript2
-rw-r--r--Swiften/Avatars/AvatarManager.h2
-rw-r--r--Swiften/Avatars/AvatarManagerImpl.cpp8
-rw-r--r--Swiften/Avatars/AvatarManagerImpl.h1
-rw-r--r--Swiften/Avatars/DummyAvatarManager.h32
-rw-r--r--Swiften/Avatars/NullAvatarManager.h4
-rw-r--r--Swiften/Chat/ChatStateTracker.cpp2
-rw-r--r--Swiften/Chat/ChatStateTracker.h2
-rw-r--r--Swiften/Network/DummyTimerFactory.cpp30
-rw-r--r--Swiften/Presence/PresenceOracle.cpp59
-rw-r--r--Swiften/Presence/PresenceOracle.h42
-rw-r--r--Swiften/Presence/PresenceSender.cpp22
-rw-r--r--Swiften/Presence/PresenceSender.h4
-rw-r--r--Swiften/Presence/UnitTest/PresenceOracleTest.cpp143
-rw-r--r--Swiften/Roster/XMPPRoster.cpp13
-rw-r--r--Swiften/Roster/XMPPRoster.h63
38 files changed, 902 insertions, 267 deletions
diff --git a/SwifTools/Notifier/GrowlNotifier.cpp b/SwifTools/Notifier/GrowlNotifier.cpp
index 4c671ac..7ea7193 100644
--- a/SwifTools/Notifier/GrowlNotifier.cpp
+++ b/SwifTools/Notifier/GrowlNotifier.cpp
@@ -17,22 +17,30 @@
namespace {
struct Context {
Context() {}
- Context(const boost::function<void()>& callback) : callback(callback) {}
+ Context(const boost::function<void()>& callback) : callback(new boost::function<void()>(callback)) {}
- boost::function<void()> callback;
+ boost::function<void()>* callback;
};
- void notificationClicked(CFPropertyListRef growlContext) {
+ void processNotification(CFPropertyListRef growlContext, bool activateCallback) {
Context context;
CFDataRef growlContextData = (CFDataRef) CFArrayGetValueAtIndex((CFArrayRef) growlContext, 0);
assert(CFDataGetLength(growlContextData) == sizeof(Context));
CFDataGetBytes(growlContextData, CFRangeMake(0, CFDataGetLength(growlContextData)), (UInt8*) &context);
- context.callback();
+ if (activateCallback) {
+ (*context.callback)();
+ }
+ delete context.callback;
+ }
+
+ void notificationClicked(CFPropertyListRef growlContext) {
+ processNotification(growlContext, true);
}
- void notificationTimedout(CFPropertyListRef) {
+ void notificationTimedout(CFPropertyListRef growlContext) {
+ processNotification(growlContext, false);
}
}
diff --git a/SwifTools/Notifier/LoggingNotifier.h b/SwifTools/Notifier/LoggingNotifier.h
new file mode 100644
index 0000000..93349d9
--- /dev/null
+++ b/SwifTools/Notifier/LoggingNotifier.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include "SwifTools/Notifier/Notifier.h"
+#include "Swiften/Base/ByteArray.h"
+
+namespace Swift {
+ class LoggingNotifier : public Notifier {
+ public:
+ virtual void showMessage(Type type, const String& subject, const String& description, const ByteArray& picture, boost::function<void()> callback) {
+ notifications.push_back(Notification(type, subject, description, picture, callback));
+ }
+
+ struct Notification {
+ Notification(Type type, const String& subject, const String& description, const ByteArray& picture, boost::function<void()> callback) : type(type), subject(subject), description(description), picture(picture), callback(callback) {}
+ Type type;
+ String subject;
+ String description;
+ ByteArray picture;
+ boost::function<void()> callback;
+ };
+
+ std::vector<Notification> notifications;
+ };
+}
diff --git a/Swift/Controllers/Chat/ChatController.cpp b/Swift/Controllers/Chat/ChatController.cpp
index 9154b9a..e621a6d 100644
--- a/Swift/Controllers/Chat/ChatController.cpp
+++ b/Swift/Controllers/Chat/ChatController.cpp
@@ -31,7 +31,7 @@ ChatController::ChatController(const JID& self, StanzaChannel* stanzaChannel, IQ
chatStateMessageSender_ = new ChatStateMessageSender(chatStateNotifier_, stanzaChannel, contact);
chatStateTracker_ = new ChatStateTracker();
nickResolver_ = nickResolver;
- presenceOracle_->onPresenceChange.connect(boost::bind(&ChatController::handlePresenceChange, this, _1, _2));
+ presenceOracle_->onPresenceChange.connect(boost::bind(&ChatController::handlePresenceChange, this, _1));
chatStateTracker_->onChatStateChange.connect(boost::bind(&ChatWindow::setContactChatState, chatWindow_, _1));
stanzaChannel_->onStanzaAcked.connect(boost::bind(&ChatController::handleStanzaAcked, this, _1));
String nick = nickResolver_->jidToNick(toJID_);
@@ -134,19 +134,20 @@ String ChatController::getStatusChangeString(boost::shared_ptr<Presence> presenc
return "";
}
-void ChatController::handlePresenceChange(boost::shared_ptr<Presence> newPresence, boost::shared_ptr<Presence> previousPresence) {
+void ChatController::handlePresenceChange(boost::shared_ptr<Presence> newPresence) {
if (!toJID_.equals(newPresence->getFrom(), toJID_.isBare() ? JID::WithoutResource : JID::WithResource)) {
return;
}
- chatStateTracker_->handlePresenceChange(newPresence, previousPresence);
+ chatStateTracker_->handlePresenceChange(newPresence);
String newStatusChangeString = getStatusChangeString(newPresence);
- if (!previousPresence || newStatusChangeString != getStatusChangeString(previousPresence)) {
+ if (newStatusChangeString != lastStatusChangeString_) {
if (lastWasPresence_) {
chatWindow_->replaceLastMessage(newStatusChangeString);
} else {
chatWindow_->addPresenceMessage(newStatusChangeString);
}
+ lastStatusChangeString_ = newStatusChangeString;
lastWasPresence_ = true;
}
}
diff --git a/Swift/Controllers/Chat/ChatController.h b/Swift/Controllers/Chat/ChatController.h
index 971fca9..c226ed8 100644
--- a/Swift/Controllers/Chat/ChatController.h
+++ b/Swift/Controllers/Chat/ChatController.h
@@ -23,7 +23,7 @@ namespace Swift {
virtual void setEnabled(bool enabled);
private:
- void handlePresenceChange(boost::shared_ptr<Presence> newPresence, boost::shared_ptr<Presence> previousPresence);
+ void handlePresenceChange(boost::shared_ptr<Presence> newPresence);
String getStatusChangeString(boost::shared_ptr<Presence> presence);
bool isIncomingMessageFromMe(boost::shared_ptr<Message> message);
void postSendMessage(const String &body, boost::shared_ptr<Stanza> sentStanza);
@@ -41,6 +41,7 @@ namespace Swift {
ChatStateTracker* chatStateTracker_;
bool isInMUC_;
bool lastWasPresence_;
+ String lastStatusChangeString_;
std::map<boost::shared_ptr<Stanza>, String> unackedStanzas_;
};
}
diff --git a/Swift/Controllers/Chat/ChatsManager.cpp b/Swift/Controllers/Chat/ChatsManager.cpp
index e1a53b4..08b1453 100644
--- a/Swift/Controllers/Chat/ChatsManager.cpp
+++ b/Swift/Controllers/Chat/ChatsManager.cpp
@@ -42,7 +42,7 @@ ChatsManager::ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRo
mucBookmarkManager_->onBookmarksReady.connect(boost::bind(&ChatsManager::handleBookmarksReady, this));
mucBookmarkManager_->onBookmarkAdded.connect(boost::bind(&ChatsManager::handleMUCBookmarkAdded, this, _1));
mucBookmarkManager_->onBookmarkRemoved.connect(boost::bind(&ChatsManager::handleMUCBookmarkRemoved, this, _1));
- presenceOracle_->onPresenceChange.connect(boost::bind(&ChatsManager::handlePresenceChange, this, _1, _2));
+ presenceOracle_->onPresenceChange.connect(boost::bind(&ChatsManager::handlePresenceChange, this, _1));
uiEventConnection_ = uiEventStream_->onUIEvent.connect(boost::bind(&ChatsManager::handleUIEvent, this, _1));
chatListWindow_ = chatListWindowFactory->createWindow(uiEventStream_);
if (chatListWindow_) {
@@ -122,7 +122,7 @@ void ChatsManager::handleUIEvent(boost::shared_ptr<UIEvent> event) {
/**
* If a resource goes offline, release bound chatdialog to that resource.
*/
-void ChatsManager::handlePresenceChange(boost::shared_ptr<Presence> newPresence, boost::shared_ptr<Presence> /*lastPresence*/) {
+void ChatsManager::handlePresenceChange(boost::shared_ptr<Presence> newPresence) {
if (mucRegistry_->isMUC(newPresence->getFrom().toBare())) return;
if (newPresence->getType() != Presence::Unavailable) return;
JID fullJID(newPresence->getFrom());
diff --git a/Swift/Controllers/Chat/ChatsManager.h b/Swift/Controllers/Chat/ChatsManager.h
index 752acff..17a5d94 100644
--- a/Swift/Controllers/Chat/ChatsManager.h
+++ b/Swift/Controllers/Chat/ChatsManager.h
@@ -47,7 +47,7 @@ namespace Swift {
void handleChatRequest(const String& contact);
void handleJoinMUCRequest(const JID& muc, const boost::optional<String>& nick);
void rebindControllerJID(const JID& from, const JID& to);
- void handlePresenceChange(boost::shared_ptr<Presence> newPresence, boost::shared_ptr<Presence> lastPresence);
+ void handlePresenceChange(boost::shared_ptr<Presence> newPresence);
void handleUIEvent(boost::shared_ptr<UIEvent> event);
void handleMUCBookmarkAdded(const MUCBookmark& bookmark);
void handleMUCBookmarkRemoved(const MUCBookmark& bookmark);
diff --git a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
index ffd5185..bf27dd5 100644
--- a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
+++ b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
@@ -59,7 +59,7 @@ public:
iqRouter_ = new IQRouter(iqChannel_);
eventController_ = new EventController();
chatWindowFactory_ = mocks_->InterfaceMock<ChatWindowFactory>();
- xmppRoster_ = boost::shared_ptr<XMPPRoster>(new XMPPRoster());
+ xmppRoster_ = new XMPPRoster();
mucRegistry_ = new MUCRegistry();
nickResolver_ = new NickResolver(jid_.toBare(), xmppRoster_, NULL, mucRegistry_);
presenceOracle_ = new PresenceOracle(stanzaChannel_);
@@ -195,7 +195,7 @@ public:
boost::shared_ptr<Presence> jid1Offline(new Presence());
jid1Offline->setFrom(JID(fullJIDString1));
jid1Offline->setType(Presence::Unavailable);
- presenceOracle_->onPresenceChange(jid1Offline, jid1Online);
+ presenceOracle_->onPresenceChange(jid1Offline);
boost::shared_ptr<Message> message2(new Message());
message2->setFrom(JID(fullJIDString2));
@@ -273,14 +273,14 @@ public:
boost::shared_ptr<Presence> jid1Offline(new Presence());
jid1Offline->setFrom(JID(messageJID1));
jid1Offline->setType(Presence::Unavailable);
- presenceOracle_->onPresenceChange(jid1Offline, jid1Online);
+ presenceOracle_->onPresenceChange(jid1Offline);
boost::shared_ptr<Presence> jid2Online(new Presence());
jid2Online->setFrom(JID(messageJID2));
boost::shared_ptr<Presence> jid2Offline(new Presence());
jid2Offline->setFrom(JID(messageJID2));
jid2Offline->setType(Presence::Unavailable);
- presenceOracle_->onPresenceChange(jid2Offline, jid2Online);
+ presenceOracle_->onPresenceChange(jid2Offline);
JID messageJID3("testling@test.com/resource3");
@@ -311,7 +311,7 @@ private:
PresenceOracle* presenceOracle_;
AvatarManager* avatarManager_;
boost::shared_ptr<DiscoInfo> serverDiscoInfo_;
- boost::shared_ptr<XMPPRoster> xmppRoster_;
+ XMPPRoster* xmppRoster_;
PresenceSender* presenceSender_;
MockRepository* mocks_;
UIEventStream* uiEventStream_;
diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp
index 834dacd..6221f21 100644
--- a/Swift/Controllers/MainController.cpp
+++ b/Swift/Controllers/MainController.cpp
@@ -12,11 +12,10 @@
#include <stdlib.h>
#include <sstream>
-#include "Swiften/Network/TimerFactory.h"
+#include "Swiften/Network/BoostTimerFactory.h"
#include "Swiften/Network/BoostIOServiceThread.h"
#include "Swiften/Network/MainBoostIOServiceThread.h"
#include "Swift/Controllers/BuildVersion.h"
-#include "Swift/Controllers/Chat/ChatController.h"
#include "Swiften/VCards/VCardStorageFactory.h"
#include "Swiften/VCards/VCardManager.h"
#include "Swiften/VCards/VCardStorage.h"
@@ -38,6 +37,7 @@
#include "Swift/Controllers/XMLConsoleController.h"
#include "Swift/Controllers/XMPPRosterController.h"
#include "Swift/Controllers/UIEvents/UIEventStream.h"
+#include "Swift/Controllers/PresenceNotifier.h"
#include "SwifTools/Dock/Dock.h"
#include "Swiften/Base/foreach.h"
#include "Swiften/Base/String.h"
@@ -59,6 +59,7 @@
#include "Swiften/Disco/EntityCapsManager.h"
#include "Swiften/StringCodecs/SHA1.h"
#include "Swiften/StringCodecs/Hexify.h"
+#include "Swift/Controllers/UIEvents/RequestChatUIEvent.h"
namespace Swift {
@@ -94,22 +95,27 @@ MainController::MainController(
vcardStorageFactory_(vcardStorageFactory),
loginWindow_(NULL) ,
useDelayForLatency_(useDelayForLatency) {
+
+ statusTracker_ = NULL;
+ client_ = NULL;
+ presenceSender_ = NULL;
presenceOracle_ = NULL;
- chatsManager_ = NULL;
- eventController_ = NULL;
- eventWindowController_ = NULL;
- nickResolver_ = NULL;
mucRegistry_ = NULL;
- avatarManager_ = NULL;
+ xmppRoster_ = NULL;
vcardManager_ = NULL;
+ avatarManager_ = NULL;
+ capsManager_ = NULL;
+ entityCapsManager_ = NULL;
+ presenceNotifier_ = NULL;
+ nickResolver_ = NULL;
rosterController_ = NULL;
xmppRosterController_ = NULL;
+ chatsManager_ = NULL;
+ eventWindowController_ = NULL;
clientVersionResponder_ = NULL;
discoResponder_ = NULL;
- presenceSender_ = NULL;
- client_ = NULL;
mucSearchController_ = NULL;
- statusTracker_ = NULL;
+
timeBeforeNextReconnect_ = -1;
mucSearchWindowFactory_ = mucSearchWindowFactory;
@@ -177,13 +183,24 @@ void MainController::resetClient() {
resetCurrentError();
resetPendingReconnects();
serverDiscoInfo_ = boost::shared_ptr<DiscoInfo>();
- xmppRoster_ = boost::shared_ptr<XMPPRoster>();
+ delete mucSearchController_;
+ mucSearchController_ = NULL;
+ delete discoResponder_;
+ discoResponder_ = NULL;
+ delete clientVersionResponder_;
+ clientVersionResponder_ = NULL;
+ delete eventWindowController_;
+ eventWindowController_ = NULL;
+ delete xmppRosterController_;
+ xmppRosterController_ = NULL;
delete chatsManager_;
chatsManager_ = NULL;
- delete presenceOracle_;
- presenceOracle_ = NULL;
+ delete rosterController_;
+ rosterController_ = NULL;
delete nickResolver_;
nickResolver_ = NULL;
+ delete presenceNotifier_;
+ presenceNotifier_ = NULL;
delete entityCapsManager_;
entityCapsManager_ = NULL;
delete capsManager_;
@@ -192,28 +209,20 @@ void MainController::resetClient() {
avatarManager_ = NULL;
delete vcardManager_;
vcardManager_ = NULL;
- delete eventWindowController_;
- eventWindowController_ = NULL;
- delete rosterController_;
- rosterController_ = NULL;
- delete xmppRosterController_;
- xmppRosterController_ = NULL;
- delete clientVersionResponder_;
- clientVersionResponder_ = NULL;
- delete discoResponder_;
- discoResponder_ = NULL;
+ delete xmppRoster_;
+ xmppRoster_ = NULL;
+ delete mucRegistry_;
+ mucRegistry_ = NULL;
+ delete presenceOracle_;
+ presenceOracle_ = NULL;
delete presenceSender_;
presenceSender_ = NULL;
delete client_;
client_ = NULL;
- delete mucSearchController_;
- mucSearchController_ = NULL;
delete statusTracker_;
statusTracker_ = NULL;
delete profileSettings_;
profileSettings_ = NULL;
- delete mucRegistry_;
- mucRegistry_ = NULL;
}
void MainController::resetPendingReconnects() {
@@ -239,18 +248,10 @@ void MainController::handleConnected() {
bool freshLogin = rosterController_ == NULL;
if (freshLogin) {
serverDiscoInfo_ = boost::shared_ptr<DiscoInfo>(new DiscoInfo());
- xmppRoster_ = boost::shared_ptr<XMPPRoster>(new XMPPRoster());
- presenceOracle_ = new PresenceOracle(client_);
- mucRegistry_ = new MUCRegistry();
- vcardManager_ = new VCardManager(jid_, client_->getIQRouter(), getVCardStorageForProfile(jid_));
- vcardManager_->onVCardChanged.connect(boost::bind(&MainController::handleVCardReceived, this, _1, _2));
- avatarManager_ = new AvatarManagerImpl(vcardManager_, client_, avatarStorage_, mucRegistry_);
- capsManager_ = new CapsManager(capsStorage_, client_, client_->getIQRouter());
- entityCapsManager_ = new EntityCapsManager(capsManager_, client_);
nickResolver_ = new NickResolver(this->jid_.toBare(), xmppRoster_, vcardManager_, mucRegistry_);
- rosterController_ = new RosterController(jid_, xmppRoster_, avatarManager_, mainWindowFactory_, nickResolver_, presenceOracle_, eventController_, uiEventStream_, client_->getIQRouter());
+ rosterController_ = new RosterController(jid_, xmppRoster_, avatarManager_, mainWindowFactory_, nickResolver_, presenceOracle_, presenceSender_, eventController_, uiEventStream_, client_->getIQRouter());
rosterController_->onChangeStatusRequest.connect(boost::bind(&MainController::handleChangeStatusRequest, this, _1, _2));
rosterController_->onSignOutRequest.connect(boost::bind(&MainController::signOut, this));
@@ -384,6 +385,16 @@ void MainController::performLoginFromCachedCredentials() {
if (!client_) {
client_ = new Swift::Client(jid_, password_);
presenceSender_ = new PresenceSender(client_);
+ presenceOracle_ = new PresenceOracle(client_);
+ mucRegistry_ = new MUCRegistry();
+ xmppRoster_ = new XMPPRoster();
+ vcardManager_ = new VCardManager(jid_, client_->getIQRouter(), getVCardStorageForProfile(jid_));
+ vcardManager_->onVCardChanged.connect(boost::bind(&MainController::handleVCardReceived, this, _1, _2));
+ avatarManager_ = new AvatarManagerImpl(vcardManager_, client_, avatarStorage_, mucRegistry_);
+ capsManager_ = new CapsManager(capsStorage_, client_, client_->getIQRouter());
+ entityCapsManager_ = new EntityCapsManager(capsManager_, client_);
+ presenceNotifier_ = new PresenceNotifier(client_, notifier_, mucRegistry_, avatarManager_, xmppRoster_, presenceOracle_, &timerFactory_);
+ presenceNotifier_->onNotificationActivated.connect(boost::bind(&MainController::handleNotificationClicked, this, _1));
client_->onDataRead.connect(boost::bind(
&XMLConsoleController::handleDataRead, xmlConsoleController_, _1));
client_->onDataWritten.connect(boost::bind(
@@ -510,6 +521,11 @@ void MainController::handleVCardReceived(const JID& jid, VCard::ref vCard) {
}
}
+void MainController::handleNotificationClicked(const JID& jid) {
+ assert(chatsManager_);
+ uiEventStream_->send(boost::shared_ptr<UIEvent>(new RequestChatUIEvent(jid)));
+}
+
VCardStorage* MainController::getVCardStorageForProfile(const JID& jid) {
String profile = jid.toBare().toString();
std::pair<VCardStorageMap::iterator, bool> r = vcardStorages_.insert(std::make_pair<String, VCardStorage*>(profile, NULL));
diff --git a/Swift/Controllers/MainController.h b/Swift/Controllers/MainController.h
index 7fbf54f..df12a17 100644
--- a/Swift/Controllers/MainController.h
+++ b/Swift/Controllers/MainController.h
@@ -56,6 +56,7 @@ namespace Swift {
class MUCController;
class Notifier;
class PresenceOracle;
+ class PresenceNotifier;
class SystemTray;
class SystemTrayController;
class SoundEventController;
@@ -115,6 +116,7 @@ namespace Swift {
void performLoginFromCachedCredentials();
void reconnectAfterError();
void setManagersEnabled(bool enabled);
+ void handleNotificationClicked(const JID& jid);
VCardStorage* getVCardStorageForProfile(const JID& jid);
@@ -137,7 +139,7 @@ namespace Swift {
VCardManager* vcardManager_;
Dock* dock_;
Notifier* notifier_;
- ChatController* chatController_;
+ PresenceNotifier* presenceNotifier_;
XMPPRosterController* xmppRosterController_;
RosterController* rosterController_;
EventController* eventController_;
@@ -151,7 +153,7 @@ namespace Swift {
ChatsManager* chatsManager_;
boost::shared_ptr<CapsInfo> capsInfo_;
boost::shared_ptr<DiscoInfo> serverDiscoInfo_;
- boost::shared_ptr<XMPPRoster> xmppRoster_;;
+ XMPPRoster* xmppRoster_;;
JID jid_;
PresenceOracle* presenceOracle_;
SystemTrayController* systemTrayController_;
diff --git a/Swift/Controllers/NickResolver.cpp b/Swift/Controllers/NickResolver.cpp
index b6fefe3..8faada9 100644
--- a/Swift/Controllers/NickResolver.cpp
+++ b/Swift/Controllers/NickResolver.cpp
@@ -15,7 +15,7 @@
namespace Swift {
-NickResolver::NickResolver(const JID& ownJID, boost::shared_ptr<XMPPRoster> xmppRoster, VCardManager* vcardManager, MUCRegistry* mucRegistry) : ownJID_(ownJID) {
+NickResolver::NickResolver(const JID& ownJID, XMPPRoster* xmppRoster, VCardManager* vcardManager, MUCRegistry* mucRegistry) : ownJID_(ownJID) {
xmppRoster_ = xmppRoster;
vcardManager_ = vcardManager;
if (vcardManager_) {
diff --git a/Swift/Controllers/NickResolver.h b/Swift/Controllers/NickResolver.h
index 24081b2..b5ed76f 100644
--- a/Swift/Controllers/NickResolver.h
+++ b/Swift/Controllers/NickResolver.h
@@ -21,7 +21,7 @@ namespace Swift {
class VCardManager;
class NickResolver {
public:
- NickResolver(const JID& ownJID, boost::shared_ptr<XMPPRoster> xmppRoster, VCardManager* vcardManager, MUCRegistry* mucRegistry);
+ NickResolver(const JID& ownJID, XMPPRoster* xmppRoster, VCardManager* vcardManager, MUCRegistry* mucRegistry);
String jidToNick(const JID& jid);
void setMUCRegistry(MUCRegistry* registry);
@@ -33,7 +33,7 @@ namespace Swift {
String ownNick_;
std::map<JID, String> map_;
- boost::shared_ptr<XMPPRoster> xmppRoster_;
+ XMPPRoster* xmppRoster_;
MUCRegistry* mucRegistry_;
VCardManager* vcardManager_;
};
diff --git a/Swift/Controllers/PresenceNotifier.cpp b/Swift/Controllers/PresenceNotifier.cpp
new file mode 100644
index 0000000..ce7ae40
--- /dev/null
+++ b/Swift/Controllers/PresenceNotifier.cpp
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include "Swift/Controllers/PresenceNotifier.h"
+
+#include <boost/bind.hpp>
+
+#include "Swiften/Client/StanzaChannel.h"
+#include "Swiften/Base/ByteArray.h"
+#include "Swiften/MUC/MUCRegistry.h"
+#include "Swiften/Roster/XMPPRoster.h"
+#include "Swiften/Presence/PresenceOracle.h"
+#include "Swiften/Network/TimerFactory.h"
+
+namespace Swift {
+
+PresenceNotifier::PresenceNotifier(StanzaChannel* stanzaChannel, Notifier* notifier, const MUCRegistry* mucRegistry, AvatarManager* avatarManager, const XMPPRoster* roster, const PresenceOracle* presenceOracle, TimerFactory* timerFactory) : stanzaChannel(stanzaChannel), notifier(notifier), mucRegistry(mucRegistry), avatarManager(avatarManager), roster(roster), presenceOracle(presenceOracle), timerFactory(timerFactory) {
+ justInitialized = true;
+ inQuietPeriod = false;
+ stanzaChannel->onPresenceReceived.connect(boost::bind(&PresenceNotifier::handlePresenceReceived, this, _1));
+ stanzaChannel->onAvailableChanged.connect(boost::bind(&PresenceNotifier::handleStanzaChannelAvailableChanged, this, _1));
+ setInitialQuietPeriodMS(3000);
+}
+
+PresenceNotifier::~PresenceNotifier() {
+ if (timer) {
+ timer->stop();
+ timer->onTick.disconnect(boost::bind(&PresenceNotifier::handleTimerTick, this));
+ timer.reset();
+ }
+ stanzaChannel->onAvailableChanged.disconnect(boost::bind(&PresenceNotifier::handleStanzaChannelAvailableChanged, this, _1));
+ stanzaChannel->onPresenceReceived.disconnect(boost::bind(&PresenceNotifier::handlePresenceReceived, this, _1));
+}
+
+void PresenceNotifier::handlePresenceReceived(boost::shared_ptr<Presence> presence) {
+ JID from = presence->getFrom();
+
+ if (mucRegistry->isMUC(from.toBare())) {
+ return;
+ }
+
+ if (justInitialized) {
+ justInitialized = false;
+ if (timer) {
+ inQuietPeriod = true;
+ }
+ }
+
+ if (inQuietPeriod) {
+ timer->stop();
+ timer->start();
+ return;
+ }
+
+ std::set<JID>::iterator i = availableUsers.find(from);
+ if (presence->isAvailable()) {
+ if (i != availableUsers.end()) {
+ showNotification(from, Notifier::ContactStatusChange);
+ }
+ else {
+ showNotification(from, Notifier::ContactAvailable);
+ availableUsers.insert(from);
+ }
+ }
+ else {
+ if (i != availableUsers.end()) {
+ showNotification(from, Notifier::ContactUnavailable);
+ availableUsers.erase(i);
+ }
+ }
+}
+
+void PresenceNotifier::handleStanzaChannelAvailableChanged(bool available) {
+ if (available) {
+ availableUsers.clear();
+ justInitialized = true;
+ if (timer) {
+ timer->stop();
+ }
+ }
+}
+
+void PresenceNotifier::showNotification(const JID& jid, Notifier::Type type) {
+ String name = roster->getNameForJID(jid);
+ if (name.isEmpty()) {
+ name = jid.toBare().toString();
+ }
+ String title = name + " (" + getStatusType(jid) + ")";
+ String message = getStatusMessage(jid);
+ notifier->showMessage(type, title, message, avatarManager->getAvatar(jid), boost::bind(&PresenceNotifier::handleNotificationActivated, this, jid));
+}
+
+void PresenceNotifier::handleNotificationActivated(JID jid) {
+ onNotificationActivated(jid);
+}
+
+String PresenceNotifier::getStatusType(const JID& jid) const {
+ Presence::ref presence = presenceOracle->getLastPresence(jid);
+ if (presence) {
+ return StatusShow::typeToFriendlyName(presence->getShow());
+ }
+ else {
+ return "Unavailable";
+ }
+}
+
+String PresenceNotifier::getStatusMessage(const JID& jid) const {
+ Presence::ref presence = presenceOracle->getLastPresence(jid);
+ if (presence) {
+ return presence->getStatus();
+ }
+ else {
+ return String();
+ }
+}
+
+void PresenceNotifier::setInitialQuietPeriodMS(int ms) {
+ if (timer) {
+ timer->stop();
+ timer->onTick.disconnect(boost::bind(&PresenceNotifier::handleTimerTick, this));
+ timer.reset();
+ }
+ if (ms > 0) {
+ timer = timerFactory->createTimer(ms);
+ timer->onTick.connect(boost::bind(&PresenceNotifier::handleTimerTick, this));
+ }
+}
+
+void PresenceNotifier::handleTimerTick() {
+ inQuietPeriod = false;
+ timer->stop();
+}
+
+
+}
diff --git a/Swift/Controllers/PresenceNotifier.h b/Swift/Controllers/PresenceNotifier.h
new file mode 100644
index 0000000..f5bf3d4
--- /dev/null
+++ b/Swift/Controllers/PresenceNotifier.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <set>
+
+#include "Swiften/Base/boost_bsignals.h"
+#include "Swiften/Elements/Presence.h"
+#include "Swiften/JID/JID.h"
+#include "SwifTools/Notifier/Notifier.h"
+#include "Swiften/Avatars/AvatarManager.h"
+#include "Swiften/Network/Timer.h"
+
+namespace Swift {
+ class TimerFactory;
+ class StanzaChannel;
+ class MUCRegistry;
+ class XMPPRoster;
+ class PresenceOracle;
+
+ class PresenceNotifier {
+ public:
+ PresenceNotifier(StanzaChannel* stanzaChannel, Notifier* notifier, const MUCRegistry* mucRegistry, AvatarManager* avatarManager, const XMPPRoster* roster, const PresenceOracle* presenceOracle, TimerFactory* timerFactory);
+ ~PresenceNotifier();
+
+ void setInitialQuietPeriodMS(int ms);
+
+ boost::signal<void (const JID&)> onNotificationActivated;
+
+ private:
+ void handlePresenceReceived(boost::shared_ptr<Presence>);
+ void handleStanzaChannelAvailableChanged(bool);
+ void handleNotificationActivated(JID jid);
+ void handleTimerTick();
+ String getStatusType(const JID&) const;
+ String getStatusMessage(const JID&) const;
+
+ private:
+ void showNotification(const JID& jid, Notifier::Type type);
+
+ private:
+ StanzaChannel* stanzaChannel;
+ Notifier* notifier;
+ const MUCRegistry* mucRegistry;
+ AvatarManager* avatarManager;
+ const XMPPRoster* roster;
+ const PresenceOracle* presenceOracle;
+ TimerFactory* timerFactory;
+ boost::shared_ptr<Timer> timer;
+ bool justInitialized;
+ bool inQuietPeriod;
+ std::set<JID> availableUsers;
+ };
+}
+
diff --git a/Swift/Controllers/RosterController.cpp b/Swift/Controllers/RosterController.cpp
index 7285f38..da10e5b 100644
--- a/Swift/Controllers/RosterController.cpp
+++ b/Swift/Controllers/RosterController.cpp
@@ -17,6 +17,7 @@
#include "Swiften/Events/SubscriptionRequestEvent.h"
#include "Swiften/Events/ErrorEvent.h"
#include "Swiften/Presence/PresenceOracle.h"
+#include "Swiften/Presence/PresenceSender.h"
#include "Swift/Controllers/EventController.h"
#include "Swiften/Queries/IQRouter.h"
#include "Swiften/Roster/Roster.h"
@@ -35,10 +36,11 @@ namespace Swift {
/**
* The controller does not gain ownership of these parameters.
*/
-RosterController::RosterController(const JID& jid, boost::shared_ptr<XMPPRoster> xmppRoster, AvatarManager* avatarManager, MainWindowFactory* mainWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, EventController* eventController, UIEventStream* uiEventStream, IQRouter* iqRouter)
+RosterController::RosterController(const JID& jid, XMPPRoster* xmppRoster, AvatarManager* avatarManager, MainWindowFactory* mainWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, PresenceSender* presenceSender, EventController* eventController, UIEventStream* uiEventStream, IQRouter* iqRouter)
: myJID_(jid), xmppRoster_(xmppRoster), mainWindowFactory_(mainWindowFactory), mainWindow_(mainWindowFactory_->createMainWindow(uiEventStream)), roster_(new Roster()), offlineFilter_(new OfflineRosterFilter()) {
iqRouter_ = iqRouter;
presenceOracle_ = presenceOracle;
+ presenceSender_ = presenceSender;
eventController_ = eventController;
roster_->addFilter(offlineFilter_);
mainWindow_->setRosterModel(roster_);
@@ -51,7 +53,7 @@ RosterController::RosterController(const JID& jid, boost::shared_ptr<XMPPRoster>
xmppRoster_->onJIDRemoved.connect(boost::bind(&RosterController::handleOnJIDRemoved, this, _1));
xmppRoster_->onRosterCleared.connect(boost::bind(&RosterController::handleRosterCleared, this));
presenceOracle_->onPresenceSubscriptionRequest.connect(boost::bind(&RosterController::handleSubscriptionRequest, this, _1, _2));
- presenceOracle_->onPresenceChange.connect(boost::bind(&RosterController::handleIncomingPresence, this, _1, _2));
+ presenceOracle_->onPresenceChange.connect(boost::bind(&RosterController::handleIncomingPresence, this, _1));
uiEventConnection_ = uiEventStream->onUIEvent.connect(boost::bind(&RosterController::handleUIEvent, this, _1));
avatarManager_ = avatarManager;
avatarManager_->onAvatarChanged.connect(boost::bind(&RosterController::handleAvatarChanged, this, _1));
@@ -156,7 +158,7 @@ void RosterController::handleUIEvent(boost::shared_ptr<UIEvent> event) {
boost::shared_ptr<SetRosterRequest> request(new SetRosterRequest(roster, iqRouter_));
request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster));
request->send();
- presenceOracle_->requestSubscription(addContactEvent->getJID());
+ presenceSender_->requestSubscription(addContactEvent->getJID());
return;
}
boost::shared_ptr<RemoveRosterItemUIEvent> removeEvent = boost::dynamic_pointer_cast<RemoveRosterItemUIEvent>(event);
@@ -185,13 +187,13 @@ void RosterController::handleRosterSetError(boost::optional<ErrorPayload> error,
eventController_->handleIncomingEvent(errorEvent);
}
-void RosterController::handleIncomingPresence(boost::shared_ptr<Presence> newPresence, boost::shared_ptr<Presence> /*oldPresence*/) {
+void RosterController::handleIncomingPresence(boost::shared_ptr<Presence> newPresence) {
roster_->applyOnItems(SetPresence(newPresence));
}
void RosterController::handleSubscriptionRequest(const JID& jid, const String& message) {
if (xmppRoster_->containsJID(jid) && (xmppRoster_->getSubscriptionStateForJID(jid) == RosterItemPayload::To || xmppRoster_->getSubscriptionStateForJID(jid) == RosterItemPayload::Both)) {
- presenceOracle_->confirmSubscription(jid);
+ presenceSender_->confirmSubscription(jid);
return;
}
SubscriptionRequestEvent* eventPointer = new SubscriptionRequestEvent(jid, message);
@@ -202,14 +204,14 @@ void RosterController::handleSubscriptionRequest(const JID& jid, const String& m
}
void RosterController::handleSubscriptionRequestAccepted(SubscriptionRequestEvent* event) {
- presenceOracle_->confirmSubscription(event->getJID());
+ presenceSender_->confirmSubscription(event->getJID());
if (!xmppRoster_->containsJID(event->getJID()) || xmppRoster_->getSubscriptionStateForJID(event->getJID()) == RosterItemPayload::None || xmppRoster_->getSubscriptionStateForJID(event->getJID()) == RosterItemPayload::From) {
- presenceOracle_->requestSubscription(event->getJID());
+ presenceSender_->requestSubscription(event->getJID());
}
}
void RosterController::handleSubscriptionRequestDeclined(SubscriptionRequestEvent* event) {
- presenceOracle_->cancelSubscription(event->getJID());
+ presenceSender_->cancelSubscription(event->getJID());
}
void RosterController::handleAvatarChanged(const JID& jid) {
diff --git a/Swift/Controllers/RosterController.h b/Swift/Controllers/RosterController.h
index 389df44..80e7e3e 100644
--- a/Swift/Controllers/RosterController.h
+++ b/Swift/Controllers/RosterController.h
@@ -27,6 +27,7 @@ namespace Swift {
class OfflineRosterFilter;
class NickResolver;
class PresenceOracle;
+ class PresenceSender;
class EventController;
class SubscriptionRequestEvent;
class UIEventStream;
@@ -34,7 +35,7 @@ namespace Swift {
class RosterController {
public:
- RosterController(const JID& jid, boost::shared_ptr<XMPPRoster> xmppRoster, AvatarManager* avatarManager, MainWindowFactory* mainWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, EventController* eventController, UIEventStream* uiEventStream, IQRouter* iqRouter_);
+ RosterController(const JID& jid, XMPPRoster* xmppRoster, AvatarManager* avatarManager, MainWindowFactory* mainWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, PresenceSender* presenceSender, EventController* eventController, UIEventStream* uiEventStream, IQRouter* iqRouter_);
~RosterController();
void showRosterWindow();
MainWindow* getWindow() {return mainWindow_;};
@@ -51,7 +52,7 @@ namespace Swift {
void handleStartChatRequest(const JID& contact);
void handleChangeStatusRequest(StatusShow::Type show, const String &statusText);
void handleShowOfflineToggled(bool state);
- void handleIncomingPresence(boost::shared_ptr<Presence> newPresence, boost::shared_ptr<Presence> oldPresence);
+ void handleIncomingPresence(boost::shared_ptr<Presence> newPresence);
void handleSubscriptionRequest(const JID& jid, const String& message);
void handleSubscriptionRequestAccepted(SubscriptionRequestEvent* event);
void handleSubscriptionRequestDeclined(SubscriptionRequestEvent* event);
@@ -59,7 +60,7 @@ namespace Swift {
void handleRosterSetError(boost::optional<ErrorPayload> error, boost::shared_ptr<RosterPayload> rosterPayload);
void handleOwnNickChanged(const String& nick);
JID myJID_;
- boost::shared_ptr<XMPPRoster> xmppRoster_;
+ XMPPRoster* xmppRoster_;
MainWindowFactory* mainWindowFactory_;
MainWindow* mainWindow_;
Roster* roster_;
@@ -67,6 +68,7 @@ namespace Swift {
AvatarManager* avatarManager_;
NickResolver* nickResolver_;
PresenceOracle* presenceOracle_;
+ PresenceSender* presenceSender_;
EventController* eventController_;
IQRouter* iqRouter_;
boost::bsignals::scoped_connection changeStatusConnection_;
diff --git a/Swift/Controllers/SConscript b/Swift/Controllers/SConscript
index 1ccee64..30c9590 100644
--- a/Swift/Controllers/SConscript
+++ b/Swift/Controllers/SConscript
@@ -36,6 +36,7 @@ if env["SCONS_STAGE"] == "build" :
"SystemTrayController.cpp",
"XMLConsoleController.cpp",
"StatusTracker.cpp",
+ "PresenceNotifier.cpp",
"UIEvents/UIEvent.cpp",
"UIInterfaces/XMLConsoleWidget.cpp",
"UIInterfaces/ChatListWindow.cpp",
@@ -47,6 +48,7 @@ if env["SCONS_STAGE"] == "build" :
File("UnitTest/RosterControllerTest.cpp"),
File("UnitTest/XMPPRosterControllerTest.cpp"),
File("UnitTest/PreviousStatusStoreTest.cpp"),
+ File("UnitTest/PresenceNotifierTest.cpp"),
File("Chat/UnitTest/ChatsManagerTest.cpp"),
File("Chat/UnitTest/MUCControllerTest.cpp"),
File("UnitTest/MockChatWindow.cpp"),
diff --git a/Swift/Controllers/UnitTest/NickResolverTest.cpp b/Swift/Controllers/UnitTest/NickResolverTest.cpp
index dfb459f..f42a28a 100644
--- a/Swift/Controllers/UnitTest/NickResolverTest.cpp
+++ b/Swift/Controllers/UnitTest/NickResolverTest.cpp
@@ -35,7 +35,7 @@ class NickResolverTest : public CppUnit::TestFixture {
public:
void setUp() {
ownJID_ = JID("kev@wonderland.lit");
- xmppRoster_ = boost::shared_ptr<XMPPRoster>(new XMPPRoster());
+ xmppRoster_ = new XMPPRoster();
stanzaChannel_ = new DummyStanzaChannel();
iqRouter_ = new IQRouter(stanzaChannel_);
vCardStorage_ = new VCardMemoryStorage();
@@ -135,7 +135,7 @@ class NickResolverTest : public CppUnit::TestFixture {
private:
std::vector<String> groups_;
- boost::shared_ptr<XMPPRoster> xmppRoster_;
+ XMPPRoster* xmppRoster_;
VCardStorage* vCardStorage_;
IQRouter* iqRouter_;
DummyStanzaChannel* stanzaChannel_;
diff --git a/Swift/Controllers/UnitTest/PresenceNotifierTest.cpp b/Swift/Controllers/UnitTest/PresenceNotifierTest.cpp
new file mode 100644
index 0000000..85433f3
--- /dev/null
+++ b/Swift/Controllers/UnitTest/PresenceNotifierTest.cpp
@@ -0,0 +1,310 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <vector>
+#include <boost/bind.hpp>
+
+#include "Swift/Controllers/PresenceNotifier.h"
+#include "SwifTools/Notifier/LoggingNotifier.h"
+#include "Swiften/Client/DummyStanzaChannel.h"
+#include "Swiften/MUC/MUCRegistry.h"
+#include "Swiften/Roster/XMPPRoster.h"
+#include "Swiften/Presence/PresenceOracle.h"
+#include "Swiften/Avatars/DummyAvatarManager.h"
+#include "Swiften/Network/DummyTimerFactory.h"
+
+using namespace Swift;
+
+class PresenceNotifierTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(PresenceNotifierTest);
+ CPPUNIT_TEST(testReceiveFirstPresenceCreatesAvailableNotification);
+ CPPUNIT_TEST(testReceiveSecondPresenceCreatesStatusChangeNotification);
+ CPPUNIT_TEST(testReceiveUnavailablePresenceAfterAvailablePresenceCreatesUnavailableNotification);
+ CPPUNIT_TEST(testReceiveUnavailablePresenceWithoutAvailableDoesNotCreateNotification);
+ CPPUNIT_TEST(testReceiveAvailablePresenceAfterUnavailableCreatesAvailableNotification);
+ CPPUNIT_TEST(testReceiveAvailablePresenceAfterReconnectCreatesAvailableNotification);
+ CPPUNIT_TEST(testReceiveAvailablePresenceFromMUCDoesNotCreateNotification);
+ CPPUNIT_TEST(testNotificationSubjectContainsNameForJIDInRoster);
+ CPPUNIT_TEST(testNotificationSubjectContainsJIDForJIDNotInRoster);
+ CPPUNIT_TEST(testNotificationSubjectContainsStatus);
+ CPPUNIT_TEST(testNotificationMessageContainsStatusMessage);
+ CPPUNIT_TEST(testNotificationPicture);
+ CPPUNIT_TEST(testNotificationActivationEmitsSignal);
+ CPPUNIT_TEST(testReceiveFirstPresenceWithQuietPeriodDoesNotNotify);
+ CPPUNIT_TEST(testReceiveFirstPresenceWithQuietPeriodDoesNotCountAsQuietPeriod);
+ CPPUNIT_TEST(testReceivePresenceDuringQuietPeriodDoesNotNotify);
+ CPPUNIT_TEST(testReceivePresenceDuringQuietPeriodResetsTimer);
+ CPPUNIT_TEST(testReceivePresenceAfterQuietPeriodNotifies);
+ CPPUNIT_TEST(testReceiveFirstPresenceAfterReconnectWithQuietPeriodDoesNotNotify);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ void setUp() {
+ stanzaChannel = new DummyStanzaChannel();
+ notifier = new LoggingNotifier();
+ mucRegistry = new MUCRegistry();
+ user1 = JID("user1@bar.com/bla");
+ user2 = JID("user2@foo.com/baz");
+ avatarManager = new DummyAvatarManager();
+ roster = new XMPPRoster();
+ presenceOracle = new PresenceOracle(stanzaChannel);
+ timerFactory = new DummyTimerFactory();
+ }
+
+ void tearDown() {
+ delete presenceOracle;
+ delete roster;
+ delete avatarManager;
+ delete mucRegistry;
+ delete notifier;
+ delete stanzaChannel;
+ }
+
+ void testReceiveFirstPresenceCreatesAvailableNotification() {
+ std::auto_ptr<PresenceNotifier> testling = createNotifier();
+
+ sendPresence(user1, StatusShow::Online);
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(notifier->notifications.size()));
+ CPPUNIT_ASSERT_EQUAL(Notifier::ContactAvailable, notifier->notifications[0].type);
+ }
+
+ void testReceiveSecondPresenceCreatesStatusChangeNotification() {
+ std::auto_ptr<PresenceNotifier> testling = createNotifier();
+ sendPresence(user1, StatusShow::Away);
+ notifier->notifications.clear();
+
+ sendPresence(user1, StatusShow::Online);
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(notifier->notifications.size()));
+ CPPUNIT_ASSERT_EQUAL(Notifier::ContactStatusChange, notifier->notifications[0].type);
+ }
+
+ void testReceiveUnavailablePresenceAfterAvailablePresenceCreatesUnavailableNotification() {
+ std::auto_ptr<PresenceNotifier> testling = createNotifier();
+ sendPresence(user1, StatusShow::Away);
+ notifier->notifications.clear();
+
+ sendUnavailablePresence(user1);
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(notifier->notifications.size()));
+ CPPUNIT_ASSERT_EQUAL(Notifier::ContactUnavailable, notifier->notifications[0].type);
+ }
+
+ void testReceiveUnavailablePresenceWithoutAvailableDoesNotCreateNotification() {
+ std::auto_ptr<PresenceNotifier> testling = createNotifier();
+
+ sendUnavailablePresence(user1);
+
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(notifier->notifications.size()));
+ }
+
+ void testReceiveAvailablePresenceAfterUnavailableCreatesAvailableNotification() {
+ std::auto_ptr<PresenceNotifier> testling = createNotifier();
+ sendPresence(user1, StatusShow::Away);
+ sendUnavailablePresence(user1);
+ notifier->notifications.clear();
+
+ sendPresence(user1, StatusShow::Away);
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(notifier->notifications.size()));
+ CPPUNIT_ASSERT_EQUAL(Notifier::ContactAvailable, notifier->notifications[0].type);
+ }
+
+ void testReceiveAvailablePresenceAfterReconnectCreatesAvailableNotification() {
+ std::auto_ptr<PresenceNotifier> testling = createNotifier();
+ sendPresence(user1, StatusShow::Away);
+ stanzaChannel->setAvailable(false);
+ stanzaChannel->setAvailable(true);
+ notifier->notifications.clear();
+
+ sendPresence(user1, StatusShow::Away);
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(notifier->notifications.size()));
+ CPPUNIT_ASSERT_EQUAL(Notifier::ContactAvailable, notifier->notifications[0].type);
+ }
+
+ void testReceiveAvailablePresenceFromMUCDoesNotCreateNotification() {
+ std::auto_ptr<PresenceNotifier> testling = createNotifier();
+ mucRegistry->addMUC(JID("teaparty@wonderland.lit"));
+
+ sendPresence(JID("teaparty@wonderland.lit/Alice"), StatusShow::Away);
+
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(notifier->notifications.size()));
+ }
+
+ void testNotificationPicture() {
+ std::auto_ptr<PresenceNotifier> testling = createNotifier();
+ avatarManager->avatars[user1] = ByteArray("abcdef");
+
+ sendPresence(user1, StatusShow::Online);
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(notifier->notifications.size()));
+ CPPUNIT_ASSERT_EQUAL(ByteArray("abcdef"), notifier->notifications[0].picture);
+ }
+
+ void testNotificationActivationEmitsSignal() {
+ std::auto_ptr<PresenceNotifier> testling = createNotifier();
+
+ sendPresence(user1, StatusShow::Online);
+ CPPUNIT_ASSERT(notifier->notifications[0].callback);
+ notifier->notifications[0].callback();
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(activatedNotifications.size()));
+ CPPUNIT_ASSERT_EQUAL(user1, activatedNotifications[0]);
+ }
+
+ void testNotificationSubjectContainsNameForJIDInRoster() {
+ std::auto_ptr<PresenceNotifier> testling = createNotifier();
+ roster->addContact(user1.toBare(), "User 1", std::vector<String>(), RosterItemPayload::Both);
+
+ sendPresence(user1, StatusShow::Online);
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(notifier->notifications.size()));
+ CPPUNIT_ASSERT(notifier->notifications[0].subject.contains("User 1"));
+ }
+
+ void testNotificationSubjectContainsJIDForJIDNotInRoster() {
+ std::auto_ptr<PresenceNotifier> testling = createNotifier();
+
+ sendPresence(user1, StatusShow::Online);
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(notifier->notifications.size()));
+ CPPUNIT_ASSERT(notifier->notifications[0].subject.contains(user1.toBare().toString()));
+ }
+
+ void testNotificationSubjectContainsStatus() {
+ std::auto_ptr<PresenceNotifier> testling = createNotifier();
+
+ sendPresence(user1, StatusShow::Away);
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(notifier->notifications.size()));
+ CPPUNIT_ASSERT(notifier->notifications[0].subject.contains("Away"));
+ }
+
+ void testNotificationMessageContainsStatusMessage() {
+ std::auto_ptr<PresenceNotifier> testling = createNotifier();
+
+ sendPresence(user1, StatusShow::Away);
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(notifier->notifications.size()));
+ CPPUNIT_ASSERT(notifier->notifications[0].description.contains("Status Message"));
+ }
+
+ void testReceiveFirstPresenceWithQuietPeriodDoesNotNotify() {
+ std::auto_ptr<PresenceNotifier> testling = createNotifier();
+ testling->setInitialQuietPeriodMS(10);
+
+ sendPresence(user1, StatusShow::Online);
+
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(notifier->notifications.size()));
+ }
+
+ void testReceivePresenceDuringQuietPeriodDoesNotNotify() {
+ std::auto_ptr<PresenceNotifier> testling = createNotifier();
+ testling->setInitialQuietPeriodMS(10);
+
+ sendPresence(user1, StatusShow::Online);
+ timerFactory->setTime(1);
+ sendPresence(user2, StatusShow::Away);
+
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(notifier->notifications.size()));
+ }
+
+ void testReceivePresenceDuringQuietPeriodResetsTimer() {
+ std::auto_ptr<PresenceNotifier> testling = createNotifier();
+ testling->setInitialQuietPeriodMS(10);
+
+ sendPresence(user1, StatusShow::Online);
+ timerFactory->setTime(9);
+ sendPresence(user2, StatusShow::Away);
+ timerFactory->setTime(18);
+ sendPresence(user1, StatusShow::Away);
+
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(notifier->notifications.size()));
+ }
+
+ void testReceivePresenceAfterQuietPeriodNotifies() {
+ std::auto_ptr<PresenceNotifier> testling = createNotifier();
+ testling->setInitialQuietPeriodMS(10);
+
+ sendPresence(user1, StatusShow::Online);
+ timerFactory->setTime(11);
+ sendPresence(user2, StatusShow::Away);
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(notifier->notifications.size()));
+ }
+
+ void testReceiveFirstPresenceWithQuietPeriodDoesNotCountAsQuietPeriod() {
+ std::auto_ptr<PresenceNotifier> testling = createNotifier();
+ testling->setInitialQuietPeriodMS(10);
+
+ timerFactory->setTime(11);
+ sendPresence(user1, StatusShow::Away);
+
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(notifier->notifications.size()));
+ }
+
+ void testReceiveFirstPresenceAfterReconnectWithQuietPeriodDoesNotNotify() {
+ std::auto_ptr<PresenceNotifier> testling = createNotifier();
+ testling->setInitialQuietPeriodMS(10);
+ sendPresence(user1, StatusShow::Online);
+ timerFactory->setTime(15);
+ notifier->notifications.clear();
+
+ stanzaChannel->setAvailable(false);
+ stanzaChannel->setAvailable(true);
+ sendPresence(user1, StatusShow::Online);
+ timerFactory->setTime(21);
+ sendPresence(user2, StatusShow::Online);
+
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(notifier->notifications.size()));
+ }
+
+
+ private:
+ std::auto_ptr<PresenceNotifier> createNotifier() {
+ std::auto_ptr<PresenceNotifier> result(new PresenceNotifier(stanzaChannel, notifier, mucRegistry, avatarManager, roster, presenceOracle, timerFactory));
+ result->onNotificationActivated.connect(boost::bind(&PresenceNotifierTest::handleNotificationActivated, this, _1));
+ result->setInitialQuietPeriodMS(0);
+ return result;
+ }
+
+ void sendPresence(const JID& jid, StatusShow::Type type) {
+ boost::shared_ptr<Presence> presence(new Presence());
+ presence->setFrom(jid);
+ presence->setShow(type);
+ presence->setStatus("Status Message");
+ stanzaChannel->onPresenceReceived(presence);
+ }
+
+ void sendUnavailablePresence(const JID& jid) {
+ boost::shared_ptr<Presence> presence(new Presence());
+ presence->setType(Presence::Unavailable);
+ presence->setFrom(jid);
+ stanzaChannel->onPresenceReceived(presence);
+ }
+
+ void handleNotificationActivated(const JID& j) {
+ activatedNotifications.push_back(j);
+ }
+
+ private:
+ DummyStanzaChannel* stanzaChannel;
+ LoggingNotifier* notifier;
+ MUCRegistry* mucRegistry;
+ DummyAvatarManager* avatarManager;
+ XMPPRoster* roster;
+ PresenceOracle* presenceOracle;
+ DummyTimerFactory* timerFactory;
+ JID user1;
+ JID user2;
+ std::vector<JID> activatedNotifications;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(PresenceNotifierTest);
diff --git a/Swift/Controllers/UnitTest/RosterControllerTest.cpp b/Swift/Controllers/UnitTest/RosterControllerTest.cpp
index fdcc44f..174b682 100644
--- a/Swift/Controllers/UnitTest/RosterControllerTest.cpp
+++ b/Swift/Controllers/UnitTest/RosterControllerTest.cpp
@@ -23,6 +23,7 @@
#include "Swiften/Avatars/NullAvatarManager.h"
#include "Swift/Controllers/EventController.h"
#include "Swiften/Presence/PresenceOracle.h"
+#include "Swiften/Presence/PresenceSender.h"
#include "Swift/Controllers/NickResolver.h"
#include "Swift/Controllers/UIEvents/UIEventStream.h"
#include "Swiften/MUC/MUCRegistry.h"
@@ -30,6 +31,7 @@
using namespace Swift;
#define CHILDREN mainWindow_->roster->getRoot()->getChildren()
+
class RosterControllerTest : public CppUnit::TestFixture
{
CPPUNIT_TEST_SUITE(RosterControllerTest);
@@ -43,7 +45,7 @@ class RosterControllerTest : public CppUnit::TestFixture
void setUp() {
jid_ = JID("testjid@swift.im/swift");
- xmppRoster_ = boost::shared_ptr<XMPPRoster>(new XMPPRoster());
+ xmppRoster_ = new XMPPRoster();
avatarManager_ = new NullAvatarManager();
mainWindowFactory_ = new MockMainWindowFactory();
mucRegistry_ = new MUCRegistry();
@@ -52,9 +54,10 @@ class RosterControllerTest : public CppUnit::TestFixture
router_ = new IQRouter(channel_);
stanzaChannel_ = new DummyStanzaChannel();
presenceOracle_ = new PresenceOracle(stanzaChannel_);
+ presenceSender_ = new PresenceSender(stanzaChannel_);
eventController_ = new EventController();
uiEventStream_ = new UIEventStream();
- rosterController_ = new RosterController(jid_, xmppRoster_, avatarManager_, mainWindowFactory_, nickResolver_, presenceOracle_, eventController_, uiEventStream_, router_);
+ rosterController_ = new RosterController(jid_, xmppRoster_, avatarManager_, mainWindowFactory_, nickResolver_, presenceOracle_, presenceSender_, eventController_, uiEventStream_, router_);
mainWindow_ = mainWindowFactory_->last;
};
@@ -67,6 +70,7 @@ class RosterControllerTest : public CppUnit::TestFixture
delete channel_;
delete router_;
delete eventController_;
+ delete presenceSender_;
delete presenceOracle_;
delete stanzaChannel_;
delete uiEventStream_;
@@ -119,7 +123,7 @@ class RosterControllerTest : public CppUnit::TestFixture
private:
JID jid_;
- boost::shared_ptr<XMPPRoster> xmppRoster_;
+ XMPPRoster* xmppRoster_;
MUCRegistry* mucRegistry_;
AvatarManager* avatarManager_;
MockMainWindowFactory* mainWindowFactory_;
@@ -129,10 +133,10 @@ class RosterControllerTest : public CppUnit::TestFixture
DummyStanzaChannel* stanzaChannel_;
IQRouter* router_;
PresenceOracle* presenceOracle_;
+ PresenceSender* presenceSender_;
EventController* eventController_;
UIEventStream* uiEventStream_;
MockMainWindow* mainWindow_;
};
-#undef children
CPPUNIT_TEST_SUITE_REGISTRATION(RosterControllerTest);
diff --git a/Swift/Controllers/UnitTest/XMPPRosterControllerTest.cpp b/Swift/Controllers/UnitTest/XMPPRosterControllerTest.cpp
index 2bfd8ce..6787528 100644
--- a/Swift/Controllers/UnitTest/XMPPRosterControllerTest.cpp
+++ b/Swift/Controllers/UnitTest/XMPPRosterControllerTest.cpp
@@ -31,7 +31,7 @@ class XMPPRosterControllerTest : public CppUnit::TestFixture
void setUp() {
channel_ = new DummyIQChannel();
router_ = new IQRouter(channel_);
- xmppRoster_ = boost::shared_ptr<XMPPRoster>(new XMPPRoster());
+ xmppRoster_ = new XMPPRoster();
}
void tearDown() {
@@ -78,7 +78,7 @@ class XMPPRosterControllerTest : public CppUnit::TestFixture
private:
DummyIQChannel* channel_;
IQRouter* router_;
- boost::shared_ptr<XMPPRoster> xmppRoster_;
+ XMPPRoster* xmppRoster_;
};
CPPUNIT_TEST_SUITE_REGISTRATION(XMPPRosterControllerTest);
diff --git a/Swift/Controllers/XMPPRosterController.cpp b/Swift/Controllers/XMPPRosterController.cpp
index c107315..c3144b7 100644
--- a/Swift/Controllers/XMPPRosterController.cpp
+++ b/Swift/Controllers/XMPPRosterController.cpp
@@ -23,7 +23,7 @@ namespace Swift {
/**
* The controller does not gain ownership of these parameters.
*/
-XMPPRosterController::XMPPRosterController(IQRouter* iqRouter, boost::shared_ptr<XMPPRoster> xmppRoster) : iqRouter_(iqRouter), rosterPushResponder_(iqRouter), xmppRoster_(xmppRoster) {
+XMPPRosterController::XMPPRosterController(IQRouter* iqRouter, XMPPRoster* xmppRoster) : iqRouter_(iqRouter), rosterPushResponder_(iqRouter), xmppRoster_(xmppRoster) {
rosterPushResponder_.onRosterReceived.connect(boost::bind(&XMPPRosterController::handleRosterReceived, this, _1));
}
diff --git a/Swift/Controllers/XMPPRosterController.h b/Swift/Controllers/XMPPRosterController.h
index 9306051..14159c7 100644
--- a/Swift/Controllers/XMPPRosterController.h
+++ b/Swift/Controllers/XMPPRosterController.h
@@ -21,18 +21,16 @@ namespace Swift {
class XMPPRosterController {
public:
- XMPPRosterController(IQRouter *iqRouter, boost::shared_ptr<XMPPRoster> xmppRoster);
+ XMPPRosterController(IQRouter *iqRouter, XMPPRoster* xmppRoster);
void requestRoster();
- boost::shared_ptr<XMPPRoster> getXMPPRoster() {return xmppRoster_;};
-
void handleRosterReceived(boost::shared_ptr<RosterPayload> rosterPayload);
private:
IQRouter* iqRouter_;
RosterPushResponder rosterPushResponder_;
- boost::shared_ptr<XMPPRoster> xmppRoster_;
+ XMPPRoster* xmppRoster_;
};
}
diff --git a/Swift/QtUI/SConscript b/Swift/QtUI/SConscript
index bf9e5cf..634207c 100644
--- a/Swift/QtUI/SConscript
+++ b/Swift/QtUI/SConscript
@@ -138,6 +138,8 @@ if env["PLATFORM"] == "darwin" :
frameworks = []
if env["HAVE_SPARKLE"] :
frameworks.append(env["SPARKLE_FRAMEWORK"])
+ if env["HAVE_GROWL"] :
+ frameworks.append(env["GROWL_FRAMEWORK"])
app = myenv.AppBundle("Swift", version = myenv["SWIFT_VERSION"], resources = ["../resources/MacOSX/Swift.icns"] + commonResources, frameworks = frameworks)
if env["DIST"] :
myenv.Command(["Swift-${SWIFT_VERSION}.dmg"], [app], [
diff --git a/Swiften/Avatars/AvatarManager.h b/Swiften/Avatars/AvatarManager.h
index 74e58f7..d40c3c0 100644
--- a/Swiften/Avatars/AvatarManager.h
+++ b/Swiften/Avatars/AvatarManager.h
@@ -9,6 +9,7 @@
#include <boost/filesystem.hpp>
#include "Swiften/Base/boost_bsignals.h"
+#include "Swiften/Base/ByteArray.h"
namespace Swift {
class JID;
@@ -17,6 +18,7 @@ namespace Swift {
public:
virtual ~AvatarManager();
+ virtual ByteArray getAvatar(const JID&) const = 0;
virtual boost::filesystem::path getAvatarPath(const JID&) const = 0;
boost::signal<void (const JID&)> onAvatarChanged;
diff --git a/Swiften/Avatars/AvatarManagerImpl.cpp b/Swiften/Avatars/AvatarManagerImpl.cpp
index 384994b..9813aed 100644
--- a/Swiften/Avatars/AvatarManagerImpl.cpp
+++ b/Swiften/Avatars/AvatarManagerImpl.cpp
@@ -11,6 +11,7 @@
#include "Swiften/Avatars/VCardUpdateAvatarManager.h"
#include "Swiften/Avatars/VCardAvatarManager.h"
#include "Swiften/Avatars/AvatarStorage.h"
+#include "Swiften/Base/ByteArray.h"
namespace Swift {
@@ -39,5 +40,12 @@ boost::filesystem::path AvatarManagerImpl::getAvatarPath(const JID& jid) const {
return boost::filesystem::path();
}
+ByteArray AvatarManagerImpl::getAvatar(const JID& jid) const {
+ String hash = combinedAvatarProvider.getAvatarHash(jid);
+ if (!hash.isEmpty()) {
+ return avatarStorage->getAvatar(hash);
+ }
+ return ByteArray();
+}
}
diff --git a/Swiften/Avatars/AvatarManagerImpl.h b/Swiften/Avatars/AvatarManagerImpl.h
index f533160..a28d490 100644
--- a/Swiften/Avatars/AvatarManagerImpl.h
+++ b/Swiften/Avatars/AvatarManagerImpl.h
@@ -33,6 +33,7 @@ namespace Swift {
virtual ~AvatarManagerImpl();
virtual boost::filesystem::path getAvatarPath(const JID&) const;
+ virtual ByteArray getAvatar(const JID&) const;
private:
CombinedAvatarProvider combinedAvatarProvider;
diff --git a/Swiften/Avatars/DummyAvatarManager.h b/Swiften/Avatars/DummyAvatarManager.h
new file mode 100644
index 0000000..db63b05
--- /dev/null
+++ b/Swiften/Avatars/DummyAvatarManager.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <map>
+
+#include "Swiften/Avatars/AvatarManager.h"
+
+namespace Swift {
+ class DummyAvatarManager : public AvatarManager {
+ public:
+ virtual boost::filesystem::path getAvatarPath(const JID&) const {
+ return boost::filesystem::path();
+ }
+
+ virtual ByteArray getAvatar(const JID& jid) const {
+ std::map<JID, ByteArray>::const_iterator i = avatars.find(jid);
+ if (i != avatars.end()) {
+ return i->second;
+ }
+ else {
+ return ByteArray();
+ }
+ }
+
+ std::map<JID, ByteArray> avatars;
+ };
+}
diff --git a/Swiften/Avatars/NullAvatarManager.h b/Swiften/Avatars/NullAvatarManager.h
index 7f3c646..e96ed7a 100644
--- a/Swiften/Avatars/NullAvatarManager.h
+++ b/Swiften/Avatars/NullAvatarManager.h
@@ -14,5 +14,9 @@ namespace Swift {
virtual boost::filesystem::path getAvatarPath(const JID&) const {
return boost::filesystem::path();
}
+
+ virtual ByteArray getAvatar(const JID&) const {
+ return ByteArray();
+ }
};
}
diff --git a/Swiften/Chat/ChatStateTracker.cpp b/Swiften/Chat/ChatStateTracker.cpp
index b8d76da..985f04a 100644
--- a/Swiften/Chat/ChatStateTracker.cpp
+++ b/Swiften/Chat/ChatStateTracker.cpp
@@ -18,7 +18,7 @@ void ChatStateTracker::handleMessageReceived(boost::shared_ptr<Message> message)
}
}
-void ChatStateTracker::handlePresenceChange(boost::shared_ptr<Presence> newPresence, boost::shared_ptr<Presence>) {
+void ChatStateTracker::handlePresenceChange(boost::shared_ptr<Presence> newPresence) {
if (newPresence->getType() == Presence::Unavailable) {
onChatStateChange(ChatState::Gone);
}
diff --git a/Swiften/Chat/ChatStateTracker.h b/Swiften/Chat/ChatStateTracker.h
index c8e8cb5..343d828 100644
--- a/Swiften/Chat/ChatStateTracker.h
+++ b/Swiften/Chat/ChatStateTracker.h
@@ -18,7 +18,7 @@ namespace Swift {
public:
ChatStateTracker();
void handleMessageReceived(boost::shared_ptr<Message> message);
- void handlePresenceChange(boost::shared_ptr<Presence> newPresence, boost::shared_ptr<Presence> oldPresence);
+ void handlePresenceChange(boost::shared_ptr<Presence> newPresence);
boost::signal<void (ChatState::ChatStateType)> onChatStateChange;
private:
void changeState(ChatState::ChatStateType state);
diff --git a/Swiften/Network/DummyTimerFactory.cpp b/Swiften/Network/DummyTimerFactory.cpp
index 466dd38..105b103 100644
--- a/Swiften/Network/DummyTimerFactory.cpp
+++ b/Swiften/Network/DummyTimerFactory.cpp
@@ -15,19 +15,26 @@ namespace Swift {
class DummyTimerFactory::DummyTimer : public Timer {
public:
- DummyTimer(int timeout) : timeout(timeout), isRunning(false) {
+ DummyTimer(int timeout, DummyTimerFactory* factory) : timeout(timeout), factory(factory), isRunning(false), startTime(~0) {
}
virtual void start() {
isRunning = true;
+ startTime = factory->currentTime;
}
virtual void stop() {
isRunning = false;
}
+
+ int getAlarmTime() const {
+ return startTime + timeout;
+ }
int timeout;
+ DummyTimerFactory* factory;
bool isRunning;
+ int startTime;
};
@@ -35,31 +42,18 @@ DummyTimerFactory::DummyTimerFactory() : currentTime(0) {
}
boost::shared_ptr<Timer> DummyTimerFactory::createTimer(int milliseconds) {
- boost::shared_ptr<DummyTimer> timer(new DummyTimer(milliseconds));
+ boost::shared_ptr<DummyTimer> timer(new DummyTimer(milliseconds, this));
timers.push_back(timer);
return timer;
}
-static bool hasZeroTimeout(boost::shared_ptr<DummyTimerFactory::DummyTimer> timer) {
- return timer->timeout == 0;
-}
-
void DummyTimerFactory::setTime(int time) {
assert(time > currentTime);
- int increment = time - currentTime;
- std::vector< boost::shared_ptr<DummyTimer> > notifyTimers(timers.begin(), timers.end());
- foreach(boost::shared_ptr<DummyTimer> timer, notifyTimers) {
- if (increment >= timer->timeout) {
- if (timer->isRunning) {
- timer->onTick();
- }
- timer->timeout = 0;
- }
- else {
- timer->timeout -= increment;
+ foreach(boost::shared_ptr<DummyTimer> timer, timers) {
+ if (timer->getAlarmTime() > currentTime && timer->getAlarmTime() <= time && timer->isRunning) {
+ timer->onTick();
}
}
- timers.erase(std::remove_if(timers.begin(), timers.end(), hasZeroTimeout), timers.end());
currentTime = time;
}
diff --git a/Swiften/Presence/PresenceOracle.cpp b/Swiften/Presence/PresenceOracle.cpp
index 44934e6..758ae7c 100644
--- a/Swiften/Presence/PresenceOracle.cpp
+++ b/Swiften/Presence/PresenceOracle.cpp
@@ -7,37 +7,26 @@
#include "PresenceOracle.h"
#include <boost/bind.hpp>
+
#include "Swiften/Client/StanzaChannel.h"
-namespace Swift {
-typedef std::pair<JID, std::map<JID, boost::shared_ptr<Presence> > > JIDMapPair;
-typedef std::pair<JID, boost::shared_ptr<Presence> > JIDPresencePair;
+namespace Swift {
PresenceOracle::PresenceOracle(StanzaChannel* stanzaChannel) {
stanzaChannel_ = stanzaChannel;
stanzaChannel_->onPresenceReceived.connect(boost::bind(&PresenceOracle::handleIncomingPresence, this, _1));
+ stanzaChannel_->onAvailableChanged.connect(boost::bind(&PresenceOracle::handleStanzaChannelAvailableChanged, this, _1));
}
-void PresenceOracle::cancelSubscription(const JID& jid) {
- boost::shared_ptr<Presence> stanza(new Presence());
- stanza->setType(Presence::Unsubscribed);
- stanza->setTo(jid);
- stanzaChannel_->sendPresence(stanza);
+PresenceOracle::~PresenceOracle() {
+ stanzaChannel_->onPresenceReceived.disconnect(boost::bind(&PresenceOracle::handleIncomingPresence, this, _1));
+ stanzaChannel_->onAvailableChanged.disconnect(boost::bind(&PresenceOracle::handleStanzaChannelAvailableChanged, this, _1));
}
-void PresenceOracle::confirmSubscription(const JID& jid) {
- boost::shared_ptr<Presence> stanza(new Presence());
- stanza->setType(Presence::Subscribed);
- stanza->setTo(jid);
- stanzaChannel_->sendPresence(stanza);
-}
-
-
-void PresenceOracle::requestSubscription(const JID& jid) {
- boost::shared_ptr<Presence> stanza(new Presence());
- stanza->setType(Presence::Subscribe);
- stanza->setTo(jid);
- stanzaChannel_->sendPresence(stanza);
+void PresenceOracle::handleStanzaChannelAvailableChanged(bool available) {
+ if (available) {
+ entries_.clear();
+ }
}
@@ -46,19 +35,29 @@ void PresenceOracle::handleIncomingPresence(boost::shared_ptr<Presence> presence
if (presence->getType() == Presence::Subscribe) {
onPresenceSubscriptionRequest(bareJID, presence->getStatus());
- } else {
+ }
+ else {
std::map<JID, boost::shared_ptr<Presence> > jidMap = entries_[bareJID];
- boost::shared_ptr<Presence> last;
- foreach(JIDPresencePair pair, jidMap) {
- if (pair.first == presence->getFrom()) {
- last = pair.second;
- break;
- }
- }
jidMap[presence->getFrom()] = presence;
entries_[bareJID] = jidMap;
- onPresenceChange(presence, last);
+ onPresenceChange(presence);
}
}
+Presence::ref PresenceOracle::getLastPresence(const JID& jid) const {
+ PresencesMap::const_iterator i = entries_.find(jid.toBare());
+ if (i == entries_.end()) {
+ return Presence::ref();
+ }
+ PresenceMap presenceMap = i->second;
+ PresenceMap::const_iterator j = presenceMap.find(jid);
+ if (j != presenceMap.end()) {
+ return j->second;
+ }
+ else {
+ return Presence::ref();
+ }
+}
+
+
}
diff --git a/Swiften/Presence/PresenceOracle.h b/Swiften/Presence/PresenceOracle.h
index 9f64000..e5f0372 100644
--- a/Swiften/Presence/PresenceOracle.h
+++ b/Swiften/Presence/PresenceOracle.h
@@ -6,31 +6,35 @@
#pragma once
+#include <map>
+
#include "Swiften/Base/String.h"
#include "Swiften/Elements/Presence.h"
-#include <map>
#include "Swiften/Base/boost_bsignals.h"
namespace Swift {
class StanzaChannel;
-
-class PresenceOracle {
- public:
- PresenceOracle(StanzaChannel* stanzaChannel);
- ~PresenceOracle() {};
-
- void cancelSubscription(const JID& jid);
- void confirmSubscription(const JID& jid);
- void requestSubscription(const JID& jid);
-
- boost::signal<void (boost::shared_ptr<Presence>, boost::shared_ptr<Presence>)> onPresenceChange;
- boost::signal<void (const JID&, const String&)> onPresenceSubscriptionRequest;
-
- private:
- void handleIncomingPresence(boost::shared_ptr<Presence> presence);
- std::map<JID, std::map<JID, boost::shared_ptr<Presence> > > entries_;
- StanzaChannel* stanzaChannel_;
-};
+ class PresenceOracle {
+ public:
+ PresenceOracle(StanzaChannel* stanzaChannel);
+ ~PresenceOracle();
+
+ Presence::ref getLastPresence(const JID&) const;
+
+ public:
+ boost::signal<void (boost::shared_ptr<Presence>)> onPresenceChange;
+ boost::signal<void (const JID&, const String&)> onPresenceSubscriptionRequest;
+
+ private:
+ void handleIncomingPresence(boost::shared_ptr<Presence> presence);
+ void handleStanzaChannelAvailableChanged(bool);
+
+ private:
+ typedef std::map<JID, Presence::ref> PresenceMap;
+ typedef std::map<JID, PresenceMap> PresencesMap;
+ PresencesMap entries_;
+ StanzaChannel* stanzaChannel_;
+ };
}
diff --git a/Swiften/Presence/PresenceSender.cpp b/Swiften/Presence/PresenceSender.cpp
index e4562a9..082c841 100644
--- a/Swiften/Presence/PresenceSender.cpp
+++ b/Swiften/Presence/PresenceSender.cpp
@@ -52,4 +52,26 @@ void PresenceSender::removeDirectedPresenceReceiver(const JID& jid) {
}
}
+void PresenceSender::cancelSubscription(const JID& jid) {
+ boost::shared_ptr<Presence> stanza(new Presence());
+ stanza->setType(Presence::Unsubscribed);
+ stanza->setTo(jid);
+ channel->sendPresence(stanza);
+}
+
+void PresenceSender::confirmSubscription(const JID& jid) {
+ boost::shared_ptr<Presence> stanza(new Presence());
+ stanza->setType(Presence::Subscribed);
+ stanza->setTo(jid);
+ channel->sendPresence(stanza);
+}
+
+
+void PresenceSender::requestSubscription(const JID& jid) {
+ boost::shared_ptr<Presence> stanza(new Presence());
+ stanza->setType(Presence::Subscribe);
+ stanza->setTo(jid);
+ channel->sendPresence(stanza);
+}
+
}
diff --git a/Swiften/Presence/PresenceSender.h b/Swiften/Presence/PresenceSender.h
index da4bc70..87be753 100644
--- a/Swiften/Presence/PresenceSender.h
+++ b/Swiften/Presence/PresenceSender.h
@@ -22,6 +22,10 @@ namespace Swift {
void sendPresence(boost::shared_ptr<Presence>);
+ void cancelSubscription(const JID& jid);
+ void confirmSubscription(const JID& jid);
+ void requestSubscription(const JID& jid);
+
private:
boost::shared_ptr<Presence> lastSentUndirectedPresence;
StanzaChannel* channel;
diff --git a/Swiften/Presence/UnitTest/PresenceOracleTest.cpp b/Swiften/Presence/UnitTest/PresenceOracleTest.cpp
index 691beb7..e96d8a4 100644
--- a/Swiften/Presence/UnitTest/PresenceOracleTest.cpp
+++ b/Swiften/Presence/UnitTest/PresenceOracleTest.cpp
@@ -8,58 +8,29 @@
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
-#include <boost/optional.hpp>
#include "Swiften/Presence/PresenceOracle.h"
#include "Swiften/Client/DummyStanzaChannel.h"
using namespace Swift;
-class PresencePointerPair {
- public:
- boost::shared_ptr<Presence> one;
- boost::shared_ptr<Presence> two;
-};
-
-class SubscriptionRequestInfo {
- public:
- boost::optional<JID> jid;
- boost::optional<String> reason;
-};
-
-class PresenceOracleTest : public CppUnit::TestFixture
-{
+class PresenceOracleTest : public CppUnit::TestFixture {
CPPUNIT_TEST_SUITE(PresenceOracleTest);
- CPPUNIT_TEST(testFirstPresence);
- CPPUNIT_TEST(testSecondPresence);
+ CPPUNIT_TEST(testReceivePresence);
+ CPPUNIT_TEST(testReceivePresenceFromDifferentResources);
CPPUNIT_TEST(testSubscriptionRequest);
+ CPPUNIT_TEST(testReconnectResetsPresences);
CPPUNIT_TEST_SUITE_END();
- private:
- PresenceOracle* oracle_;
- DummyStanzaChannel* stanzaChannel_;
-
public:
-
- void handlePresenceChange(boost::shared_ptr<Presence> newPresence, boost::shared_ptr<Presence> lastPresence, PresencePointerPair* out) {
- CPPUNIT_ASSERT(out->one.get() == NULL);
- CPPUNIT_ASSERT(out->two.get() == NULL);
- out->one = newPresence;
- out->two = lastPresence;
- CPPUNIT_ASSERT(newPresence.get());
- CPPUNIT_ASSERT_EQUAL(newPresence, out->one);
- }
-
- void handlePresenceSubscriptionRequest(const JID& jid, const String& reason, SubscriptionRequestInfo* info) {
- CPPUNIT_ASSERT(!info->jid);
- CPPUNIT_ASSERT(!info->reason);
- info->jid = jid;
- info->reason = reason;
- }
-
void setUp() {
stanzaChannel_ = new DummyStanzaChannel();
oracle_ = new PresenceOracle(stanzaChannel_);
+ oracle_->onPresenceChange.connect(boost::bind(&PresenceOracleTest::handlePresenceChange, this, _1));
+ oracle_->onPresenceSubscriptionRequest.connect(boost::bind(&PresenceOracleTest::handlePresenceSubscriptionRequest, this, _1, _2));
+ user1 = JID("user1@foo.com/Foo");
+ user1alt = JID("user1@foo.com/Bar");
+ user2 = JID("user2@bar.com/Bar");
}
void tearDown() {
@@ -67,49 +38,27 @@ class PresenceOracleTest : public CppUnit::TestFixture
delete stanzaChannel_;
}
- void testFirstPresence() {
- PresencePointerPair out;
- oracle_->onPresenceChange.connect(boost::bind(&PresenceOracleTest::handlePresenceChange, this, _1, _2, &out));
-
- SubscriptionRequestInfo info;
- oracle_->onPresenceSubscriptionRequest.connect(boost::bind(&PresenceOracleTest::handlePresenceSubscriptionRequest, this, _1, _2, &info));
-
- boost::shared_ptr<Presence> sentPresence(new Presence("blarb"));
+ void testReceivePresence() {
+ boost::shared_ptr<Presence> sentPresence(createPresence(user1));
stanzaChannel_->onPresenceReceived(sentPresence);
- CPPUNIT_ASSERT(!info.jid);
- CPPUNIT_ASSERT(!info.reason);
- CPPUNIT_ASSERT(out.two.get() == NULL);
- CPPUNIT_ASSERT_EQUAL(sentPresence, out.one);
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(changes.size()));
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(subscriptionRequests.size()));
+ CPPUNIT_ASSERT_EQUAL(sentPresence, changes[0]);
+ CPPUNIT_ASSERT_EQUAL(sentPresence, oracle_->getLastPresence(user1));
}
-
- void testSecondPresence() {
- PresencePointerPair out;
- oracle_->onPresenceChange.connect(boost::bind(&PresenceOracleTest::handlePresenceChange, this, _1, _2, &out));
- boost::shared_ptr<Presence> sentPresence1(new Presence("blarb"));
+ void testReceivePresenceFromDifferentResources() {
+ boost::shared_ptr<Presence> sentPresence1(createPresence(user1));
+ boost::shared_ptr<Presence> sentPresence2(createPresence(user1alt));
stanzaChannel_->onPresenceReceived(sentPresence1);
- CPPUNIT_ASSERT_EQUAL(sentPresence1, out.one);
- out.one = boost::shared_ptr<Presence>();
-
- SubscriptionRequestInfo info;
- oracle_->onPresenceSubscriptionRequest.connect(boost::bind(&PresenceOracleTest::handlePresenceSubscriptionRequest, this, _1, _2, &info));
-
- boost::shared_ptr<Presence> sentPresence2(new Presence("test2"));
stanzaChannel_->onPresenceReceived(sentPresence2);
- CPPUNIT_ASSERT(!info.jid);
- CPPUNIT_ASSERT(!info.reason);
- CPPUNIT_ASSERT_EQUAL(sentPresence1, out.two);
- CPPUNIT_ASSERT_EQUAL(sentPresence2, out.one);
+ CPPUNIT_ASSERT_EQUAL(sentPresence1, oracle_->getLastPresence(user1));
+ CPPUNIT_ASSERT_EQUAL(sentPresence2, oracle_->getLastPresence(user1alt));
}
-
+
void testSubscriptionRequest() {
- PresencePointerPair out;
- oracle_->onPresenceChange.connect(boost::bind(&PresenceOracleTest::handlePresenceChange, this, _1, _2, &out));
- SubscriptionRequestInfo info;
- oracle_->onPresenceSubscriptionRequest.connect(boost::bind(&PresenceOracleTest::handlePresenceSubscriptionRequest, this, _1, _2, &info));
-
String reasonText = "Because I want to";
JID sentJID = JID("me@example.com");
@@ -119,14 +68,52 @@ class PresenceOracleTest : public CppUnit::TestFixture
sentPresence->setStatus(reasonText);
stanzaChannel_->onPresenceReceived(sentPresence);
- CPPUNIT_ASSERT(info.jid);
- CPPUNIT_ASSERT(info.reason);
- CPPUNIT_ASSERT_EQUAL(sentJID, info.jid.get());
- CPPUNIT_ASSERT_EQUAL(reasonText, info.reason.get());
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(changes.size()));
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(subscriptionRequests.size()));
+ CPPUNIT_ASSERT_EQUAL(sentJID, subscriptionRequests[0].jid);
+ CPPUNIT_ASSERT_EQUAL(reasonText, subscriptionRequests[0].reason);
+ }
+
+ void testReconnectResetsPresences() {
+ boost::shared_ptr<Presence> sentPresence(createPresence(user1));
+ stanzaChannel_->onPresenceReceived(sentPresence);
+ stanzaChannel_->setAvailable(false);
+ stanzaChannel_->setAvailable(true);
- CPPUNIT_ASSERT(!out.two);
- CPPUNIT_ASSERT(!out.one);
+ CPPUNIT_ASSERT(!oracle_->getLastPresence(user1));
}
+
+ private:
+ void handlePresenceChange(boost::shared_ptr<Presence> newPresence) {
+ changes.push_back(newPresence);
+ }
+
+ void handlePresenceSubscriptionRequest(const JID& jid, const String& reason) {
+ SubscriptionRequestInfo subscriptionRequest;
+ subscriptionRequest.jid = jid;
+ subscriptionRequest.reason = reason;
+ subscriptionRequests.push_back(subscriptionRequest);
+ }
+
+ boost::shared_ptr<Presence> createPresence(const JID& jid) {
+ boost::shared_ptr<Presence> sentPresence(new Presence("blarb"));
+ sentPresence->setFrom(jid);
+ return sentPresence;
+ }
+
+ private:
+ struct SubscriptionRequestInfo {
+ JID jid;
+ String reason;
+ };
+ PresenceOracle* oracle_;
+ DummyStanzaChannel* stanzaChannel_;
+ std::vector<Presence::ref> changes;
+ std::vector<SubscriptionRequestInfo> subscriptionRequests;
+ JID user1;
+ JID user1alt;
+ JID user2;
};
+
CPPUNIT_TEST_SUITE_REGISTRATION(PresenceOracleTest);
diff --git a/Swiften/Roster/XMPPRoster.cpp b/Swiften/Roster/XMPPRoster.cpp
index 28b04c6..56616c2 100644
--- a/Swiften/Roster/XMPPRoster.cpp
+++ b/Swiften/Roster/XMPPRoster.cpp
@@ -8,6 +8,9 @@
namespace Swift {
+XMPPRoster::XMPPRoster() {
+}
+
void XMPPRoster::addContact(const JID& jid, const String& name, const std::vector<String>& groups, RosterItemPayload::Subscription subscription) {
JID bareJID(jid.toBare());
bool exists = containsJID(bareJID);
@@ -43,8 +46,14 @@ bool XMPPRoster::containsJID(const JID& jid) {
return entries_.find(JID(jid.toBare())) != entries_.end();
}
-const String& XMPPRoster::getNameForJID(const JID& jid) {
- return entries_[JID(jid.toBare())].name;
+String XMPPRoster::getNameForJID(const JID& jid) const {
+ std::map<JID, XMPPRosterItem>::const_iterator i = entries_.find(jid.toBare());
+ if (i != entries_.end()) {
+ return i->second.name;
+ }
+ else {
+ return "";
+ }
}
const std::vector<String>& XMPPRoster::getGroupsForJID(const JID& jid) {
diff --git a/Swiften/Roster/XMPPRoster.h b/Swiften/Roster/XMPPRoster.h
index e449d28..abafdfe 100644
--- a/Swiften/Roster/XMPPRoster.h
+++ b/Swiften/Roster/XMPPRoster.h
@@ -4,8 +4,7 @@
* See Documentation/Licenses/GPLv3.txt for more information.
*/
-#ifndef SWIFTEN_XMPPRoster_H
-#define SWIFTEN_XMPPRoster_H
+#pragma once
#include "Swiften/Base/String.h"
#include "Swiften/JID/JID.h"
@@ -16,37 +15,31 @@
#include "Swiften/Base/boost_bsignals.h"
namespace Swift {
-
-struct XMPPRosterItem {
- JID jid;
- String name;
- std::vector<String> groups;
- RosterItemPayload::Subscription subscription;
-};
-
-class XMPPRoster {
- public:
- XMPPRoster() {};
- ~XMPPRoster() {};
-
- void addContact(const JID& jid, const String& name, const std::vector<String>& groups, const RosterItemPayload::Subscription subscription);
- bool containsJID(const JID& jid);
- void removeContact(const JID& jid);
- void clear();
- RosterItemPayload::Subscription getSubscriptionStateForJID(const JID& jid);
- const String& getNameForJID(const JID& jid);
- const std::vector<String>& getGroupsForJID(const JID& jid);
-
- boost::signal<void (const JID&)> onJIDAdded;
- boost::signal<void (const JID&)> onJIDRemoved;
- boost::signal<void (const JID&, const String&, const std::vector<String>&)> onJIDUpdated;
- boost::signal<void ()> onRosterCleared;
-
- private:
- //std::map<JID, std::pair<String, std::vector<String> > > entries_;
- std::map<JID, XMPPRosterItem> entries_;
-};
+ class XMPPRoster {
+ public:
+ XMPPRoster();
+
+ void addContact(const JID& jid, const String& name, const std::vector<String>& groups, const RosterItemPayload::Subscription subscription);
+ void removeContact(const JID& jid);
+ void clear();
+
+ bool containsJID(const JID& jid);
+ RosterItemPayload::Subscription getSubscriptionStateForJID(const JID& jid);
+ String getNameForJID(const JID& jid) const;
+ const std::vector<String>& getGroupsForJID(const JID& jid);
+
+ boost::signal<void (const JID&)> onJIDAdded;
+ boost::signal<void (const JID&)> onJIDRemoved;
+ boost::signal<void (const JID&, const String&, const std::vector<String>&)> onJIDUpdated;
+ boost::signal<void ()> onRosterCleared;
+
+ private:
+ struct XMPPRosterItem {
+ JID jid;
+ String name;
+ std::vector<String> groups;
+ RosterItemPayload::Subscription subscription;
+ };
+ std::map<JID, XMPPRosterItem> entries_;
+ };
}
-
-#endif
-