diff options
author | Remko Tronçon <git@el-tramo.be> | 2010-09-12 18:29:39 (GMT) |
---|---|---|
committer | Remko Tronçon <git@el-tramo.be> | 2010-09-13 19:19:52 (GMT) |
commit | ef1052bbdb315aaa1c6254098ea05638d9a25b2f (patch) | |
tree | 482277f649f6c0fc00027514ea8986861fe33437 /Swift | |
parent | 3ae8cccfe9c6bfed5dda5f024a5cb046ccfc9793 (diff) | |
download | swift-ef1052bbdb315aaa1c6254098ea05638d9a25b2f.zip swift-ef1052bbdb315aaa1c6254098ea05638d9a25b2f.tar.bz2 |
Added presence notifier.
Diffstat (limited to 'Swift')
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], [ |