PageRenderTime 1256ms CodeModel.GetById 181ms app.highlight 354ms RepoModel.GetById 712ms app.codeStats 1ms

/indra/newview/llcallfloater.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 820 lines | 580 code | 126 blank | 114 comment | 86 complexity | c6f0bf624795e3da3a0cfefc4ba705a8 MD5 | raw file
  1/** 
  2 * @file llcallfloater.cpp
  3 * @author Mike Antipov
  4 * @brief Voice Control Panel in a Voice Chats (P2P, Group, Nearby...).
  5 *
  6 * $LicenseInfo:firstyear=2009&license=viewerlgpl$
  7 * Second Life Viewer Source Code
  8 * Copyright (C) 2010, Linden Research, Inc.
  9 * 
 10 * This library is free software; you can redistribute it and/or
 11 * modify it under the terms of the GNU Lesser General Public
 12 * License as published by the Free Software Foundation;
 13 * version 2.1 of the License only.
 14 * 
 15 * This library is distributed in the hope that it will be useful,
 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 18 * Lesser General Public License for more details.
 19 * 
 20 * You should have received a copy of the GNU Lesser General Public
 21 * License along with this library; if not, write to the Free Software
 22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 23 * 
 24 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 25 * $/LicenseInfo$
 26 */
 27
 28#include "llviewerprecompiledheaders.h"
 29
 30#include "llcallfloater.h"
 31
 32#include "llnotificationsutil.h"
 33#include "lltrans.h"
 34
 35#include "llagent.h"
 36#include "llagentdata.h" // for gAgentID
 37#include "llavatarnamecache.h"
 38#include "llavatariconctrl.h"
 39#include "llavatarlist.h"
 40#include "lldraghandle.h"
 41#include "llimfloater.h"
 42#include "llimview.h"
 43#include "llfloaterreg.h"
 44#include "llparticipantlist.h"
 45#include "llspeakers.h"
 46#include "lltextutil.h"
 47#include "lltransientfloatermgr.h"
 48#include "llviewercontrol.h"
 49#include "llviewerdisplayname.h"
 50#include "llviewerwindow.h"
 51#include "llvoicechannel.h"
 52#include "llviewerparcelmgr.h"
 53#include "llfirstuse.h"
 54
 55static void get_voice_participants_uuids(uuid_vec_t& speakers_uuids);
 56void reshape_floater(LLCallFloater* floater, S32 delta_height);
 57
 58class LLNonAvatarCaller : public LLAvatarListItem
 59{
 60public:
 61	LLNonAvatarCaller() : LLAvatarListItem(false)
 62	{
 63
 64	}
 65	BOOL postBuild()
 66	{
 67		BOOL rv = LLAvatarListItem::postBuild();
 68
 69		if (rv)
 70		{
 71			setOnline(true);
 72			showLastInteractionTime(false);
 73			setShowProfileBtn(false);
 74			setShowInfoBtn(false);
 75			mAvatarIcon->setValue("Avaline_Icon");
 76			mAvatarIcon->setToolTip(std::string(""));
 77		}
 78		return rv;
 79	}
 80
 81	void setName(const std::string& name)
 82	{
 83		const std::string& formatted_phone = LLTextUtil::formatPhoneNumber(name);
 84		LLAvatarListItem::setAvatarName(formatted_phone);
 85		LLAvatarListItem::setAvatarToolTip(formatted_phone);
 86	}
 87
 88	void setSpeakerId(const LLUUID& id) { mSpeakingIndicator->setSpeakerId(id); }
 89};
 90
 91
 92static void* create_non_avatar_caller(void*)
 93{
 94	return new LLNonAvatarCaller;
 95}
 96
 97LLVoiceChannel* LLCallFloater::sCurrentVoiceChannel = NULL;
 98
 99LLCallFloater::LLCallFloater(const LLSD& key)
