/** * @file lldonotdisturbnotificationstorage.cpp * @brief Implementation of lldonotdisturbnotificationstorage * @author Stinson@lindenlab.com * * $LicenseInfo:firstyear=2012&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2012, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #include "llviewerprecompiledheaders.h" #include "lldonotdisturbnotificationstorage.h" #include "llcommunicationchannel.h" #include "lldir.h" #include "llerror.h" #include "llfloaterreg.h" #include "llimview.h" #include "llnotifications.h" #include "llnotificationhandler.h" #include "llnotificationstorage.h" #include "llscriptfloater.h" #include "llsd.h" #include "llsingleton.h" #include "lluuid.h" static const F32 DND_TIMER = 3.0; const char * LLDoNotDisturbNotificationStorage::toastName = "IMToast"; const char * LLDoNotDisturbNotificationStorage::offerName = "UserGiveItem"; LLDoNotDisturbNotificationStorageTimer::LLDoNotDisturbNotificationStorageTimer() : LLEventTimer(DND_TIMER) { } LLDoNotDisturbNotificationStorageTimer::~LLDoNotDisturbNotificationStorageTimer() { } BOOL LLDoNotDisturbNotificationStorageTimer::tick() { LLDoNotDisturbNotificationStorage * doNotDisturbNotificationStorage = LLDoNotDisturbNotificationStorage::getInstance(); if(doNotDisturbNotificationStorage && doNotDisturbNotificationStorage->getDirty()) { doNotDisturbNotificationStorage->saveNotifications(); } return FALSE; } LLDoNotDisturbNotificationStorage::LLDoNotDisturbNotificationStorage() : LLSingleton<LLDoNotDisturbNotificationStorage>() , LLNotificationStorage("") , mDirty(false) { nameToPayloadParameterMap[toastName] = "SESSION_ID"; nameToPayloadParameterMap[offerName] = "object_id"; } LLDoNotDisturbNotificationStorage::~LLDoNotDisturbNotificationStorage() { } void LLDoNotDisturbNotificationStorage::initialize() { setFileName(gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "dnd_notifications.xml")); getCommunicationChannel()->connectFailedFilter(boost::bind(&LLDoNotDisturbNotificationStorage::onChannelChanged, this, _1)); } bool LLDoNotDisturbNotificationStorage::getDirty() { return mDirty; } void LLDoNotDisturbNotificationStorage::resetDirty() { mDirty = false; } static LLTrace::BlockTimerStatHandle FTM_SAVE_DND_NOTIFICATIONS("Save DND Notifications"); void LLDoNotDisturbNotificationStorage::saveNotifications() { LL_RECORD_BLOCK_TIME(FTM_SAVE_DND_NOTIFICATIONS); LLNotificationChannelPtr channelPtr = getCommunicationChannel(); const LLCommunicationChannel *commChannel = dynamic_cast<LLCommunicationChannel*>(channelPtr.get()); llassert(commChannel != NULL); LLSD output = LLSD::emptyMap(); LLSD& data = output["data"]; data = LLSD::emptyArray(); for (LLCommunicationChannel::history_list_t::const_iterator historyIter = commChannel->beginHistory(); historyIter != commChannel->endHistory(); ++historyIter) { LLNotificationPtr notificationPtr = historyIter->second; if (!notificationPtr->isRespondedTo() && !notificationPtr->isCancelled() && !notificationPtr->isExpired() && !notificationPtr->isPersistent()) { data.append(notificationPtr->asLLSD(true)); } } writeNotifications(output); resetDirty(); } static LLTrace::BlockTimerStatHandle FTM_LOAD_DND_NOTIFICATIONS("Load DND Notifications"); void LLDoNotDisturbNotificationStorage::loadNotifications() { LL_RECORD_BLOCK_TIME(FTM_LOAD_DND_NOTIFICATIONS); LL_INFOS("LLDoNotDisturbNotificationStorage") << "start loading notifications" << LL_ENDL; LLSD input; if (!readNotifications(input) ||input.isUndefined()) { return; } LLSD& data = input["data"]; if (data.isUndefined()) { return; } LLNotifications& instance = LLNotifications::instance(); bool imToastExists = false; bool group_ad_hoc_toast_exists = false; S32 toastSessionType; bool offerExists = false; for (LLSD::array_const_iterator notification_it = data.beginArray(); notification_it != data.endArray(); ++notification_it) { LLSD notification_params = *notification_it; const LLUUID& notificationID = notification_params["id"]; std::string notificationName = notification_params["name"]; LLNotificationPtr notification = instance.find(notificationID); if(notificationName == toastName) { toastSessionType = notification_params["payload"]["SESSION_TYPE"]; if(toastSessionType == LLIMModel::LLIMSession::P2P_SESSION) { imToastExists = true; } //Don't add group/ad-hoc messages to the notification system because //this means the group/ad-hoc session has to be re-created else if(toastSessionType == LLIMModel::LLIMSession::GROUP_SESSION || toastSessionType == LLIMModel::LLIMSession::ADHOC_SESSION) { //Just allows opening the conversation log for group/ad-hoc messages upon startup group_ad_hoc_toast_exists = true; continue; } } else if(notificationName == offerName) { offerExists = true; } //Notification already exists due to persistent storage adding it first into the notification system if(notification) { notification->setDND(true); instance.update(instance.find(notificationID)); } //New notification needs to be added else { notification = (LLNotificationPtr) new LLNotification(notification_params.with("is_dnd", true)); LLNotificationResponderInterface* responder = createResponder(notification_params["responder_sd"]["responder_type"], notification_params["responder_sd"]); if (responder == NULL) { LL_WARNS("LLDoNotDisturbNotificationStorage") << "cannot create responder for notification of type '" << notification->getType() << "'" << LL_ENDL; } else { LLNotificationResponderPtr responderPtr(responder); notification->setResponseFunctor(responderPtr); } instance.add(notification); } } bool isConversationLoggingAllowed = gSavedPerAccountSettings.getS32("KeepConversationLogTranscripts") > 0; if(group_ad_hoc_toast_exists && isConversationLoggingAllowed) { LLFloaterReg::showInstance("conversation"); } if(imToastExists || group_ad_hoc_toast_exists || offerExists) { make_ui_sound_deferred("UISndNewIncomingIMSession"); } //writes out empty .xml file (since LLCommunicationChannel::mHistory is empty) saveNotifications(); LL_INFOS("LLDoNotDisturbNotificationStorage") << "finished loading notifications" << LL_ENDL; } void LLDoNotDisturbNotificationStorage::updateNotifications() { LLNotificationChannelPtr channelPtr = getCommunicationChannel(); LLCommunicationChannel *commChannel = dynamic_cast<LLCommunicationChannel*>(channelPtr.get()); llassert(commChannel != NULL); LLNotifications& instance = LLNotifications::instance(); bool imToastExists = false; bool offerExists = false; for (LLCommunicationChannel::history_list_t::const_iterator it = commChannel->beginHistory(); it != commChannel->endHistory(); ++it) { LLNotificationPtr notification = it->second; std::string notificationName = notification->getName(); if(notificationName == toastName) { imToastExists = true; } else if(notificationName == offerName) { offerExists = true; } //Notification already exists in notification pipeline (same instance of app running) if (notification) { notification->setDND(true); instance.update(notification); } } if(imToastExists || offerExists) { make_ui_sound("UISndNewIncomingIMSession"); } //When exit DND mode, write empty notifications file if(commChannel->getHistorySize()) { commChannel->clearHistory(); saveNotifications(); } } LLNotificationChannelPtr LLDoNotDisturbNotificationStorage::getCommunicationChannel() const { LLNotificationChannelPtr channelPtr = LLNotifications::getInstance()->getChannel("Communication"); llassert(channelPtr); return channelPtr; } void LLDoNotDisturbNotificationStorage::removeNotification(const char * name, const LLUUID& id) { LLNotifications& instance = LLNotifications::instance(); LLNotificationChannelPtr channelPtr = getCommunicationChannel(); LLCommunicationChannel *commChannel = dynamic_cast<LLCommunicationChannel*>(channelPtr.get()); LLNotificationPtr notification; LLSD payload; LLUUID notificationObjectID; std::string notificationName; std::string payloadVariable = nameToPayloadParameterMap[name]; LLCommunicationChannel::history_list_t::iterator it; std::vector<LLCommunicationChannel::history_list_t::iterator> itemsToRemove; //Find notification with the matching session id for (it = commChannel->beginHistory(); it != commChannel->endHistory(); ++it) { notification = it->second; payload = notification->getPayload(); notificationObjectID = payload[payloadVariable].asUUID(); notificationName = notification->getName(); if((notificationName == name) && id == notificationObjectID) { itemsToRemove.push_back(it); } } //Remove the notifications if(itemsToRemove.size()) { while(itemsToRemove.size()) { it = itemsToRemove.back(); notification = it->second; commChannel->removeItemFromHistory(notification); instance.cancel(notification); itemsToRemove.pop_back(); } //Trigger saving of notifications to xml once all have been removed saveNotifications(); } } bool LLDoNotDisturbNotificationStorage::onChannelChanged(const LLSD& pPayload) { if (pPayload["sigtype"].asString() != "load") { mDirty = true; } return false; }