summaryrefslogtreecommitdiffstats
path: root/Swift
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 /Swift
parent3ae8cccfe9c6bfed5dda5f024a5cb046ccfc9793 (diff)
downloadswift-ef1052bbdb315aaa1c6254098ea05638d9a25b2f.zip
swift-ef1052bbdb315aaa1c6254098ea05638d9a25b2f.tar.bz2
Added presence notifier.
Diffstat (limited to 'Swift')
-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
21 files changed, 616 insertions, 78 deletions
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], [