Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions indra/newview/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,7 @@ set(viewer_SOURCE_FILES
llfloaterimnearbychat.cpp
llfloaterimnearbychathandler.cpp
llfloaterimnearbychatlistener.cpp
llnearbyvoicemoderation.cpp
llnetmap.cpp
llnotificationalerthandler.cpp
llnotificationgrouphandler.cpp
Expand Down Expand Up @@ -1103,6 +1104,7 @@ set(viewer_HEADER_FILES
llnameeditor.h
llnamelistctrl.h
llnavigationbar.h
llnearbyvoicemoderation.h
llnetmap.h
llnotificationhandler.h
llnotificationlistitem.h
Expand Down
101 changes: 98 additions & 3 deletions indra/newview/llfloaterimcontainer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@
#include "llsdserialize.h"
#include "llviewermenu.h" // is_agent_mappable
#include "llviewerobjectlist.h"
#include "llvoavatar.h"
#include "llnearbyvoicemoderation.h"


const S32 EVENTS_PER_IDLE_LOOP_CURRENT_SESSION = 80;
Expand Down Expand Up @@ -90,6 +92,7 @@ LLFloaterIMContainer::LLFloaterIMContainer(const LLSD& seed, const Params& param

mAutoResize = false;
LLTransientFloaterMgr::getInstance()->addControlView(LLTransientFloaterMgr::IM, this);
LLNearbyVoiceModeration::getInstance();
}

LLFloaterIMContainer::~LLFloaterIMContainer()
Expand Down Expand Up @@ -530,6 +533,23 @@ void LLFloaterIMContainer::idleUpdate()
mGeneralTitleInUse = !needs_override;
setTitle(needs_override ? conversation_floaterp->getTitle() : mGeneralTitle);
}
const LLConversationItem* nearby_session = getSessionModel(LLUUID());
if (nearby_session)
{
LLFolderViewModelItemCommon::child_list_t::const_iterator current_participant_model = nearby_session->getChildrenBegin();
LLFolderViewModelItemCommon::child_list_t::const_iterator end_participant_model = nearby_session->getChildrenEnd();
while (current_participant_model != end_participant_model)
{
LLConversationItemParticipant* participant_model =
dynamic_cast<LLConversationItemParticipant*>((*current_participant_model).get());
if (participant_model)
{
participant_model->setModeratorOptionsVisible(LLNearbyVoiceModeration::getInstance()->isNearbyChatModerator());
}

current_participant_model++;
}
}
}

mParticipantRefreshTimer.setTimerExpirySec(1.0f);
Expand Down Expand Up @@ -1685,6 +1705,10 @@ bool LLFloaterIMContainer::visibleContextMenuItem(const LLSD& userdata)
{
return isMuted(conversation_item->getUUID());
}
else if ("can_allow_text_chat" == item)
{
return !isNearbyChatSpeakerSelected();
}

return true;
}
Expand Down Expand Up @@ -2014,9 +2038,27 @@ LLConversationViewParticipant* LLFloaterIMContainer::createConversationViewParti

