/** * @file llviewerparcelaskplay.cpp * @brief stores data about parcel media user wants to auto-play and shows related notifications * * $LicenseInfo:firstyear=2019&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2019, 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 "llviewerparcelaskplay.h" #include "llnotifications.h" #include "llnotificationsutil.h" #include "llparcel.h" #include "llstartup.h" #include "llviewerparcelmgr.h" #include "llviewerregion.h" #include "llagent.h" #include "llsdserialize.h" #include <boost/lexical_cast.hpp> // class LLViewerParcelAskPlay LLViewerParcelAskPlay::LLViewerParcelAskPlay() : pNotification(NULL) { } LLViewerParcelAskPlay::~LLViewerParcelAskPlay() { } void LLViewerParcelAskPlay::initSingleton() { } void LLViewerParcelAskPlay::cleanupSingleton() { if (LLNotifications::instanceExists()) { cancelNotification(); } } void LLViewerParcelAskPlay::askToPlay(const LLUUID ®ion_id, const S32 &parcel_id, const std::string &url, ask_callback cb) { EAskPlayMode mode = getPlayMode(region_id, parcel_id); switch (mode) { case ASK_PLAY_IGNORE: cb(region_id, parcel_id, url, false); break; case ASK_PLAY_PLAY: cb(region_id, parcel_id, url, true); break; case ASK_PLAY_ASK: default: { // create or re-create notification // Note: will create and immediately cancel one notification if region has both media and music // since ask play does not distinguish media from music and media can be used as music cancelNotification(); if (LLStartUp::getStartupState() > STATE_PRECACHE) { LLSD args; args["URL"] = url; LLSD payload; payload["url"] = url; // or we can extract it from notification["substitutions"] payload["parcel_id"] = parcel_id; payload["region_id"] = region_id; pNotification = LLNotificationsUtil::add("ParcelPlayingMedia", args, payload, boost::bind(onAskPlayResponse, _1, _2, cb)); } else { // workaround: avoid 'new notifications arrived' on startup and just play // (alternative: move to different channel, may be create new one...) cb(region_id, parcel_id, url, true); } } } } void LLViewerParcelAskPlay::cancelNotification() { if (pNotification) { if (!pNotification->isCancelled()) { // Force a responce // Alternative is to mark notification as unique pNotification->setIgnored(false); LLNotifications::getInstance()->cancel(pNotification); } pNotification = NULL; } } void LLViewerParcelAskPlay::resetCurrentParcelSetting() { LLParcel *agent_parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); if (agent_parcel && gAgent.getRegion()) { LLViewerParcelAskPlay::resetSetting(gAgent.getRegion()->getRegionID(), agent_parcel->getLocalID()); } } void LLViewerParcelAskPlay::resetSetting(const LLUUID ®ion_id, const S32 &parcel_id) { region_map_t::iterator found = mRegionMap.find(region_id); if (found != mRegionMap.end()) { found->second.erase(parcel_id); } } void LLViewerParcelAskPlay::resetSettings() { if (LLViewerParcelAskPlay::instanceExists()) { LLViewerParcelAskPlay::getInstance()->mRegionMap.clear(); } LLFile::remove(getAskPlayFilename()); } void LLViewerParcelAskPlay::setSetting(const LLUUID ®ion_id, const S32 &parcel_id, const LLViewerParcelAskPlay::ParcelData &data) { mRegionMap[region_id][parcel_id] = data; } LLViewerParcelAskPlay::ParcelData* LLViewerParcelAskPlay::getSetting(const LLUUID ®ion_id, const S32 &parcel_id) { region_map_t::iterator found = mRegionMap.find(region_id); if (found != mRegionMap.end()) { parcel_data_map_t::iterator found_parcel = found->second.find(parcel_id); if (found_parcel != found->second.end()) { return &(found_parcel->second); } } return NULL; } LLViewerParcelAskPlay::EAskPlayMode LLViewerParcelAskPlay::getPlayMode(const LLUUID ®ion_id, const S32 &parcel_id) { EAskPlayMode mode = ASK_PLAY_ASK; ParcelData* data = getSetting(region_id, parcel_id); if (data) { mode = data->mMode; // refresh date data->mDate = LLDate::now(); } return mode; } void LLViewerParcelAskPlay::setPlayMode(const LLUUID ®ion_id, const S32 &parcel_id, LLViewerParcelAskPlay::EAskPlayMode mode) { ParcelData data; data.mMode = mode; data.mDate = LLDate::now(); setSetting(region_id, parcel_id, data); } //static void LLViewerParcelAskPlay::onAskPlayResponse(const LLSD& notification, const LLSD& response, ask_callback cb) { S32 option = LLNotificationsUtil::getSelectedOption(notification, response); LLUUID region_id = notification["payload"]["region_id"]; S32 parcel_id = notification["payload"]["parcel_id"]; std::string url = notification["payload"]["url"]; bool play = option == 1; cb(region_id, parcel_id, url, play); LLViewerParcelAskPlay *inst = getInstance(); bool save_choice = inst->pNotification->isIgnored(); // checkbox selected if (save_choice) { EAskPlayMode mode = (play) ? ASK_PLAY_PLAY : ASK_PLAY_IGNORE; inst->setPlayMode(region_id, parcel_id, mode); } } // static std::string LLViewerParcelAskPlay::getAskPlayFilename() { return gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, "media_autoplay.xml"); } void LLViewerParcelAskPlay::loadSettings() { mRegionMap.clear(); std::string path = getAskPlayFilename(); if (!gDirUtilp->fileExists(path)) { return; } LLSD autoplay_llsd; llifstream file; file.open(path.c_str()); if (!file.is_open()) { return; } S32 status = LLSDSerialize::fromXML(autoplay_llsd, file); file.close(); if (status == LLSDParser::PARSE_FAILURE || !autoplay_llsd.isMap()) { return; } for (LLSD::map_const_iterator iter_region = autoplay_llsd.beginMap(); iter_region != autoplay_llsd.endMap(); ++iter_region) { LLUUID region_id = LLUUID(iter_region->first); mRegionMap[region_id] = parcel_data_map_t(); const LLSD &parcel_map = iter_region->second; if (parcel_map.isMap()) { for (LLSD::map_const_iterator iter_parcel = parcel_map.beginMap(); iter_parcel != parcel_map.endMap(); ++iter_parcel) { if (!iter_parcel->second.isMap()) { break; } S32 parcel_id = boost::lexical_cast<S32>(iter_parcel->first.c_str()); ParcelData data; data.mMode = (EAskPlayMode)(iter_parcel->second["mode"].asInteger()); data.mDate = iter_parcel->second["date"].asDate(); mRegionMap[region_id][parcel_id] = data; } } } } void LLViewerParcelAskPlay::saveSettings() { LLSD write_llsd; std::string key; for (region_map_t::iterator iter_region = mRegionMap.begin(); iter_region != mRegionMap.end(); ++iter_region) { if (iter_region->second.empty()) { continue; } key = iter_region->first.asString(); write_llsd[key] = LLSD(); for (parcel_data_map_t::iterator iter_parcel = iter_region->second.begin(); iter_parcel != iter_region->second.end(); ++iter_parcel) { if ((iter_parcel->second.mDate.secondsSinceEpoch() + (F64SecondsImplicit)U32Days(30)) > LLTimer::getTotalSeconds()) { // write unexpired parcels std::string parcel_id = std::to_string(iter_parcel->first); write_llsd[key][parcel_id] = LLSD(); write_llsd[key][parcel_id]["mode"] = (LLSD::Integer)iter_parcel->second.mMode; write_llsd[key][parcel_id]["date"] = iter_parcel->second.mDate; } } } llofstream file; file.open(getAskPlayFilename().c_str()); if (file.is_open()) { LLSDSerialize::toPrettyXML(write_llsd, file); file.close(); } }