diff options
23 files changed, 630 insertions, 59 deletions
diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp index 7bafd711cb..2e6dc6731b 100644 --- a/indra/llui/llnotifications.cpp +++ b/indra/llui/llnotifications.cpp @@ -68,7 +68,8 @@ LLNotificationForm::FormIgnore::FormIgnore() control("control"), invert_control("invert_control", false), save_option("save_option", false), - session_only("session_only", false) + session_only("session_only", false), + checkbox_only("checkbox_only", false) {} LLNotificationForm::FormButton::FormButton() @@ -195,9 +196,14 @@ LLNotificationForm::LLNotificationForm(const std::string& name, const LLNotifica { if (p.ignore.isProvided()) { + // For all cases but IGNORE_CHECKBOX_ONLY this is name for use in preferences mIgnoreMsg = p.ignore.text; - if (!p.ignore.save_option) + if (p.ignore.checkbox_only) + { + mIgnore = IGNORE_CHECKBOX_ONLY; + } + else if (!p.ignore.save_option) { mIgnore = p.ignore.session_only ? IGNORE_WITH_DEFAULT_RESPONSE_SESSION_ONLY : IGNORE_WITH_DEFAULT_RESPONSE; } @@ -214,7 +220,7 @@ LLNotificationForm::LLNotificationForm(const std::string& name, const LLNotifica mIgnoreSetting = LLUI::sSettingGroups["config"]->getControl(p.ignore.control); mInvertSetting = p.ignore.invert_control; } - else + else if (mIgnore > IGNORE_NO) { LLUI::sSettingGroups["ignores"]->declareBOOL(name, show_notification, "Show notification with this name", LLControlVariable::PERSIST_NONDFT); mIgnoreSetting = LLUI::sSettingGroups["ignores"]->getControl(name); @@ -388,13 +394,12 @@ LLControlVariablePtr LLNotificationForm::getIgnoreSetting() bool LLNotificationForm::getIgnored() { bool show = true; - if (mIgnore != LLNotificationForm::IGNORE_NO + if (mIgnore > LLNotificationForm::IGNORE_NO && mIgnoreSetting) { show = mIgnoreSetting->getValue().asBoolean(); if (mInvertSetting) show = !show; } - return !show; } @@ -695,7 +700,7 @@ void LLNotification::respond(const LLSD& response) mTemporaryResponder = false; } - if (mForm->getIgnoreType() != LLNotificationForm::IGNORE_NO) + if (mForm->getIgnoreType() > LLNotificationForm::IGNORE_NO) { mForm->setIgnored(mIgnored); if (mIgnored && mForm->getIgnoreType() == LLNotificationForm::IGNORE_WITH_LAST_RESPONSE) diff --git a/indra/llui/llnotifications.h b/indra/llui/llnotifications.h index 1509446920..62cf41256b 100644 --- a/indra/llui/llnotifications.h +++ b/indra/llui/llnotifications.h @@ -180,6 +180,7 @@ public: Optional<std::string> control; Optional<bool> invert_control; Optional<bool> session_only; + Optional<bool> checkbox_only; FormIgnore(); }; @@ -232,7 +233,8 @@ public: typedef enum e_ignore_type { - IGNORE_NO, + IGNORE_CHECKBOX_ONLY = -1, // ignore won't be handled, will set value/checkbox only + IGNORE_NO = 0, IGNORE_WITH_DEFAULT_RESPONSE, IGNORE_WITH_DEFAULT_RESPONSE_SESSION_ONLY, IGNORE_WITH_LAST_RESPONSE, diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 0ca5a08dfc..95a916d7cb 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -664,6 +664,7 @@ set(viewer_SOURCE_FILES llviewerobject.cpp llviewerobjectlist.cpp llvieweroctree.cpp + llviewerparcelaskplay.cpp llviewerparcelmedia.cpp llviewerparcelmediaautoplay.cpp llviewerparcelmgr.cpp @@ -1284,6 +1285,7 @@ set(viewer_HEADER_FILES llviewerobject.h llviewerobjectlist.h llvieweroctree.h + llviewerparcelaskplay.h llviewerparcelmedia.h llviewerparcelmediaautoplay.h llviewerparcelmgr.h diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index f54f683757..b9cb613b96 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -7494,11 +7494,11 @@ <key>ParcelMediaAutoPlayEnable</key> <map> <key>Comment</key> - <string>Auto play parcel media when available</string> + <string>Auto play parcel media when available. 0 - Do not autoplay; 1- Autoplay; 2 - Ask</string> <key>Persist</key> <integer>1</integer> <key>Type</key> - <string>Boolean</string> + <string>S32</string> <key>Value</key> <integer>1</integer> </map> diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 12a459b2b8..2a928a47a2 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -68,6 +68,7 @@ #include "llviewerwindow.h" #include "llviewerdisplay.h" #include "llviewermedia.h" +#include "llviewerparcelaskplay.h" #include "llviewerparcelmedia.h" #include "llviewermediafocus.h" #include "llviewermessage.h" @@ -1938,6 +1939,11 @@ bool LLAppViewer::cleanup() { gSavedPerAccountSettings.saveToFile(gSavedSettings.getString("PerAccountSettingsFile"), TRUE); LL_INFOS() << "Saved settings" << LL_ENDL; + + if (LLViewerParcelAskPlay::instanceExists()) + { + LLViewerParcelAskPlay::getInstance()->saveSettings(); + } } std::string warnings_settings_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, getSettingsFilename("Default", "Warnings")); diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index 9d0e1972a3..b48495b5b2 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -1242,7 +1242,7 @@ void LLFloaterPreference::buildPopupLists() LLNotificationFormPtr formp = templatep->mForm; LLNotificationForm::EIgnoreType ignore = formp->getIgnoreType(); - if (ignore == LLNotificationForm::IGNORE_NO) + if (ignore <= LLNotificationForm::IGNORE_NO) continue; LLSD row; @@ -1824,7 +1824,7 @@ void LLFloaterPreference::resetAllIgnored() iter != LLNotifications::instance().templatesEnd(); ++iter) { - if (iter->second->mForm->getIgnoreType() != LLNotificationForm::IGNORE_NO) + if (iter->second->mForm->getIgnoreType() > LLNotificationForm::IGNORE_NO) { iter->second->mForm->setIgnored(false); } @@ -1837,7 +1837,7 @@ void LLFloaterPreference::setAllIgnored() iter != LLNotifications::instance().templatesEnd(); ++iter) { - if (iter->second->mForm->getIgnoreType() != LLNotificationForm::IGNORE_NO) + if (iter->second->mForm->getIgnoreType() > LLNotificationForm::IGNORE_NO) { iter->second->mForm->setIgnored(true); } @@ -2674,7 +2674,7 @@ void LLPanelPreference::updateMediaAutoPlayCheckbox(LLUICtrl* ctrl) bool music_enabled = getChild<LLCheckBoxCtrl>("enable_music")->get(); bool media_enabled = getChild<LLCheckBoxCtrl>("enable_media")->get(); - getChild<LLCheckBoxCtrl>("media_auto_play_btn")->setEnabled(music_enabled || media_enabled); + getChild<LLCheckBoxCtrl>("media_auto_play_combo")->setEnabled(music_enabled || media_enabled); } } diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h index 8345ad1bb6..4412c95473 100644 --- a/indra/newview/llfloaterpreference.h +++ b/indra/newview/llfloaterpreference.h @@ -168,7 +168,6 @@ public: void refreshUI(); - void onCommitParcelMediaAutoPlayEnable(); void onCommitMediaEnabled(); void onCommitMusicEnabled(); void applyResolution(); diff --git a/indra/newview/llpanelnearbymedia.cpp b/indra/newview/llpanelnearbymedia.cpp index f3a2ed9408..af28ff8e61 100644 --- a/indra/newview/llpanelnearbymedia.cpp +++ b/indra/newview/llpanelnearbymedia.cpp @@ -43,6 +43,7 @@ #include "llbutton.h" #include "lltextbox.h" #include "llviewermedia.h" +#include "llviewerparcelaskplay.h" #include "llviewerparcelmedia.h" #include "llviewerregion.h" #include "llviewermediafocus.h" @@ -83,10 +84,11 @@ LLPanelNearByMedia::LLPanelNearByMedia() { mHoverTimer.stop(); - mParcelAudioAutoStart = gSavedSettings.getBOOL(LLViewerMedia::AUTO_PLAY_MEDIA_SETTING) && - gSavedSettings.getBOOL("MediaTentativeAutoPlay"); + // This is just an initial value, mParcelAudioAutoStart does not affect ParcelMediaAutoPlayEnable + mParcelAudioAutoStart = gSavedSettings.getS32("ParcelMediaAutoPlayEnable") != 0 + && gSavedSettings.getBOOL("MediaTentativeAutoPlay"); - gSavedSettings.getControl(LLViewerMedia::AUTO_PLAY_MEDIA_SETTING)->getSignal()->connect(boost::bind(&LLPanelNearByMedia::handleMediaAutoPlayChanged, this, _2)); + gSavedSettings.getControl("ParcelMediaAutoPlayEnable")->getSignal()->connect(boost::bind(&LLPanelNearByMedia::handleMediaAutoPlayChanged, this, _2)); mCommitCallbackRegistrar.add("MediaListCtrl.EnableAll", boost::bind(&LLPanelNearByMedia::onClickEnableAll, this)); mCommitCallbackRegistrar.add("MediaListCtrl.DisableAll", boost::bind(&LLPanelNearByMedia::onClickDisableAll, this)); @@ -177,9 +179,18 @@ BOOL LLPanelNearByMedia::postBuild() void LLPanelNearByMedia::handleMediaAutoPlayChanged(const LLSD& newvalue) { - // update mParcelAudioAutoStart if AUTO_PLAY_MEDIA_SETTING changes - mParcelAudioAutoStart = gSavedSettings.getBOOL(LLViewerMedia::AUTO_PLAY_MEDIA_SETTING) && - gSavedSettings.getBOOL("MediaTentativeAutoPlay"); + // update mParcelAudioAutoStartMode if "ParcelMediaAutoPlayEnable" changes + S32 value = gSavedSettings.getS32("ParcelMediaAutoPlayEnable"); + mParcelAudioAutoStart = value != 0 + && gSavedSettings.getBOOL("MediaTentativeAutoPlay"); + + LLViewerParcelAskPlay *inst = LLViewerParcelAskPlay::getInstance(); + if (value == 2 && !inst->hasData()) + { + // Init if nessesary + inst->loadSettings(); + } + inst->cancelNotification(); } /*virtual*/ diff --git a/indra/newview/llpanelvolumepulldown.cpp b/indra/newview/llpanelvolumepulldown.cpp index 6792137350..f063d84272 100644 --- a/indra/newview/llpanelvolumepulldown.cpp +++ b/indra/newview/llpanelvolumepulldown.cpp @@ -140,7 +140,7 @@ void LLPanelVolumePulldown::updateMediaAutoPlayCheckbox(LLUICtrl* ctrl) bool music_enabled = getChild<LLCheckBoxCtrl>("enable_music")->get(); bool media_enabled = getChild<LLCheckBoxCtrl>("enable_media")->get(); - getChild<LLCheckBoxCtrl>("media_auto_play_btn")->setEnabled(music_enabled || media_enabled); + getChild<LLCheckBoxCtrl>("media_auto_play_combo")->setEnabled(music_enabled || media_enabled); } } diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 77aa7b36b9..5f235c05a6 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -158,6 +158,7 @@ #include "llviewermessage.h" #include "llviewernetwork.h" #include "llviewerobjectlist.h" +#include "llviewerparcelaskplay.h" #include "llviewerparcelmedia.h" #include "llviewerparcelmgr.h" #include "llviewerregion.h" @@ -1425,6 +1426,10 @@ bool idle_startup() // create a container's instance for start a controlling conversation windows // by the voice's events LLFloaterIMContainer::getInstance(); + if (gSavedSettings.getS32("ParcelMediaAutoPlayEnable") == 2) + { + LLViewerParcelAskPlay::getInstance()->loadSettings(); + } // *Note: this is where gWorldMap used to be initialized. diff --git a/indra/newview/lltoastalertpanel.cpp b/indra/newview/lltoastalertpanel.cpp index 023d1a685c..8df83c64cd 100644 --- a/indra/newview/lltoastalertpanel.cpp +++ b/indra/newview/lltoastalertpanel.cpp @@ -34,7 +34,6 @@ #include "llfontgl.h" #include "lltextbox.h" #include "llbutton.h" -#include "llcheckboxctrl.h" #include "llkeyboard.h" #include "llfocusmgr.h" #include "lliconctrl.h" diff --git a/indra/newview/lltoastnotifypanel.cpp b/indra/newview/lltoastnotifypanel.cpp index 83009a78d4..bccf88128d 100644 --- a/indra/newview/lltoastnotifypanel.cpp +++ b/indra/newview/lltoastnotifypanel.cpp @@ -163,6 +163,11 @@ void LLToastNotifyPanel::updateButtonsLayout(const std::vector<index_button_pair { left = (max_width - btn_rect.getWidth()) / 2; } + else if (left == 0 && buttons.size() == 2) + { + // Note: this and "size() == 1" shouldn't be inside the cycle, might be good idea to refactor whole placing process + left = (max_width - (btn_rect.getWidth() * 2) - h_pad) / 2; + } else if (left + btn_rect.getWidth() > max_width)// whether there is still some place for button+h_pad in the mControlPanel { // looks like we need to add button to the next row @@ -412,7 +417,7 @@ void LLToastNotifyPanel::init( LLRect rect, bool show_images ) mInfoPanel->setFollowsAll(); // Add checkbox (one of couple types) if nessesary. - setCheckBoxes(HPAD * 3, 0, mInfoPanel); + setCheckBoxes(HPAD * 2, 0, mInfoPanel); if (mCheck) { mCheck->setFollows(FOLLOWS_BOTTOM | FOLLOWS_LEFT); diff --git a/indra/newview/lltoastpanel.cpp b/indra/newview/lltoastpanel.cpp index 1abd35d931..100d5ee713 100644 --- a/indra/newview/lltoastpanel.cpp +++ b/indra/newview/lltoastpanel.cpp @@ -161,7 +161,20 @@ void LLCheckBoxToastPanel::setCheckBoxes(const S32 &h_pad, const S32 &v_pad, LLV std::string ignore_label; LLNotificationFormPtr form = mNotification->getForm(); - if (form->getIgnoreType() == LLNotificationForm::IGNORE_WITH_DEFAULT_RESPONSE) + if (form->getIgnoreType() == LLNotificationForm::IGNORE_CHECKBOX_ONLY) + { + // Normally text is only used to describe notification in preferences, + // but this one is not displayed in preferences and works on case by case + // basis. + // Display text if present, display 'always chose' if not. + std::string ignore_message = form->getIgnoreMessage(); + if (ignore_message.empty()) + { + ignore_message = LLNotifications::instance().getGlobalString("alwayschoose"); + } + setCheckBox(ignore_message, ignore_label, boost::bind(&LLCheckBoxToastPanel::onCommitCheckbox, this, _1), h_pad, v_pad, parent_view); + } + else if (form->getIgnoreType() == LLNotificationForm::IGNORE_WITH_DEFAULT_RESPONSE) { setCheckBox(LLNotifications::instance().getGlobalString("skipnexttime"), ignore_label, boost::bind(&LLCheckBoxToastPanel::onCommitCheckbox, this, _1), h_pad, v_pad, parent_view); } diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index a9eb79b649..c97a42bf06 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -57,6 +57,7 @@ #include "llviewercontrol.h" #include "llviewermenufile.h" // LLFilePickerThread #include "llviewernetwork.h" +#include "llviewerparcelaskplay.h" #include "llviewerparcelmedia.h" #include "llviewerparcelmgr.h" #include "llviewerregion.h" @@ -77,7 +78,6 @@ #include <boost/bind.hpp> // for SkinFolder listener #include <boost/signals2.hpp> -/*static*/ const char* LLViewerMedia::AUTO_PLAY_MEDIA_SETTING = "ParcelMediaAutoPlayEnable"; /*static*/ const char* LLViewerMedia::SHOW_MEDIA_ON_OTHERS_SETTING = "MediaShowOnOthers"; /*static*/ const char* LLViewerMedia::SHOW_MEDIA_WITHIN_PARCEL_SETTING = "MediaShowWithinParcel"; /*static*/ const char* LLViewerMedia::SHOW_MEDIA_OUTSIDE_PARCEL_SETTING = "MediaShowOutsideParcel"; @@ -1005,12 +1005,14 @@ void LLViewerMedia::setAllMediaPaused(bool val) } } + LLParcel *agent_parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + // Also do Parcel Media and Parcel Audio if (!val) { if (!LLViewerMedia::isParcelMediaPlaying() && LLViewerMedia::hasParcelMedia()) { - LLViewerParcelMedia::play(LLViewerParcelMgr::getInstance()->getAgentParcel()); + LLViewerParcelMedia::play(agent_parcel); } static LLCachedControl<bool> audio_streaming_music(gSavedSettings, "AudioStreamingMusic", true); @@ -1038,6 +1040,12 @@ void LLViewerMedia::setAllMediaPaused(bool val) LLViewerAudio::getInstance()->stopInternetStreamWithAutoFade(); } } + + // remove play choice for current parcel + if (agent_parcel && gAgent.getRegion()) + { + LLViewerParcelAskPlay::getInstance()->resetSetting(gAgent.getRegion()->getRegionID(), agent_parcel->getLocalID()); + } } ////////////////////////////////////////////////////////////////////////////////////////// @@ -3777,7 +3785,7 @@ void LLViewerMediaImpl::setTextureID(LLUUID id) bool LLViewerMediaImpl::isAutoPlayable() const { return (mMediaAutoPlay && - gSavedSettings.getBOOL(LLViewerMedia::AUTO_PLAY_MEDIA_SETTING) && + gSavedSettings.getS32("ParcelMediaAutoPlayEnable") != 0 && gSavedSettings.getBOOL("MediaTentativeAutoPlay")); } diff --git a/indra/newview/llviewermedia.h b/indra/newview/llviewermedia.h index bea024e952..03d97a3a72 100644 --- a/indra/newview/llviewermedia.h +++ b/indra/newview/llviewermedia.h @@ -76,7 +76,6 @@ class LLViewerMedia public: // String to get/set media autoplay in gSavedSettings - static const char* AUTO_PLAY_MEDIA_SETTING; static const char* SHOW_MEDIA_ON_OTHERS_SETTING; static const char* SHOW_MEDIA_WITHIN_PARCEL_SETTING; static const char* SHOW_MEDIA_OUTSIDE_PARCEL_SETTING; diff --git a/indra/newview/llviewerparcelaskplay.cpp b/indra/newview/llviewerparcelaskplay.cpp new file mode 100644 index 0000000000..d4aa783f12 --- /dev/null +++ b/indra/newview/llviewerparcelaskplay.cpp @@ -0,0 +1,301 @@ +/** + * @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() +{ + 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 + 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 = boost::lexical_cast<std::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(); + } +} + diff --git a/indra/newview/llviewerparcelaskplay.h b/indra/newview/llviewerparcelaskplay.h new file mode 100644 index 0000000000..dc711917d2 --- /dev/null +++ b/indra/newview/llviewerparcelaskplay.h @@ -0,0 +1,89 @@ +/** + * @file llviewerparcelaskplay.h + * @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$ + */ + +#ifndef LLVIEWERPARCELASKPLAY_H +#define LLVIEWERPARCELASKPLAY_H + +#include "llnotificationptr.h" +#include "lluuid.h" + +class LLViewerParcelAskPlay : public LLSingleton<LLViewerParcelAskPlay> +{ + LLSINGLETON(LLViewerParcelAskPlay); + ~LLViewerParcelAskPlay(); + void initSingleton(); + void cleanupSingleton(); +public: + // functor expects functor(region_id, parcel_id, url, play/stop) + typedef boost::function<void(const LLUUID&, const S32&, const std::string&, const bool&)> ask_callback; + void askToPlay(const LLUUID ®ion_id, const S32 &parcel_id, const std::string &url, ask_callback cb); + void cancelNotification(); + + void resetCurrentParcelSetting(); + void resetSetting(const LLUUID ®ion_id, const S32 &parcel_id); + static void resetSettings(); + + void loadSettings(); + void saveSettings(); + + S32 hasData() { return !mRegionMap.empty(); } // subsitution for 'isInitialized' + +private: + enum EAskPlayMode{ + ASK_PLAY_IGNORE = 0, + ASK_PLAY_PLAY, + ASK_PLAY_ASK + }; + + class ParcelData + { + public: + LLDate mDate; + EAskPlayMode mMode; + }; + + void setSetting(const LLUUID ®ion_id, const S32 &parcel_id, const ParcelData &data); + ParcelData* getSetting(const LLUUID ®ion_id, const S32 &parcel_id); + EAskPlayMode getPlayMode(const LLUUID ®ion_id, const S32 &parcel_id); + void setPlayMode(const LLUUID ®ion_id, const S32 &parcel_id, EAskPlayMode); + + static void onAskPlayResponse(const LLSD& notification, const LLSD& response, ask_callback cb); + + static std::string getAskPlayFilename(); + +private: + // Variables + + typedef std::map<S32, ParcelData> parcel_data_map_t; + typedef std::map<LLUUID, parcel_data_map_t> region_map_t; + region_map_t mRegionMap; + + // only one notification is supposed to exists and be visible + LLNotificationPtr pNotification; +}; + + +#endif // LLVIEWERPARCELASKPLAY_H diff --git a/indra/newview/llviewerparcelmediaautoplay.cpp b/indra/newview/llviewerparcelmediaautoplay.cpp index 57ee583eae..b4dc6932be 100644 --- a/indra/newview/llviewerparcelmediaautoplay.cpp +++ b/indra/newview/llviewerparcelmediaautoplay.cpp @@ -24,17 +24,20 @@ * $/LicenseInfo$ */ + #include "llviewerprecompiledheaders.h" + #include "llviewerparcelmediaautoplay.h" -#include "llviewerparcelmedia.h" + +#include "llparcel.h" #include "llviewercontrol.h" #include "llviewermedia.h" -#include "llviewerregion.h" -#include "llparcel.h" +#include "llviewerparcelaskplay.h" +#include "llviewerparcelmedia.h" #include "llviewerparcelmgr.h" -#include "lluuid.h" -#include "message.h" +#include "llviewerregion.h" #include "llviewertexturelist.h" // for texture stats +#include "message.h" #include "llagent.h" #include "llmimetypes.h" @@ -141,11 +144,28 @@ BOOL LLViewerParcelMediaAutoPlay::tick() { if (this_parcel) { - if (gSavedSettings.getBOOL("ParcelMediaAutoPlayEnable")) + static LLCachedControl<S32> autoplay_mode(gSavedSettings, "ParcelMediaAutoPlayEnable"); + + switch (autoplay_mode()) { - // and last but not least, only play when autoplay is enabled - LLViewerParcelMedia::play(this_parcel); - } + case 0: + // Disabled + break; + case 1: + // Play, default value for ParcelMediaAutoPlayEnable + LLViewerParcelMedia::play(this_parcel); + break; + case 2: + default: + { + // Ask + LLViewerParcelAskPlay::getInstance()->askToPlay(this_region->getRegionID(), + this_parcel->getLocalID(), + this_parcel->getMediaURL(), + onStartMusicResponse); + break; + } + } } mPlayed = TRUE; @@ -158,5 +178,18 @@ BOOL LLViewerParcelMediaAutoPlay::tick() return FALSE; // continue ticking forever please. } - +//static +void LLViewerParcelMediaAutoPlay::onStartMusicResponse(const LLUUID ®ion_id, const S32 &parcel_id, const std::string &url, const bool &play) +{ + if (play) + { + LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + + // make sure we are still there + if (parcel->getLocalID() == parcel_id && gAgent.getRegion()->getRegionID() == region_id) + { + LLViewerParcelMedia::play(parcel); + } + } +} diff --git a/indra/newview/llviewerparcelmediaautoplay.h b/indra/newview/llviewerparcelmediaautoplay.h index a052362829..a404f60da1 100644 --- a/indra/newview/llviewerparcelmediaautoplay.h +++ b/indra/newview/llviewerparcelmediaautoplay.h @@ -41,6 +41,9 @@ class LLViewerParcelMediaAutoPlay : LLEventTimer static void playStarted(); private: + static void onStartMusicResponse(const LLUUID ®ion_id, const S32 &parcel_id, const std::string &url, const bool &play); + + private: S32 mLastParcelID; LLUUID mLastRegionID; BOOL mPlayed; diff --git a/indra/newview/llviewerparcelmgr.cpp b/indra/newview/llviewerparcelmgr.cpp index 416d5d8e2e..86cdf96e21 100644 --- a/indra/newview/llviewerparcelmgr.cpp +++ b/indra/newview/llviewerparcelmgr.cpp @@ -42,6 +42,7 @@ // Viewer includes #include "llagent.h" #include "llagentaccess.h" +#include "llviewerparcelaskplay.h" #include "llviewerwindow.h" #include "llviewercontrol.h" //#include "llfirstuse.h" @@ -1832,6 +1833,7 @@ void LLViewerParcelMgr::processParcelProperties(LLMessageSystem *msg, void **use // Only update stream if parcel changed (recreated) or music is playing (enabled) if (!agent_parcel_update || gSavedSettings.getBOOL("MediaTentativeAutoPlay")) { + LLViewerParcelAskPlay::getInstance()->cancelNotification(); std::string music_url_raw = parcel->getMusicURL(); // Trim off whitespace from front and back @@ -1843,7 +1845,8 @@ void LLViewerParcelMgr::processParcelProperties(LLMessageSystem *msg, void **use { if (music_url.substr(0, 7) == "http://") { - optionally_start_music(music_url); + LLViewerRegion *region = LLWorld::getInstance()->getRegion(msg->getSender()); + optionally_start_music(music_url, parcel->mLocalID, region->getRegionID()); } else { @@ -1864,25 +1867,61 @@ void LLViewerParcelMgr::processParcelProperties(LLMessageSystem *msg, void **use else { // Public land has no music + LLViewerParcelAskPlay::getInstance()->cancelNotification(); LLViewerAudio::getInstance()->stopInternetStreamWithAutoFade(); } }//if gAudiop }; } -void LLViewerParcelMgr::optionally_start_music(const std::string& music_url) +//static +void LLViewerParcelMgr::onStartMusicResponse(const LLUUID ®ion_id, const S32 &parcel_id, const std::string &url, const bool &play) { - if (gSavedSettings.getBOOL("AudioStreamingMusic")) + if (play) + { + LL_INFOS("ParcelMgr") << "Starting parcel music " << url << LL_ENDL; + LLViewerAudio::getInstance()->startInternetStreamWithAutoFade(url); + } +} + +void LLViewerParcelMgr::optionally_start_music(const std::string &music_url, const S32 &local_id, const LLUUID ®ion_id) +{ + static LLCachedControl<bool> streaming_music(gSavedSettings, "AudioStreamingMusic", true); + if (streaming_music) { + static LLCachedControl<S32> autoplay_mode(gSavedSettings, "ParcelMediaAutoPlayEnable", 1); + static LLCachedControl<bool> tentative_autoplay(gSavedSettings, "MediaTentativeAutoPlay", true); // only play music when you enter a new parcel if the UI control for this // was not *explicitly* stopped by the user. (part of SL-4878) LLPanelNearByMedia* nearby_media_panel = gStatusBar->getNearbyMediaPanel(); - if ((nearby_media_panel && - nearby_media_panel->getParcelAudioAutoStart()) || - // or they have expressed no opinion in the UI, but have autoplay on... - (!nearby_media_panel && - gSavedSettings.getBOOL(LLViewerMedia::AUTO_PLAY_MEDIA_SETTING) && - gSavedSettings.getBOOL("MediaTentativeAutoPlay"))) + + // ask mode //todo constants + if (autoplay_mode == 2) + { + // stop previous stream + LLViewerAudio::getInstance()->startInternetStreamWithAutoFade(LLStringUtil::null); + + // if user set media to play - ask + if ((nearby_media_panel && nearby_media_panel->getParcelAudioAutoStart()) + || (!nearby_media_panel && tentative_autoplay)) + { + LLViewerParcelAskPlay::getInstance()->askToPlay(region_id, + local_id, + music_url, + onStartMusicResponse); + } + else + { + LLViewerParcelAskPlay::getInstance()->cancelNotification(); + } + } + // autoplay + else if ((nearby_media_panel + && nearby_media_panel->getParcelAudioAutoStart()) + // or they have expressed no opinion in the UI, but have autoplay on... + || (!nearby_media_panel + && autoplay_mode == 1 + && tentative_autoplay)) { LL_INFOS("ParcelMgr") << "Starting parcel music " << music_url << LL_ENDL; LLViewerAudio::getInstance()->startInternetStreamWithAutoFade(music_url); diff --git a/indra/newview/llviewerparcelmgr.h b/indra/newview/llviewerparcelmgr.h index 29219843c9..288077fafc 100644 --- a/indra/newview/llviewerparcelmgr.h +++ b/indra/newview/llviewerparcelmgr.h @@ -268,7 +268,8 @@ public: // *NOTE: Taken out 2005-03-21. Phoenix. //void makeLandmarkAtSelection(); - static void optionally_start_music(const std::string& music_url); + static void onStartMusicResponse(const LLUUID ®ion_id, const S32 &parcel_id, const std::string &url, const bool &play); + static void optionally_start_music(const std::string &music_url, const S32 &local_id, const LLUUID ®ion_id); static void processParcelOverlay(LLMessageSystem *msg, void **user_data); static void processParcelProperties(LLMessageSystem *msg, void **user_data); diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 874fbe19fd..a757a4beaa 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -6057,6 +6057,34 @@ Would you like to turn off Do Not Disturb before completing this transaction? </notification> <notification + icon="notify.tga" + label="Parcel is Playing Media" + name="ParcelPlayingMedia" + persist="false" + type="notify"> +This land has media: +[URL] +Would you like to play it? + <tag>confirm</tag> + <form name="form"> + <ignore name="ignore" + checkbox_only="true" + text="Always choose this option for this land."/> + <button + ignore="Play Media" + index="1" + name="Yes" + text="Play"/> + <button + default="true" + ignore="Ignore Media" + index="0" + name="No" + text="Ignore"/> + </form> + </notification> + + <notification icon="alertmodal.tga" name="ConfirmDeleteProtectedCategory" type="alertmodal"> diff --git a/indra/newview/skins/default/xui/en/panel_preferences_sound.xml b/indra/newview/skins/default/xui/en/panel_preferences_sound.xml index 649403184d..90f2ca2713 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_sound.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_sound.xml @@ -322,17 +322,40 @@ name="enable_voice_check" width="110"/> <!-- --> - <check_box - name="media_auto_play_btn" - control_name="ParcelMediaAutoPlayEnable" - enabled_control="AudioStreamingMedia" - value="true" - follows="left|bottom|right" - height="15" - tool_tip="Check this to let media auto-play if it wants" - label="Allow Media to auto-play" - top_pad="1" - left="25"/> + <text + follows="left|top" + layout="topleft" + height="15" + left="0" + top_pad="3" + width="120" + halign="right" + name="media_autoplay_label"> + Media auto-play + </text> + <combo_box + control_name="ParcelMediaAutoPlayEnable" + enabled_control="AudioStreamingMedia" + follows="left|top" + layout="topleft" + height="23" + left_pad="7" + top_delta="-4" + name="media_auto_play_combo" + width="100"> + <item + label="Disabled" + name="autoplay_disabled" + value="0"/> + <item + label="Enabled" + name="autoplay_enabled" + value="1"/> + <item + label="Ask" + name="autoplay_ask" + value="2"/> + </combo_box> <check_box name="media_show_on_others_btn" control_name="MediaShowOnOthers" |