bool LLFloaterIMContainer::enableModerateContextMenuItem(const std::string& userdata, bool is_self)
{
// only group moderators can perform actions related to this "enable callback"
if (!isGroupModerator())
if (LLNearbyVoiceModeration::getInstance()->isNearbyChatModerator() && isNearbyChatSpeakerSelected())
{
// Determine here which actions are allowed
if ("can_moderate_voice" == userdata)
{
return true;
}
else if (("can_mute" == userdata))
{
return !is_self;
}
else if ("can_unmute" == userdata)
{
return true;
}

return false;
}
else if (!isGroupModerator())
{
// only group moderators can perform actions related to this "enable callback"
return false;
}

Expand Down Expand Up @@ -2149,7 +2191,35 @@ void LLFloaterIMContainer::banSelectedMember(const LLUUID& participant_uuid)

void LLFloaterIMContainer::moderateVoice(const std::string& command, const LLUUID& userID)
{
if (!gAgent.getRegion()) return;
if (!gAgent.getRegion())
{
return;
}

if (isNearbyChatSpeakerSelected())
{
if ("selected" == command)
{
// Request a mute/unmute using a capability request via the simulator
LLNearbyVoiceModeration::getInstance()->requestMuteIndividual(userID, !isMuted(userID));
}
else
if ("mute_all" == command)
{
// Send the mute_all request to the server
const bool mute_state = true;
LLNearbyVoiceModeration::getInstance()->requestMuteAll(mute_state);
}
else
if ("unmute_all" == command)
{
// Send the unmute_all request to the server
const bool mute_state = false;
LLNearbyVoiceModeration::getInstance()->requestMuteAll(mute_state);
}

return;
}

if (command.compare("selected"))
{
Expand Down Expand Up @@ -2267,6 +2337,31 @@ LLSpeaker * LLFloaterIMContainer::getSpeakerOfSelectedParticipant(LLSpeakerMgr *
return speaker_managerp->findSpeaker(participant_itemp->getUUID());
}

bool LLFloaterIMContainer::isNearbyChatSpeakerSelected()
{
LLFolderViewItem *selectedItem = mConversationsRoot->getCurSelectedItem();
if (!selectedItem)
{
LL_WARNS() << "Current selected item is null" << LL_ENDL;
return NULL;
}

conversations_widgets_map::const_iterator iter = mConversationsWidgets.begin();
conversations_widgets_map::const_iterator end = mConversationsWidgets.end();
const LLUUID * conversation_uuidp = NULL;
while(iter != end)
{
if (iter->second == selectedItem || iter->second == selectedItem->getParentFolder())
{
conversation_uuidp = &iter->first;
break;
}
++iter;
}
// Nearby chat ID is LLUUID::null
return conversation_uuidp->isNull();
}

void LLFloaterIMContainer::toggleAllowTextChat(const LLUUID& participant_uuid)
{
LLIMSpeakerMgr * speaker_managerp = dynamic_cast<LLIMSpeakerMgr*>(getSpeakerMgrForSelectedParticipant());
Expand Down
1 change: 1 addition & 0 deletions indra/newview/llfloaterimcontainer.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ class LLFloaterIMContainer
void banSelectedMember(const LLUUID& participant_uuid);
void openNearbyChat();
bool isParticipantListExpanded();
bool isNearbyChatSpeakerSelected();

void idleUpdate(); // for convenience (self) from static idle
void idleProcessEvents();
Expand Down
200 changes: 200 additions & 0 deletions indra/newview/llnearbyvoicemoderation.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
/**
* @file llnearbyvoicemoderation.cpp
*
* $LicenseInfo:firstyear=2008&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, 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 "llagent.h"
#include "llnotificationsutil.h"
#include "llviewerregion.h"
#include "llvoavatar.h"
#include "llvoiceclient.h"
#include "llviewerobjectlist.h"
#include "llviewerparcelmgr.h"
#include "roles_constants.h"

#include "llnearbyvoicemoderation.h"

LLNearbyVoiceModeration::LLNearbyVoiceModeration()
{
}

LLNearbyVoiceModeration::~LLNearbyVoiceModeration()
{
}

LLVOAvatar* LLNearbyVoiceModeration::getVOAvatarFromId(const LLUUID& agent_id)
{
LLViewerObject *obj = gObjectList.findObject(agent_id);
while (obj && obj->isAttachment())
{
obj = (LLViewerObject*)obj->getParent();
}

if (obj && obj->isAvatar())
{
return (LLVOAvatar*)obj;
}
else
{
return NULL;
}
}

const std::string LLNearbyVoiceModeration::getCapUrlFromRegion(LLViewerRegion* region)
{
if (! region || ! region->capabilitiesReceived())
{
return std::string();
}

std::string url = region->getCapability("SpatialVoiceModerationRequest");
if (url.empty())
{
LL_INFOS() << "Capability URL for region " << region->getName() << " is empty" << LL_ENDL;
return std::string();
}
LL_INFOS() << "Capability URL for region " << region->getName() << " is " << url << LL_ENDL;

return url;
}

void LLNearbyVoiceModeration::requestMuteIndividual(const LLUUID& agent_id, bool mute)
{
LLVOAvatar* avatar = getVOAvatarFromId(agent_id);
if (avatar)
{
const std::string cap_url = getCapUrlFromRegion(avatar->getRegion());
if (cap_url.length())
{
const std::string operand = mute ? "mute" : "unmute";

LLSD body;
body["operand"] = operand;
body["agent_id"] = agent_id;

const std::string agent_name = avatar->getFullname();
LL_INFOS() << "Resident " << agent_name
<< " (" << agent_id << ")" << " applying " << operand << LL_ENDL;

std::string success_msg =
STRINGIZE("Resident " << agent_name
<< " (" << agent_id << ")" << " nearby voice was set to " << operand);

std::string failure_msg =
STRINGIZE("Unable to change voice muting for resident "
<< agent_name << " (" << agent_id << ")");

LLCoreHttpUtil::HttpCoroutineAdapter::messageHttpPost(
cap_url,
body,
success_msg,
failure_msg);
}
}
}

void LLNearbyVoiceModeration::requestMuteAll(bool mute)
{
// Use our own avatar to get the region name
LLViewerRegion* region = gAgent.getRegion();

const std::string cap_url = getCapUrlFromRegion(region);
if (cap_url.length())
{
const std::string operand = mute ? "mute_all" : "unmute_all";

LLSD body;
body["operand"] = operand;

LL_INFOS() << "For all residents in this region, applying: " << operand << LL_ENDL;

std::string success_msg =
STRINGIZE("Nearby voice for all residents was set to: " << operand);

std::string failure_msg =
STRINGIZE("Unable to set nearby voice for all residents to: " << operand);

LLCoreHttpUtil::HttpCoroutineAdapter::messageHttpPost(
cap_url,
body,
success_msg,
failure_msg);
}
}

void LLNearbyVoiceModeration::setMutedInfo(const std::string& channelID, bool mute)
{
auto it = mChannelMuteMap.find(channelID);
if (it == mChannelMuteMap.end())
{
if (mute)
{
// Channel is new and being muted
showMutedNotification(true);
}
mChannelMuteMap[channelID] = mute;
}
else
{
if (it->second != mute)
{
// Flag changed
showMutedNotification(mute);
it->second = mute;
}
}
if (mute && LLVoiceClient::getInstance()->getUserPTTState())
{
LLVoiceClient::getInstance()->setUserPTTState(false);
}
}

bool LLNearbyVoiceModeration::showNotificationIfNeeded()
{
if (LLVoiceClient::getInstance()->inProximalChannel() &&
LLVoiceClient::getInstance()->getIsModeratorMuted(gAgentID))
{
return showMutedNotification(true);
}
return false;
}

bool LLNearbyVoiceModeration::showMutedNotification(bool is_muted)
{
// Check if the current voice channel is nearby chat
if (LLVoiceClient::getInstance()->inProximalChannel())
{
LLNotificationsUtil::add(is_muted ? "NearbyVoiceMutedByModerator" : "NearbyVoiceUnmutedByModerator");
return true;
}
return false;
}

bool LLNearbyVoiceModeration::isNearbyChatModerator()
{
return gAgent.getRegion() && gAgent.getRegion()->isRegionWebRTCEnabled() &&
(gAgent.canManageEstate() || LLViewerParcelMgr::getInstance()->allowVoiceModeration());
}

Loading
Loading