PageRenderTime 50ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

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