100: LLTransientDockableFloater(NULL, false, key)
101, mSpeakerManager(NULL)
102, mParticipants(NULL)
103, mAvatarList(NULL)
104, mNonAvatarCaller(NULL)
105, mVoiceType(VC_LOCAL_CHAT)
106, mAgentPanel(NULL)
107, mSpeakingIndicator(NULL)
108, mIsModeratorMutedVoice(false)
109, mInitParticipantsVoiceState(false)
110{
111	static LLUICachedControl<S32> voice_left_remove_delay ("VoiceParticipantLeftRemoveDelay", 10);
112	mSpeakerDelayRemover = new LLSpeakersDelayActionsStorage(boost::bind(&LLCallFloater::removeVoiceLeftParticipant, this, _1), voice_left_remove_delay);
113
114	mFactoryMap["non_avatar_caller"] = LLCallbackMap(create_non_avatar_caller, NULL);
115	LLVoiceClient::instance().addObserver(this);
116	LLTransientFloaterMgr::getInstance()->addControlView(this);
117
118	// update the agent's name if display name setting change
119	LLAvatarNameCache::addUseDisplayNamesCallback(boost::bind(&LLCallFloater::updateAgentModeratorState, this));
120	LLViewerDisplayName::addNameChangedCallback(boost::bind(&LLCallFloater::updateAgentModeratorState, this));
121
122}
123
124LLCallFloater::~LLCallFloater()
125{
126	resetVoiceRemoveTimers();
127	delete mSpeakerDelayRemover;
128
129	delete mParticipants;
130	mParticipants = NULL;
131
132	mAvatarListRefreshConnection.disconnect();
133	mVoiceChannelStateChangeConnection.disconnect();
134
135	if(LLVoiceClient::instanceExists())
136	{
137		LLVoiceClient::getInstance()->removeObserver(this);
138	}
139	LLTransientFloaterMgr::getInstance()->removeControlView(this);
140}
141
142// virtual
143BOOL LLCallFloater::postBuild()
144{
145	mAvatarList = getChild<LLAvatarList>("speakers_list");
146	mAvatarListRefreshConnection = mAvatarList->setRefreshCompleteCallback(boost::bind(&LLCallFloater::onAvatarListRefreshed, this));
147
148	childSetAction("leave_call_btn", boost::bind(&LLCallFloater::leaveCall, this));
149
150	mNonAvatarCaller = findChild<LLNonAvatarCaller>("non_avatar_caller");
151	mNonAvatarCaller->setVisible(FALSE);
152
153	initAgentData();
154
155	connectToChannel(LLVoiceChannel::getCurrentVoiceChannel());
156
157	updateTransparency(TT_ACTIVE); // force using active floater transparency (STORM-730)
158	
159	updateSession();
160	return TRUE;
161}
162
163// virtual
164void LLCallFloater::onOpen(const LLSD& /*key*/)
165{
166	LLFirstUse::speak(false);
167}
168
169// virtual
170void LLCallFloater::draw()
171{
172	// we have to refresh participants to display ones not in voice as disabled.
173	// It should be done only when she joins or leaves voice chat.
174	// But seems that LLVoiceClientParticipantObserver is not enough to satisfy this requirement.
175	// *TODO: mantipov: remove from draw()
176
177	// NOTE: it looks like calling onChange() here is not necessary,
178	// but sometime it is not called properly from the observable object.
179	// Seems this is a problem somewhere in Voice Client (LLVoiceClient::participantAddedEvent)
180//	onChange();
181
182	bool is_moderator_muted = LLVoiceClient::getInstance()->getIsModeratorMuted(gAgentID);
183
184	if (mIsModeratorMutedVoice != is_moderator_muted)
185	{
186		setModeratorMutedVoice(is_moderator_muted);
187	}
188
189	// Need to resort the participant list if it's in sort by recent speaker order.
190	if (mParticipants)
191		mParticipants->updateRecentSpeakersOrder();
192
193	LLFloater::draw();
194}
195
196// virtual
197void LLCallFloater::setFocus( BOOL b )
198{
199	LLFloater::setFocus(b);
200
201	// Force using active floater transparency (STORM-730).
202	// We have to override setFocus() for LLCallFloater because selecting an item
203	// of the voice morphing combobox causes the floater to lose focus and thus become transparent.
204	updateTransparency(TT_ACTIVE);
205}
206
207// virtual
208void LLCallFloater::onParticipantsChanged()
209{
210	if (NULL == mParticipants) return;
211	updateParticipantsVoiceState();
212
213	// Add newly joined participants.
214	uuid_vec_t speakers_uuids;
215	get_voice_participants_uuids(speakers_uuids);
216	for (uuid_vec_t::const_iterator it = speakers_uuids.begin(); it != speakers_uuids.end(); it++)
217	{
218		mParticipants->addAvatarIDExceptAgent(*it);
219	}
220}
221
222//////////////////////////////////////////////////////////////////////////
223/// PRIVATE SECTION
224//////////////////////////////////////////////////////////////////////////
225
226void LLCallFloater::leaveCall()
227{
228	LLVoiceChannel* voice_channel = LLVoiceChannel::getCurrentVoiceChannel();
229	if (voice_channel)
230	{
231		gIMMgr->endCall(voice_channel->getSessionID());
232	}
233}
234
235void LLCallFloater::updateSession()
236{
237	LLVoiceChannel* voice_channel = LLVoiceChannel::getCurrentVoiceChannel();
238	if (voice_channel)
239	{
240		LL_DEBUGS("Voice") << "Current voice channel: " << voice_channel->getSessionID() << LL_ENDL;
241
242		if (mSpeakerManager && voice_channel->getSessionID() == mSpeakerManager->getSessionID())
243		{
244			LL_DEBUGS("Voice") << "Speaker manager is already set for session: " << voice_channel->getSessionID() << LL_ENDL;
245			return;
246		}
247		else
248		{
249			mSpeakerManager = NULL;
250		}
251	}
252
253	const LLUUID& session_id = voice_channel ? voice_channel->getSessionID() : LLUUID::null;
254
255	LLIMModel::LLIMSession* im_session = LLIMModel::getInstance()->findIMSession(session_id);
256	if (im_session)
257	{
258		mSpeakerManager = LLIMModel::getInstance()->getSpeakerManager(session_id);
259		switch (im_session->mType)
260		{
261		case IM_NOTHING_SPECIAL:
262		case IM_SESSION_P2P_INVITE:
263			mVoiceType = VC_PEER_TO_PEER;
264
265			if (!im_session->mOtherParticipantIsAvatar)
266			{
267				mVoiceType = VC_PEER_TO_PEER_AVALINE;
268			}
269			break;
270		case IM_SESSION_CONFERENCE_START:
271		case IM_SESSION_GROUP_START:
272		case IM_SESSION_INVITE:
273			if (gAgent.isInGroup(session_id))
274			{
275				mVoiceType = VC_GROUP_CHAT;
276			}
277			else
278			{
279				mVoiceType = VC_AD_HOC_CHAT;				
280			}
281			break;
282		default:
283			llwarning("Failed to determine voice call IM type", 0);
284			mVoiceType = VC_GROUP_CHAT;
285			break;
286		}
287	}
288
289	if (NULL == mSpeakerManager)
290	{
291		// By default show nearby chat participants
292		mSpeakerManager = LLLocalSpeakerMgr::getInstance();
293		LL_DEBUGS("Voice") << "Set DEFAULT speaker manager" << LL_ENDL;
294		mVoiceType = VC_LOCAL_CHAT;
295	}
296
297	updateTitle();
298
299	// Hide "Leave Call" button for nearby chat
300	bool is_local_chat = mVoiceType == VC_LOCAL_CHAT;
301	getChildView("leave_call_btn_panel")->setVisible( !is_local_chat);
302
303	refreshParticipantList();
304	updateAgentModeratorState();
305
306	// Show floater for voice calls & only in CONNECTED to voice channel state
307	if (!is_local_chat &&
308	    voice_channel &&
309	    LLVoiceChannel::STATE_CONNECTED == voice_channel->getState())
310	{
311		LLIMFloater* im_floater = LLIMFloater::findInstance(session_id);
312		bool show_me = !(im_floater && im_floater->getVisible());
313		if (show_me) 
314		{
315			setVisible(true);
316		}
317	}
318}
319
320void LLCallFloater::refreshParticipantList()
321{
322	bool non_avatar_caller = VC_PEER_TO_PEER_AVALINE == mVoiceType;
323
324	if (non_avatar_caller)
325	{
326		LLIMModel::LLIMSession* session = LLIMModel::instance().findIMSession(mSpeakerManager->getSessionID());
327		mNonAvatarCaller->setSpeakerId(session->mOtherParticipantID);
328		mNonAvatarCaller->setName(session->mName);
329	}
330
331	mNonAvatarCaller->setVisible(non_avatar_caller);
332	mAvatarList->setVisible(!non_avatar_caller);
333
334	if (!non_avatar_caller)
335	{
336		mParticipants = new LLParticipantList(mSpeakerManager, mAvatarList, true, mVoiceType != VC_GROUP_CHAT && mVoiceType != VC_AD_HOC_CHAT, false);
337		mParticipants->setValidateSpeakerCallback(boost::bind(&LLCallFloater::validateSpeaker, this, _1));
338		const U32 speaker_sort_order = gSavedSettings.getU32("SpeakerParticipantDefaultOrder");
339		mParticipants->setSortOrder(LLParticipantList::EParticipantSortOrder(speaker_sort_order));
340		
341		if (LLLocalSpeakerMgr::getInstance() == mSpeakerManager)
342		{
343			mAvatarList->setNoItemsCommentText(getString("no_one_near"));
344		}
345
346		// we have to made delayed initialization of voice state of participant list.
347		// it will be performed after first LLAvatarList refreshing in the onAvatarListRefreshed().
348		mInitParticipantsVoiceState = true;
349	}
350}
351
352void LLCallFloater::onAvatarListRefreshed()
353{
354	if (mInitParticipantsVoiceState)
355	{
356		initParticipantsVoiceState();
357		mInitParticipantsVoiceState = false;
358	}
359	else
360	{
361		updateParticipantsVoiceState();
362	}
363}
364
365// static
366void LLCallFloater::sOnCurrentChannelChanged(const LLUUID& /*session_id*/)
367{
368	LLVoiceChannel* channel = LLVoiceChannel::getCurrentVoiceChannel();
369
370	// *NOTE: if signal was sent for voice channel with LLVoiceChannel::STATE_NO_CHANNEL_INFO
371	// it sill be sent for the same channel again (when state is changed).
372	// So, lets ignore this call.
373	if (channel == sCurrentVoiceChannel) return;
374
375	LLCallFloater* call_floater = LLFloaterReg::getTypedInstance<LLCallFloater>("voice_controls");
376
377	call_floater->connectToChannel(channel);
378}
379
380void LLCallFloater::onAvatarNameCache(const LLUUID& agent_id,
381									  const LLAvatarName& av_name)
382{
383	LLStringUtil::format_map_t args;
384	args["[NAME]"] = av_name.getCompleteName();
385	std::string title = getString("title_peer_2_peer", args);
386	setTitle(title);
387}
388
389void LLCallFloater::updateTitle()
390{
391	LLVoiceChannel* voice_channel = LLVoiceChannel::getCurrentVoiceChannel();
392	if (mVoiceType == VC_PEER_TO_PEER)
393	{
394		LLUUID session_id = voice_channel->getSessionID();
395		LLIMModel::LLIMSession* im_session =
396			LLIMModel::getInstance()->findIMSession(session_id);
397		if (im_session)
398		{
399			LLAvatarNameCache::get(im_session->mOtherParticipantID,
400				boost::bind(&LLCallFloater::onAvatarNameCache,
401					this, _1, _2));
402			return;
403		}
404	}
405	std::string title;
406	switch (mVoiceType)
407	{
408	case VC_LOCAL_CHAT:
409		title = getString("title_nearby");
410		break;
411	case VC_PEER_TO_PEER:
412	case VC_PEER_TO_PEER_AVALINE:
413		{
414			title = voice_channel->getSessionName();
415
416			if (VC_PEER_TO_PEER_AVALINE == mVoiceType)
417			{
418				title = LLTextUtil::formatPhoneNumber(title);
419			}
420
421			LLStringUtil::format_map_t args;
422			args["[NAME]"] = title;
423			title = getString("title_peer_2_peer", args);
424		}
425		break;
426	case VC_AD_HOC_CHAT:
427		title = getString("title_adhoc");
428		break;
429	case VC_GROUP_CHAT:
430		{
431			LLStringUtil::format_map_t args;
432			args["[GROUP]"] = voice_channel->getSessionName();
433			title = getString("title_group", args);
434		}
435		break;
436	}
437
438	setTitle(title);
439}
440
441void LLCallFloater::initAgentData()
442{
443	mAgentPanel = getChild<LLPanel> ("my_panel");
444
445	if ( mAgentPanel )
446	{
447		mAgentPanel->getChild<LLUICtrl>("user_icon")->setValue(gAgentID);
448
449		// Just use display name, because it's you
450		LLAvatarName av_name;
451		LLAvatarNameCache::get( gAgentID, &av_name );
452		mAgentPanel->getChild<LLUICtrl>("user_text")->setValue(av_name.mDisplayName);
453
454		mSpeakingIndicator = mAgentPanel->getChild<LLOutputMonitorCtrl>("speaking_indicator");
455		mSpeakingIndicator->setSpeakerId(gAgentID);
456	}
457}
458
459void LLCallFloater::setModeratorMutedVoice(bool moderator_muted)
460{
461	mIsModeratorMutedVoice = moderator_muted;
462
463	if (moderator_muted)
464	{
465		LLNotificationsUtil::add("VoiceIsMutedByModerator");
466	}
467	mSpeakingIndicator->setIsMuted(moderator_muted);
468}
469
470void LLCallFloater::onModeratorNameCache(const LLAvatarName& av_name)
471{
472	std::string name;
473	name = av_name.mDisplayName;
474
475	if(mSpeakerManager && gAgent.isInGroup(mSpeakerManager->getSessionID()))
476	{
477		// This method can be called when LLVoiceChannel.mState == STATE_NO_CHANNEL_INFO
478		// in this case there are not any speakers yet.
479		if (mSpeakerManager->findSpeaker(gAgentID))
480		{
481			// Agent is Moderator
482			if (mSpeakerManager->findSpeaker(gAgentID)->mIsModerator)
483
484			{
485				const std::string moderator_indicator(LLTrans::getString("IM_moderator_label")); 
486				name += " " + moderator_indicator;
487			}
488		}
489	}
490	mAgentPanel->getChild<LLUICtrl>("user_text")->setValue(name);
491}
492
493void LLCallFloater::updateAgentModeratorState()
494{
495	LLAvatarNameCache::get(gAgentID, boost::bind(&LLCallFloater::onModeratorNameCache, this, _2));
496}
497
498static void get_voice_participants_uuids(uuid_vec_t& speakers_uuids)
499{
500	// Get a list of participants from VoiceClient
501       std::set<LLUUID> participants;
502       LLVoiceClient::getInstance()->getParticipantList(participants);
503	
504	for (std::set<LLUUID>::const_iterator iter = participants.begin();
505		 iter != participants.end(); ++iter)
506	{
507		speakers_uuids.push_back(*iter);
508	}
509
510}
511
512void LLCallFloater::initParticipantsVoiceState()
513{
514	// Set initial status for each participant in the list.
515	std::vector<LLPanel*> items;
516	mAvatarList->getItems(items);
517	std::vector<LLPanel*>::const_iterator
518		it = items.begin(),
519		it_end = items.end();
520
521
522	uuid_vec_t speakers_uuids;
523	get_voice_participants_uuids(speakers_uuids);
524
525	for(; it != it_end; ++it)
526	{
527		LLAvatarListItem *item = dynamic_cast<LLAvatarListItem*>(*it);
528		
529		if (!item)	continue;
530		
531		LLUUID speaker_id = item->getAvatarId();
532
533		uuid_vec_t::const_iterator speaker_iter = std::find(speakers_uuids.begin(), speakers_uuids.end(), speaker_id);
534
535		// If an avatarID assigned to a panel is found in a speakers list
536		// obtained from VoiceClient we assign the JOINED status to the owner
537		// of this avatarID.
538		if (speaker_iter != speakers_uuids.end())
539		{
540			setState(item, STATE_JOINED);
541		}
542		else
543		{
544			LLPointer<LLSpeaker> speakerp = mSpeakerManager->findSpeaker(speaker_id);
545			// If someone has already left the call before, we create his
546			// avatar row panel with HAS_LEFT status and remove it after
547			// the timeout, otherwise we create a panel with INVITED status
548			if (speakerp.notNull() && speakerp.get()->mHasLeftCurrentCall)
549			{
550				setState(item, STATE_LEFT);
551			}
552			else
553			{
554				setState(item, STATE_INVITED);
555			}
556		}
557	}
558}
559
560void LLCallFloater::updateParticipantsVoiceState()
561{
562	uuid_vec_t speakers_list;
563
564	// Get a list of participants from VoiceClient
565	uuid_vec_t speakers_uuids;
566	get_voice_participants_uuids(speakers_uuids);
567
568	// Updating the status for each participant already in list.
569	std::vector<LLPanel*> items;
570	mAvatarList->getItems(items);
571	std::vector<LLPanel*>::const_iterator
572		it = items.begin(),
573		it_end = items.end();
574
575	for(; it != it_end; ++it)
576	{
577		LLAvatarListItem *item = dynamic_cast<LLAvatarListItem*>(*it);
578		if (!item) continue;
579
580		const LLUUID participant_id = item->getAvatarId();
581		bool found = false;
582
583		uuid_vec_t::iterator speakers_iter = std::find(speakers_uuids.begin(), speakers_uuids.end(), participant_id);
584
585		LL_DEBUGS("Voice") << "processing speaker: " << item->getAvatarName() << ", " << item->getAvatarId() << LL_ENDL;
586
587		// If an avatarID assigned to a panel is found in a speakers list
588		// obtained from VoiceClient we assign the JOINED status to the owner
589		// of this avatarID.
590		if (speakers_iter != speakers_uuids.end())
591		{
592			setState(item, STATE_JOINED);
593
594			LLPointer<LLSpeaker> speaker = mSpeakerManager->findSpeaker(participant_id);
595			if (speaker.isNull())
596				continue;
597			speaker->mHasLeftCurrentCall = FALSE;
598
599			speakers_uuids.erase(speakers_iter);
600			found = true;
601		}
602
603		if (!found)
604		{
605			updateNotInVoiceParticipantState(item);
606		}
607	}
608}
609
610void LLCallFloater::updateNotInVoiceParticipantState(LLAvatarListItem* item)
611{
612	LLUUID participant_id = item->getAvatarId();
613	ESpeakerState current_state = getState(participant_id);
614
615	switch (current_state)
616	{
617	case STATE_JOINED:
618		// If an avatarID is not found in a speakers list from VoiceClient and
619		// a panel with this ID has a JOINED status this means that this person
620		// HAS LEFT the call.
621		setState(item, STATE_LEFT);
622
623		{
624			LLPointer<LLSpeaker> speaker = mSpeakerManager->findSpeaker(participant_id);
625			if (speaker.notNull())
626			{
627				speaker->mHasLeftCurrentCall = TRUE;
628			}
629		}
630		break;
631	case STATE_LEFT:
632		// nothing to do. These states should not be changed.
633		break;
634	case STATE_INVITED:
635		// If avatar was invited into group chat and went offline it is still exists in mSpeakerStateMap
636		// If it goes online it will be rendered as JOINED via LAvatarListItem.
637		// Lets update its visual representation. See EXT-6660
638	case STATE_UNKNOWN:
639		// If an avatarID is not found in a speakers list from VoiceClient and
640		// a panel with this ID has an UNKNOWN status this means that this person
641		// HAS ENTERED session but it is not in voice chat yet. So, set INVITED status
642		setState(item, STATE_INVITED);
643		break;
644	default:
645		// for possible new future states.
646		llwarns << "Unsupported (" << getState(participant_id) << ") state for: " << item->getAvatarName()  << llendl;
647		break;
648	}
649}
650
651void LLCallFloater::setState(LLAvatarListItem* item, ESpeakerState state)
652{
653	// *HACK: mantipov: sometimes such situation is possible while switching to voice channel:
654/*
655	- voice channel is switched to the one user is joining
656	- participant list is initialized with voice states: agent is in voice
657	- than such log messages were found (with agent UUID)
658			- 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>
659			- LLVoiceClient::sessionState::removeParticipant: participant "sip:x2pwNkMbpR_mK4rtB_awASA==@bhr.vivox.com" (da9c0d90-c6e9-47f9-8ae2-bb41fdac0048) removed.
660	- and than while updating participants voice states agent is marked as HAS LEFT
661	- next updating of LLVoiceClient state makes agent JOINED
662	So, lets skip HAS LEFT state for agent's avatar
663*/
664	if (STATE_LEFT == state && item->getAvatarId() == gAgentID) return;
665
666	setState(item->getAvatarId(), state);
667
668	switch (state)
669	{
670	case STATE_INVITED:
671		item->setState(LLAvatarListItem::IS_VOICE_INVITED);
672		break;
673	case STATE_JOINED:
674		removeVoiceRemoveTimer(item->getAvatarId());
675		item->setState(LLAvatarListItem::IS_VOICE_JOINED);
676		break;
677	case STATE_LEFT:
678		{
679			setVoiceRemoveTimer(item->getAvatarId());
680			item->setState(LLAvatarListItem::IS_VOICE_LEFT);
681		}
682		break;
683	default:
684		llwarns << "Unrecognized avatar panel state (" << state << ")" << llendl;
685		break;
686	}
687}
688
689void LLCallFloater::setVoiceRemoveTimer(const LLUUID& voice_speaker_id)
690{
691	mSpeakerDelayRemover->setActionTimer(voice_speaker_id);
692}
693
694bool LLCallFloater::removeVoiceLeftParticipant(const LLUUID& voice_speaker_id)
695{
696	uuid_vec_t& speaker_uuids = mAvatarList->getIDs();
697	uuid_vec_t::iterator pos = std::find(speaker_uuids.begin(), speaker_uuids.end(), voice_speaker_id);
698	if(pos != speaker_uuids.end())
699	{
700		speaker_uuids.erase(pos);
701		mAvatarList->setDirty();
702	}
703
704	return false;
705}
706
707
708void LLCallFloater::resetVoiceRemoveTimers()
709{
710	mSpeakerDelayRemover->removeAllTimers();
711}
712
713void LLCallFloater::removeVoiceRemoveTimer(const LLUUID& voice_speaker_id)
714{
715	mSpeakerDelayRemover->unsetActionTimer(voice_speaker_id);
716}
717
718bool LLCallFloater::validateSpeaker(const LLUUID& speaker_id)
719{
720	bool is_valid = true;
721	switch (mVoiceType)
722	{
723	case  VC_LOCAL_CHAT:
724		{
725			// A nearby chat speaker is considered valid it it's known to LLVoiceClient (i.e. has enabled voice).
726			uuid_vec_t speakers;
727			get_voice_participants_uuids(speakers);
728			is_valid = std::find(speakers.begin(), speakers.end(), speaker_id) != speakers.end();
729		}
730		break;
731	case VC_GROUP_CHAT:
732		// if participant had left this call before do not allow add her again. See EXT-4216.
733		// but if she Join she will be added into the list from the LLCallFloater::onChange()
734		is_valid = STATE_LEFT != getState(speaker_id);
735		break;
736	default:
737		// do nothing. required for Linux build
738		break;
739	}
740
741	return is_valid;
742}
743
744void LLCallFloater::connectToChannel(LLVoiceChannel* channel)
745{
746	mVoiceChannelStateChangeConnection.disconnect();
747
748	sCurrentVoiceChannel = channel;
749
750	mVoiceChannelStateChangeConnection = sCurrentVoiceChannel->setStateChangedCallback(boost::bind(&LLCallFloater::onVoiceChannelStateChanged, this, _1, _2));
751
752	updateState(channel->getState());
753}
754
755void LLCallFloater::onVoiceChannelStateChanged(const LLVoiceChannel::EState& old_state, const LLVoiceChannel::EState& new_state)
756{
757	// check is voice operational and if it doesn't work hide VCP (EXT-4397)
758	if(LLVoiceClient::getInstance()->voiceEnabled() && LLVoiceClient::getInstance()->isVoiceWorking())
759	{
760		updateState(new_state);
761	}
762	else
763	{
764		closeFloater();
765	}
766}
767
768void LLCallFloater::updateState(const LLVoiceChannel::EState& new_state)
769{
770	LL_DEBUGS("Voice") << "Updating state: " << new_state << ", session name: " << sCurrentVoiceChannel->getSessionName() << LL_ENDL;
771	if (LLVoiceChannel::STATE_CONNECTED == new_state)
772	{
773		updateSession();
774	}
775	else
776	{
777		reset(new_state);
778	}
779}
780
781void LLCallFloater::reset(const LLVoiceChannel::EState& new_state)
782{
783	// lets forget states from the previous session
784	// for timers...
785	resetVoiceRemoveTimers();
786
787	// ...and for speaker state
788	mSpeakerStateMap.clear();
789
790	delete mParticipants;
791	mParticipants = NULL;
792	mAvatarList->clear();
793
794	// These ifs were added instead of simply showing "loading" to make VCP work correctly in parcels
795	// with disabled voice (EXT-4648 and EXT-4649)
796	if (!LLViewerParcelMgr::getInstance()->allowAgentVoice() && LLVoiceChannel::STATE_HUNG_UP == new_state)
797	{
798		// hides "Leave Call" when call is ended in parcel with disabled voice- hiding usually happens in
799		// updateSession() which won't be called here because connect to nearby voice never happens 
800		getChildView("leave_call_btn_panel")->setVisible( false);
801		// setting title to nearby chat an "no one near..." text- because in region with disabled
802		// voice we won't have chance to really connect to nearby, so VCP is changed here manually
803		setTitle(getString("title_nearby"));
804		mAvatarList->setNoItemsCommentText(getString("no_one_near"));
805	}
806	// "loading" is shown  only when state is "ringing" to avoid showing it in nearby chat vcp
807	// of parcels with disabled voice all the time- "no_one_near" is now shown there (EXT-4648)
808	else if (new_state == LLVoiceChannel::STATE_RINGING)
809	{
810		// update floater to show Loading while waiting for data.
811		mAvatarList->setNoItemsCommentText(LLTrans::getString("LoadingData"));
812	}
813
814	mAvatarList->setVisible(TRUE);
815	mNonAvatarCaller->setVisible(FALSE);
816
817	mSpeakerManager = NULL;
818}
819
820//EOF