summaryrefslogtreecommitdiff
path: root/indra/newview/llcallfloater.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview/llcallfloater.cpp')
-rw-r--r--indra/newview/llcallfloater.cpp783
1 files changed, 783 insertions, 0 deletions
diff --git a/indra/newview/llcallfloater.cpp b/indra/newview/llcallfloater.cpp
new file mode 100644
index 0000000000..b11bba58e4
--- /dev/null
+++ b/indra/newview/llcallfloater.cpp
@@ -0,0 +1,783 @@
+/**
+ * @file llcallfloater.cpp
+ * @author Mike Antipov
+ * @brief Voice Control Panel in a Voice Chats (P2P, Group, Nearby...).
+ *
+ * $LicenseInfo:firstyear=2009&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 "llnotificationsutil.h"
+#include "lltrans.h"
+
+#include "llcallfloater.h"
+
+#include "llagent.h"
+#include "llagentdata.h" // for gAgentID
+#include "llavatariconctrl.h"
+#include "llavatarlist.h"
+#include "llbottomtray.h"
+#include "lldraghandle.h"
+#include "llimfloater.h"
+#include "llfloaterreg.h"
+#include "llparticipantlist.h"
+#include "llspeakers.h"
+#include "lltextutil.h"
+#include "lltransientfloatermgr.h"
+#include "llviewerwindow.h"
+#include "llvoicechannel.h"
+#include "llviewerparcelmgr.h"
+
+static void get_voice_participants_uuids(uuid_vec_t& speakers_uuids);
+void reshape_floater(LLCallFloater* floater, S32 delta_height);
+
+class LLNonAvatarCaller : public LLAvatarListItem
+{
+public:
+ LLNonAvatarCaller() : LLAvatarListItem(false)
+ {
+
+ }
+ BOOL postBuild()
+ {
+ BOOL rv = LLAvatarListItem::postBuild();
+
+ if (rv)
+ {
+ setOnline(true);
+ showLastInteractionTime(false);
+ setShowProfileBtn(false);
+ setShowInfoBtn(false);
+ mAvatarIcon->setValue("Avaline_Icon");
+ mAvatarIcon->setToolTip(std::string(""));
+ }
+ return rv;
+ }
+
+ void setName(const std::string& name)
+ {
+ const std::string& formatted_phone = LLTextUtil::formatPhoneNumber(name);
+ LLAvatarListItem::setName(formatted_phone);
+ }
+
+ void setSpeakerId(const LLUUID& id) { mSpeakingIndicator->setSpeakerId(id); }
+};
+
+
+static void* create_non_avatar_caller(void*)
+{
+ return new LLNonAvatarCaller;
+}
+
+LLVoiceChannel* LLCallFloater::sCurrentVoiceChannel = NULL;
+
+LLCallFloater::LLCallFloater(const LLSD& key)
+: LLTransientDockableFloater(NULL, false, key)
+, mSpeakerManager(NULL)
+, mParticipants(NULL)
+, mAvatarList(NULL)
+, mNonAvatarCaller(NULL)
+, mVoiceType(VC_LOCAL_CHAT)
+, mAgentPanel(NULL)
+, mSpeakingIndicator(NULL)
+, mIsModeratorMutedVoice(false)
+, mInitParticipantsVoiceState(false)
+{
+ static LLUICachedControl<S32> voice_left_remove_delay ("VoiceParticipantLeftRemoveDelay", 10);
+ mSpeakerDelayRemover = new LLSpeakersDelayActionsStorage(boost::bind(&LLCallFloater::removeVoiceLeftParticipant, this, _1), voice_left_remove_delay);
+
+ mFactoryMap["non_avatar_caller"] = LLCallbackMap(create_non_avatar_caller, NULL);
+ LLVoiceClient::instance().addObserver(this);
+ LLTransientFloaterMgr::getInstance()->addControlView(this);
+
+ // force docked state since this floater doesn't save it between recreations
+ setDocked(true);
+}
+
+LLCallFloater::~LLCallFloater()
+{
+ resetVoiceRemoveTimers();
+ delete mSpeakerDelayRemover;
+
+ delete mParticipants;
+ mParticipants = NULL;
+
+ mAvatarListRefreshConnection.disconnect();
+ mVoiceChannelStateChangeConnection.disconnect();
+
+ if(LLVoiceClient::instanceExists())
+ {
+ LLVoiceClient::getInstance()->removeObserver(this);
+ }
+ LLTransientFloaterMgr::getInstance()->removeControlView(this);
+}
+
+// virtual
+BOOL LLCallFloater::postBuild()
+{
+ LLTransientDockableFloater::postBuild();
+ mAvatarList = getChild<LLAvatarList>("speakers_list");
+ mAvatarListRefreshConnection = mAvatarList->setRefreshCompleteCallback(boost::bind(&LLCallFloater::onAvatarListRefreshed, this));
+
+ childSetAction("leave_call_btn", boost::bind(&LLCallFloater::leaveCall, this));
+
+ mNonAvatarCaller = findChild<LLNonAvatarCaller>("non_avatar_caller");
+ mNonAvatarCaller->setVisible(FALSE);
+
+ LLView *anchor_panel = LLBottomTray::getInstance()->getChild<LLView>("speak_flyout_btn");
+
+ setDockControl(new LLDockControl(
+ anchor_panel, this,
+ getDockTongue(), LLDockControl::TOP));
+
+ initAgentData();
+
+ connectToChannel(LLVoiceChannel::getCurrentVoiceChannel());
+
+ setIsChrome(true);
+ //chrome="true" hides floater caption
+ if (mDragHandle)
+ mDragHandle->setTitleVisible(TRUE);
+
+ updateSession();
+
+ return TRUE;
+}
+
+// virtual
+void LLCallFloater::onOpen(const LLSD& /*key*/)
+{
+}
+
+// virtual
+void LLCallFloater::draw()
+{
+ // we have to refresh participants to display ones not in voice as disabled.
+ // It should be done only when she joins or leaves voice chat.
+ // But seems that LLVoiceClientParticipantObserver is not enough to satisfy this requirement.
+ // *TODO: mantipov: remove from draw()
+
+ // NOTE: it looks like calling onChange() here is not necessary,
+ // but sometime it is not called properly from the observable object.
+ // Seems this is a problem somewhere in Voice Client (LLVoiceClient::participantAddedEvent)
+// onChange();
+
+ bool is_moderator_muted = LLVoiceClient::getInstance()->getIsModeratorMuted(gAgentID);
+
+ if (mIsModeratorMutedVoice != is_moderator_muted)
+ {
+ setModeratorMutedVoice(is_moderator_muted);
+ }
+
+ // Need to resort the participant list if it's in sort by recent speaker order.
+ if (mParticipants)
+ mParticipants->updateRecentSpeakersOrder();
+
+ LLTransientDockableFloater::draw();
+}
+
+// virtual
+void LLCallFloater::onParticipantsChanged()
+{
+ if (NULL == mParticipants) return;
+ updateParticipantsVoiceState();
+
+ // Add newly joined participants.
+ uuid_vec_t speakers_uuids;
+ get_voice_participants_uuids(speakers_uuids);
+ for (uuid_vec_t::const_iterator it = speakers_uuids.begin(); it != speakers_uuids.end(); it++)
+ {
+ mParticipants->addAvatarIDExceptAgent(*it);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+/// PRIVATE SECTION
+//////////////////////////////////////////////////////////////////////////
+
+void LLCallFloater::leaveCall()
+{
+ LLVoiceChannel* voice_channel = LLVoiceChannel::getCurrentVoiceChannel();
+ if (voice_channel)
+ {
+ gIMMgr->endCall(voice_channel->getSessionID());
+ }
+}
+
+void LLCallFloater::updateSession()
+{
+ LLVoiceChannel* voice_channel = LLVoiceChannel::getCurrentVoiceChannel();
+ if (voice_channel)
+ {
+ LL_DEBUGS("Voice") << "Current voice channel: " << voice_channel->getSessionID() << LL_ENDL;
+
+ if (mSpeakerManager && voice_channel->getSessionID() == mSpeakerManager->getSessionID())
+ {
+ LL_DEBUGS("Voice") << "Speaker manager is already set for session: " << voice_channel->getSessionID() << LL_ENDL;
+ return;
+ }
+ else
+ {
+ mSpeakerManager = NULL;
+ }
+ }
+
+ const LLUUID& session_id = voice_channel ? voice_channel->getSessionID() : LLUUID::null;
+
+ LLIMModel::LLIMSession* im_session = LLIMModel::getInstance()->findIMSession(session_id);
+ if (im_session)
+ {
+ mSpeakerManager = LLIMModel::getInstance()->getSpeakerManager(session_id);
+ switch (im_session->mType)
+ {
+ case IM_NOTHING_SPECIAL:
+ case IM_SESSION_P2P_INVITE:
+ mVoiceType = VC_PEER_TO_PEER;
+
+ if (!im_session->mOtherParticipantIsAvatar)
+ {
+ mVoiceType = VC_PEER_TO_PEER_AVALINE;
+ }
+ break;
+ case IM_SESSION_CONFERENCE_START:
+ case IM_SESSION_GROUP_START:
+ case IM_SESSION_INVITE:
+ if (gAgent.isInGroup(session_id))
+ {
+ mVoiceType = VC_GROUP_CHAT;
+ }
+ else
+ {
+ mVoiceType = VC_AD_HOC_CHAT;
+ }
+ break;
+ default:
+ llwarning("Failed to determine voice call IM type", 0);
+ mVoiceType = VC_GROUP_CHAT;
+ break;
+ }
+ }
+
+ if (NULL == mSpeakerManager)
+ {
+ // By default show nearby chat participants
+ mSpeakerManager = LLLocalSpeakerMgr::getInstance();
+ LL_DEBUGS("Voice") << "Set DEFAULT speaker manager" << LL_ENDL;
+ mVoiceType = VC_LOCAL_CHAT;
+ }
+
+ updateTitle();
+
+ // Hide "Leave Call" button for nearby chat
+ bool is_local_chat = mVoiceType == VC_LOCAL_CHAT;
+ getChildView("leave_call_btn_panel")->setVisible( !is_local_chat);
+
+ refreshParticipantList();
+ updateAgentModeratorState();
+
+ // Show floater for voice calls & only in CONNECTED to voice channel state
+ if (!is_local_chat &&
+ voice_channel &&
+ LLVoiceChannel::STATE_CONNECTED == voice_channel->getState())
+ {
+ LLIMFloater* im_floater = LLIMFloater::findInstance(session_id);
+ bool show_me = !(im_floater && im_floater->getVisible());
+ if (show_me)
+ {
+ setVisible(true);
+ }
+ }
+}
+
+void LLCallFloater::refreshParticipantList()
+{
+ bool non_avatar_caller = VC_PEER_TO_PEER_AVALINE == mVoiceType;
+
+ if (non_avatar_caller)
+ {
+ LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(mSpeakerManager->getSessionID());
+ mNonAvatarCaller->setSpeakerId(session->mOtherParticipantID);
+ mNonAvatarCaller->setName(session->mName);
+ }
+
+ mNonAvatarCaller->setVisible(non_avatar_caller);
+ mAvatarList->setVisible(!non_avatar_caller);
+
+ if (!non_avatar_caller)
+ {
+ mParticipants = new LLParticipantList(mSpeakerManager, mAvatarList, true, mVoiceType != VC_GROUP_CHAT && mVoiceType != VC_AD_HOC_CHAT, false);
+ mParticipants->setValidateSpeakerCallback(boost::bind(&LLCallFloater::validateSpeaker, this, _1));
+ mParticipants->setSortOrder(LLParticipantList::E_SORT_BY_RECENT_SPEAKERS);
+
+ if (LLLocalSpeakerMgr::getInstance() == mSpeakerManager)
+ {
+ mAvatarList->setNoItemsCommentText(getString("no_one_near"));
+ }
+
+ // we have to made delayed initialization of voice state of participant list.
+ // it will be performed after first LLAvatarList refreshing in the onAvatarListRefreshed().
+ mInitParticipantsVoiceState = true;
+ }
+}
+
+void LLCallFloater::onAvatarListRefreshed()
+{
+ if (mInitParticipantsVoiceState)
+ {
+ initParticipantsVoiceState();
+ mInitParticipantsVoiceState = false;
+ }
+ else
+ {
+ updateParticipantsVoiceState();
+ }
+}
+
+// static
+void LLCallFloater::sOnCurrentChannelChanged(const LLUUID& /*session_id*/)
+{
+ LLVoiceChannel* channel = LLVoiceChannel::getCurrentVoiceChannel();
+
+ // *NOTE: if signal was sent for voice channel with LLVoiceChannel::STATE_NO_CHANNEL_INFO
+ // it sill be sent for the same channel again (when state is changed).
+ // So, lets ignore this call.
+ if (channel == sCurrentVoiceChannel) return;
+
+ LLCallFloater* call_floater = LLFloaterReg::getTypedInstance<LLCallFloater>("voice_controls");
+
+ call_floater->connectToChannel(channel);
+}
+
+void LLCallFloater::updateTitle()
+{
+ LLVoiceChannel* voice_channel = LLVoiceChannel::getCurrentVoiceChannel();
+ std::string title;
+ switch (mVoiceType)
+ {
+ case VC_LOCAL_CHAT:
+ title = getString("title_nearby");
+ break;
+ case VC_PEER_TO_PEER:
+ case VC_PEER_TO_PEER_AVALINE:
+ {
+ title = voice_channel->getSessionName();
+
+ if (VC_PEER_TO_PEER_AVALINE == mVoiceType)
+ {
+ title = LLTextUtil::formatPhoneNumber(title);
+ }
+
+ LLStringUtil::format_map_t args;
+ args["[NAME]"] = title;
+ title = getString("title_peer_2_peer", args);
+ }
+ break;
+ case VC_AD_HOC_CHAT:
+ title = getString("title_adhoc");
+ break;
+ case VC_GROUP_CHAT:
+ {
+ LLStringUtil::format_map_t args;
+ args["[GROUP]"] = voice_channel->getSessionName();
+ title = getString("title_group", args);
+ }
+ break;
+ }
+
+ setTitle(title);
+}
+
+void LLCallFloater::initAgentData()
+{
+ mAgentPanel = getChild<LLPanel> ("my_panel");
+
+ if ( mAgentPanel )
+ {
+ mAgentPanel->getChild<LLUICtrl>("user_icon")->setValue(gAgentID);
+
+ std::string name;
+ gCacheName->getFullName(gAgentID, name);
+ mAgentPanel->getChild<LLUICtrl>("user_text")->setValue(name);
+
+ mSpeakingIndicator = mAgentPanel->getChild<LLOutputMonitorCtrl>("speaking_indicator");
+ mSpeakingIndicator->setSpeakerId(gAgentID);
+ }
+}
+
+void LLCallFloater::setModeratorMutedVoice(bool moderator_muted)
+{
+ mIsModeratorMutedVoice = moderator_muted;
+
+ if (moderator_muted)
+ {
+ LLNotificationsUtil::add("VoiceIsMutedByModerator");
+ }
+ mSpeakingIndicator->setIsMuted(moderator_muted);
+}
+
+void LLCallFloater::updateAgentModeratorState()
+{
+ std::string name;
+ gCacheName->getFullName(gAgentID, name);
+
+ if(gAgent.isInGroup(mSpeakerManager->getSessionID()))
+ {
+ // This method can be called when LLVoiceChannel.mState == STATE_NO_CHANNEL_INFO
+ // in this case there are not any speakers yet.
+ if (mSpeakerManager->findSpeaker(gAgentID))
+ {
+ // Agent is Moderator
+ if (mSpeakerManager->findSpeaker(gAgentID)->mIsModerator)
+
+ {
+ const std::string moderator_indicator(LLTrans::getString("IM_moderator_label"));
+ name += " " + moderator_indicator;
+ }
+ }
+ }
+ mAgentPanel->getChild<LLUICtrl>("user_text")->setValue(name);
+}
+
+static void get_voice_participants_uuids(uuid_vec_t& speakers_uuids)
+{
+ // Get a list of participants from VoiceClient
+ std::set<LLUUID> participants;
+ LLVoiceClient::getInstance()->getParticipantList(participants);
+
+ for (std::set<LLUUID>::const_iterator iter = participants.begin();
+ iter != participants.end(); ++iter)
+ {
+ speakers_uuids.push_back(*iter);
+ }
+
+}
+
+void LLCallFloater::initParticipantsVoiceState()
+{
+ // Set initial status for each participant in the list.
+ std::vector<LLPanel*> items;
+ mAvatarList->getItems(items);
+ std::vector<LLPanel*>::const_iterator
+ it = items.begin(),
+ it_end = items.end();
+
+
+ uuid_vec_t speakers_uuids;
+ get_voice_participants_uuids(speakers_uuids);
+
+ for(; it != it_end; ++it)
+ {
+ LLAvatarListItem *item = dynamic_cast<LLAvatarListItem*>(*it);
+
+ if (!item) continue;
+
+ LLUUID speaker_id = item->getAvatarId();
+
+ uuid_vec_t::const_iterator speaker_iter = std::find(speakers_uuids.begin(), speakers_uuids.end(), speaker_id);
+
+ // If an avatarID assigned to a panel is found in a speakers list
+ // obtained from VoiceClient we assign the JOINED status to the owner
+ // of this avatarID.
+ if (speaker_iter != speakers_uuids.end())
+ {
+ setState(item, STATE_JOINED);
+ }
+ else
+ {
+ LLPointer<LLSpeaker> speakerp = mSpeakerManager->findSpeaker(speaker_id);
+ // If someone has already left the call before, we create his
+ // avatar row panel with HAS_LEFT status and remove it after
+ // the timeout, otherwise we create a panel with INVITED status
+ if (speakerp.notNull() && speakerp.get()->mHasLeftCurrentCall)
+ {
+ setState(item, STATE_LEFT);
+ }
+ else
+ {
+ setState(item, STATE_INVITED);
+ }
+ }
+ }
+}
+
+void LLCallFloater::updateParticipantsVoiceState()
+{
+ uuid_vec_t speakers_list;
+
+ // Get a list of participants from VoiceClient
+ uuid_vec_t speakers_uuids;
+ get_voice_participants_uuids(speakers_uuids);
+
+ // Updating the status for each participant already in list.
+ std::vector<LLPanel*> items;
+ mAvatarList->getItems(items);
+ std::vector<LLPanel*>::const_iterator
+ it = items.begin(),
+ it_end = items.end();
+
+ for(; it != it_end; ++it)
+ {
+ LLAvatarListItem *item = dynamic_cast<LLAvatarListItem*>(*it);
+ if (!item) continue;
+
+ const LLUUID participant_id = item->getAvatarId();
+ bool found = false;
+
+ uuid_vec_t::iterator speakers_iter = std::find(speakers_uuids.begin(), speakers_uuids.end(), participant_id);
+
+ LL_DEBUGS("Voice") << "processing speaker: " << item->getAvatarName() << ", " << item->getAvatarId() << LL_ENDL;
+
+ // If an avatarID assigned to a panel is found in a speakers list
+ // obtained from VoiceClient we assign the JOINED status to the owner
+ // of this avatarID.
+ if (speakers_iter != speakers_uuids.end())
+ {
+ setState(item, STATE_JOINED);
+
+ LLPointer<LLSpeaker> speaker = mSpeakerManager->findSpeaker(participant_id);
+ if (speaker.isNull())
+ continue;
+ speaker->mHasLeftCurrentCall = FALSE;
+
+ speakers_uuids.erase(speakers_iter);
+ found = true;
+ }
+
+ if (!found)
+ {
+ updateNotInVoiceParticipantState(item);
+ }
+ }
+}
+
+void LLCallFloater::updateNotInVoiceParticipantState(LLAvatarListItem* item)
+{
+ LLUUID participant_id = item->getAvatarId();
+ ESpeakerState current_state = getState(participant_id);
+
+ switch (current_state)
+ {
+ case STATE_JOINED:
+ // If an avatarID is not found in a speakers list from VoiceClient and
+ // a panel with this ID has a JOINED status this means that this person
+ // HAS LEFT the call.
+ setState(item, STATE_LEFT);
+
+ {
+ LLPointer<LLSpeaker> speaker = mSpeakerManager->findSpeaker(participant_id);
+ if (speaker.notNull())
+ {
+ speaker->mHasLeftCurrentCall = TRUE;
+ }
+ }
+ break;
+ case STATE_LEFT:
+ // nothing to do. These states should not be changed.
+ break;
+ case STATE_INVITED:
+ // If avatar was invited into group chat and went offline it is still exists in mSpeakerStateMap
+ // If it goes online it will be rendered as JOINED via LAvatarListItem.
+ // Lets update its visual representation. See EXT-6660
+ case STATE_UNKNOWN:
+ // If an avatarID is not found in a speakers list from VoiceClient and
+ // a panel with this ID has an UNKNOWN status this means that this person
+ // HAS ENTERED session but it is not in voice chat yet. So, set INVITED status
+ setState(item, STATE_INVITED);
+ break;
+ default:
+ // for possible new future states.
+ llwarns << "Unsupported (" << getState(participant_id) << ") state for: " << item->getAvatarName() << llendl;
+ break;
+ }
+}
+
+void LLCallFloater::setState(LLAvatarListItem* item, ESpeakerState state)
+{
+ // *HACK: mantipov: sometimes such situation is possible while switching to voice channel:
+/*
+ - voice channel is switched to the one user is joining
+ - participant list is initialized with voice states: agent is in voice
+ - than such log messages were found (with agent UUID)
+ - LLVivoxProtocolParser::process_impl: parsing: <Response requestId="22" action="Session.MediaDisconnect.1"><ReturnCode>0</ReturnCode><Results><StatusCode>0</StatusCode><StatusString /></Results><InputXml><Request requestId="22" action="Session.MediaDisconnect.1"><SessionGroupHandle>9</SessionGroupHandle><SessionHandle>12</SessionHandle><Media>Audio</Media></Request></InputXml></Response>
+ - LLVoiceClient::sessionState::removeParticipant: participant "sip:x2pwNkMbpR_mK4rtB_awASA==@bhr.vivox.com" (da9c0d90-c6e9-47f9-8ae2-bb41fdac0048) removed.
+ - and than while updating participants voice states agent is marked as HAS LEFT
+ - next updating of LLVoiceClient state makes agent JOINED
+ So, lets skip HAS LEFT state for agent's avatar
+*/
+ if (STATE_LEFT == state && item->getAvatarId() == gAgentID) return;
+
+ setState(item->getAvatarId(), state);
+
+ switch (state)
+ {
+ case STATE_INVITED:
+ item->setState(LLAvatarListItem::IS_VOICE_INVITED);
+ break;
+ case STATE_JOINED:
+ removeVoiceRemoveTimer(item->getAvatarId());
+ item->setState(LLAvatarListItem::IS_VOICE_JOINED);
+ break;
+ case STATE_LEFT:
+ {
+ setVoiceRemoveTimer(item->getAvatarId());
+ item->setState(LLAvatarListItem::IS_VOICE_LEFT);
+ }
+ break;
+ default:
+ llwarns << "Unrecognized avatar panel state (" << state << ")" << llendl;
+ break;
+ }
+}
+
+void LLCallFloater::setVoiceRemoveTimer(const LLUUID& voice_speaker_id)
+{
+ mSpeakerDelayRemover->setActionTimer(voice_speaker_id);
+}
+
+bool LLCallFloater::removeVoiceLeftParticipant(const LLUUID& voice_speaker_id)
+{
+ uuid_vec_t& speaker_uuids = mAvatarList->getIDs();
+ uuid_vec_t::iterator pos = std::find(speaker_uuids.begin(), speaker_uuids.end(), voice_speaker_id);
+ if(pos != speaker_uuids.end())
+ {
+ speaker_uuids.erase(pos);
+ mAvatarList->setDirty();
+ }
+
+ return false;
+}
+
+
+void LLCallFloater::resetVoiceRemoveTimers()
+{
+ mSpeakerDelayRemover->removeAllTimers();
+}
+
+void LLCallFloater::removeVoiceRemoveTimer(const LLUUID& voice_speaker_id)
+{
+ mSpeakerDelayRemover->unsetActionTimer(voice_speaker_id);
+}
+
+bool LLCallFloater::validateSpeaker(const LLUUID& speaker_id)
+{
+ bool is_valid = true;
+ switch (mVoiceType)
+ {
+ case VC_LOCAL_CHAT:
+ {
+ // A nearby chat speaker is considered valid it it's known to LLVoiceClient (i.e. has enabled voice).
+ uuid_vec_t speakers;
+ get_voice_participants_uuids(speakers);
+ is_valid = std::find(speakers.begin(), speakers.end(), speaker_id) != speakers.end();
+ }
+ break;
+ case VC_GROUP_CHAT:
+ // if participant had left this call before do not allow add her again. See EXT-4216.
+ // but if she Join she will be added into the list from the LLCallFloater::onChange()
+ is_valid = STATE_LEFT != getState(speaker_id);
+ break;
+ default:
+ // do nothing. required for Linux build
+ break;
+ }
+
+ return is_valid;
+}
+
+void LLCallFloater::connectToChannel(LLVoiceChannel* channel)
+{
+ mVoiceChannelStateChangeConnection.disconnect();
+
+ sCurrentVoiceChannel = channel;
+
+ mVoiceChannelStateChangeConnection = sCurrentVoiceChannel->setStateChangedCallback(boost::bind(&LLCallFloater::onVoiceChannelStateChanged, this, _1, _2));
+
+ updateState(channel->getState());
+}
+
+void LLCallFloater::onVoiceChannelStateChanged(const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state)
+{
+ // check is voice operational and if it doesn't work hide VCP (EXT-4397)
+ if(LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking())
+ {
+ updateState(new_state);
+ }
+ else
+ {
+ closeFloater();
+ }
+}
+
+void LLCallFloater::updateState(const LLVoiceChannel::EState& new_state)
+{
+ LL_DEBUGS("Voice") << "Updating state: " << new_state << ", session name: " << sCurrentVoiceChannel->getSessionName() << LL_ENDL;
+ if (LLVoiceChannel::STATE_CONNECTED == new_state)
+ {
+ updateSession();
+ }
+ else
+ {
+ reset(new_state);
+ }
+}
+
+void LLCallFloater::reset(const LLVoiceChannel::EState& new_state)
+{
+ // lets forget states from the previous session
+ // for timers...
+ resetVoiceRemoveTimers();
+
+ // ...and for speaker state
+ mSpeakerStateMap.clear();
+
+ delete mParticipants;
+ mParticipants = NULL;
+ mAvatarList->clear();
+
+ // These ifs were added instead of simply showing "loading" to make VCP work correctly in parcels
+ // with disabled voice (EXT-4648 and EXT-4649)
+ if (!LLViewerParcelMgr::getInstance()->allowAgentVoice() && LLVoiceChannel::STATE_HUNG_UP == new_state)
+ {
+ // hides "Leave Call" when call is ended in parcel with disabled voice- hiding usually happens in
+ // updateSession() which won't be called here because connect to nearby voice never happens
+ getChildView("leave_call_btn_panel")->setVisible( false);
+ // setting title to nearby chat an "no one near..." text- because in region with disabled
+ // voice we won't have chance to really connect to nearby, so VCP is changed here manually
+ setTitle(getString("title_nearby"));
+ mAvatarList->setNoItemsCommentText(getString("no_one_near"));
+ }
+ // "loading" is shown only when state is "ringing" to avoid showing it in nearby chat vcp
+ // of parcels with disabled voice all the time- "no_one_near" is now shown there (EXT-4648)
+ else if (new_state == LLVoiceChannel::STATE_RINGING)
+ {
+ // update floater to show Loading while waiting for data.
+ mAvatarList->setNoItemsCommentText(LLTrans::getString("LoadingData"));
+ }
+
+ mAvatarList->setVisible(TRUE);
+ mNonAvatarCaller->setVisible(FALSE);
+
+ mSpeakerManager = NULL;
+}
+
+//EOF